diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog index 0762e4e6b925..d012520dc2bb 100644 --- a/contrib/bmake/ChangeLog +++ b/contrib/bmake/ChangeLog @@ -1,3987 +1,4116 @@ +2023-01-26 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20230126 + Merge with NetBSD make, pick up + o variables like .newline and .MAKE.{GID,PID,PPID,UID} + should be read-only. + +2023-01-23 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20230123 + Merge with NetBSD make, pick up + o .[NO]READONLY: for control of read-only variables + o .SYSPATH: for controlling the path searched for makefiles + +2023-01-20 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20230120 + Merge with NetBSD make, pick up + o allow for white-space between command specifiers @+- + o add more details to warning 'Extra targets ignored' + +2023-01-12 Simon J Gerraty + + * machine.sh: leverage os.sh rather than duplicate + also dispence with the $OS.$MACHINE values - we have $HOST_TARGET + for that purpose for the past decade or so. + We invariably get MACHINE and MACHINE_ARCH at runtime anyway. + +2023-01-02 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20230101 + Merge with NetBSD make, pick up + o cleanup comments, inline some LazyBuf_ methods + o unit-tests/ add/improve comments in tests + o make.1: sync list of built-in variables with reality + sort list of built-in variables + reduce indentation of the long list of variable names + use consistent markup for boolean flags + move description of .MAKE.MODE below the .MAKE.META block + clarify in which case an expression may omit braces + +2022-11-08 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20221024 + Merge with NetBSD make, pick up + o change return type of unlink_file back to int + +2022-10-07 Simon J Gerraty + + * Makefile: Darwin and Linux can handle MANTARGET=man + +2022-09-28 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220928 + Merge with NetBSD make, pick up + o fix more ignored returns from snprintf + o compile with higher warnings + +2022-09-26 Simon J Gerraty + + * main.c meta.c: do not ignore return from snprintf + + * meta.c strlcpy.c: we need prototype for strlcpy + + * sigcompat.c: fix unused function warnings + +2022-09-24 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220924 + Merge with NetBSD make, pick up + o fix bug in .break reset of conditional depth + o overhaul and simplify tracking of conditional depth + +2022-09-17 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220912 + Merge with NetBSD make, pick up + o man page updates + +2022-09-09 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220909 + Merge with NetBSD make, pick up + o update unit-tests to handle deprecation of egrep + o cond.c: add more details to error message for numeric comparison + + * configure.in: allow for deprecation of egrep + + * Makefile: Linux can handle MANTARGET=man + +2022-09-03 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220903 + Merge with NetBSD make, pick up + o job.c: fix handling of null bytes in output + +2022-09-02 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220902 + Merge with NetBSD make, pick up + o Allow .break to terminate a .for loop early + +2022-09-01 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220901 + Merge with NetBSD make, pick up + o var.c: fix out-of-bounds errors when parsing + +2022-08-24 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220824 + Merge with NetBSD make, pick up + o var.c: revert change to modifier parsing that breaks + shell variable references within ':@var@body@' + o adjust unit-tests + +2022-08-18 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220818 + Merge with NetBSD make, pick up + o fix exit status for '-q' (since 1994) + +2022-08-08 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20220808 + Merge with NetBSD make, pick up + o var.c: fix parsing of modifiers containing unbalanced subexpressions + extract parsing of ':D' and ':U' modifiers into separate function + 2022-07-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220726 * Auto-create objdir for bmake/unit-tests if appropriate 2022-07-24 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220724 Merge with NetBSD make, pick up o make.1: describe variable assignment and evaluation more precisely o parse.c: fix out-of-bounds read when parsing an invalid line o var.c: simplify return type of IsShortVarnameValid 2022-06-12 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220612 Merge with NetBSD make, pick up o allow to randomize build order of targets .MAKE.MODE += randomize-targets can help uncover dependency bugs within a makefile. o compat.c: rename Compat_Run to Compat_MakeAll o make.c: inline MakeBuildParent inline make_abort, improve error details o parse.c: reorganize Parse_Error fix memory leak in wildcard targets and sources separate cases in HandleDependencyTargetMundane extract HandleSingleDependencyTargetMundane rename loadfile to LoadFile split IncludeFile into separate functions condense code for searching a file in the paths fix off-by-one error in buffer for .WAIT nodes o str.c: condense Str_Match make code for string matching syntactically more consistent 2022-04-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220418 Merge with NetBSD make, pick up o ignore '.POSIX:' if not in first non-comment line of Makefile as specified by POSIX. add unit-tests for above. o meta.c: make it easier to find usage of identifiers o targ.c: add .USEBEFORE to Targ_PrintType 2022-04-14 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220414 * unit-tests/Makefile: simplify checks for shells with BROKEN_TESTS, this helps with other Linux distros that use dash. 2022-03-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220330 Merge with NetBSD make, pick up o var.c: fix spacing, and a typo in a test 2022-03-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220326 Merge with NetBSD make, pick up o parse.c: try to include 'posix.mk' the first time .POSIX: is encountered, to allow for beter POSIX compliance. o var.c: make debug logs more readable prefer 'long long' over 'long' on 32-bit C99 platforms fix crash on .undef of an environment variable 2022-03-03 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220303 Merge with NetBSD make, pick up o tell meta mode unit tests not to expect filemon o cond.c: make debug logging for comparisons less technical o lst.c: fix mem leak in Lst_Remove o str.c: make code for string matching syntactically more consistent o var.c: simplify ParseModifier_Match 2022-02-14 Simon J Gerraty * unit-tests/Makefile: control MAKESYSPATH for deptgt-phony * VERSION (_MAKE_VERSION): 20220214 Merge with NetBSD make, pick up o cond.c: simplify control flow in CondParser_Comparison o job.c: fix echoing of command with '-' in silent target in jobs mode o main.c: prefix the warning about read-only .OBJDIR with a colon o parse.c: remove redundant conditions o var.c: simplify control flow in ModifyWord_SysVSubst 2022-02-08 Simon J Gerraty * unit-tests/Makefile: disable opt-debug-x-trace on Linux if there is any chance we have dash as .SHELL * VERSION (_MAKE_VERSION): 20220208 Merge with NetBSD make, pick up o more unit tests o meta.c: use a variable to hold command line to be filtered to avoid any side effects from content of command line. 2022-02-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220204 Merge with NetBSD make, pick up o use unsigned consistently for line numbers, avoid the need for %z o parse.c: do not step off end of input in Parse_IsVar when checking for target local variable assignments 2022-02-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220202 Merge with NetBSD make, pick up o remove redundant declaration of HashIter_Init o make DEBUG0 simpler 2022-01-30 Simon J Gerraty * cast gn->lineno to avoid %z * VERSION (_MAKE_VERSION): 20220130 Merge with NetBSD make, pick up o more unit tests o make GNode lineno unsigned to please lint o print location of recursive variable references in commands o print "stack trace" (makefile includes) on fatal errors o make.1: refine documentation for target local assignments 2022-01-28 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220128 Merge with NetBSD make, pick up o inline functions called only once o for.c: clean up AddEscape for building the body of a .for loop o hash.c: merge duplicate code for finding an entry in a hash table replace HashEntry_KeyEquals with strncmp o make.1: document quirks of target local variable assignments. o parse.c: cleanup white-space 2022-01-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220126 Merge with NetBSD make, pick up o allow setting target local variables o more unit tests o add missing newline after "cannot continue" message o meta.c: clean up eat_dots o parse.c: fix filename in warning about duplicate script o var.c: when expanding nested variables, check simple things first 2022-01-16 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220116 Merge with NetBSD make, pick up o fix for unit-tests/varname-makeflags on non-BSD systems o use Var_Exists rather than Var_Value where appropriate o remove unnecessary functions for expanding variable names o cond.c: inline EvalBare o main.c: lint cleanup o parse.c: condense code in Parse_IsVar use islower for parsing directives (none have upper case) 2022-01-12 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220112 Merge with NetBSD make, pick up o meta.c: add .MAKE.META.CMP_FILTER for filtering commands before comparion, rarely needed but useful when it is. 2022-01-10 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220110 Merge with NetBSD make, pick up o inline Buf_Clear o remove redundant braces o rename and inline Targ_Precious o cond.c: remove redundant initializer in CondParser_ComparisonOrLeaf o for.c: clean up handling of .for loops fix reported line numbers of continuation lines add details about .for loop variables to stack traces o job.c: reduce code for initializing error handling in shell o main.c: in Cmd_Exec, return error message instead of format string have as few statements as possible between va_start and va_end add debug logging for capturing the output of external commands o make.c: use consistent variable names for varargs o make_malloc.c: remove duplicate code from bmake_strdup o parse.c: add missing printflike annotations remove redundant lines from stack traces fix stack traces in -dp mode reduce confusing code in ParseForLoop fix line number in debug log after returning from a file rename IFile and its fields to match their actual content clean up ParseDependencySources o var.c: shorten ApplyModifier_Assign rename is_shell_metachar, fix character conversion warning merge calls to ApplyModifier_Time merge duplicate code for modifiers 'gmtime' and 'localtime' 2022-01-04 Simon J Gerraty * parse.c: loadfile restore extra byte in buffer. 2022-01-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20220101 Merge with NetBSD make, pick up o more unit-tests o remove unnecessary words from command line options in CmdOpts o rename eunlink to unlink_file o cond.c: make ParseWord in condition parser simpler internally return false for irrelevant leaves in conditions replace table for function lookup in conditions with simple code merge duplicate types CondEvalResult and CondResult o for.c: clean up handling of .for loops and .include directives o main.c: constify cached_realpath clean up Cmd_Exec o parse.c: sync API documentation fix error message when reading more than 1 GB from stdin clean up parsing of makefiles fix line number in error message about open conditionals unexport types VarAssignOp and VarAssign clean up function names remove redundant parameters in dependency parsing functions reduce scope of the list of wildcard target names extract OP_NOTARGET into separate function clean up variable names for parsing dependency lines make debug logging a bit more human-friendly o var.c: condense code in ApplyModifier_Assign 2021-12-21 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211221 Merge with NetBSD make, pick up o more unit-tests o style cleanup o in CLEANUP mode, free interned strings at the very end o fix memory leak for filenames in .for loops o buf.c: avoid memory leak o cond.c: condense CondParser_ComparisonOp o hash.c: change return type of HashTable_Set to void o job.c: change return type of Compat_RunCommand from int to bool o main.c: remove bmake_free o parse.c: condense repetetive code in ParseDirective remove dead code for handling traditional include directives clean up parsing of variable assignments remove unreachable code for parsing the dependency operator clean up loading of files fix memory leak in IncludeFile o var.c: fix memory leak when parsing a variable name fix memory leak from ${.SUFFIXES} reduce memory allocation in modifier ':?' and ':C' condense RegexReplace for the modifier ':C' and avoid strlen merge duplicate code for memory handling in Var_Parse distinguish between short-lived and environment variables rename VarFreeEnv to VarFreeShortLived 2021-12-15 Simon J Gerraty * cond.c: fix mem leak in CondParser_Leaf 2021-12-12 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211212 Merge with NetBSD make, pick up o rename Parse_SetInput to Parse_PushInput o remove remove period from end of error messages and warnings to be more consistent o arch.c: use simpler memory management for parsing archive members o cond.c: rework and reduce recursion o for.c: rename some functions to better reflect purpose o suff.c: add Suff_NamesStr to provide .SUFFIXES as a string. o var.c: in parse errors, mark whitespace more clearly inline ParseEmptyArg into CondParser_FuncCallEmpty minimize calls to LazyBuf_Get in ParseVarnameLong treat .SUFFIXES as a read-only variable 2021-12-07 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211207 Merge with NetBSD make, pick up o inline HashIter_Init o parse.c: inline common subexpression in ParseRawLine o var.c: merge branches for modifiers ':D' and ':U' extract common code into Expr_Words extract common code into Expr_Str move low-level implementation details out of Var_Parse 2021-12-06 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211206 Merge with NetBSD make, pick up o add unit-tests/varmod-loop-delete o for.c: inline Str_Words - reduce memory allocation o parse.c: do not try to expand fixed variable names only allocate the name of an included file if necessary clean up ParseInclude o var.c: fix use-after-free in modifier ':@' save a memory allocation in each modifier ':O' and ':u' save a memory allocation in the modifier ':[...]' in UnexportVars, replace Str_Words with Substring_Words to reduce allocations and copying. 2021-12-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211204 Merge with NetBSD make, pick up o flesh out a number of tests o replace enums with bitfields, this simplifies a lot of code. o var.c: refactor ParseModifierPartSubst 2021-10-24 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211024 Merge with NetBSD make, pick up o Punt on write errors - ENOSPC etc. 2021-10-22 Simon J Gerraty * configure.in: use_defshell, set both DEFSHELL_INDEX and defshell_path if appropriate. This makes it easier to use say the KSH specification with and alternate path for the shell. * configure.in compat.c: for SCO we need to force UseShell * configure.in: SCO /bin/sh is not usable, provide a list of alternatives for use as .SHELL. We still have to mark some tests as broken, plus more if we end up with ksh as .SHELL. Issue a warning about skipped tests. * boot-strap: leave TOOL_DIFF to configure * configure.in: on SCO native cc is not usable, gcc is to be found in /usr/gnu/bin and while ancient is at least able to compile bmake. Thus we add /usr/gnu/bin to PATH if it exists, and later check if $CC would have been found via $PATH. If not we set CC to the full path of $CC. Also gnu diff is known to support -u, so if it exists use it. * configure.in: move getopt to AC_REPLACE_FUNCS also add AC_C_INLINE - in an attempt to compile using native cc on SCO. * configure.in: check for stresep as well as strsep, since we define the later to the former if necessary, and if we have to provide stresep we also need to provide a prototype. * configure.in: we no longer need to worry about sys/cdefs.h providing __RCSID which simplifies things quite a bit. * make.h: make sure we have __RCSID * unit-tests/Makefile.config.in: add TOOL_DIFF so configure can control it. 2021-10-20 Simon J Gerraty * VERSION: 20211020 Merge with NetBSD make, pick up o confirm sync of unit-tests 2021-10-18 Simon J Gerraty * configure.in: check if timezone Europe/Berlin is supported if not try UTC-1 * configure.in: if .OBJDIR is $srcdir/obj we need to create a symlink unit-tests -> ../unit-tests/obj so that unit-tests/Makefile.config is put in the right place. * refine filtering of .OBJDIR in unit-tests 2021-10-16 Simon J Gerraty * Fix unit-tests on Minix 3.2.0 o job.c: do not punt if read of token pipe fails for EAGAIN. On Minix at least, we are not ready to read the childExitJob pipe when poll says we are. There should actually be no reason for this pipe to be non-blocking, but while that works fine on {Net,Free}BSD it breaks another test case on Minix. o unit-tests/Makefile: deal with variants of error messages and use of obj as .OBJDIR 2021-10-14 Simon J Gerraty * configure.in: add sigaction to AC_REPLACE_FUNCS we also need to check for sigaddset etc just for the benefit of sigact.c * Add sigact.c as sigaction.c so this "just works". This should have been done back when bmake_signal started using sigaction (I only just noticed that sigact.c wasn't here ;-) Note: I no longer have access to any system where this would matter. 2021-10-13 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211011 * Makefile: cleanup a little * configure.in: check for sigsetmask 2021-10-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20211001 Merge with NetBSD make, pick up o reduce locations reducing text size o remove unnecessary const o cond.c: fix lint warning on i386 do not allow unquoted 'left == right' after modifier ':?' o hash.c: fix build for DEBUG_HASH_LOOKUP o var.c: fix memory leak in error case of the ':?' modifier 2021-09-11 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210911 Merge with NetBSD make, pick up o var.c: replace remaining ModChain_ShouldEval with Expr_ShouldEval 2021-09-08 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210906 Merge with NetBSD make, pick up o more unit tests o lint cleanup o rename some functions to better fit purpose o for.c: cleanup - remove unnecessary optimization fix embedded newlines o parse.c: correct case for CVS/RCS 2021-08-11 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210808 Merge with NetBSD make, pick up o var.c: remove redundant initialization in ApplyModifier_Order * mk/options.mk: issue warning for incorrect usage 2021-08-03 Simon J Gerraty * var.c: use long for :On if we don't have a 64bit int type * VERSION (_MAKE_VERSION): 20210803 Merge with NetBSD make, pick up o rework varmod-order tests to avoid qsort instability o make.1: clarify :On entry 2021-07-31 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210731 Merge with NetBSD make, pick up o fix some lint issues o more unit tests o var.c: rework of ApplyModifier_Order 2021-07-30 Simon J Gerraty * util.c: add strto*l if HAVE_STRTO*L not defined * VERSION (_MAKE_VERSION): 20210730 Merge with NetBSD make, pick up o var.c: add :On and :Orn for numeric sort disabled if no 64bit type available. o _strtol.h: to implement strto*l functions 2021-07-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210704 Merge with NetBSD make, pick up o unit-tests: fix some tests to be more portable - job-output-null not all shells do the same number of write calls - objdir-writable if TMPDIR is set; /tmp may not be usable 2021-07-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210701 Merge with NetBSD make, pick up o unit-tests: allow for BROKEN_TESTS to list TESTS to be skipped; some tests just cannot work in some environments. o buf.c: simpler upper bound for length in Buf_AddInt o cond.c: fix grammar in error message for malformed conditional o for.c: prevent newline injection (from ${.newline}) in .for loops o var.c: use more practical data type in RegexReplace (avoid need for %zu) extract RegexReplace from ModifyWord_SubstRegex 2021-06-21 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210621 Merge with NetBSD make, pick up o var.c: only report error for unmatched regex subexpression when linting (-dL) since we cannot tell when an unmatched subexpression is an expected result. o move unmatched regex subexpression tests to varmod-subst-regex.mk and enable strict (lint) mode 2021-06-16 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210616 Merge with NetBSD make, pick up o more unit tests o cond.c: rename If_Eval to EvalBare improve function names for parsing conditions o job.c: fix error handling of targets that cannot be made o var.c: uncompress code in ApplyModifier_Unique 2021-05-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210518 Merge with NetBSD make, pick up o fix unit-tests/opt-chdir to cope with /nonexistent existing. o job.c: Print -de error information when running multiple jobs 2021-04-20 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210420 Merge with NetBSD make, pick up o use C99 bool type o convert VarEvalFlags back into an enum o cond.c: do not complain when skipping the condition 'no >= 10' o hash.c: avoid allocating memory for simple variable names o job.c: use distinct wording for writing to the shell commands file remove type name for the abort status in job handling rename PrintOutput to PrintFilteredOutput to avoid confusion o main.c: avoid double slash in name of temporary directory o var.c: use straight quotes for error 'Bad conditional expression' reduce memory allocations in the modifiers ':D' and ':U' rename members of ModifyWord_LoopArgs clean up pattern flags for the modifiers ':S' and ':C' reduce memory allocation and strlen calls in modifier ':from=to' in the ':Q' modifier, only allocate memory if necessary improve performance for LazyBuf remove redundant parameter from ParseVarnameLong migrate ParseModifierPart to use Substring avoid unnecessary calls to strlen when evaluating modifiers migrate ModifyWord functions to use Substring migrate handling of the modifier ':S,from,to,' to Substring reduce debug logging and memory allocation for ${:U...} reduce verbosity of the -dv debug logging for standard cases clean up debug logging for ':M' and ':N' disallow '$' in the variable name of the modifier ':@' simplify access to the name of an expression during evaluation 2021-03-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210330 Merge with NetBSD make, pick up o replace enum bit-field with struct bit-field for VarEvalFlags o rename VARE_NONE to VARE_PARSE_ONLY o var.c: rename ApplyModifiersState to ModChain fix double varname expansion in the variable modifier '::=' change debug log for variable evaluation flags to lowercase 2021-03-14 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210314 Merge with NetBSD make, pick up o var.c: avoid evaluating many modifiers in parse only mode in strict mode (-dL) many variable references are parsed twice, the first time just to report parse errors early, so we want to avoid side effects and wasted effort to the extent possible. 2021-02-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210226 Merge with NetBSD make, pick up o remove freestanding freeIt variables link via FStr o var.c: restructure code in ParseVarname to target human readers improve error message for; bad modifier in variable expression unclosed modifier unknown modifier remove redundant parameter of ApplySingleModifier explain non-obvious code around indirect variable modifiers quote ':S' in error message about missing delimiter extract ParseModifier_Match into separate function add context information to error message about ':range' modifier add quotes around variable name in an error message reorder code in ModifyWords use more common parameter order for VarSelectWords make ModifyWord_Subst a little easier to understand do not expand variable name from the command line twice extract ExistsInCmdline from Var_SetWithFlags save a hash map lookup when defining a cmdline variable clean up VarAdd, Var_Delete, Var_ReexportVars use bit-shift expressions for VarFlags constants rename constants for VarFlags rename ExprDefined constants for debug logging rename ExprStatus to ExprDefined split parameters for evaluating variable expressions reduce redundant code around ModifyWords print error about failed shell command before overwriting variable clean up ValidShortVarname, ParseVarnameShort rename VarExprStatus to ExprStatus add functions for assigning the value of an expression rename ApplyModifiersState_Define to Expr_Define condense the code for parsing :S and :C modifiers 2021-02-06 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210206 Merge with NetBSD make, pick up o unit-tests: use private TMPDIR to avoid errors from other users 2021-02-05 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210205 Merge with NetBSD make, pick up o avoid strdup in mkTempFile o always use vfork o rename context and ctxt to scope o rename some VAR constants to SCOPE o Var_ functions, move the scope to the front o use shortcut functions Global_Set and Global_Append o add shortcut Global_Delete for deleting a global variable o rename Var_Delete to Var_DeleteExpand, Var_DeleteVar to Var_Delete o compat.c: when exiting due to an error, print graph information o enum.c: remove overengineered Enum_ValueToString o make.c: remove unused INTERNAL flag remove unused return type of MakeBuildParent o parse.c: replace parse error "Need an operator" with better message o var.c: improve documentation about variable scopes rename Var_ValueDirect to GNode_ValueDirect rename old Var_SetWithFlags to Var_SetExpandWithFlags merge SetVar into Var_SetWithFlags split Var_Exists into plain Var_Exists and Var_ExistsExpand split Var_Append into Var_Append and Var_AppendExpand replace enum bit-set with bit-field o unit-tests/var-op-shell: use kill rather than kill -14 which broke on darwin with recent update. 2021-02-01 Simon J Gerraty * configure.in: check for sig_atomic_t and define it as 'int' if missing. * VERSION (_MAKE_VERSION): 20210201 Merge with NetBSD make, pick up o use sig_atomic_t for caught_sigchld 2021-01-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210130 Merge with NetBSD make, pick up o more unit tests o convert SearchPath to struct o split Buf_Destroy into Buf_Done and Buf_DoneData o for.c: split For_Eval into separate functions rename struct For to struct ForLoop o job.c: do not create empty shell files in jobs mode rename JobOpenTmpFile to JobWriteShellCommands reduce unnecessary calls to waitpid o parse.c: in -dp mode, print stack trace with each diagnostic 2021-01-23 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210123 Merge with NetBSD make, pick up o rename Dir_Expand to SearchPath_Expand o rename Dir_AddDir, reorder parameters of SearchPath_ToFlags o cond.c: fix debug output for comparison operators in conditionals o dir.c: split Dir_FindFile into separate functions 2021-01-20 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210120 Merge with NetBSD make, pick up o fix some more lint nits o refine some unit tests for portability o cond.c: rework parsing 2021-01-10 Simon J Gerraty * 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 * 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 * 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 * main.c: use .MAKE.DEPENDFILE as set by makefiles 2020-12-22 Simon J Gerraty * 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 * VERSION (_MAKE_VERSION): 20201221 Merge with NetBSD make, pick up o some unit-test updates 2020-12-20 Simon J Gerraty * 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 * 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 * 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 * 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 * 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 * 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 * VERSION (_MAKE_VERSION): 20201117 Merge with NetBSD make, pick up 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 cleanup PrintOnError, getTmpdir and ParseBoolean o var.c: fix error message of failed :!cmd! modifier 2020-11-14 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201114 Merge with NetBSD make, pick up o replace a few HashTable_CreateEntry with HashTable_Set o clean up cached_stats o rename DEFAULT to defaultNode o remove redundant struct make_stat o cond.c: in lint mode, check for ".else " use bitset for IfState replace large switch with if-else in Cond_EvalLine o job.c: clean up JobExec, JobStart, JobDoOutput use stderr for error message about failed touch clean up Job_Touch replace macro DBPRINTF with JobPrintln rename JobState to JobStatus main.c: switch cache for realpath from GNode to HashTable clean up Fatal clean up InitDefSysIncPath use progname instead of hard-coded 'make' in warning rename Main_SetVarObjdir to SetVarObjdir make.1: document the -S option make.c: fix debug output for GNode details use symbolic names in debug output of GNodes 2020-11-12 Simon J Gerraty * configure.in: fix --with-force-machine-arch * VERSION (_MAKE_VERSION): 20201112 Merge with NetBSD make, pick up o allow env var MAKE_OBJDIR_CHECK_WRITABLE=no to skip writable checks in InitObjdir. Explicit .OBJDIR target always allows read-only directory. o cond.c: clean up Cond_EvalLine 2020-11-11 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201111 Merge with NetBSD make, pick up o more unit-tests o style cleanup remove redundant parentheses from sizeof operator replace character literal 0 with '\0'. replace pointer literal 0 with NULL. remove redundant parentheses. replace (expr & mask) == 0 with !(expr & mask). use strict typing in conditions of the form !var o rename Make_OODate to GNode_IsOODate o rename Make_TimeStamp to GNode_UpdateYoungestChild o rename Var_Set_with_flags to Var_SetWithFlags o rename dieQuietly to shouldDieQuietly o buf.c: make API of Buf_Init simpler o compat.c: clean up Compat_Make, Compat_RunCommand, CompatDeleteTarget and CompatInterrupt o cond.c: in lint mode, only allow '&&' and '||', not '&' and '|' clean up CondParser_Comparison o main.c: rename getBoolean and s2Boolean rename MAKEFILE_PREFERENCE for consistency o parse.c: replace strstr in ParseMaybeSubMake with optimized code o var.c: rename VARE_ASSIGN to VARE_KEEP_DOLLAR replace emptyString with allocated empty string error out on unclosed expressions after the colon 2020-11-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201101 Merge with NetBSD make, pick up o negate NoExecute to GNode_ShouldExecute o job.c: rename JobMatchShell to FindShellByName extract EscapeShellDblQuot from JobPrintCommand extract ParseRunOptions from JobPrintCommand o var.c: extract ApplyModifiersIndirect from ApplyModifiers treat malformed :range, :ts and :[...] as errors add tests for the variable modifiers :[words] and :range 2020-10-31 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201031 Merge with NetBSD make, pick up o format #include directives consistently o do not look up local variables like .TARGET anywhere else o main.c: Main_SetObjdir is first called for curdir which may be readonly reduce the scope where recursive expressions are detected remove redundant :tl from getBoolean clean up mkTempFile o meta.c: simplify memory allocation in meta_create and meta_oodate o parse.c: extract loadedfile_mmap from loadfile o trace.c: document possible undefined behavior with .CURDIR o var.c: make parsing of the :gmtime and :localtime modifiers stricter rename ismeta to is_shell_metachar remove debug logging for the :Q variable modifier rename VarIsDynamic to VarnameIsDynamic use consistent parameter order in varname parsing functions extract ParseVarnameLong from Var_Parse extract ParseVarnameShort from Var_Parse fix type of ParseModifierPart parameter delim extract IsEscapedModifierPart from ParseModifierPart clean up ModifyWords add test for combining the :@ and :? variable modifiers 2020-10-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201030 Merge with NetBSD make, pick up o change char * to void * in Var_Value o make iterating over HashTable simpler o rename VAR_CMD to VAR_CMDLINE o cond.c: clean up is_separator fix parse error in string literal in conditional o main.c: do not use objdir that is not writable in lint mode, exit with error status on errors o parse.c: clean up StrContainsWord fix out-of-bounds pointer in ParseTrackInput o var.c: rename Str_SYSVMatch and its parameters remove unsatisfiable conditions in Var_Set_with_flags document where the variable name is expanded fix documentation for VARP_SUB_ONE rename VAR_EXPORTED_YES to VAR_EXPORTED_SOME document VAR_READONLY prevent appending to read-only variables extract MayExport from Var_Export1 remove redundant evaluations in VarFind replace VarFindFlags with a simple Boolean rename FIND_CMD to FIND_CMDLINE, to match VAR_CMDLINE 2020-10-28 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201028 Merge with NetBSD make, pick up o rename defIncPath to defSysIncPath o initialize all CmdOpts fields o lst.c: inline Vector_Get o main.c: refactor main extract InitMaxJobs,InitObjdir,InitVarMake,InitRandom, ReadMakefiles,CleanUp,InitVpath,ReadBuiltinRules, InitDefIncPath,CmdOpts_Init,UnlimitFiles o parse.c: merge curFile into includes rename predecessor to order_pred sort ParseSpecial alphabetically remove unused, undocumented .NOEXPORT rename ParseSpecial enum values consistently rename some fields of struct IFile 2020-10-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201026 Merge with NetBSD make, pick up o group the command line options and arguments into a struct o rename GNode.cmgn to youngestChild o rename hash functions to identify the type name o negate OP_NOP and rename it to GNode_IsTarget o add GNode_Path to access the path of a GNode o remove macros MIN and MAX o remove unused Lst_Find and Lst_FindFrom o arch.c: and make Arch_FindLib simpler clean up code layout make Arch_ParseArchive simpler o cond.c: inline CondFindStrMatch into FuncMake o dir.c: replace Dir_CopyDir with Dir_CopyDirSearchPath omit trailing space in debug output for expanding file patterns refactor DirMatchFiles document that the SearchPath of Dir_FindFile may be NULL remove UNCONST from Dir_Expand inline DirFindName o for.c: clean up code for handling .for loops o hash.c: print hash in debug log with fixed width clean up hash table functions reduce amount of string hashing o job.c: refactor JobDeleteTarget use proper enum constants for aborting convert result of JobStart from macros to enum convert abort reason macros to enum rework Job_CheckCommands to reduce indentation rename Shell fields add field names in declaration of DEFSHELL_CUSTOM convert JobState and JobFlags to enum types move handling of the "..." command to JobPrintCommands o lst.c: clean up refactor LstNodeNew remove Lst_Open, Lst_Next, Lst_Close remove code for circular lists from Lst_Next o main.c: do not attempt to read .MAKE.DEPENFILE if set to /dev/null or anything starting with "no" convert macros for debug flags into enum o make.c: inline Lst_Copy in Make_ExpandUse o meta.c: inline Lst_Find in meta_oodate make Lst_RemoveIf simpler in meta_oodate o parse.c: convert error level for Parse_Error to an enum o suff.c: properly terminate debug output with newline add more details to DEBUG_SRC log replace Dir_CopyDir with Dir_CopyDirSearchPath don't modify GNode name while rebuilding the suffix graph o var.c: reduce duplicate code in VarFind 2020-10-22 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201022 Merge with NetBSD make, pick up o more refactoring and simplification to reduce code size o var.c: extract CanonicalVarname from VarFind o make.c: extract UpdateImplicitParentsVars from Make_Update o main.c: extract PrintVar from doPrintVars extract HandlePWD from main o lst.c: inline simple Lst getters remove unused Lst_ForEach o job.c: move struct Shell from job.h to job.c o more unit tests 2020-10-19 Simon J Gerraty * configure.in: remove inappropriate use of AC_INCLUDES_DEFAULT 2020-10-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201018 Merge with NetBSD make, pick up o remove USE_IOVEC o rename some Hash_* apis to Hash* o replace execError with execDie o rename Lst_Init to Lst_New o add tags to enum types o rename Stack to Vector o parse.c: more refactoring o unit-tests: make some tests use line buffered stdout o unit-tests/Makefile: in meta mode do not make all tests depend on Makefile, it isn't necessary. 2020-10-10 Simon J Gerraty * main.c: check for CTL_HW being defined. * unit-tests/Makefile: ensure export tests output are POSIX compliant disable opt-debug-jobs test until it works on ubuntu * VERSION (_MAKE_VERSION): 20201010 Merge with NetBSD make, pick up o dir.c: remove pathname limit for Dir_FindHereOrAbove o hash.c: replace strcpy with memcpy in Hash_CreateEntry o main.c: extract init_machine and init_machine_arch from main allow to disable debug logging options o parse.c: enable format string truncation warnings extract parsing of sources from ParseDoDependency split ParseDoSrc into smaller functions hide implementation details from Parse_DoVar clean up parsing of variable assignments split Parse_DoVar into manageable pieces don't modify the given line during Parse_DoVar fix out-of-bounds memory access in Parse_DoVar fix parsing of the :sh assignment modifier o var.c: rework memory allocation for the name of variables extract ApplyModifier_Literal into separate function in lint mode, reject modifiers without delimiter do not export variable names starting with '-' o fix double-free bug in -DCLEANUP mode o more cleanup to enable higher warnings level o more unit tests 2020-10-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201002 Merge with NetBSD make, pick up o dir.c: use hash table for looking up open directories by name o main.c: clean up option handling o parse.c: add missing const for Parse_AddIncludeDir o var.c: ApplyModifier_To, update pp in each branch o remove redundant function prototypes o more unit tests 2020-10-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20201001 Merge with NetBSD make, pick up o compat.c: comment about "..." 2020-09-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200930 Merge with NetBSD make, pick up o job.c: split Job.jobPipe into 2 separate fields replace Lst_Open with direct iteration o lst.c: remove redundant assertions o targ.c: replace Lst_Open with direct iteration o var.c: fix bug in evaluation of indirect variable modifiers extract ApplyModifier_Quote into separate function o make debug logging simpler 2020-09-27 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200927 Merge with NetBSD make, pick up o parse.c: ensure parse errors result in 'stopped in' message. o compat.c: make parameter of Compat_RunCommand const o main.c: extract InitVarTarget from main o parse.c: rename ParseFinishLine to FinishDependencyGroup refactor ParseDoDependency o var.c: Var_Subst no longer returns string result rename Var_ParsePP back to Var_Parse in lint mode, improve error handling for undefined variables extract ParseVarname from Var_Parse o rename Lst_ForEach to Lst_ForEachUntil o inline Lst_ForEachUntil in several cases o clean up API for finding and creating GNodes o fix assertion failure in -j mode with .END node o inline and remove LstNode_Prev and LstNode_Next o use fine-grained type names for lists and their nodes o more unit tests 2020-09-11 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200911 Merge with NetBSD make, pick up o cond.c: split EvalComparison into smaller functions reorder parameters of condition parsing functions reduce code size in CondParser_Eval rename CondGetString to CondParser_String add CondLexer_SkipWhitespace group the condition parsing state into a struct in CondGetString, replace repeated Buf_Add with Buf_AddStr o migrate Var_Parse to Var_ParsePP o add wrappers around ctype.h functions o lst.c: use a stack instead of a list for the nested include path o more unit tests 2020-09-04 Simon J Gerraty * make-bootstrap.sh.in: adjust object list 2020-09-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200902 Merge with NetBSD make, pick up o use make_stat to ensure no confusion over valid fields returned by cached_stat o var.c: make VarQuote const-correct o add unit tests for .for 2020-09-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200901 Merge with NetBSD make, pick up o rename Hash_Table fields o make data types in Dir_HasWildcards more precise 2020-08-31 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200831 Merge with NetBSD make, pick up o suff.c: fix unbalanced Lst_Open/Lst_Close in SuffFindCmds o lst.c: Lst_Open renable assert that list isn't open o unit test for .TARGET dependent flags o var.c: fix aliasing bug in VarUniq o more unit tests for :u 2020-08-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200830 Merge with NetBSD make, pick up o allow for strict type checking for Boolean o Var_Parse never returns NULL o Var_Subst never returns NULL o Lst_Find now takes boolean match function o rename Lst_Memeber to Lst_FindDatum o rename LstNode functions to match their type o rename GNode.iParents to implicitParents o fix assertion failure for .SUFFIXES in archives o compat.c: clean up documentation for CompatInterrupt and Compat_Run remove unreachable code from CompatRunCommand o main.c: simplify getBoolean o stc.c: replace brk_string with simpler Str_Words o suff.c: add debug macros 2020-08-28 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200828 Merge with NetBSD make, pick up o lst.c: inline LstIsValid and LstNodeIsValid o remove trailing S from Lst function names after migration complete o more comment cleanup/clarification o suff.c: clean up suffix handling o more unit tests 2020-08-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200826 Merge with NetBSD make, pick up o enum.c: distinguish between bitsets containing flags and ordinary enums o var.c: fix error message for ::!= modifier with shell error o fix bugs in -DCLEANUP mode 2020-08-24 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200824 Merge with NetBSD make, pick up o in debug mode, print GNode details in symbols 2020-08-23 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200823 Merge with NetBSD make, pick up o lst.c: more asserts, make args to Lst_Find match others. o var.c: pass flags to VarAdd o arch.c: use Buffer o str.c: brk_string return size_t for nwords o more unit tests 2020-08-22 Simon J Gerraty * VERSION (_MAKE_VERSION): Merge with NetBSD make, pick up o var.c: support for read-only variables eg .SHELL being the shell used to run scripts. o lst.c: more simplification o more documentation and style cleanup o more unit tests o ensure unit-test/Makefile is run by TEST_MAKE o reduce duplication of header inclusion 2020-08-21 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200821 Merge with NetBSD make, pick up o lst.c: revert invalid assertion - but document it o dir.c: split Dir_Init into two functions 2020-08-20 Simon J Gerraty * lst.c: needs inttypes.h on Linux * VERSION (_MAKE_VERSION): 20200820 Merge with NetBSD make, pick up o make.1: clarify some passages o var.c: more cleanup, clarify comments o make_malloc.c: remove unreachable code o cond.c: make CondGetString easier to debug o simplify list usage o unit-tests: more 2020-08-16 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200816 Merge with NetBSD make, pick up o refactor unit-tests to be more fine grained not all tests moved yet 2020-08-14 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200814 Merge with NetBSD make, pick up o more str_concat variants o more enums for flags o var.c: cleanup for higher warnings level 2020-08-10 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200810 Merge with NetBSD make, pick up o more unit tests o general comment and style cleanup 2020-08-08 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200808 Merge with NetBSD make, pick up o enum.[ch]: streamline, enums for use in flags and debug output o cond.c: cleanup o var.c: reduce duplicate code for modifiers debug logging for Var_Parse more detailed debug output o more unit tests 2020-08-06 Simon J Gerraty * unit-tests/Makefile: -r for recursive and include Makefile.inc so I can run tests in meta mode supress extra noise if in meta mode * VERSION (_MAKE_VERSION): 20200806 Merge with NetBSD make, pick up o parse.c: remove VARE_WANTRES for LINT we just want to check parsing (for now). 2020-08-05 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200805 Merge with NetBSD make, pick up o make.1: Rework the description of dependence operators 2020-08-03 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200803 Merge with NetBSD make, pick up o revert some C99 usage, for max portability o unit-tests/lint 2020-08-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200802 Merge with NetBSD make, pick up o more unit tests 2020-08-01 Simon J Gerraty * Remove NetBSD specific plumbing from unit-tests/Makefile * VERSION (_MAKE_VERSION): 20200801 Merge with NetBSD make, pick up o make Var_Value return const o size_t for buf sizes o optimize some buffer operations - avoid strlen 2020-07-31 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200731 Merge with NetBSD make, pick up o var.c: fix undefinded behavior for incomplete :t modifier fixes unit-test/moderrs on Ubuntu o parse.c: When parsing variable assignments other than := if DEBUG(LINT) test substition of value, so we get a file and line number in the resulting error. o dir.c: fix parsing of nested braces in dependency lines add unit-tests 2020-07-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200730 Merge with NetBSD make, pick up o var.c: minor cleanup o unit-tests: more tests to improve code coverage 2020-07-28 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200728 Merge with NetBSD make, pick up o var.c: more optimizations 2020-07-26 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200726 Merge with NetBSD make, pick up o collapse lsd.lib into lst.c - reduce code size and allow inlining o lots of function comment updates o var.c: more optimizations o make return of Var_Parse const 2020-07-20 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200720 Merge with NetBSD make, pick up o DEBUG_HASH report stats at end and tone down the noise o var.c: each flag type gets its own prefix. move SysV string matching to var.c make ampersand in ${VAR:from=to&} an ordinary character cleanup and simplify implementation of modifiers o make.1: move documentation for assignment modifiers 2020-07-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200718 Merge with NetBSD make, pick up o DEBUG_HASH to see how well the hash tables are working 2020-07-11 Simon J Gerraty * bsd.after-import.mk: make sure we update unit-tests/Makefile 2020-07-10 Simon J Gerraty * configure.in: use AC_INCLUDES_DEFAULT rather than AC_HEADER_STDC * VERSION (_MAKE_VERSION): 20200710 Merge with NetBSD make, pick up o filemon/filemon_dev.c: use O_CLOEXEC rather than extra syscall o meta.c: target flagged .META is out-of-date if meta file missing 2020-07-09 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200709 Merge with NetBSD make, pick up o cond.c: fix for compare_expression when doEval=0 o unit-tests/Makefile: rework o filemon/filemon_dev.c: ensure filemon fd is closed on exec. 2020-07-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200704 Merge with NetBSD make, pick up (most of this by rillig@) o lots of style and white-space cleanup o lots more unit tests for variable modifiers o simplified description of some functions o str.c: refactor Str_Match o var.c: debugging output for :@ constify VarModify parameter fix :hash modifier on 16-bit platforms remove unnecessary forward declarations refactor ApplyModifier_SysV to have less indentation simplify code for :E and :R clean up code for :H and :T refactor ApplyModifiers * var.c: we need stdint.h on some platforms to get uint32_t * unit-test/Makefile: we need to supress the specific error for RE substitution error in modmisc, since it varies accross different OS. 2020-07-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200702 Merge with NetBSD make, pick up o var.c: more improvements to avoiding unnecessary evaluation use enums for flags o remove flags arg to Var_Set which outside of var.c is always 0 2020-07-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200701 Merge with NetBSD make, pick up o var.c: with change to cond.c; ensure that nested variables within a variable name are expanded. o unit-tests/varmisc.mk: test for nested varname 2020-06-29 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200629 Merge with NetBSD make, pick up o cond.c: do not eval unnecessary terms of conditionals. 2020-06-25 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200625 Merge with NetBSD make, pick up o meta.c: report error if lseek in filemon_read fails 2020-06-22 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200622 Merge with NetBSD make, pick up o dieQuietly: ignore OP_SUBMAKE as too aggressive 2020-06-19 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200619 Merge with NetBSD make, pick up o str.c: performance improvement for Str_Match for multiple '*' o dieQuietly: supress the failure output from make when failing node is a sub-make or a sibling failed. This cuts down greatly on unhelpful noise at the end of build log. Disabled by -dj or .MAKE.DIE_QUIETLY=no 2020-06-10 Simon J Gerraty * FILES: add LICENSE to appease some packagers. This is an attempt to fairly represent the license on almost 200 files, which are almost all BSD-3-Clause The few exceptions being more liberal. * VERSION (_MAKE_VERSION): 20200610 Merge with NetBSD make, pick up o unit test for :Or 2020-06-06 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200606 Merge with NetBSD make, pick up o make.1: cleanup * Makefile: fix depends for main.o which broke MAKE_VERSION 2020-06-05 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200605 Merge with NetBSD make, pick up o dir.c: cached_stats - don't confuse stat and lstat results. o var.c: add :Or for reverse sort. 2020-05-24 Simon J Gerraty * configure.in: add AC_PROG_CC_C99 for mipspro compiler also if --with-filemon= specifies path to filemon.h set use_filemon=dev * dirname.c: remove include of namespace.h 2020-05-17 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200517 Merge with NetBSD make, pick up o modified dollar tests to avoid shell dependencies o new tests for .INCLUDEFROM 2020-05-16 Simon J Gerraty * unit-tests/dollar.mk: tweak '1 dollar literal' test to not depend so much on shell behavior 2020-05-10 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200510 Merge with NetBSD make, pick up o unit test for dollar handling 2020-05-06 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200506 Merge with NetBSD make, pick up o str.c: empty string does not match % pattern plus unit-test changes 2020-05-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200504 May the 4th be with you Merge with NetBSD make, pick up o var.c: import handling of old sysV style modifier using '%' o str.c: refactor brk_string o unit-tests: add test case for lazy conditions 2020-04-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200418 * configure.in: use_makefile=no for cygwin et al. case insensitive filesystems just don't work if both makefile and Makefile exist. NOTE: bmake does not support cygwin and likely never will, but if brave souls want to try it - help them out. 2020-04-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200402 Merge with NetBSD make, pick up o meta.c: meta_oodate, CHECK_VALID_META is too aggressive for CMD a blank command is perfectly valid. 2020-03-30 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200330 Merge with NetBSD make, pick up o make.h: extern debug_file 2020-03-18 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200318 Merge with NetBSD make, pick up o meta.c: meta_oodate, check for corrupted meta file earlier and more often. 2020-02-20 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200220 2020-02-19 Simon J Gerraty * boot-strap: unset MAKEFLAGS 2020-02-12 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200212 * meta.c: meta_compat_parent check for USE_FILEMON patch from Soeren Tempel 2020-02-05 Simon J Gerraty * VERSION: 20200205 Merge with NetBSD make, pick up o meta.c: fix compat mode, need to call meta_job_output() o job.c: extra fds for meta mode not needed if using filemon_dev 2020-01-22 Simon J Gerraty * VERSION: 20200122 Merge with NetBSD make, pick up o meta.c: avoid passing NULL to filemon_*() when meta_needed() returns FALSE. 2020-01-21 Simon J Gerraty * VERSION: 20200121 Merge with NetBSD make, pick up o filemon/filemon_{dev,ktrace}.c: allow selection of filemon implementation. filemon_dev.c uses the kernel module while filemon_ktrace.c leverages the fktrace api available in NetBSD. filemon_ktrace.c can hopefully form the basis for adding support for other tracing mechanisms such as strace on Linux. o meta.c: when target is out-of-date per normal make rules record value of .OODATE in meta file. 2019-09-26 Simon J Gerraty * VERSION: 20190926 Merge with NetBSD make, pick up o parse.c: don't pass NULL to realpath(3) some versions cannot handle it. 2019-04-09 Simon J Gerraty * VERSION: 20190409 Merge with NetBSD make, pick up o parse.c: ParseDoDependency: free paths rather than assert 2018-12-22 Simon J Gerraty * VERSION: 20181222 * configure.in: add --without-makefile to avoid generating makefile and make-bootstrap.sh * include Makefile.inc if it exists * Use Makefile and Makefile.config.in in unit-tests so we can use just: make obj && make && make test when bmake is already available. We add --without-makefile to CONFIGURE_ARGS in this case. * tweak bsd.after-import.mk (captures Makefile.config etc after import to FreeBSD for example) to cope with all the above. 2018-12-21 Simon J Gerraty * VERSION: 20181221 Merge with NetBSD make, pick up o parse.c: ParseVErrorInternal use .PARSEDIR and apply if relative, and then use .PARSEFILE for consistent result. 2018-12-20 Simon J Gerraty * VERSION: 20181220 Merge with NetBSD make, pick up o parse.c: ParseVErrorInternal use .CURDIR if .PARSEDIR is relative o var.c: avoid SEGFAULT in .unexport-env when MAKELEVEL is not set 2018-12-16 Simon J Gerraty * VERSION: 20181216 Merge with NetBSD make, pick up o fix for unit-tests/varquote.mk on Debian 2018-09-21 Simon J. Gerraty * VERSION: 20180919 Merge with NetBSD make, pick up o var.c: add :q o dir.c: cleanup caching of stats 2018-09-21 Simon J Gerraty * Makefile.config.in: use += where it makes sense. 2018-05-12 Simon J. Gerraty * VERSION: 20180512 Merge with NetBSD make, pick up o job.c: skip polling job token pipe 2018-04-05 Simon J. Gerraty * VERSION: 20180405 Merge with NetBSD make, pick up o parse.c: be more cautious about detecting depenency line rather than sysV style include. 2018-02-22 Simon J. Gerraty * VERSION: 20180222 Merge with NetBSD make, pick up o parse.c: avoid calling sysconf for every call to loadfile 2018-02-18 Simon J. Gerraty * VERSION: 20180218 Merge with NetBSD make, pick up o var.c: Var_Set handle NULL value anytime. 2018-02-12 Simon J. Gerraty * VERSION: 20180212 Merge with NetBSD make, pick up o parse.c: do not treat .info as warning with -W 2017-12-07 Simon J. Gerraty * VERSION: 20171207 Merge with NetBSD make, pick up o var.c: Var_Append use Var_Set if var not previously set so that VAR_CMD is handled correctly. Add a suitable unit-test. 2017-11-26 Simon J. Gerraty * VERSION (_MAKE_VERSION): 20171126 * aclocal.m4: use AC_LINK_IFELSE for AC_C___ATTRIBUTE__ since AC_TRY_COMPILE puts input inside main() which upsets modern compilers. 2017-11-18 Simon J. Gerraty * VERSION: 20171118 Merge with NetBSD make, pick up o var.c: do not append to variable set on command line add unit-test to catch this. 2017-10-28 Simon J. Gerraty * VERSION: 20171028 Merge with NetBSD make, pick up o main.c: ignore empty MAKEOBJDIR * Makefile.config.in: make @prefix@ @machine*@ and @default_sys_path@ defaults. 2017-10-05 Simon J. Gerraty * VERSION: 20171005 * unit-tests/dotwait.mk: redirect stderr through pipe for more consistent result on some platforms. 2017-08-13 Simon J. Gerraty * machine.sh: entry for AIX 2017-08-12 Simon J. Gerraty * VERSION (_MAKE_VERSION): Move the setting of _MAKE_VERSION to a file that can be included by configure as well as make. This allows configure to set set _MAKE_VERSION in make-bootstrap.sh 2017-08-10 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170810 Merge with NetBSD make, pick up o meta.c: if target is in subdir we only need subdir name in meta_name. 2017-07-20 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170720 Merge with NetBSD make, pick up o compat.c: pass SIGINT etc onto child and wait for it to exit before we self-terminate. 2017-07-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170711 forgot to update after merge on 20170708 ;-) o main.c: refactor to reduce size of main function. add -v option to always fully expand values. o meta.c: ensure command output in meta file has ending newline even when filemon not being used. When matching ${.MAKE.META.IGNORE_PATTERNS} do not use pathname via ':L' since any ':' in pathname breaks that. Instead set a '${.p.}' to pathname in the target context and use that. 2017-05-10 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170510 Merge with NetBSD make, pick up o main.c: Main_SetObjdir: ensure buf2 is in scope 2017-05-08 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170505 see mk/ChangeLog 2017-05-05 Simon J. Gerraty * parse.c: not everyone has stdint.h 2017-05-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170501 see mk/ChangeLog 2017-04-21 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170421 Merge with NetBSD make, pick up o str.c: Str_Match: fix closure tests for [^] and add unit-test. 2017-04-20 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170420 Merge with NetBSD make, pick up o main.c: only use -C arg "as is" if it contains no relative component. 2017-04-18 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170418 Merge with NetBSD make, pick up o main.c: fix Main_SetObjdir() for relative paths (eg obj). 2017-04-17 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170417 Merge with NetBSD make, pick up o fixes a number of coverity complaints - check return value of fseek, fcntl - plug memory leak in Dir_FindFile, Var_LoopExpand, JobPrintCommand, ParseTraditionalInclude - use bmake_malloc() where NULL is not tollerated - use MAKE_ATTR_UNUSED rather that kludges like return(unused ? 0 : 0) - use purge_cached_realpaths() rather than abuse cached_realpath() 2017-04-13 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170413 Merge with NetBSD make, pick up o main.c: when setting .OBJDIR ignore '$' in paths. * job.c: use MALLOC_OPTIONS to set malloc_options. 2017-04-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170411 Merge with NetBSD make, pick up o str.c: Str_Match: allow [^a-z] to behave as expected. 2017-03-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170326 Merge with NetBSD make, pick up o main.c: purge relative paths from realpath cache when .OBJDIR is changed. 2017-03-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170311 Merge with NetBSD make, pick up o main.c: only use -C arg "as is" if it starts with '/'. 2017-03-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170301 Merge with NetBSD make, pick up o main.c: use -C arg "as is" rather than getcwd() if they identify the same directory. o parse.c: ensure loadfile buffer is \n terminated in non-mmap case 2017-02-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170201 Merge with NetBSD make, pick up o var.c: allow :_=var and avoid use of special context. 2017-01-30 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170130 Merge with NetBSD make, pick up o var.c: add :range and :_ o main.c: partially initialize Dir_* before MainParseArgs() can be called. If -V, skip Main_ExportMAKEFLAGS() 2017-01-14 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170114 Merge with NetBSD make, pick up o var.c: allow specifying the utc value used by :{gm,local}time 2016-12-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161212 Merge with NetBSD make, pick up o main.c: look for obj.${MACHINE}-${MACHINE_ARCH} too. 2016-12-09 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161209 Merge with NetBSD make, pick up o main.c: cleanup setting of .OBJDIR o parse.c: avoid coredump from (var)=val 2016-11-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161126 Merge with NetBSD make, pick up o make.c: Make_OODate: report src node name if path not set 2016-09-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160926 Merge with NetBSD make, pick up o support for .DELETE_ON_ERROR: (remove targets that fail) 2016-09-26 Simon J. Gerraty * Makefile MAN: tweak .Dt to match ${PROG} 2016-08-18 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160818 its a neater number; pick up whitespace fixes to man page. 2016-08-17 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160817 Merge with NetBSD make, pick up o meta.c: move handling of .MAKE.META.IGNORE_* to meta_ignore() so we can call it before adding entries to missingFiles. Thus we do not track files we have been told to ignore. 2016-08-15 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160815 Merge with NetBSD make, pick up o meta_oodate: apply .MAKE.META.IGNORE_FILTER (if defined) to pathnames, and skip if the expansion is empty. Useful for dirdeps.mk when checking DIRDEPS_CACHE. 2016-08-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160812 Merge with NetBSD make, pick up o meta.c: remove all missingFiles entries that match a deleted dir. o main.c: set .ERROR_CMD if possible. 2016-06-06 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160606 Merge with NetBSD make, pick up o dir.c: extend mtimes cache to others via cached_stat() 2016-06-04 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160604 Merge with NetBSD make, pick up o meta.c: missing filemon data is only relevant if we read a meta file. Also do not return oodate for a missing metafile if gn->path points to .CURDIR 2016-06-02 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160602 Merge with NetBSD make, pick up o cached_realpath(): avoid hitting filesystem more than necessary. o meta.c: refactor need_meta decision, add knobs for missing meta file and filemon data wrt out-of-datedness. 2016-05-28 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160528 * boot-strap, make-bootstrap.sh.in: Makefile now uses _MAKE_VERSION 2016-05-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160512 Merge with NetBSD make, pick up o meta.c: ignore paths that match .MAKE.META.IGNORE_PATTERNS this is useful for gcov builds. o propagate errors from filemon(4). 2016-05-09 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160509 Merge with NetBSD make, pick up o remove use of non-standard types u_int etc. o meta.c: apply realpath() before matching against metaIgnorePaths 2016-04-04 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160404 Merge with NetBSD make, pick up o allow makefile to set .MAKE.JOBS * Makefile (PROG_NAME): use ${_MAKE_VERSION} 2016-03-15 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160315 Merge with NetBSD make, pick up o fix handling of archive members 2016-03-13 Simon J. Gerraty * Makefile (_MAKE_VERSION): rename variable to avoid interference with checks for ${MAKE_VERSION} 2016-03-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160310 Merge with NetBSD make, pick up o meta.c: treat missing Read file same as Write, incase we Delete it. 2016-03-07 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160307 Merge with NetBSD make, pick up o var.c: fix :ts\nnn to be octal by default. o meta.c: meta_finish() to cleanup memory. 2016-02-26 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160226 Merge with NetBSD make, pick up o meta.c: allow meta file for makeDepend if makefiles want it. 2016-02-19 Simon J. Gerraty * var.c: default .MAKE.SAVE_DOLLARS to FALSE for backwards compatability. * Makefile (MAKE_VERSION): 20160220 Merge with NetBSD make, pick up o var.c: add knob to control handling of '$$' in := 2016-02-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160218 Merge with NetBSD make, pick up o var.c: add .export-literal allows us to fix sys.clean-env.mk post the changes to Var_Subst. Var_Subst now takes flags, and does not consume '$$' in := 2016-02-17 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160217 Merge with NetBSD make, pick up o var.c: preserve '$$' in := o parse.c: add .dinclude for handling included makefile like .depend 2015-12-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151220 Merge with NetBSD make, pick up o suff.c: re-initialize suffNull when clearing suffixes. 2015-12-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151201 Merge with NetBSD make, pick up o cond.c: CondCvtArg: avoid access beyond end of empty buffer. o meta.c: meta_oodate: use lstat(2) for checking link target in case it is a symlink. o var.c: avoid calling brk_string and Var_Export1 with empty strings. 2015-11-26 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151126 Merge with NetBSD make, pick up o parse.c: ParseTrackInput don't access beyond end of old value. 2015-10-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151022 * Add support for BSD/OS which lacks inttypes.h and really needs sys/param.h for sys/sysctl.h also 'type' is not a shell builtin. * var.c: eliminate uint32_t and need for inttypes.h * main.c: PrintOnError flush stdout before run .ERROR * parse.c: cope with _SC_PAGESIZE not being defined. 2015-10-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151020 Merge with NetBSD make, pick up o var.c: fix uninitialized var 2015-10-12 Simon J. Gerraty * var.c: the conditional expressions used with ':?' can be expensive, if already discarding do not evaluate or expand anything. 2015-10-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151010 Merge with NetBSD make, pick up o Add Boolean wantit flag to Var_Subst and Var_Parse when FALSE we know we are discarding the result and can skip operations like Cmd_Exec. 2015-10-09 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151009 Merge with NetBSD make, pick up o var.c: don't check for NULL before free() o meta.c: meta_oodate, do not hard code ignore of makeDependfile 2015-09-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150910 Merge with NetBSD make, pick up o main.c: with -w print Enter/Leaving messages for objdir too if necessary. o centralize shell metachar handling * FILES: add metachar.[ch] 2015-06-06 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150606 Merge with NetBSD make, pick up o make.1: document .OBJDIR target 2015-05-05 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150505 Merge with NetBSD make, pick up o cond.c: be strict about lhs of comparison when evaluating .if but less so when called from variable expansion. o unit-tests/cond2.mk: test various error conditions 2015-05-04 Simon J. Gerraty * machine.sh (MACHINE): Add Bitrig patch from joerg@netbsd.org 2015-04-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150418 Merge with NetBSD make, pick up o job.c: use memmove() rather than memcpy() * unit-tests/varshell.mk: SunOS cannot handle the TERMINATED_BY_SIGNAL case, so skip it. 2015-04-11 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150411 bump version - only mk/ changes. 2015-04-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150410 Merge with NetBSD make, pick up o document different handling of '-' in jobs mode vs compat o fix jobs mode so that '-' only applies to whole job when shell lacks hasErrCtl o meta.c: use separate vars to track lcwd and latestdir (read) per process 2015-04-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150401 Merge with NetBSD make, pick up o meta.c: close meta file in child * Makefile: use BINDIR.bmake if set. Same for MANDIR and SHAREDIR Handy for testing release candidates in various environments. 2015-03-26 Simon J. Gerraty * move initialization of savederr to block where it is used to avoid spurious warning from gcc5 2014-11-11 Simon J. Gerraty * Makefile (MAKE_VERSION): 20141111 just a cooler number 2014-11-05 Simon J. Gerraty * Makefile (MAKE_VERSION): 20141105 Merge with NetBSD make, pick up o revert major overhaul of suffix handling and POSIX compliance - too much breakage and impossible to make backwards compatible. o we still have the new unit test structure which is ok. o meta.c ensure "-- filemon" is at start of line. 2014-09-17 Simon J. Gerraty * configure.in: test that result of getconf PATH_MAX is numeric and discard if not. Apparently needed for Hurd. 2014-08-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140830 Merge with NetBSD make, pick up o major overhaul of suffix handling o improved POSIX compliance o overhauled unit-tests 2014-06-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140620 Merge with NetBSD make, pick up o var.c return varNoError rather than var_Error for ::= modifiers. 2014-05-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140522 Merge with NetBSD make, pick up o var.c detect some parse errors. 2014-04-05 Simon J. Gerraty * Fix spelling errors - patch from Pedro Giffuni 2014-02-14 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140214 Merge with NetBSD make, pick up o .INCLUDEFROM* o use Var_Value to get MAKEOBJDIR[PREFIX] o reduced realloc'ign in brk_string. * configure.in: add a check for compiler supporting __func__ 2014-01-03 Simon J. Gerraty * boot-strap: ignore mksrc=none 2014-01-02 Simon J. Gerraty * Makefile (DEFAULT_SYS_PATH?): use just ${prefix}/share/mk 2014-01-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140101 * configure.in: set bmake_path_max to min(_SC_PATH_MAX,1024) * Makefile.config: defined BMAKE_PATH_MAX to bmake_path_max * make.h: use BMAKE_PATH_MAX if MAXPATHLEN not defined (needed for Hurd) * configure.in: Add AC_PREREQ and check for sysctl; patch from Andrew Shadura andrewsh at debian.org 2013-10-16 Simon J. Gerraty * Makefile (MAKE_VERSION): 20131010 * lose the const from arg to systcl to avoid problems on older BSDs. 2013-10-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20131001 Merge with NetBSD make, pick up o main.c: for NATIVE build sysctl to get MACHINE_ARCH from hw.machine_arch if necessary. o meta.c: meta_oodate - need to look at src of Link and target of Move as well. * main.c: check that CTL_HW and HW_MACHINE_ARCH exist. provide __arraycount() if needed. 2013-09-04 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130904 Merge with NetBSD make, pick up o Add VAR_INTERNAL context, so that internal setting of MAKEFILE does not override value set by makefiles. 2013-09-02 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130902 Merge with NetBSD make, pick up o CompatRunCommand: only apply shellErrFlag when errCheck is true 2013-08-28 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130828 Merge with NetBSD make, pick up o Fix VAR :sh = syntax from Will Andrews at freebsd.org o Call Job_SetPrefix() from Job_Init() so makefiles have opportunity to set .MAKE.JOB.PREFIX 2013-07-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130730 Merge with NetBSD make, pick up o Allow suppression of --- job -- tokens by setting .MAKE.JOB.PREFIX empty. 2013-07-16 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130716 Merge with NetBSD make, pick up o number of gmake compatibility tweaks -w for gmake style entering/leaving messages if .MAKE.LEVEL > 0 indicate it in progname "make[1]" etc. handle MAKEFLAGS containing only letters. o when overriding a GLOBAL variable on the command line, delete it from GLOBAL context so -V doesn't show the wrong value. 2013-07-06 Simon J. Gerraty * configure.in: We don't need MAKE_LEVEL_SAFE anymore. * Makefile (MAKE_VERSION): 20130706 Merge with NetBSD make, pick up o Shell_Init(): export shellErrFlag if commandShell hasErrCtl is true so that CompatRunCommand() can use it, to ensure consistent behavior with jobs mode. o use MAKE_LEVEL_ENV to define the variable to propagate .MAKE.LEVEL - currently set to MAKELEVEL (same as gmake). o meta.c: use .MAKE.META.IGNORE_PATHS to allow customization of paths to ignore. 2013-06-04 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130604 Merge with NetBSD make, pick up o job.c: JobCreatePipe: do fcntl() after any tweaking of fd's to avoid leaking descriptors. 2013-05-28 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130528 Merge with NetBSD make, pick up o var.c: cleanup some left-overs in VarHash() 2013-05-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130520 generate manifest from component FILES rather than have to update FILES when mk/FILES changes. 2013-05-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130518 Merge with NetBSD make, pick up o suff.c: don't skip all processsing for .PHONY targets else wildcard srcs do not get expanded. o var.c: expand name of variable to delete if necessary. 2013-03-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130330 Merge with NetBSD make, pick up o meta.c: refine the handling of .OODATE in commands. Rather than suppress command comparison for the entire script as though .NOMETA_CMP had been used, only suppress it for the one command line. This allows something like ${.OODATE:M.NOMETA_CMP} to be used to suppress comparison of a command without otherwise affecting it. o make.1: document that 2013-03-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130321 yes, not quite right but its a cooler number. Merge with NetBSD make, pick up o parse.c: fix ParseGmakeExport to be portable and add a unit-test. * meta.c: call meta_init() before makefiles are read and if built with filemon support set .MAKE.PATH_FILEMON to _PATH_FILEMON this let's makefiles test for support. Call meta_mode_init() to process .MAKE.MODE. 2013-03-13 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130305 Merge with NetBSD make, pick up o run .STALE: target when a dependency from .depend is missing. o job.c: add Job_RunTarget() for the above and .BEGIN 2013-03-03 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130303 Merge with NetBSD make, pick up o main.c: set .MAKE.OS to utsname.sysname o job.c: more checks for read and poll errors o var.c: lose VarChangeCase() saves 4% time 2013-03-02 Simon J. Gerraty * boot-strap: remove MAKEOBJDIRPREFIX from environment since we want to use MAKEOBJDIR 2013-01-27 Simon J. Gerraty * Merge with NetBSD make, pick up o make.1: more info on how shell commands are handled. o job.c,main.c: detect write errors to job pipes. 2013-01-25 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130123 Merge with NetBSD make, pick up o meta.c: if script uses .OODATE and meta_oodate() decides rebuild is needed, .OODATE will be empty - set it to .ALLSRC. o var.c: in debug output indicate which variabale modifiers apply to. o remove Check_Cwd logic the makefiles have been fixed. 2012-12-12 Simon J. Gerraty * makefile.in: add a simple makefile for folk who insist on ./configure; make; make install it just runs boot-strap * include mk/* to accommodate the above * boot-strap: re-work to accommodate the above mksrc defaults to $Mydir/mk allow op={configure,build,install,clean,all} add options to facilitate install * Makefile.config.in: just the bits set by configure * Makefile: bump version to 20121212 abandon Makefile.in (NetBSD Makefile) leverage mk/* instead * configure.in: ensure srcdir is absolute 2012-11-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121111 fix generation of bmake.cat1 2012-11-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121109 Merge with NetBSD make, pick up o make.c: MakeBuildChild: return 0 so search continues if a .ORDER dependency is detected. o unit-tests/order: test the above 2012-11-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121102 Merge with NetBSD make, pick up o cond.c: allow cond_state[] to grow. In meta mode with a very large tree, we can hit the limit while processing dirdeps. 2012-10-25 Simon J. Gerraty * Makefile.in: we need to use ${srcdir} not ${.CURDIR} 2012-10-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121010 o protect syntax that only bmake parses correctly. o remove auto setting of FORCE_MACHINE, use configure's --with-force-machine=whatever if that is desired. 2012-10-08 Simon J. Gerraty * Makefile.in: do not lose history from make.1 when generating bmake.1 2012-10-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121007 Merge with NetBSD make, pick up o compat.c: ignore empty commands - same as jobs mode. o make.1: document meta chars that cause use of shell 2012-09-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120911 * bsd.after-import.mk: include Makefile.inc early and allow it to override PROG 2012-08-31 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120831 Merge with NetBSD make, pick up o cast sizeof() to int for comparison o minor make.1 tweak 2012-08-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120830 Merge with NetBSD make, pick up o .MAKE.EXPAND_VARIABLES knob can control default behavior of -V o debug flag -dV causes -V to show raw value regardless. 2012-07-05 Simon J. Gerraty * bsd.after-import.mk (after-import): ensure unit-tests/Makefile gets SRCTOP set. 2012-07-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120704 Merge with NetBSD make, pick up o Job_ParseShell should call Shell_Init if it has been previously called. * Makefile.in: set USE_META based on configure result. also .PARSEDIR is safer indicator of bmake. 2012-06-26 Simon J. Gerraty * Makefile.in: bump version to 20120626 ensure CPPFLAGS is in CFLAGS * meta.c: avoid nested externs * bsd.after-import.mk: avoid ${.CURDIR}/Makefile as target 2012-06-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120620 Merge with NetBSD make, pick up o make_malloc.c: avoid including make_malloc.h again * Makefile.in: avoid bmake only syntax or protect with .if defined(.MAKE.LEVEL) * bsd.after-import.mk: replace .-include with .sinclude ensure? SRCTOP gets a value * configure.in: look for filemon.h in /usr/include/dev/filemon first. 2012-06-19 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120612 Merge with NetBSD make, pick up o use MAKE_ATTR_* rather than those defined by cdefs.h or compiler for greater portability. o unit-tests/forloop: check that .for works as expected wrt number of times and with "quoted strings". 2012-06-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120606 Merge with NetBSD make, pick up o compat.c: use kill(2) rather than raise(3). * configure.in: look for sys/dev/filemon * bsd.after-import.mk: add a .-include "Makefile.inc" to Makefile and pass BOOTSTRAP_XTRAS to boot-strap. 2012-06-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120604 Merge with NetBSD make, pick up o util.c and var.c share same var for tracking if environ has been reallocated. o util.c provide getenv with setenv. * Add MAKE_LEVEL_SAFE as an alternate means of passing MAKE_LEVEL when the shell actively strips .MAKE.* from the environment. We still refer to the variable always as .MAKE.LEVEL * util.c fix bug in findenv() was finding prefix of name. * compat.c: re-raising SIGINT etc after running .INTERRUPT results in more reliable termination of all activity on many platforms. 2012-06-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120602 Merge with NetBSD make, pick up o for.c: handle quoted items in .for list 2012-05-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120530 Merge with NetBSD make, pick up o compat.c: ignore empty command. 2012-05-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120524 * FILES: add bsd.after-import.mk: A simple means of integrating bmake into a BSD build system. 2012-05-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120520 Merge with NetBSD make, pick up o increased limit for nested conditionals. 2012-05-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120518 Merge with NetBSD make, pick up o use _exit(2) in signal hanlder o Don't use the [dir] cache when building nodes that might have changed since the last exec. o Avoid nested extern declaration warnings. 2012-04-27 Simon J. Gerraty * meta.c (fgetLine): avoid %z - not portable. * parse.c: Since we moved include of sys/mman.h and def's of MAP_COPY etc. we got dups from a merge. 2012-04-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120420 Merge with NetBSD make, pick up o restore duplicate supression in .MAKE.MAKEFILES runtime saving can be significant. o Var_Subst() uses Buf_DestroyCompact() to reduce memory consumption up to 20%. 2012-04-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120420 Merge with NetBSD make, pick up o remove duplicate supression in .MAKE.MAKEFILES o improved dir cache behavior o gmake'ish export command 2012-03-25 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120325 Merge with NetBSD make, pick up o fix parsing of :[#] in conditionals. 2012-02-10 Simon J. Gerraty * Makefile.in: replace use of .Nx in bmake.1 with NetBSD since some systems cannot cope with .Nx 2011-11-14 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111111 Merge with NetBSD make, pick up o debug output for .PARSEDIR and .PARSEFILE 2011-10-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111010 2011-10-09 Simon J. Gerraty * boot-strap: check for an expected file in the dirs we look for. * make-bootstrap.sh: pass on LDSTATIC 2011-10-01 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111001 Merge with NetBSD make, pick up o ensure .PREFIX is set for .PHONY and .TARGET set for .PHONY run via .END o __dead used consistently 2011-09-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20110909 is a better number ;-) 2011-09-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110905 Merge with NetBSD make, pick up o meta_oodate: ignore makeDependfile 2011-08-28 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110828 Merge with NetBSD make, pick up o silent=yes in .MAKE.MODE causes meta mode to mark targets as SILENT if a .meta file is created 2011-08-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110818 Merge with NetBSD make, pick up o in meta mode, if target flagged .META a missing .meta file means target is out-of-date o fixes for gcc 4.5 warnings o simplify job printing code 2011-08-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110808 Merge with NetBSD make, pick up o do not touch OP_SPECIAL targets when doing make -t 2011-06-22 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110622 Merge with NetBSD make, pick up o meta_oodate detect corrupted .meta file and declare oodate. * configure.in: add check for setsid 2011-06-07 Simon J. Gerraty * Merge with NetBSD make, pick up o unit-tests/modts now works on MirBSD 2011-06-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110606 Merge with NetBSD make, pick up o ApplyModifiers: when we parse a variable which is not the entire modifier string, or not followed by ':', do not consider it as containing modifiers. o loadfile: ensure newline at end of mapped file. 2011-05-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110505 Merge with NetBSD make, pick up o .MAKE.META.BAILIWICK - list of prefixes which define the scope of make's control. In meta mode, any generated file within said bailiwick, which is found to be missing, causes current target to be out-of-date. 2011-04-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110411 Merge with NetBSD make, pick up o when long modifiers fail to match, check sysV style. - add a test case 2011-04-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110410 Merge with NetBSD make, pick up o :hash - cheap 32bit hash of value o :localtime, :gmtime - use value as format string for strftime. 2011-03-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110330 mostly because its a cooler version. Merge with NetBSD make, pick up o NetBSD tags for meta.[ch] o job.c call meta_job_finish() after meta_job_error(). o meta_job_error() should call meta_job_finish() to ensure .meta file is closed, and safe to copy - if .ERROR target wants. meta_job_finish() is safe to call repeatedly. 2011-03-29 Simon J. Gerraty * unit-tests/modts: use printf if it is a builtin, to save us from MirBSD * Makefile.in (MAKE_VERSION): bump version to 20110329 Merge with NetBSD make, pick up o fix for use after free() in CondDoExists(). o meta_oodate() report extra commands and return earlier. 2011-03-27 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110327 Merge with NetBSD make, pick up o meta.c, if .MAKE.MODE contains curdirOk=yes allow creating .meta files in .CURDIR * boot-strap (TOOL_DIFF): aparently at least on linux distro formats the output of 'type' differently - so eat any "()" 2011-03-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110306 Merge with NetBSD make, pick up o meta.c, only do getcwd() once 2011-03-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110305 Merge with NetBSD make, pick up o correct sysV substitution handling of empty lhs and variable o correct exists() check for dir with trailing / o correct handling of modifiers for non-existant variables during evaluation of conditionals. o ensure MAP_FILE is defined. o meta.c use curdir[] now exported by main.c 2011-02-25 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110225 Merge with NetBSD make, pick up o fix for incorrect .PARSEDIR when .OBJDIR is re-computed after makefiles have been read. o fix example of :? modifier in man page. 2011-02-13 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110214 Merge with NetBSD make, pick up o meta.c handle realpath() failing when generating meta file name. * sigcompat.c: convert to ansi so we can use higher warning levels. 2011-02-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110207 Merge with NetBSD make, pick up o fix for bug in meta mode. 2011-01-03 Simon J. Gerraty * parse.c: SunOS 5.8 at least does not have MAP_FILE 2011-01-01 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110101 Merge with NetBSD make, pick up o use mmap(2) if available, for reading makefiles 2010-12-15 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101215 Merge with NetBSD make, pick up o ensure meta_job_error() does not report a previous .meta file as being culprit. 2010-12-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101210 Merge with NetBSD make, pick up o meta_oodate: track cwd per process, and only consider target out-of-date if missing file is outside make's CWD. Ignore files in /tmp/ etc. o to ensure unit-tests results match, need to control LC_ALL as well as LANG. o fix for parsing bug in var.c 2010-11-26 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101126 Merge with NetBSD make, pick up o if stale dependency is an IMPSRC, search via .PATH o meta_oodate: if a referenced file is missing, target is out-of-date. o meta_oodate: if a target uses .OODATE in its commands, it (.OODATE) needs to be recomputed. o keep a pointer to youngest child node, rather than just its mtime. 2010-11-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101101 2010-10-16 Simon J. Gerraty * machine.sh: like os.sh, allow for uname -p producing useless drivel 2010-09-13 Simon J. Gerraty * boot-strap: document configure knobs for meta and filemon. * Makefile.in (MAKE_VERSION): bump version to 20100911 Merge with NetBSD make, pick up o meta.c - meta mode * make-bootstrap.sh.in: handle meta.c * configure.in: add knobs for use_meta and filemon_h also, look for dirname, str[e]sep and strlcpy * util.c: add simple err[x] and warn[x] 2010-08-08 Simon J. Gerraty * boot-strap (TOOL_DIFF): set this to ensure tests use the same version of diff that configure tested * Makefile.in (MAKE_VERSION): bump version to 20100808 Merge with NetBSD make, pick up o in jobs mode, when we discover we cannot make something, call PrintOnError before exit. 2010-08-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100806 Merge with NetBSD make, pick up o formatting fixes for ignored errors o ensure jobs are cleaned up regardless of where wait() was called. 2010-06-28 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100618 * os.sh (MACHINE_ARCH): watch out for drivel from uname -p 2010-06-16 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100616 Merge with NetBSD make, pick up o man page update o call PrintOnError from JobFinish when we detect an error we are not ignoring. 2010-06-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100606 Merge with NetBSD make, pick up o man page update 2010-06-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100605 Merge with NetBSD make, pick up o use bmake_signal() which is a wrapper around sigaction() in place of signal() o add .export-env to allow exporting variables to environment without tracking (so no re-export when the internal value is changed). 2010-05-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100524 Merge with NetBSD make, pick up o fix for .info et al being greedy. 2010-05-23 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100520 Merge with NetBSD make, pick up o back to using realpath on argv[0] but only if contains '/' and does not start with '/'. 2010-05-10 Simon J. Gerraty * boot-strap: use absolute path for bmake when running tests. * Makefile.in (MAKE_VERSION): bump version to 20100510 Merge with NetBSD make, pick up o revert use of realpath on argv[0] too many corner cases. o print MAKE_PRINT_VAR_ON_ERROR before running .ERROR target. 2010-05-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100505 Merge with NetBSD make, pick up o fix for missed SIGCHLD when compiled with SunPRO actually for bmake, defining FORCE_POSIX_SIGNALS would have done the job. 2010-04-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100430 Merge with NetBSD make, pick up o fflush stdout before writing to stdout 2010-04-23 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100423 Merge with NetBSD make, pick up o updated unit tests for Haiku (this time for sure). * boot-strap: based on patch from joerg honor --with-default-sys-path better. * boot-strap: remove mention of --with-prefix-sys-path 2010-04-22 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100422 * Merge with NetBSD make, pick up o fix for vfork() on Darwin. o fix for bogus $TMPDIR. o set .MAKE.MODE=compat for -B o set .MAKE.JOBS=max_jobs for -j max_jobs o allow unit-tests to run without any *.mk o unit-tests/modmisc be more conservative in dirs presumed to exist. * boot-strap: ignore /usr/share/mk except on NetBSD. * unit-tests/Makefile.in: set LANG=C when running unit-tests to ensure sort(1) behaves as expected. 2010-04-21 Simon J. Gerraty * boot-strap: add FindHereOrAbove so we can use -m .../mk 2010-04-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100420 * Merge with NetBSD make, pick up o fix for variable realpath() behavior. we have to stat(2) the result to be sure. o fix for .export (all) when nested vars use :sh 2010-04-14 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100414 * Merge with NetBSD make, pick up o use realpath to resolve argv[0] (for .MAKE) if needed. o add realpath from libc. o add :tA to resolve variable via realpath(3) if possible. 2010-04-08 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100408 * Merge with NetBSD make, pick up o unit tests for .ERROR, .error o fix for .ERROR to ensure it cannot be default target. 2010-04-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100406 * Merge with NetBSD make, pick up o fix for compat mode "Error code" going to debug_file. o fix for .ALLSRC being populated twice. o support for .info, .warning and .error directives o .MAKE.MODE to control make's operational mode o .MAKE.MAKEFILE_PREFERENCE to control the preferred makefile name(s). o .MAKE.DEPENDFILE to control the name of the depend file o .ERROR target - run on failure. 2010-03-18 Simon J. Gerraty * make-bootstrap.sh.in: extract MAKE_VERSION from Makefile * os.sh,arch.c: patch for Haiku from joerg at netbsd 2010-03-17 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100222 * Merge with NetBSD make, pick up o better error msg for .for with mutiple inter vars * boot-strap: o use make-bootstrap.sh from joerg at netbsd to avoid the need for a native make when bootstrapping. o add "" everywhere ;-) o if /usr/share/tmac/andoc.tmac exists install nroff bmake.1 otherwise the pre-formated version. 2010-01-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100102 * Merge with NetBSD make, pick up: o fix for -m .../ 2009-11-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20091118 * Merge with NetBSD make, pick up: o .unexport o report lines that start with '.' and should have ':' (catch typo's of .el*if). 2009-10-30 Simon J. Gerraty * configure.in: Ensure that srcdir and mksrc are absolute paths. 2009-10-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): fix version to 20091007 2009-10-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 200910007 * Merge with NetBSD make, pick up: o fix for parsing of :S;...;...; applied to .for loop iterator appearing in a dependency line. 2009-09-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090909 * Merge with NetBSD make, pick up: o fix for -C, .CURDIR and .OBJDIR * boot-strap: o allow share_dir to be set independent of prefix. o select default share_dir better when prefix ends in $HOST_TARGET o if FORCE_BSD_MK etc were set, include them in the suggested install-mk command. 2009-09-08 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090908 * Merge with NetBSD make, pick up: o .MAKE.LEVEL for recursion tracking o fix for :M scanning \: 2009-09-03 Simon J. Gerraty * configure.in: Don't -D__EXTENSIONS__ if AC_USE_SYSTEM_EXTENSIONS says "no". 2009-08-26 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090826 Simplify MAKE_VERSION to just the bare date. * Merge with NetBSD make, pick up: o -C directory support. o support for SIGINFO o use $TMPDIR for temp files. o child of vfork should be careful about modifying parent's state. 2009-03-26 Simon J. Gerraty * Appy some patches for MiNT from David Brownlee 2009-02-26 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20090222 * Merge with NetBSD make, pick up: o Possible null pointer de-ref in Var_Set. 2009-02-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20090204 * Merge with NetBSD make, pick up: o bmake_malloc et al moved to their own .c o Count both () and {} when looking for the end of a :M pattern o Change 'Buffer' so that it is the actual struct, not a pointer to it. o strlist.c - functions for processing extendable arrays of pointers to strings. o ClientData replaced with void *, so const void * can be used. o New debug flag C for DEBUG_CWD 2008-11-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081111 Apply patch from Joerg Sonnenberge to configure.in: o remove some redundant checks o check for emlloc etc only in libutil and require the whole family. util.c: o remove [v]asprintf which is no longer used. 2008-11-04 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081101 * Merge with NetBSD make, pick up: o util.c: avoid use of putenv() - christos 2008-10-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081030 pick up man page tweaks. 2008-10-29 Simon J. Gerraty * Makefile.in: move processing of LIBOBJS to after is definition! thus we'll have getenv.c in SRCS only if needed. * make.1: add examples of how to use :? * Makefile.in (BMAKE_VERSION): bump version to 20081029 * Merge with NetBSD make, pick up: o fix for .END processing with -j o segfault from Parse_Error when no makefile is open o handle numeric expressions in any variable expansion o debug output now defaults to stderr, -dF to change it - apb o make now uses bmake_malloc etc so that it can build natively on A/UX - wasn't an issue for bmake, but we want to keep in sync. 2008-09-27 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080808 * Merge with NetBSD make, pick up: o fix for PR/38840: Pierre Pronchery: make crashes while parsing long lines in Makefiles o optimizations for VarQuote by joerg o fix for PR/38756: dominik: make dumps core on invalid makefile 2008-05-15 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080515 * Merge with NetBSD make, pick up: o fix skip setting vars in VAR_GLOBAL context, to handle cases where VAR_CMD is used for other than command line vars. 2008-05-14 Simon J. Gerraty * boot-strap (make_version): we may need to look in $prefix/share/mk for sys.mk * Makefile.in (BMAKE_VERSION): bump version to 20080514 * Merge with NetBSD make, pick up: o skip setting vars in VAR_GLOBAL context, when already set in VAR_CMD which takes precedence. 2008-03-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080330 * Merge with NetBSD make, pick up: o fix for ?= when LHS contains variable reference. 2008-02-15 Simon J. Gerraty * merge some patches from NetBSD pkgsrc. * makefile.boot.in (BOOTSTRAP_SYS_PATH): Allow better control of the MAKSYSPATH used during bootstrap. * Makefile.in (BMAKE_VERSION): bump version to 20080215 * Merge with NetBSD make, pick up: o warn if non-space chars follow 'empty' in a conditional. 2008-01-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080118 * Merge with NetBSD make, pick up: o consider dependencies read from .depend as optional - dsl o remember when buffer for reading makefile grows - dsl o add -dl (aka LOUD) - David O'Brien 2007-10-22 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071022 * Merge with NetBSD make, pick up: o Allow .PATH to be used for .include "" * boot-strap: source default settings from .bmake-boot-strap.rc 2007-10-16 Simon J. Gerraty * Makefile.in: fix maninstall on various systems provided that our man.mk is used. For non-BSD systems we install the preformatted page into $MANDIR/cat1 2007-10-15 Simon J. Gerraty * boot-strap: make bmake.1 too, so maninstall works. 2007-10-14 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071014 * Merge with NetBSD make, pick up: o revamped handling of defshell - configure no longer needs to know the content of the shells array - apb o stop Var_Subst modifying its input - apb o avoid calling ParseTrackInput too often - dsl 2007-10-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071011 * Merge with NetBSD make, pick up: o fix Shell_Init for case that _BASENAME_DEFSHELL is absolute path. * sigcompat.c: some tweaks for HP-UX 11.x based on patch from Tobias Nygren * configure.in: update handling of --with-defshell to match new make behavior. --with-defshell=/usr/xpg4/bin/sh will now do what one might hope - provided the chosen shell behaves enough like sh. 2007-10-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20071008 * Merge with NetBSD make, pick up: o .MAKE.JOB.PREFIX - control the token output before jobs - sjg o .export/.MAKE.EXPORTED - export of variables - sjg o .MAKE.MAKEFILES - track all makefiles read - sjg o performance improvements - dsl o revamp parallel job scheduling - dsl 2006-07-28 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060728 * Merge with NetBSD make, pick up: o extra debug info during variable and cond processing - sjg o shell definition now covers newline - rillig o minor mem leak in PrintOnError - sjg 2006-05-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060511 * Merge with NetBSD make, pick up: o more memory leaks - coverity o possible overflow in ArchFindMember - coverity o extract variable modifier code out of Var_Parse() so it can be called recursively - sjg o unit-tests/moderrs - sjg 2006-04-12 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060412 * Merge with NetBSD make, pick up: o fixes for some memory leaks - coverity o only read first sys.mk etc when searching sysIncPath - sjg * main.c (ReadMakefile): remove hack for __INTERIX that prevented setting ${MAKEFILE} - OBATA Akio 2006-03-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060318 * Merge with NetBSD make, pick up: o cleanup of job.c to remove remote handling, distcc is more useful and this code was likely bit-rotting - dsl o fix for :P modifier - sjg * boot-strap: set default prefix to something reasonable (for me anyway). 2006-03-01 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060301 * Merge with NetBSD make, pick up: o make .WAIT apply recursively, document and test case - apb o allow variable modifiers in a variable appear anywhere in modifier list, document and test case - sjg 2006-02-22 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060222 * Merge with NetBSD make, pick up: o improved job token handling - dsl o SIG_DFL the correct signal before exec - dsl o more debug info during parsing - dsl o allow variable modifiers to be specified via variable - sjg * boot-strap: explain why we died if no mksrc 2005-11-05 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051105 * configure.in: always set default_sys_path default is ${prefix}/share/mk - remove prefix_sys_path, anyone wanting more than above needs to set it manually. 2005-11-04 Simon J. Gerraty * boot-strap: make this a bit easier for pkgsrc folk. bootstrap still fails on IRIX64 since MACHINE_ARCH gets set to 'mips' while pkgsrc wants 'mipseb' or 'mipsel' 2005-11-02 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051102 * job.c (JobFinish): fix likely ancient merge lossage fix from Todd Vierling. * boot-strap (srcdir): allow setting mksrc=none 2005-10-31 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051031 * ranlib.h: skip on OSF too. (NetBSD PR 31864) 2005-10-10 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051002 fix a silly typo 2005-10-09 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051001 support for UnixWare and some other systems, based on patches from pkgsrc/bootstrap 2005-09-03 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050901 * Merge with NetBSD make, pick up: o possible parse error causing us to wander off. 2005-06-06 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050606 * Merge with NetBSD make, pick up: o :0x modifier for randomizing a list o fixes for a number of -Wuninitialized issues. 2005-05-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050530 * Merge with NetBSD make, pick up: o Handle dependencies for .BEGIN, .END and .INTERRUPT * README: was seriously out of date. 2005-03-22 Simon J. Gerraty * Important to use .MAKE rather than MAKE. 2005-03-15 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050315 * Merge with NetBSD make, pick up: o don't mistake .elsefoo for .else o use suffix-specific search path correctly o bunch of style nits 2004-05-11 Simon J. Gerraty * boot-strap: o ensure that args to --src and --with-mksrc are resolved before giving them to configure. o add -o "objdir" so that builder can control it, default is $OS as determined by os.sh o add -q to suppress all the install instructions. 2004-05-08 Simon J. Gerraty * Remove __IDSTRING() * Makefile.in (BMAKE_VERSION): bump to 20040508 * Merge with NetBSD make, pick up: o posix fixes - remove '-e' from compat mode - add support for '+' command-line prefix. o fix for handling '--' on command-line. o fix include in lst.lib/lstInt.h to simplify '-I's o we also picked up replacement of MAKE_BOOTSTRAP with !MAKE_NATIVE which is a noop, but possibly confusing. 2004-04-14 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040414 * Merge with NetBSD make, pick up: o allow quoted strings on lhs of conditionals o issue warning when extra .else is seen o print line numer when errors encountered during parsing from string. 2004-02-20 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040220 * Merge with NetBSD make, pick up: o fix for old :M parsing bug. o re-jigged unit-tests 2004-02-15 Simon J. Gerraty * Makefile.in (accept test): use ${.MAKE:S,^./,${.CURDIR}/,} so that './bmake -f Makefile test' works. 2004-02-14 Simon J. Gerraty * Makefile.in: (BMAKE_VERSION): bump to 20040214 * Merge with NetBSD make, pick up: o search upwards for *.mk o fix for double free of var substitution buffers o use of getopt replaced with custom code, since the usage (re-scanning) isn't posix compatible. 2004-02-12 Simon J. Gerraty * arch.c: don't include ranlib.h on ELF systems (thanks to Chuck Cranor ). 2004-01-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040118 * boot-strap (while): export vars we assign to on cmdline * unit-test/Makefile.in: ternary is .PHONY 2004-01-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20040108 * Merge with NetBSD make, pick up: o fix for ternary modifier 2004-01-06 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20040105 * Merge with NetBSD make, pick up: o fix for cond.c to handle compound expressions better o variable expansion within sysV style replacements 2003-12-22 Simon J. Gerraty * Make portable snprintf safer - output to /dev/null first to check space needed. * Makefile.in (BMAKE_VERSION): bump version to 20031222 * Merge with NetBSD make, pick up: o -dg3 to show input graph when things go wrong. o explicitly look for makefiles in objdir if not found in curdir so that errors in .depend etc will be reported accurarely. o avoid use of -e in shell scripts in jobs mode, use '|| exit $?' instead as it more accurately reflects the expected behavior and is more consistently implemented. o avoid use of asprintf. 2003-09-28 Simon J. Gerraty * util.c: Add asprintf and vasprintf. * Makefile.in (BMAKE_VERSION): bump version to 20030928 * Merge with NetBSD make, pick up: :[] modifier - allows picking words from a variable. :tW modifier - allows treating value as one big word. W flag for :C and :S - allows treating value as one big word. 2003-09-12 Simon J. Gerraty * Merge with NetBSD make pick up -de flag to enable printing failed command. don't skip 1st two dir entries (normally . and ..) since coda does not have them. 2003-09-09 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20030909 * Merge with NetBSD make, pick up: - changes for -V '${VAR}' to print fully expanded value cf. -V VAR - CompatRunCommand now prints the command that failed. - several files got updated 3 clause Berkeley license. 2003-08-02 Simon J. Gerraty * boot-strap: Allow setting configure args on command line. 2003-07-31 Simon J. Gerraty * configure.in: add --with-defshell to allow sh or ksh to be selected as default shell. * Makefile.in: bump version to 20030731 * Merge with NetBSD make Pick up .SHELL spec for ksh and associate man page changes. Also compat mode now uses the same shell specs. 2003-07-29 Simon J. Gerraty * var.c (Var_Parse): ensure delim is initialized. * unit-tests/Makefile.in: use single quotes to avoid problems from some shells. * makefile.boot.in: Run the unit-tests as part of the bootstrap procedure. 2003-07-28 Simon J. Gerraty * unit-tests/Makefile.in: always force complaints from ${TEST_MAKE} to be from 'make'. * configure.in: add check for 'diff -u' also fix some old autoconf'isms * Makefile.in (BMAKE_VERSION): bump version to 20030728. if using GCC add -Wno-cast-qual to CFLAGS for var.o * Merge with NetBSD make Pick up fix for :ts parsing error in some cases. Pick unit-tests. 2003-07-23 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20030723. * var.c (Var_Parse): fix bug in :ts modifier, after const correctness fixes, must pass nstr to VarModify. 2003-07-14 Simon J. Gerraty * Makefile.in: BMAKE_VERSION switch to a date based version. We'll generally use the date of last import from NetBSD. * Merge with NetBSD make Pick up fixes for const-correctness, now passes WARNS=3 on NetBSD. Pick up :ts modifier, allows controlling the separator used between words in variable expansion. 2003-07-11 Simon J. Gerraty * FILES: include boot-strap and os.sh * Makefile.in: only set WARNS if we are NetBSD, the effect on FreeBSD is known to be bad. * makefile.boot.in (bootstrap): make this the default target. * Makefile.in: bump version to 3.1.19 * machine.sh: avoid A-Z with tr as it is bound to lose. 2003-07-10 Simon J. Gerraty * Merge with NetBSD make Pick up fix for PR/19781 - unhelpful error msg on unclosed ${var:foo Plus some doc fixes. 2003-04-27 Simon J. Gerraty * Merge with NetBSD make Pick up fix for PR/1523 - don't count a library as built, if there is no way to build it * Bump version to 3.1.18 2003-03-23 Simon J. Gerraty * Merge with NetBSD make Pick up fix for ParseDoSpecialSrc - we only use it if .WAIT appears in src list. 2003-03-21 Simon J. Gerraty * Merge with NetBSD make (mmm 10th anniversary!) pick up fix for .WAIT in srcs that refer to $@ or $* (PR#20828) pick up -X which tells us to not export VAR=val via setenv if we are already doing so via MAKEFLAGS. This saves valuable env space on systems like Darwin. set MAKE_VERSION to 3.1.17 * parse.c: pix up fix for suffix rules 2003-03-06 Simon J. Gerraty * Merge with NetBSD make. pick up fix for propagating -B via MAKEFLAGS. set MAKE_VERSION to 3.1.16 * Apply some patches from pkgsrc-bootstrap/bmake Originally by Grant Beattie I may have missed some - since they are based on bmake-3.1.12 2002-12-03 Simon J. Gerraty * makefile.boot.in (bmake): update install targets for those that use them, also clear MAKEFLAGS when invoking bmake.boot to avoid havoc from gmake -w. Thanks to Harlan Stenn . * bmake.cat1: update the pre-formatted man page! 2002-11-30 Simon J. Gerraty * Merge with NetBSD make. pick up fix for premature free of pointer used in call to Dir_InitCur(). set MAKE_VERSION to 3.1.15 2002-11-26 Simon J. Gerraty * configure.in: determine suitable value for MKSRC. override using --with-mksrc=PATH. * machine.sh: use `uname -p` for MACHINE_ARCH on modern SunOS systems. configs(8) will use 'sun4' as an alias for 'sparc'. 2002-11-25 Simon J. Gerraty * Merge with NetBSD make. pick up ${.PATH} pick up fix for finding ../cat.c via .PATH when .CURDIR=.. set MAKE_VERSION to 3.1.14 add configure checks for killpg and sys/socket.h 2002-09-16 Simon J. Gerraty * tag bmake-3-1-13 * makefile.boot.in (bmake): use install-mk Also setup ./mk before trying to invoke bmake.boot incase we needed install-mk to create a sys.mk for us. * configure.in: If we need to add -I${srcdir}/missing, make it an absolute path so that it works for lst.lib too. * make.h: always include sys/cdefs.h since we provide one if the host does not. * Makefile.in (install-mk): use MKSRC/install-mk which will do the right thing. use uname -p for ARCH if possible. since install-mk will setup links bsd.prog.mk -> prog.mk if needed, just .include bsd.prog.mk * Merge with NetBSD make (NetBSD-1.6) Code is ansi-C only now. Bug in handling of dotLast is fixed. Can now assign .OBJDIR and make will reset its notions of life. New modifiers :tu :tl for toUpper and toLower. Tue Oct 16 12:18:42 2001 Simon J. Gerraty * Merge with NetBSD make pick up fix for .END failure in compat mode. pick up fix for extra va_end() in ParseVErrorInternal. Thu Oct 11 13:20:06 2001 Simon J. Gerraty * configure.in: for systems that have sys/cdefs.h check if it is compatible. If not, include the one under missing, but tell it to include the native one too - necessary on Linux. * missing/sys/cdefs.h: if NEED_HOST_CDEFS_H is defined, use include_next (for gcc) to get the native sys/cdefs.h Tue Aug 21 02:29:34 2001 Simon J. Gerraty * job.c (JobFinish): Fix an earlier merge bug that resulted in leaking descriptors when using -jN. * job.c (JobPrintCommand): See if "curdir" exists before attempting to chdir(). Doing the chdir directly in make (when in compat mode) fails silently, so let the -jN version do the same. This can happen when building kernels in an object tree and playing clever games to reset .CURDIR. * Merged with NetBSD make pick up .USEBEFORE Tue Jun 26 23:45:11 2001 Simon J. Gerraty * makefile.boot.in: Give bmake.boot a MAKESYSPATH that might work. Tue Jun 12 16:48:57 2001 Simon J. Gerraty * var.c (Var_Set): Add 4th (flags) arg so VarLoopExpand can tell us not to export the iterator variable when using VAR_CMD context. Sun Jun 10 21:55:21 2001 Simon J. Gerraty * job.c (Job_CatchChildren): don't call Job_CatchOutput() here, its the wrong "fix". Sat Jun 9 00:11:24 2001 Simon J. Gerraty * Redesigned export of VAR_CMD's via MAKEFLAGS. We now simply append the variable names to .MAKEOVERRIDES, and handle duplicate suppression and quoting in ExportMAKEFLAGS using: ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@} Apart from fixing quoting bugs in previous version, this allows us to export vars to the environment by simply doing: .MAKEOVERRIDES+= PATH Merged again with NetBSD make, but the above is the only change. * configure.in: added --disable-pwd-override disable $PWD overriding getcwd() --disable-check-make-chdir disable make trying to guess when it should automatically cd ${.CURDIR} * Merge with NetBSD make, changes include: parse.c (ParseDoDependency): Spot that the syntax error is caused by an unresolved cvs/rcs conflict and say so. var.c: most of Var* functions now take a ctxt as 1st arg. now does variable substituion on rhs of sysv style modifiers. * var.c (Var_Set): exporting of command line variables (VAR_CMD) is now done here. We append the name='value' to .MAKEOVERRIDES rather than directly into MAKEFLAGS as this allows a Makefile to use .MAKEOVERRIDES= to disable this behaviour. GNU make uses a very similar mechanism. Note that in adding name='value' to .MAKEOVERRIDES we do the moral equivalent of: .MAKEOVERRIDES:= ${.MAKEOVERRIDES:Nname=*} name='val' Fri Jun 1 14:08:02 2001 Simon J. Gerraty * make-conf.h (USE_IOVEC): make it conditional on HAVE_SYS_UIO_H * Merged with NetBSD make make -dx can now be used to run commands via sh -x better error messages on exec failures. Thu May 31 01:44:54 2001 Simon J. Gerraty * Makefile.in (main.o): depends on ${SRCS} ${MAKEFILE} so that MAKE_VERSION gets updated. Also don't use ?= for MAKE_VERSION, MACHINE etc otherwise they propagate from the previous bmake. * configure.in (machine): allow --with-machine=generic to make configure use machine.sh to set MACHINE. * job.c (JobInterrupt): convert to using WAIT_T and friends. * Makefile.in: mention in bmake.1 that we use autoconf. * make.1: mention MAKE_PRINT_VAR_ON_ERROR. Wed May 30 23:17:18 2001 Simon J. Gerraty * main.c (ReadMakefile): don't set MAKEFILE if reading ".depend" as that rather defeats the usefulness of ${MAKEFILE}. * main.c (MainParseArgs): append command line variable assignments to MAKEFLAGS so that they get propagated to child make's. Apparently this is required POSIX behaviour? Its useful anyway. Tue May 29 02:20:07 2001 Simon J. Gerraty * compat.c (CompatRunCommand): don't use perror() since stdio may cause problems in child of vfork(). * compat.c, main.c: Call PrintOnError() when we are going to bail. This routine prints out the .curdir where we stopped and will also display any vars listed in ${MAKE_PRINT_VAR_ON_ERROR}. * main.c: add ${.newline} to hold a "\n" - sometimes handy in :@ expansion. * var.c: VarLoopExpand: ignore addSpace if a \n is present. * Added RCSid's for the files we've touched. Thu May 24 15:41:37 2001 Simon J. Gerraty * configure.in: Thanks to some clues from mdb@juniper.net, added autoconf magic to control setting of MACHINE, MACHINE_ARCH as well as what ends up in _PATH_DEFSYSPATH. We now have: --with-machine=MACHINE explicitly set MACHINE --with-force-machine=MACHINE set FORCE_MACHINE --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX If _PATH_OBJDIRPREFIX is set to "no" we won't define it. * makefile: added a pathetically simple makefile to drive bootstrapping. Running configure by hand is more useful. * Makefile.in: added MAKE_VERSION, and reworked things to be less dependent on NetBSD bsd.*.mk * pathnames.h: allow NO_PATH_OBJDIRPREFIX to stop us defining _PATH_OBJDIRPREFIX for those that don't want a default. construct _PATH_DEFSYSPATH from the info we get from configure. * main.c: allow for no _PATH_OBJDIRPREFIX, set ${MAKE_VERSION} if MAKE_VERSION is defined. * compat.c: when we bail, print out the .CURDIR we were in. Sat May 12 00:34:12 2001 Simon J. Gerraty * Merged with NetBSD make * var.c: fixed a bug in the handling of the modifier :P if the node as found but the path was null, we segfault trying to duplicate it. Mon Mar 5 16:20:33 2001 Simon J. Gerraty * Merged with NetBSD make * make.c: Make_OODate's test for a library out of date was using cmtime where it should have used mtime (my bug). * compat.c: Use perror() to tell us what really went wrong when we cannot exec a command. Fri Dec 15 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Sat Jun 10 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Thu Jun 1 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Tue May 30 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Thu Apr 27 00:07:47 2000 Simon J. Gerraty * util.c: don't provide signal() since we use sigcompat.c * Makefile.in: added a build target. * var.c (Var_Parse): added ODE modifiers :U, :D, :L, :P, :@ and :! These allow some quite clever magic. * main.c (main): added support for getenv(MAKESYSPATH). Mon Apr 2 16:25:13 2000 Simon J. Gerraty * Disable $PWD overriding getcwd() if MAKEOBJDIRPREFIX is set. This avoids objdir having a different value depending on how a directory was reached (via command line, or subdir.mk). * If FORCE_MACHINE is defined, ignore getenv("MACHINE"). Mon Apr 2 23:15:31 2000 Simon J. Gerraty * Do a chdir(${.CURDIR}) before invoking ${.MAKE} or ${.MAKE:T} if MAKEOBJDIRPREFIX is set and NOCHECKMAKECHDIR is not. I've been testing this in NetBSD's make for some weeks. * Turn Makefile into Makefile.in and make it useful. Tue Feb 29 22:08:00 2000 Simon J. Gerraty * Imported NetBSD's -current make(1) and resolve conflicts. * Applied autoconf patches from bmake v2 * Imported clean code base from NetBSD-1.0 diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES index 7a6ca8ad6212..b88aa64271bf 100644 --- a/contrib/bmake/FILES +++ b/contrib/bmake/FILES @@ -1,869 +1,873 @@ ChangeLog FILES LICENSE Makefile Makefile.config.in PSD.doc/Makefile PSD.doc/tutorial.ms README VERSION _strtol.h aclocal.m4 arch.c bmake.1 bmake.cat1 boot-strap bsd.after-import.mk buf.c buf.h compat.c cond.c config.h.in configure configure.in dir.c dir.h dirname.c filemon/filemon.h filemon/filemon_dev.c filemon/filemon_ktrace.c find_lib.sh for.c getopt.c hash.c hash.h install-sh job.c job.h lst.c lst.h machine.sh main.c make-bootstrap.sh.in make-conf.h make.1 make.c make.h make_malloc.c make_malloc.h makefile.in meta.c meta.h metachar.c metachar.h missing/sys/cdefs.h mkdeps.sh os.sh parse.c pathnames.h ranlib.h realpath.c setenv.c sigact.h sigaction.c sigcompat.c str.c str.h stresep.c strlcpy.c suff.c targ.c trace.c trace.h unit-tests/Makefile unit-tests/Makefile.config.in 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 unit-tests/cmd-errors.mk unit-tests/cmd-interrupt.exp unit-tests/cmd-interrupt.mk unit-tests/cmdline-redirect-stdin.exp unit-tests/cmdline-redirect-stdin.mk unit-tests/cmdline-undefined.exp unit-tests/cmdline-undefined.mk 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 unit-tests/cond-cmp-numeric-ge.mk unit-tests/cond-cmp-numeric-gt.exp unit-tests/cond-cmp-numeric-gt.mk unit-tests/cond-cmp-numeric-le.exp unit-tests/cond-cmp-numeric-le.mk unit-tests/cond-cmp-numeric-lt.exp unit-tests/cond-cmp-numeric-lt.mk unit-tests/cond-cmp-numeric-ne.exp unit-tests/cond-cmp-numeric-ne.mk unit-tests/cond-cmp-numeric.exp unit-tests/cond-cmp-numeric.mk unit-tests/cond-cmp-string.exp unit-tests/cond-cmp-string.mk unit-tests/cond-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 unit-tests/cond-func-defined.mk 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 unit-tests/cond-func-target.mk unit-tests/cond-func.exp unit-tests/cond-func.mk unit-tests/cond-late.exp unit-tests/cond-late.mk unit-tests/cond-op-and-lint.exp unit-tests/cond-op-and-lint.mk unit-tests/cond-op-and.exp unit-tests/cond-op-and.mk unit-tests/cond-op-not.exp unit-tests/cond-op-not.mk unit-tests/cond-op-or-lint.exp unit-tests/cond-op-or-lint.mk unit-tests/cond-op-or.exp unit-tests/cond-op-or.mk unit-tests/cond-op-parentheses.exp unit-tests/cond-op-parentheses.mk unit-tests/cond-op.exp unit-tests/cond-op.mk unit-tests/cond-short.exp unit-tests/cond-short.mk unit-tests/cond-token-number.exp unit-tests/cond-token-number.mk unit-tests/cond-token-plain.exp unit-tests/cond-token-plain.mk unit-tests/cond-token-string.exp unit-tests/cond-token-string.mk unit-tests/cond-token-var.exp unit-tests/cond-token-var.mk unit-tests/cond-undef-lint.exp unit-tests/cond-undef-lint.mk unit-tests/counter-append.exp unit-tests/counter-append.mk unit-tests/counter.exp unit-tests/counter.mk unit-tests/dep-colon-bug-cross-file.exp unit-tests/dep-colon-bug-cross-file.mk unit-tests/dep-colon.exp unit-tests/dep-colon.mk unit-tests/dep-double-colon-indep.exp unit-tests/dep-double-colon-indep.mk unit-tests/dep-double-colon.exp unit-tests/dep-double-colon.mk unit-tests/dep-duplicate.exp unit-tests/dep-duplicate.mk unit-tests/dep-exclam.exp unit-tests/dep-exclam.mk unit-tests/dep-none.exp unit-tests/dep-none.mk unit-tests/dep-op-missing.exp unit-tests/dep-op-missing.mk unit-tests/dep-percent.exp unit-tests/dep-percent.mk unit-tests/dep-var.exp unit-tests/dep-var.mk unit-tests/dep-wildcards.exp unit-tests/dep-wildcards.mk unit-tests/dep.exp unit-tests/dep.mk unit-tests/depsrc-end.exp unit-tests/depsrc-end.mk unit-tests/depsrc-exec.exp unit-tests/depsrc-exec.mk unit-tests/depsrc-ignore.exp unit-tests/depsrc-ignore.mk unit-tests/depsrc-made.exp unit-tests/depsrc-made.mk unit-tests/depsrc-make.exp unit-tests/depsrc-make.mk unit-tests/depsrc-meta.exp unit-tests/depsrc-meta.mk unit-tests/depsrc-nometa.exp unit-tests/depsrc-nometa.mk unit-tests/depsrc-nometa_cmp.exp unit-tests/depsrc-nometa_cmp.mk unit-tests/depsrc-nopath.exp unit-tests/depsrc-nopath.mk unit-tests/depsrc-notmain.exp unit-tests/depsrc-notmain.mk unit-tests/depsrc-optional.exp unit-tests/depsrc-optional.mk unit-tests/depsrc-phony.exp unit-tests/depsrc-phony.mk unit-tests/depsrc-precious.exp unit-tests/depsrc-precious.mk unit-tests/depsrc-recursive.exp unit-tests/depsrc-recursive.mk unit-tests/depsrc-silent.exp unit-tests/depsrc-silent.mk unit-tests/depsrc-use.exp unit-tests/depsrc-use.mk unit-tests/depsrc-usebefore-double-colon.exp unit-tests/depsrc-usebefore-double-colon.mk unit-tests/depsrc-usebefore.exp unit-tests/depsrc-usebefore.mk unit-tests/depsrc-wait.exp unit-tests/depsrc-wait.mk unit-tests/depsrc.exp 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 unit-tests/deptgt-end.mk unit-tests/deptgt-error.exp unit-tests/deptgt-error.mk unit-tests/deptgt-ignore.exp unit-tests/deptgt-ignore.mk unit-tests/deptgt-interrupt.exp unit-tests/deptgt-interrupt.mk unit-tests/deptgt-main.exp unit-tests/deptgt-main.mk unit-tests/deptgt-makeflags.exp unit-tests/deptgt-makeflags.mk unit-tests/deptgt-no_parallel.exp unit-tests/deptgt-no_parallel.mk unit-tests/deptgt-nopath.exp unit-tests/deptgt-nopath.mk unit-tests/deptgt-notparallel.exp unit-tests/deptgt-notparallel.mk unit-tests/deptgt-objdir.exp unit-tests/deptgt-objdir.mk unit-tests/deptgt-order.exp unit-tests/deptgt-order.mk unit-tests/deptgt-path-suffix.exp unit-tests/deptgt-path-suffix.mk unit-tests/deptgt-path.exp unit-tests/deptgt-path.mk unit-tests/deptgt-phony.exp unit-tests/deptgt-phony.mk unit-tests/deptgt-posix.exp unit-tests/deptgt-posix.mk unit-tests/deptgt-precious.exp unit-tests/deptgt-precious.mk unit-tests/deptgt-shell.exp unit-tests/deptgt-shell.mk unit-tests/deptgt-silent-jobs.exp unit-tests/deptgt-silent-jobs.mk unit-tests/deptgt-silent.exp unit-tests/deptgt-silent.mk unit-tests/deptgt-stale.exp unit-tests/deptgt-stale.mk unit-tests/deptgt-suffixes.exp unit-tests/deptgt-suffixes.mk unit-tests/deptgt.exp unit-tests/deptgt.mk unit-tests/dir-expand-path.exp unit-tests/dir-expand-path.mk unit-tests/dir.exp unit-tests/dir.mk unit-tests/directive-dinclude.exp unit-tests/directive-dinclude.mk unit-tests/directive-elif.exp unit-tests/directive-elif.mk unit-tests/directive-elifdef.exp unit-tests/directive-elifdef.mk unit-tests/directive-elifmake.exp unit-tests/directive-elifmake.mk unit-tests/directive-elifndef.exp unit-tests/directive-elifndef.mk 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 unit-tests/directive-error.mk unit-tests/directive-export-env.exp unit-tests/directive-export-env.mk unit-tests/directive-export-gmake.exp unit-tests/directive-export-gmake.mk unit-tests/directive-export-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-break.exp +unit-tests/directive-for-break.mk unit-tests/directive-for-empty.exp unit-tests/directive-for-empty.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-if.exp unit-tests/directive-for-if.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 unit-tests/directive-hyphen-include.mk unit-tests/directive-if-nested.exp unit-tests/directive-if-nested.mk unit-tests/directive-if.exp unit-tests/directive-if.mk unit-tests/directive-ifdef.exp unit-tests/directive-ifdef.mk unit-tests/directive-ifmake.exp unit-tests/directive-ifmake.mk unit-tests/directive-ifndef.exp unit-tests/directive-ifndef.mk unit-tests/directive-ifnmake.exp unit-tests/directive-ifnmake.mk unit-tests/directive-include-fatal.exp unit-tests/directive-include-fatal.mk unit-tests/directive-include.exp unit-tests/directive-include.mk unit-tests/directive-info.exp unit-tests/directive-info.mk unit-tests/directive-misspellings.exp unit-tests/directive-misspellings.mk unit-tests/directive-sinclude.exp unit-tests/directive-sinclude.mk unit-tests/directive-undef.exp unit-tests/directive-undef.mk unit-tests/directive-unexport-env.exp unit-tests/directive-unexport-env.mk unit-tests/directive-unexport.exp unit-tests/directive-unexport.mk unit-tests/directive-warning.exp unit-tests/directive-warning.mk unit-tests/directive.exp unit-tests/directive.mk unit-tests/dollar.exp unit-tests/dollar.mk unit-tests/doterror.exp unit-tests/doterror.mk unit-tests/dotwait.exp unit-tests/dotwait.mk unit-tests/error.exp unit-tests/error.mk unit-tests/escape.exp unit-tests/escape.mk unit-tests/export-all.exp unit-tests/export-all.mk unit-tests/export-env.exp unit-tests/export-env.mk unit-tests/export-variants.exp unit-tests/export-variants.mk unit-tests/export.exp unit-tests/export.mk unit-tests/forloop.exp unit-tests/forloop.mk unit-tests/forsubst.exp unit-tests/forsubst.mk unit-tests/gnode-submake.exp unit-tests/gnode-submake.mk unit-tests/hanoi-include.exp unit-tests/hanoi-include.mk unit-tests/impsrc.exp unit-tests/impsrc.mk unit-tests/include-main.exp unit-tests/include-main.mk -unit-tests/include-sub.mk -unit-tests/include-subsub.mk +unit-tests/include-sub.inc +unit-tests/include-subsub.inc 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/job-output-null.exp unit-tests/job-output-null.mk unit-tests/jobs-empty-commands-error.exp unit-tests/jobs-empty-commands-error.mk unit-tests/jobs-empty-commands.exp unit-tests/jobs-empty-commands.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 unit-tests/modmatch.mk unit-tests/modmisc.exp unit-tests/modmisc.mk unit-tests/objdir-writable.exp unit-tests/objdir-writable.mk unit-tests/opt-backwards.exp unit-tests/opt-backwards.mk unit-tests/opt-chdir.exp unit-tests/opt-chdir.mk unit-tests/opt-debug-all.exp unit-tests/opt-debug-all.mk unit-tests/opt-debug-archive.exp unit-tests/opt-debug-archive.mk unit-tests/opt-debug-cond.exp unit-tests/opt-debug-cond.mk unit-tests/opt-debug-curdir.exp unit-tests/opt-debug-curdir.mk unit-tests/opt-debug-dir.exp unit-tests/opt-debug-dir.mk unit-tests/opt-debug-errors-jobs.exp unit-tests/opt-debug-errors-jobs.mk unit-tests/opt-debug-errors.exp unit-tests/opt-debug-errors.mk unit-tests/opt-debug-file.exp unit-tests/opt-debug-file.mk unit-tests/opt-debug-for.exp unit-tests/opt-debug-for.mk unit-tests/opt-debug-graph1.exp unit-tests/opt-debug-graph1.mk unit-tests/opt-debug-graph2.exp unit-tests/opt-debug-graph2.mk unit-tests/opt-debug-graph3.exp unit-tests/opt-debug-graph3.mk unit-tests/opt-debug-hash.exp unit-tests/opt-debug-hash.mk unit-tests/opt-debug-jobs.exp unit-tests/opt-debug-jobs.mk unit-tests/opt-debug-lint.exp unit-tests/opt-debug-lint.mk unit-tests/opt-debug-loud.exp unit-tests/opt-debug-loud.mk unit-tests/opt-debug-making.exp unit-tests/opt-debug-making.mk unit-tests/opt-debug-meta.exp unit-tests/opt-debug-meta.mk unit-tests/opt-debug-no-rm.exp unit-tests/opt-debug-no-rm.mk unit-tests/opt-debug-parse.exp unit-tests/opt-debug-parse.mk unit-tests/opt-debug-suff.exp unit-tests/opt-debug-suff.mk unit-tests/opt-debug-targets.exp unit-tests/opt-debug-targets.mk unit-tests/opt-debug-var.exp unit-tests/opt-debug-var.mk unit-tests/opt-debug-varraw.exp unit-tests/opt-debug-varraw.mk unit-tests/opt-debug-x-trace.exp unit-tests/opt-debug-x-trace.mk unit-tests/opt-debug.exp unit-tests/opt-debug.mk unit-tests/opt-define.exp unit-tests/opt-define.mk unit-tests/opt-env.exp unit-tests/opt-env.mk unit-tests/opt-file.exp unit-tests/opt-file.mk unit-tests/opt-ignore.exp unit-tests/opt-ignore.mk 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-indirect.exp unit-tests/opt-keep-going-indirect.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-touch.exp unit-tests/opt-no-action-touch.mk unit-tests/opt-no-action.exp unit-tests/opt-no-action.mk unit-tests/opt-query.exp unit-tests/opt-query.mk unit-tests/opt-raw.exp unit-tests/opt-raw.mk unit-tests/opt-silent.exp unit-tests/opt-silent.mk unit-tests/opt-touch-jobs.exp unit-tests/opt-touch-jobs.mk unit-tests/opt-touch.exp unit-tests/opt-touch.mk unit-tests/opt-tracefile.exp unit-tests/opt-tracefile.mk unit-tests/opt-var-expanded.exp unit-tests/opt-var-expanded.mk unit-tests/opt-var-literal.exp unit-tests/opt-var-literal.mk unit-tests/opt-version.exp unit-tests/opt-version.mk unit-tests/opt-warnings-as-errors.exp unit-tests/opt-warnings-as-errors.mk unit-tests/opt-where-am-i.exp unit-tests/opt-where-am-i.mk unit-tests/opt-x-reduce-exported.exp unit-tests/opt-x-reduce-exported.mk unit-tests/opt.exp unit-tests/opt.mk unit-tests/order.exp unit-tests/order.mk unit-tests/parse-var.exp unit-tests/parse-var.mk unit-tests/parse.exp unit-tests/parse.mk unit-tests/phony-end.exp unit-tests/phony-end.mk unit-tests/posix.exp unit-tests/posix.mk unit-tests/posix1.exp unit-tests/posix1.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 unit-tests/sh-jobs.mk unit-tests/sh-leading-at.exp unit-tests/sh-leading-at.mk unit-tests/sh-leading-hyphen.exp unit-tests/sh-leading-hyphen.mk unit-tests/sh-leading-plus.exp unit-tests/sh-leading-plus.mk unit-tests/sh-meta-chars.exp unit-tests/sh-meta-chars.mk unit-tests/sh-multi-line.exp unit-tests/sh-multi-line.mk unit-tests/sh-single-line.exp unit-tests/sh-single-line.mk unit-tests/sh.exp unit-tests/sh.mk unit-tests/shell-csh.exp unit-tests/shell-csh.mk unit-tests/shell-custom.exp unit-tests/shell-custom.mk unit-tests/shell-ksh.exp unit-tests/shell-ksh.mk unit-tests/shell-sh.exp unit-tests/shell-sh.mk unit-tests/suff-add-later.exp unit-tests/suff-add-later.mk unit-tests/suff-clear-regular.exp unit-tests/suff-clear-regular.mk unit-tests/suff-clear-single.exp unit-tests/suff-clear-single.mk unit-tests/suff-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 unit-tests/suff-transform-expand.mk unit-tests/suff-transform-select.exp unit-tests/suff-transform-select.mk unit-tests/suff-use.exp unit-tests/suff-use.mk unit-tests/sunshcmd.exp unit-tests/sunshcmd.mk unit-tests/ternary.exp unit-tests/ternary.mk unit-tests/unexport-env.exp unit-tests/unexport-env.mk unit-tests/unexport.exp unit-tests/unexport.mk unit-tests/use-inference.exp unit-tests/use-inference.mk unit-tests/var-eval-short.exp unit-tests/var-eval-short.mk unit-tests/var-op-append.exp unit-tests/var-op-append.mk unit-tests/var-op-assign.exp unit-tests/var-op-assign.mk unit-tests/var-op-default.exp unit-tests/var-op-default.mk unit-tests/var-op-expand.exp unit-tests/var-op-expand.mk unit-tests/var-op-shell.exp unit-tests/var-op-shell.mk unit-tests/var-op-sunsh.exp unit-tests/var-op-sunsh.mk unit-tests/var-op.exp unit-tests/var-op.mk +unit-tests/var-readonly.exp +unit-tests/var-readonly.mk unit-tests/var-recursive.exp unit-tests/var-recursive.mk unit-tests/var-scope-cmdline.exp unit-tests/var-scope-cmdline.mk unit-tests/var-scope-env.exp unit-tests/var-scope-env.mk unit-tests/var-scope-global.exp unit-tests/var-scope-global.mk unit-tests/var-scope-local-legacy.exp unit-tests/var-scope-local-legacy.mk unit-tests/var-scope-local.exp unit-tests/var-scope-local.mk unit-tests/var-scope.exp unit-tests/var-scope.mk unit-tests/varcmd.exp unit-tests/varcmd.mk unit-tests/vardebug.exp unit-tests/vardebug.mk unit-tests/varfind.exp unit-tests/varfind.mk unit-tests/varmisc.exp unit-tests/varmisc.mk unit-tests/varmod-assign-shell.exp unit-tests/varmod-assign-shell.mk unit-tests/varmod-assign.exp unit-tests/varmod-assign.mk unit-tests/varmod-defined.exp unit-tests/varmod-defined.mk unit-tests/varmod-edge.exp unit-tests/varmod-edge.mk unit-tests/varmod-exclam-shell.exp unit-tests/varmod-exclam-shell.mk unit-tests/varmod-extension.exp unit-tests/varmod-extension.mk unit-tests/varmod-gmtime.exp unit-tests/varmod-gmtime.mk unit-tests/varmod-hash.exp unit-tests/varmod-hash.mk 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 unit-tests/varmod-localtime.mk unit-tests/varmod-loop-delete.exp unit-tests/varmod-loop-delete.mk unit-tests/varmod-loop-varname.exp unit-tests/varmod-loop-varname.mk unit-tests/varmod-loop.exp unit-tests/varmod-loop.mk unit-tests/varmod-match-escape.exp unit-tests/varmod-match-escape.mk unit-tests/varmod-match.exp unit-tests/varmod-match.mk unit-tests/varmod-no-match.exp unit-tests/varmod-no-match.mk unit-tests/varmod-order-numeric.exp unit-tests/varmod-order-numeric.mk unit-tests/varmod-order-reverse.exp unit-tests/varmod-order-reverse.mk unit-tests/varmod-order-shuffle.exp unit-tests/varmod-order-shuffle.mk unit-tests/varmod-order-string.exp unit-tests/varmod-order-string.mk unit-tests/varmod-order.exp unit-tests/varmod-order.mk unit-tests/varmod-path.exp unit-tests/varmod-path.mk unit-tests/varmod-quote-dollar.exp unit-tests/varmod-quote-dollar.mk unit-tests/varmod-quote.exp unit-tests/varmod-quote.mk unit-tests/varmod-range.exp unit-tests/varmod-range.mk unit-tests/varmod-remember.exp unit-tests/varmod-remember.mk unit-tests/varmod-root.exp unit-tests/varmod-root.mk unit-tests/varmod-select-words.exp unit-tests/varmod-select-words.mk unit-tests/varmod-shell.exp unit-tests/varmod-shell.mk unit-tests/varmod-subst-regex.exp unit-tests/varmod-subst-regex.mk unit-tests/varmod-subst.exp unit-tests/varmod-subst.mk unit-tests/varmod-sun-shell.exp unit-tests/varmod-sun-shell.mk unit-tests/varmod-sysv.exp unit-tests/varmod-sysv.mk unit-tests/varmod-tail.exp unit-tests/varmod-tail.mk unit-tests/varmod-to-abs.exp unit-tests/varmod-to-abs.mk unit-tests/varmod-to-lower.exp unit-tests/varmod-to-lower.mk unit-tests/varmod-to-many-words.exp unit-tests/varmod-to-many-words.mk unit-tests/varmod-to-one-word.exp unit-tests/varmod-to-one-word.mk unit-tests/varmod-to-separator.exp unit-tests/varmod-to-separator.mk unit-tests/varmod-to-upper.exp unit-tests/varmod-to-upper.mk unit-tests/varmod-undefined.exp unit-tests/varmod-undefined.mk unit-tests/varmod-unique.exp unit-tests/varmod-unique.mk unit-tests/varmod.exp unit-tests/varmod.mk unit-tests/varname-dollar.exp unit-tests/varname-dollar.mk unit-tests/varname-dot-alltargets.exp unit-tests/varname-dot-alltargets.mk unit-tests/varname-dot-curdir.exp unit-tests/varname-dot-curdir.mk unit-tests/varname-dot-includedfromdir.exp unit-tests/varname-dot-includedfromdir.mk unit-tests/varname-dot-includedfromfile.exp unit-tests/varname-dot-includedfromfile.mk unit-tests/varname-dot-includes.exp unit-tests/varname-dot-includes.mk unit-tests/varname-dot-libs.exp unit-tests/varname-dot-libs.mk unit-tests/varname-dot-make-dependfile.exp unit-tests/varname-dot-make-dependfile.mk unit-tests/varname-dot-make-expand_variables.exp unit-tests/varname-dot-make-expand_variables.mk unit-tests/varname-dot-make-exported.exp unit-tests/varname-dot-make-exported.mk unit-tests/varname-dot-make-jobs-prefix.exp unit-tests/varname-dot-make-jobs-prefix.mk unit-tests/varname-dot-make-jobs.exp unit-tests/varname-dot-make-jobs.mk unit-tests/varname-dot-make-level.exp unit-tests/varname-dot-make-level.mk unit-tests/varname-dot-make-makefile_preference.exp unit-tests/varname-dot-make-makefile_preference.mk unit-tests/varname-dot-make-makefiles.exp unit-tests/varname-dot-make-makefiles.mk unit-tests/varname-dot-make-meta-bailiwick.exp unit-tests/varname-dot-make-meta-bailiwick.mk unit-tests/varname-dot-make-meta-created.exp unit-tests/varname-dot-make-meta-created.mk unit-tests/varname-dot-make-meta-files.exp unit-tests/varname-dot-make-meta-files.mk unit-tests/varname-dot-make-meta-ignore_filter.exp unit-tests/varname-dot-make-meta-ignore_filter.mk unit-tests/varname-dot-make-meta-ignore_paths.exp unit-tests/varname-dot-make-meta-ignore_paths.mk unit-tests/varname-dot-make-meta-ignore_patterns.exp unit-tests/varname-dot-make-meta-ignore_patterns.mk unit-tests/varname-dot-make-meta-prefix.exp unit-tests/varname-dot-make-meta-prefix.mk unit-tests/varname-dot-make-mode.exp unit-tests/varname-dot-make-mode.mk unit-tests/varname-dot-make-path_filemon.exp unit-tests/varname-dot-make-path_filemon.mk unit-tests/varname-dot-make-pid.exp unit-tests/varname-dot-make-pid.mk 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 unit-tests/varname-dot-newline.mk unit-tests/varname-dot-objdir.exp unit-tests/varname-dot-objdir.mk unit-tests/varname-dot-parsedir.exp unit-tests/varname-dot-parsedir.mk unit-tests/varname-dot-parsefile.exp unit-tests/varname-dot-parsefile.mk unit-tests/varname-dot-path.exp unit-tests/varname-dot-path.mk unit-tests/varname-dot-shell.exp unit-tests/varname-dot-shell.mk unit-tests/varname-dot-suffixes.exp unit-tests/varname-dot-suffixes.mk unit-tests/varname-dot-targets.exp unit-tests/varname-dot-targets.mk unit-tests/varname-empty.exp unit-tests/varname-empty.mk unit-tests/varname-make.exp unit-tests/varname-make.mk unit-tests/varname-make_print_var_on_error-jobs.exp unit-tests/varname-make_print_var_on_error-jobs.mk unit-tests/varname-make_print_var_on_error.exp unit-tests/varname-make_print_var_on_error.mk unit-tests/varname-makefile.exp unit-tests/varname-makefile.mk unit-tests/varname-makeflags.exp unit-tests/varname-makeflags.mk unit-tests/varname-pwd.exp unit-tests/varname-pwd.mk unit-tests/varname-vpath.exp unit-tests/varname-vpath.mk unit-tests/varname.exp unit-tests/varname.mk unit-tests/varparse-dynamic.exp unit-tests/varparse-dynamic.mk unit-tests/varparse-errors.exp unit-tests/varparse-errors.mk unit-tests/varparse-mod.exp unit-tests/varparse-mod.mk unit-tests/varparse-undef-partial.exp unit-tests/varparse-undef-partial.mk util.c var.c wait.h diff --git a/contrib/bmake/Makefile b/contrib/bmake/Makefile index 201e4869dc3f..ad78883ff890 100644 --- a/contrib/bmake/Makefile +++ b/contrib/bmake/Makefile @@ -1,238 +1,238 @@ -# $Id: Makefile,v 1.120 2022/07/26 23:02:54 sjg Exp $ +# $Id: Makefile,v 1.122 2022/10/08 02:53:30 sjg Exp $ PROG= bmake SRCS= \ arch.c \ buf.c \ compat.c \ cond.c \ dir.c \ for.c \ hash.c \ job.c \ lst.c \ main.c \ make.c \ make_malloc.c \ meta.c \ metachar.c \ parse.c \ str.c \ suff.c \ targ.c \ trace.c \ util.c \ var.c .-include "VERSION" .-include "Makefile.inc" # this file gets generated by configure .-include "Makefile.config" .if !empty(LIBOBJS) SRCS+= ${LIBOBJS:T:.o=.c} .endif # just in case prefix?= /usr srcdir?= ${.CURDIR} DEFAULT_SYS_PATH?= ${prefix}/share/mk CPPFLAGS+= -DUSE_META CFLAGS+= ${CPPFLAGS} CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}} COPTS.main.c+= "-DMAKE_VERSION=\"${_MAKE_VERSION}\"" .for x in FORCE_MACHINE FORCE_MACHINE_ARCH .ifdef $x COPTS.main.c+= "-D$x=\"${$x}\"" .endif .endfor # meta mode can be useful even without filemon # should be set by now USE_FILEMON ?= no .if ${USE_FILEMON:tl} != "no" .PATH: ${srcdir}/filemon SRCS+= filemon_${USE_FILEMON}.c COPTS.meta.c+= -DUSE_FILEMON -DUSE_FILEMON_${USE_FILEMON:tu} COPTS.job.c+= ${COPTS.meta.c} .if ${USE_FILEMON} == "dev" FILEMON_H ?= /usr/include/dev/filemon/filemon.h .if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h" COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} .endif .endif # USE_FILEMON == dev .endif # USE_FILEMON .PATH: ${srcdir} .if make(obj) || make(clean) SUBDIR+= unit-tests .endif # start-delete1 for bsd.after-import.mk # we skip a lot of this when building as part of FreeBSD etc. # list of OS's which are derrived from BSD4.4 BSD44_LIST= NetBSD FreeBSD OpenBSD DragonFly MirBSD Bitrig # we are... OS := ${.MAKE.OS:U${uname -s:L:sh}} # are we 4.4BSD ? isBSD44:=${BSD44_LIST:M${OS}} -.if ${isBSD44} == "" +.if ${isBSD44} == "" && ${OS:NDarwin:NLinux} != "" MANTARGET= cat INSTALL?=${srcdir}/install-sh .if ${MACHINE} == "sun386" # even I don't have one of these anymore :-) CFLAGS+= -DPORTAR .elif ${OS} != "SunOS" # assume the worst SRCS+= sigcompat.c CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART .endif .else MANTARGET?= man .endif # turn this on by default - ignored if we are root WITH_INSTALL_AS_USER= # suppress with -DWITHOUT_* OPTIONS_DEFAULT_YES+= \ AUTOCONF_MK \ INSTALL_MK \ PROG_LINK OPTIONS_DEFAULT_NO+= \ PROG_VERSION # process options now .include .if ${MK_PROG_VERSION} == "yes" PROG_NAME= ${PROG}-${_MAKE_VERSION} .if ${MK_PROG_LINK} == "yes" SYMLINKS+= ${PROG_NAME} ${BINDIR}/${PROG} .endif .endif EXTRACT_MAN=no # end-delete1 MAN= ${PROG}.1 MAN1= ${MAN} .if ${PROG} != "make" CLEANFILES+= my.history .if make(${MAN}) || !exists(${srcdir}/${MAN}) my.history: @(echo ".Nm"; \ echo "is derived from NetBSD"; \ echo ".Xr make 1 ."; \ echo "It uses autoconf to facilitate portability to other platforms."; \ echo ".Pp") > $@ .NOPATH: ${MAN} ${MAN}: make.1 my.history @echo making $@ @sed \ -e '/^.Dt/s/MAKE/${PROG:tu}/' \ -e 's/^.Nx/NetBSD/' \ -e '/^.Nm/s/make/${PROG}/' \ -e '/^.Sh HISTORY/rmy.history' \ -e '/^.Sh HISTORY/,$$s,^.Nm,make,' ${srcdir}/make.1 > $@ all beforeinstall: ${MAN} _mfromdir=. .endif .endif MANTARGET?= cat MANDEST?= ${MANDIR}/${MANTARGET}1 .if ${MANTARGET} == "cat" _mfromdir=${srcdir} .endif .include CPPFLAGS+= -DMAKE_NATIVE -DHAVE_CONFIG_H COPTS.var.c += -Wno-cast-qual COPTS.job.c += -Wno-format-nonliteral COPTS.parse.c += -Wno-format-nonliteral COPTS.var.c += -Wno-format-nonliteral # Force these SHAREDIR= ${SHAREDIR.bmake:U${prefix}/share} BINDIR= ${BINDIR.bmake:U${prefix}/bin} MANDIR= ${MANDIR.bmake:U${SHAREDIR}/man} .if !exists(.depend) ${OBJS}: config.h .endif # start-delete2 for bsd.after-import.mk # make sure that MAKE_VERSION gets updated. main.o: ${srcdir}/VERSION .if ${MK_AUTOCONF_MK} == "yes" CONFIGURE_DEPS += ${.CURDIR}/VERSION # we do not need or want the generated makefile CONFIGURE_ARGS += --without-makefile AUTOCONF_GENERATED_MAKEFILE = Makefile.config .include .endif SHARE_MK ?= ${SHAREDIR}/mk MKSRC = ${srcdir}/mk INSTALL ?= ${srcdir}/install-sh .if ${MK_INSTALL_MK} == "yes" install: install-mk .endif beforeinstall: test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR} test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST} install-mk: .if exists(${MKSRC}/install-mk) test -d ${DESTDIR}${SHARE_MK} || ${INSTALL} -m 775 -d ${DESTDIR}${SHARE_MK} sh ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${SHARE_MK} .else @echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false .endif # end-delete2 # A simple unit-test driver to help catch regressions TEST_MAKE ?= ${.OBJDIR}/${PROG:T} accept test: .NOMETA cd ${.CURDIR}/unit-tests && \ MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}} .if make(test) && ${MK_AUTO_OBJ} == "yes" # The test target above visits unit-tests with -r -m / # which prevents MK_AUTO_OBJ doing its job # so do it here .if defined(MAKEOBJDIRPREFIX) || ${MAKEOBJDIR:U:M*/*} != "" _utobj = ${.OBJDIR}/unit-tests .else _utobj = ${.CURDIR}/unit-tests/${MAKEOBJDIR:Uobj} .endif utobj: .NOMETA @test -d ${_utobj} && exit 0; \ echo "[Creating ${_utobj}...]"; \ umask ${OBJDIR_UMASK:U002}; \ mkdir -p ${_utobj} test: utobj .endif diff --git a/contrib/bmake/Makefile.config.in b/contrib/bmake/Makefile.config.in index 042b2570ff88..dfc26d90a02a 100644 --- a/contrib/bmake/Makefile.config.in +++ b/contrib/bmake/Makefile.config.in @@ -1,22 +1,23 @@ # things set by configure _MAKE_VERSION?=@_MAKE_VERSION@ prefix?= @prefix@ srcdir= @srcdir@ CC= @CC@ @force_machine@MACHINE?= @machine@ @force_machine_arch@MACHINE_ARCH?= @machine_arch@ DEFAULT_SYS_PATH?= @default_sys_path@ +EGREP = @egrep@ CPPFLAGS+= @CPPFLAGS@ CFLAGS+= ${CPPFLAGS} @DEFS@ LDFLAGS+= @LDFLAGS@ LIBOBJS+= @LIBOBJS@ LDADD+= @LIBS@ USE_META?= @use_meta@ USE_FILEMON?= @use_filemon@ FILEMON_H?= @filemon_h@ BMAKE_PATH_MAX?= @bmake_path_max@ # used if MAXPATHLEN not defined CPPFLAGS+= -DBMAKE_PATH_MAX=${BMAKE_PATH_MAX} diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION index bbf893546603..d57c71f1e797 100644 --- a/contrib/bmake/VERSION +++ b/contrib/bmake/VERSION @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20220726 +_MAKE_VERSION=20230126 diff --git a/contrib/bmake/arch.c b/contrib/bmake/arch.c index 5e561b132f36..15816fd0b606 100644 --- a/contrib/bmake/arch.c +++ b/contrib/bmake/arch.c @@ -1,1148 +1,1148 @@ -/* $NetBSD: arch.c,v 1.210 2022/01/15 18:34:41 rillig Exp $ */ +/* $NetBSD: arch.c,v 1.212 2022/12/07 10:28:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * 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 * on a list which is searched each time an archive member is referenced. * * The interface to this module is: * * Arch_Init Initialize this module. * * Arch_End Clean up this module. * * Arch_ParseArchive * Parse an archive specification such as * "archive.a(member1 member2)". * * Arch_Touch Alter the modification time of the archive * member described by the given node to be * the time when make was started. * * Arch_TouchLib Update the modification time of the library * described by the given node. This is special * because it also updates the modification time * of the library's table of contents. * * Arch_UpdateMTime * Find the modification time of a member of * an archive *in the archive* and place it in the * member's GNode. * * Arch_UpdateMemberMTime * Find the modification time of a member of * an archive. Called when the member doesn't * already exist. Looks in the archive for the * modification time. Returns the modification * time. * * Arch_FindLib Search for a library along a path. The * library name in the GNode should be in * -l format. * * Arch_LibOODate Decide if a library node is out-of-date. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #ifdef HAVE_AR_H #include #else struct ar_hdr { char ar_name[16]; /* name */ char ar_date[12]; /* modification time */ char ar_uid[6]; /* user id */ char ar_gid[6]; /* group id */ char ar_mode[8]; /* octal file permissions */ char ar_size[10]; /* size in bytes */ #ifndef ARFMAG #define ARFMAG "`\n" #endif char ar_fmag[2]; /* consistency check */ }; #endif #if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB)) #include #endif #ifdef HAVE_UTIME_H #include #endif #include "make.h" #include "dir.h" /* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: arch.c,v 1.210 2022/01/15 18:34:41 rillig Exp $"); +MAKE_RCSID("$NetBSD: arch.c,v 1.212 2022/12/07 10:28:48 rillig Exp $"); typedef struct List ArchList; typedef struct ListNode ArchListNode; static ArchList archives; /* The archives we've already examined */ typedef struct Arch { char *name; /* Name of archive */ HashTable members; /* All the members of the archive described * by key/value pairs */ char *fnametab; /* Extended name table strings */ size_t fnamesize; /* Size of the string table */ } Arch; static FILE *ArchFindMember(const char *, const char *, struct ar_hdr *, const char *); #if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) #define SVR4ARCHIVES static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); #endif #if defined(_AIX) # define AR_NAME _ar_name.ar_name # define AR_FMAG _ar_name.ar_fmag # define SARMAG SAIAMAG # define ARMAG AIAMAG # define ARFMAG AIAFMAG #endif #ifndef AR_NAME # define AR_NAME ar_name #endif #ifndef AR_DATE # define AR_DATE ar_date #endif #ifndef AR_SIZE # define AR_SIZE ar_size #endif #ifndef AR_FMAG # define AR_FMAG ar_fmag #endif #ifndef ARMAG # define ARMAG "!\n" #endif #ifndef SARMAG # define SARMAG 8 #endif #ifdef CLEANUP static void ArchFree(void *ap) { Arch *a = ap; HashIter hi; /* Free memory from hash entries */ HashIter_Init(&hi, &a->members); while (HashIter_Next(&hi) != NULL) free(hi.entry->value); free(a->name); free(a->fnametab); HashTable_Done(&a->members); free(a); } #endif /* Return "archive(member)". */ static char * FullName(const char *archive, const char *member) { size_t len1 = strlen(archive); size_t len3 = strlen(member); char *result = bmake_malloc(len1 + 1 + len3 + 1 + 1); memcpy(result, archive, len1); memcpy(result + len1, "(", 1); memcpy(result + len1 + 1, member, len3); memcpy(result + len1 + 1 + len3, ")", 1 + 1); return result; } /* * Parse an archive specification such as "archive.a(member1 member2.${EXT})", * adding nodes for the expanded members to gns. Nodes are created as * necessary. * * Input: * pp The start of the specification. * gns The list on which to place the nodes. * scope The scope 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. */ bool Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) { char *spec; /* For modifying some bytes of *pp */ const char *cp; /* Pointer into line */ GNode *gn; /* New node */ FStr lib; /* Library-part of specification */ FStr mem; /* Member-part of specification */ char saveChar; /* Ending delimiter of member-name */ bool expandLib; /* Whether the parsed lib contains variable * expressions that need to be expanded */ spec = *pp; lib = FStr_InitRefer(spec); expandLib = false; for (cp = lib.str; *cp != '(' && *cp != '\0';) { if (*cp == '$') { /* Expand nested variable expressions. */ /* XXX: This code can probably be shortened. */ const char *nested_p = cp; FStr result; bool isError; /* XXX: is expanded twice: once here and once below */ (void)Var_Parse(&nested_p, scope, VARE_UNDEFERR, &result); /* TODO: handle errors */ isError = result.str == var_Error; FStr_Done(&result); if (isError) return false; expandLib = true; cp += nested_p - cp; } else cp++; } spec[cp++ - spec] = '\0'; if (expandLib) Var_Expand(&lib, scope, VARE_UNDEFERR); for (;;) { /* * First skip to the start of the member's name, mark that * place and skip to the end of it (either white-space or * a close paren). */ bool doSubst = false; cpp_skip_whitespace(&cp); mem = FStr_InitRefer(cp); while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) { if (*cp == '$') { /* Expand nested variable expressions. */ /* * XXX: This code can probably be shortened. */ FStr result; bool isError; const char *nested_p = cp; (void)Var_Parse(&nested_p, scope, VARE_UNDEFERR, &result); /* TODO: handle errors */ isError = result.str == var_Error; FStr_Done(&result); if (isError) return false; doSubst = true; cp += nested_p - cp; } else { cp++; } } /* * 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 */ if (*cp == '\0') { Parse_Error(PARSE_FATAL, "No closing parenthesis " "in archive specification"); return false; } /* * If we didn't move anywhere, we must be done */ if (cp == mem.str) break; saveChar = *cp; spec[cp - spec] = '\0'; /* * XXX: This should be taken care of intelligently by * 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 slows down archive specs with dynamic sources, since + * they are (non-)substituted three times, but we need to do + * this since SuffExpandChildren calls us, otherwise we could + * assume the substitutions would be taken care of later. */ if (doSubst) { char *fullName; char *p; const char *unexpandedMem = mem.str; Var_Expand(&mem, scope, VARE_UNDEFERR); /* * Now form an archive spec and recurse to deal with * nested variables and multi-word variable values. */ fullName = FullName(lib.str, mem.str); p = fullName; if (strcmp(mem.str, unexpandedMem) == 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. */ gn = Targ_GetNode(fullName); gn->type |= OP_ARCHV; Lst_Append(gns, gn); } else if (!Arch_ParseArchive(&p, gns, scope)) { /* Error in nested call. */ free(fullName); /* XXX: does unexpandedMemName leak? */ return false; } free(fullName); /* XXX: does unexpandedMemName leak? */ } else if (Dir_HasWildcards(mem.str)) { StringList members = LST_INIT; SearchPath_Expand(&dirSearchPath, mem.str, &members); while (!Lst_IsEmpty(&members)) { char *member = Lst_Dequeue(&members); char *fullname = FullName(lib.str, member); free(member); gn = Targ_GetNode(fullname); free(fullname); gn->type |= OP_ARCHV; Lst_Append(gns, gn); } Lst_Done(&members); } else { char *fullname = FullName(lib.str, mem.str); 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. */ gn->type |= OP_ARCHV; Lst_Append(gns, gn); } FStr_Done(&mem); spec[cp - spec] = saveChar; } FStr_Done(&lib); cp++; /* skip the ')' */ /* We promised that pp would be set up at the next non-space. */ cpp_skip_whitespace(&cp); *pp += cp - *pp; return true; } /* * Locate a member of an archive, given the path of the archive and the path * of the desired member. * * Input: * archive Path to the archive * member Name of member; only its basename is used. * addToCache True if archive should be cached if not already so. * * Results: * The ar_hdr for the member, or NULL. * * See ArchFindMember for an almost identical copy of this code. */ static struct ar_hdr * ArchStatMember(const char *archive, const char *member, bool addToCache) { #define AR_MAX_NAME_LEN (sizeof arh.ar_name - 1) FILE *arch; size_t size; /* Size of archive member */ char magic[SARMAG]; ArchListNode *ln; Arch *ar; /* Archive descriptor */ struct ar_hdr arh; /* archive-member header for reading archive */ char memName[MAXPATHLEN + 1]; /* Current member name while hashing. */ /* * Because of space constraints and similar things, files are archived * using their basename, not the entire path. */ member = str_basename(member); for (ln = archives.first; ln != NULL; ln = ln->next) { const Arch *a = ln->datum; if (strcmp(a->name, archive) == 0) break; } if (ln != NULL) { struct ar_hdr *hdr; ar = ln->datum; hdr = HashTable_FindValue(&ar->members, member); if (hdr != NULL) return hdr; { /* Try truncated name */ char copy[AR_MAX_NAME_LEN + 1]; size_t len = strlen(member); if (len > AR_MAX_NAME_LEN) { snprintf(copy, sizeof copy, "%s", member); hdr = HashTable_FindValue(&ar->members, copy); } return hdr; } } 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. */ static struct ar_hdr sarh; arch = ArchFindMember(archive, member, &sarh, "r"); if (arch == NULL) return NULL; fclose(arch); return &sarh; } /* * We don't have this archive on the list yet, so we want to find out * everything that's in it and cache it so we can get at it quickly. */ arch = fopen(archive, "r"); if (arch == NULL) return NULL; /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if (fread(magic, SARMAG, 1, arch) != 1 || strncmp(magic, ARMAG, SARMAG) != 0) { (void)fclose(arch); return NULL; } ar = bmake_malloc(sizeof *ar); ar->name = bmake_strdup(archive); ar->fnametab = NULL; ar->fnamesize = 0; HashTable_Init(&ar->members); memName[AR_MAX_NAME_LEN] = '\0'; while (fread(&arh, sizeof arh, 1, arch) == 1) { char *nameend; /* If the header is bogus, there's no way we can recover. */ if (strncmp(arh.AR_FMAG, ARFMAG, sizeof arh.AR_FMAG) != 0) goto badarch; /* * 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. */ arh.AR_SIZE[sizeof arh.AR_SIZE - 1] = '\0'; size = (size_t)strtol(arh.AR_SIZE, NULL, 10); memcpy(memName, arh.AR_NAME, sizeof arh.AR_NAME); nameend = memName + AR_MAX_NAME_LEN; while (nameend > memName && *nameend == ' ') nameend--; nameend[1] = '\0'; #ifdef SVR4ARCHIVES /* * svr4 names are slash-terminated. * Also svr4 extended the AR format. */ if (memName[0] == '/') { /* svr4 magic mode; handle it */ switch (ArchSVR4Entry(ar, memName, size, arch)) { case -1: /* Invalid data */ goto badarch; case 0: /* List of files entry */ continue; default: /* Got the entry */ break; } } else { if (nameend[0] == '/') nameend[0] = '\0'; } #endif #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 && ch_isdigit(memName[sizeof AR_EFMT1 - 1])) { - size_t elen = atoi(memName + sizeof AR_EFMT1 - 1); + size_t elen = (size_t)atoi( + memName + sizeof AR_EFMT1 - 1); if (elen > MAXPATHLEN) goto badarch; if (fread(memName, elen, 1, arch) != 1) goto badarch; memName[elen] = '\0'; if (fseek(arch, -(long)elen, SEEK_CUR) != 0) goto badarch; if (DEBUG(ARCH) || DEBUG(MAKE)) debug_printf( "ArchStatMember: " "Extended format entry for %s\n", memName); } #endif { struct ar_hdr *cached_hdr = bmake_malloc( sizeof *cached_hdr); memcpy(cached_hdr, &arh, sizeof arh); HashTable_Set(&ar->members, memName, cached_hdr); } if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0) goto badarch; } fclose(arch); Lst_Append(&archives, ar); /* * Now that the archive has been read and cached, we can look into * the addToCache table to find the desired member's header. */ return HashTable_FindValue(&ar->members, member); badarch: fclose(arch); HashTable_Done(&ar->members); free(ar->fnametab); free(ar); return NULL; } #ifdef SVR4ARCHIVES /* * Parse an SVR4 style entry that begins with a slash. * If it is "//", then load the table of filenames. * If it is "/", 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. * * 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) { #define ARLONGNAMES1 "//" #define ARLONGNAMES2 "/ARFILENAMES" size_t entry; char *ptr, *eptr; if (strncmp(inout_name, ARLONGNAMES1, sizeof ARLONGNAMES1 - 1) == 0 || strncmp(inout_name, ARLONGNAMES2, sizeof ARLONGNAMES2 - 1) == 0) { if (ar->fnametab != NULL) { DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n"); return -1; } /* * This is a table of archive names, so we build one for * ourselves */ ar->fnametab = bmake_malloc(size); ar->fnamesize = size; if (fread(ar->fnametab, size, 1, arch) != 1) { DEBUG0(ARCH, "Reading an SVR4 name table failed\n"); return -1; } eptr = ar->fnametab + size; for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) if (*ptr == '/') { entry++; *ptr = '\0'; } DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n", (unsigned long)entry); return 0; } if (inout_name[1] == ' ' || inout_name[1] == '\0') return 2; entry = (size_t)strtol(&inout_name[1], &eptr, 0); if ((*eptr != ' ' && *eptr != '\0') || eptr == &inout_name[1]) { DEBUG1(ARCH, "Could not parse SVR4 name %s\n", inout_name); return 2; } if (entry >= ar->fnamesize) { DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n", inout_name, (unsigned long)ar->fnamesize); return 2; } DEBUG2(ARCH, "Replaced %s with %s\n", inout_name, &ar->fnametab[entry]); snprintf(inout_name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]); return 1; } #endif static bool ArchiveMember_HasName(const struct ar_hdr *hdr, const char *name, size_t namelen) { const size_t ar_name_len = sizeof hdr->AR_NAME; const char *ar_name = hdr->AR_NAME; if (strncmp(ar_name, name, namelen) != 0) return false; if (namelen >= ar_name_len) return namelen == ar_name_len; /* hdr->AR_NAME is space-padded to the right. */ if (ar_name[namelen] == ' ') return true; /* * In archives created by GNU binutils 2.27, the member names end * with a slash. */ if (ar_name[namelen] == '/' && (namelen == ar_name_len || ar_name[namelen + 1] == ' ')) return true; return false; } /* * Locate a member of an archive, given the path of the archive and the path * of the desired member. * * Input: * archive Path to the archive * member Name of member. If it is a path, only the last * component is used. * out_arh Archive header to be filled in * mode "r" for read-only access, "r+" for read-write access * * Output: * return The archive file, positioned at the start of the * member's struct ar_hdr, or NULL if the member doesn't * exist. * *out_arh The current struct ar_hdr for member. * * See ArchStatMember for an almost identical copy of this code. */ static FILE * ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh, const char *mode) { FILE *arch; /* Stream to archive */ int size; /* Size of archive member */ char magic[SARMAG]; size_t len; arch = fopen(archive, mode); if (arch == NULL) return NULL; /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if (fread(magic, SARMAG, 1, arch) != 1 || strncmp(magic, ARMAG, SARMAG) != 0) { fclose(arch); return NULL; } /* * Because of space constraints and similar things, files are archived * using their basename, not the entire path. */ member = str_basename(member); 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) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ fclose(arch); return NULL; } DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n", archive, (int)sizeof out_arh->AR_NAME, out_arh->AR_NAME, (int)sizeof out_arh->ar_date, out_arh->ar_date); 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. */ if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) != 0) { fclose(arch); return NULL; } return arch; } #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first 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]))) { - size_t elen = atoi( + size_t elen = (size_t)atoi( &out_arh->AR_NAME[sizeof AR_EFMT1 - 1]); char ename[MAXPATHLEN + 1]; if (elen > MAXPATHLEN) { fclose(arch); return NULL; } if (fread(ename, elen, 1, arch) != 1) { fclose(arch); return NULL; } ename[elen] = '\0'; if (DEBUG(ARCH) || DEBUG(MAKE)) 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), SEEK_CUR) != 0) { fclose(arch); return NULL; } return arch; } if (fseek(arch, -(long)elen, SEEK_CUR) != 0) { fclose(arch); return NULL; } } #endif /* * This isn't the member we're after, so 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. */ out_arh->AR_SIZE[sizeof out_arh->AR_SIZE - 1] = '\0'; size = (int)strtol(out_arh->AR_SIZE, NULL, 10); if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) { fclose(arch); return NULL; } } fclose(arch); return NULL; } /* * 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. * For a library, it may be required to run ranlib after this. * * Input: * gn Node of member to touch * * Results: * The 'time' field of the member's header is updated. */ void Arch_Touch(GNode *gn) { FILE *f; struct ar_hdr arh; f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh, "r+"); if (f == NULL) return; snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now); (void)fwrite(&arh, sizeof arh, 1, f); fclose(f); /* TODO: handle errors */ } /* * 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'. */ /*ARGSUSED*/ void Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED) { #ifdef RANLIBMAG FILE *f; struct ar_hdr arh; /* Header describing table of contents */ struct utimbuf times; f = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+"); if (f == NULL) return; snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now); (void)fwrite(&arh, sizeof arh, 1, f); fclose(f); /* TODO: handle errors */ times.actime = times.modtime = now; utime(gn->path, ×); /* TODO: handle errors */ #endif } /* * Update the mtime of the GNode with the mtime from the archive member on * disk (or in the cache). */ void Arch_UpdateMTime(GNode *gn) { struct ar_hdr *arh; arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), true); if (arh != NULL) gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10); else gn->mtime = 0; } /* * 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) { 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. */ const char *nameStart = strchr(pgn->name, '(') + 1; const char *nameEnd = strchr(nameStart, ')'); size_t nameLen = (size_t)(nameEnd - nameStart); if (pgn->flags.remake && strncmp(nameStart, gn->name, nameLen) == 0) { Arch_UpdateMTime(pgn); gn->mtime = pgn->mtime; } } else if (pgn->flags.remake) { /* * Something which isn't a library depends on the * existence of this target, so it needs to exist. */ gn->mtime = 0; break; } } } /* * 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 * flag when linking (or we cannot find the library), we assume that * the user has placed the .LIBS variable in the final linking * command (or the linker will know where to find it) and set the * TARGET variable for this node to be the node's name. Otherwise, * we set the TARGET variable to be the full path of the library, * as returned by Dir_FindFile. * * Input: * gn Node of library to find */ void Arch_FindLib(GNode *gn, SearchPath *path) { char *libName = str_concat3("lib", gn->name + 2, ".a"); gn->path = Dir_FindFile(libName, path); free(libName); #ifdef LIBRARIES Var_Set(gn, TARGET, gn->name); #else Var_Set(gn, TARGET, GNode_Path(gn)); #endif } /* ARGSUSED */ static bool RanlibOODate(const GNode *gn MAKE_ATTR_UNUSED) { #ifdef RANLIBMAG struct ar_hdr *arh; /* Header for __.SYMDEF */ int tocModTime; /* The table-of-contents' mod time */ arh = ArchStatMember(gn->path, RANLIBMAG, false); if (arh == NULL) { /* A library without a table of contents is out-of-date. */ if (DEBUG(ARCH) || DEBUG(MAKE)) debug_printf("no toc..."); return true; } tocModTime = (int)strtol(arh->ar_date, NULL, 10); if (DEBUG(ARCH) || DEBUG(MAKE)) debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(tocModTime)); return gn->youngestChild == NULL || gn->youngestChild->mtime > tocModTime; #else return false; #endif } /* * 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. * * There are several ways for a library to be out-of-date that are * not available to ordinary files. In addition, there are ways * that are open to regular files that are not available to * libraries. * * A library that is only used as a source is never * considered out-of-date by itself. This does not preclude the * library's modification time from making its parent be out-of-date. * A library will be considered out-of-date for any of these reasons, * given that it is a target on a dependency line somewhere: * * Its modification time is less than that of one of its sources * (gn->mtime < gn->youngestChild->mtime). * * Its modification time is greater than the time at which the make * began (i.e. it's been modified in the course of the make, probably * by archiving). * * The modification time of one of its sources is greater than the one * of its RANLIBMAG member (i.e. its table of contents is out-of-date). * We don't compare the archive time vs. TOC time because they can be * too close. In my opinion we should not bother with the TOC at all * since this is used by 'ar' rules that affect the data contents of the * archive, not by ranlib rules, which affect the TOC. */ bool Arch_LibOODate(GNode *gn) { if (gn->type & OP_PHONY) { return true; } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) { return false; } else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) || (gn->mtime > now) || (gn->youngestChild != NULL && gn->mtime < gn->youngestChild->mtime)) { return true; } else { return RanlibOODate(gn); } } /* Initialize the archives module. */ void Arch_Init(void) { Lst_Init(&archives); } /* Clean up the archives module. */ void Arch_End(void) { #ifdef CLEANUP Lst_DoneCall(&archives, ArchFree); #endif } bool Arch_IsLib(GNode *gn) { static const char armag[] = "!\n"; char buf[sizeof armag - 1]; int fd; if ((fd = open(gn->path, O_RDONLY)) == -1) return false; if (read(fd, buf, sizeof buf) != sizeof buf) { (void)close(fd); return false; } (void)close(fd); return memcmp(buf, armag, sizeof buf) == 0; } diff --git a/contrib/bmake/bmake.1 b/contrib/bmake/bmake.1 index 9e7889a03923..13bb9e9235b9 100644 --- a/contrib/bmake/bmake.1 +++ b/contrib/bmake/bmake.1 @@ -1,2558 +1,2718 @@ -.\" $NetBSD: make.1,v 1.315 2022/07/12 23:47:00 rillig Exp $ +.\" $NetBSD: make.1,v 1.360 2023/01/26 20:48:17 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd July 12, 2022 +.Dd January 26, 2023 .Dt BMAKE 1 .Os .Sh NAME .Nm bmake .Nd maintain program dependencies .Sh SYNOPSIS .Nm .Op Fl BeikNnqrSstWwX .Op Fl C Ar directory .Op Fl D Ar variable .Op Fl d Ar flags .Op Fl f Ar makefile .Op Fl I Ar directory .Op Fl J Ar private .Op Fl j Ar max_jobs .Op Fl m Ar directory .Op Fl T Ar file .Op Fl V Ar variable .Op Fl v Ar variable -.Op Ar variable=value -.Op Ar target ... +.Op Ar variable\| Ns Cm \&= Ns Ar value +.Op Ar target No ... .Sh DESCRIPTION .Nm is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. If no .Fl f Ar makefile -makefile option is given, +option is given, .Nm -will try to open +tries to open .Sq Pa makefile then .Sq Pa Makefile in order to find the specifications. If the file .Sq Pa .depend -exists, it is read (see -.Xr mkdep 1 ) . +exists, it is read, see +.Xr mkdep 1 . .Pp This manual page is intended as a reference document only. For a more thorough description of .Nm and makefiles, please refer to -.%T "PMake \- A Tutorial" . +.%T "PMake \- A Tutorial" +(from 1993). .Pp .Nm -will prepend the contents of the -.Va MAKEFLAGS +prepends the contents of the +.Ev MAKEFLAGS environment variable to the command line arguments before parsing them. .Pp The options are as follows: .Bl -tag -width Ds .It Fl B Try to be backwards compatible by executing a single shell per command and -by executing the commands to make the sources of a dependency line in sequence. +by making the sources of a dependency line in sequence. .It Fl C Ar directory Change to .Ar directory before reading the makefiles or doing anything else. If multiple .Fl C options are specified, each is interpreted relative to the previous one: .Fl C Pa / Fl C Pa etc is equivalent to .Fl C Pa /etc . .It Fl D Ar variable Define .Ar variable to be 1, in the global scope. -.It Fl d Ar [-]flags +.It Fl d Oo Cm \- Oc Ns Ar flags Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by -.Ql \- +.Ql \- , they are added to the -.Va MAKEFLAGS -environment variable and will be processed by any child make processes. +.Ev MAKEFLAGS +environment variable and are passed on to any child make processes. By default, debugging information is printed to standard error, but this can be changed using the -.Ar F +.Cm F 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 output is line buffered. -.Ar Flags -is one or more of the following: +the standard output is line buffered. +The available +.Ar flags +are: .Bl -tag -width Ds -.It Ar A +.It Cm A Print all possible debugging information; equivalent to specifying all of the debugging flags. -.It Ar a +.It Cm a Print debugging information about archive searching and caching. -.It Ar C -Print debugging information about current working directory. -.It Ar c +.It Cm C +Print debugging information about the current working directory. +.It Cm c Print debugging information about conditional evaluation. -.It Ar d +.It Cm d Print debugging information about directory searching and caching. -.It Ar e +.It Cm e Print debugging information about failed commands and targets. -.It Ar F Ns Oo Sy \&+ Oc Ns Ar filename +.It Cm F Ns Oo Cm \&+ Oc Ns Ar filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the argument. If the character immediately after the -.Ql F +.Cm F flag is .Ql \&+ , -then the file will be opened in append mode; -otherwise the file will be overwritten. +the file is opened in append mode; +otherwise the file is overwritten. If the file name is .Ql stdout or -.Ql stderr -then debugging output will be written to the -standard output or standard error output file descriptors respectively -(and the +.Ql stderr , +debugging output is written to the standard output or standard error output +respectively (and the .Ql \&+ option has no effect). -Otherwise, the output will be written to the named file. -If the file name ends -.Ql .%d -then the +Otherwise, the output is written to the named file. +If the file name ends with +.Ql .%d , +the .Ql %d is replaced by the pid. -.It Ar f +.It Cm f Print debugging information about loop evaluation. -.It Ar "g1" +.It Cm g1 Print the input graph before making anything. -.It Ar "g2" +.It Cm g2 Print the input graph after making everything, or before exiting on error. -.It Ar "g3" +.It Cm g3 Print the input graph before exiting on error. -.It Ar h +.It Cm h Print debugging information about hash table operations. -.It Ar j +.It Cm j Print debugging information about running multiple shells. -.It Ar L +.It Cm L Turn on lint checks. -This will throw errors for variable assignments that do not parse -correctly, at the time of assignment so the file and line number -are available. -.It Ar l +This throws errors for variable assignments that do not parse correctly, +at the time of assignment, so the file and line number are available. +.It Cm l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ -or other "quiet" flags. -Also known as "loud" behavior. -.It Ar M -Print debugging information about "meta" mode decisions about targets. -.It Ar m +or other +.Dq quiet +flags. +Also known as +.Dq loud +behavior. +.It Cm M +Print debugging information about +.Dq meta +mode decisions about targets. +.It Cm m Print debugging information about making targets, including modification dates. -.It Ar n +.It Cm n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in the directory referred to by the .Ev TMPDIR environment variable, or in .Pa /tmp if .Ev TMPDIR is unset or set to the empty string. The temporary scripts are created by .Xr mkstemp 3 , and have names of the form .Pa makeXXXXXX . .Em NOTE : This can create many files in .Ev TMPDIR or .Pa /tmp , so use with care. -.It Ar p +.It Cm p Print debugging information about makefile parsing. -.It Ar s +.It Cm s Print debugging information about suffix-transformation rules. -.It Ar t +.It Cm t Print debugging information about target list maintenance. -.It Ar V +.It Cm V Force the .Fl V -option to print raw values of variables, overriding the default behavior -set via +option to print raw values of variables, +overriding the default behavior set via .Va .MAKE.EXPAND_VARIABLES . -.It Ar v -Print debugging information about variable assignment. -.It Ar x +.It Cm v +Print debugging information about variable assignment and expansion. +.It Cm x Run shell commands with .Fl x so the actual commands are printed as they are executed. .El .It Fl e -Specify that environment variables override macro assignments within -makefiles. +Let environment variables override global variables within makefiles. .It Fl f Ar makefile Specify a makefile to read instead of the default -.Sq Pa makefile . +.Pa makefile +or +.Pa Makefile . If .Ar makefile is -.Sq Fl , +.Ql \&- , standard input is read. Multiple makefiles may be specified, and are read in the order specified. .It Fl I Ar directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the .Fl m option) is automatically included as part of this list. .It Fl i Ignore non-zero exit of shell commands in the makefile. Equivalent to specifying -.Sq Fl +.Ql \&- before each command line in the makefile. .It Fl J Ar private This option should .Em not be specified by the user. .Pp When the .Fl j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. .It Fl j Ar max_jobs Specify the maximum number of jobs that .Nm may have running at any one time. The value is saved in .Va .MAKE.JOBS . Turns compatibility mode off, unless the .Fl B option is also specified. When compatibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the traditional one shell invocation per line. This can break traditional scripts which change directories on each command invocation and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. .It Fl k Continue processing after errors are encountered, but only on those targets that do not depend on the target whose creation caused the error. .It Fl m Ar directory -Specify a directory in which to search for sys.mk and makefiles included -via the +Specify a directory in which to search for +.Pa sys.mk +and makefiles included via the .Li \&< Ns Ar file Ns Li \&> Ns -style include statement. The .Fl m option can be used multiple times to form a search path. -This path will override the default system include path: /usr/share/mk. -Furthermore the system include path will be appended to the search path used -for +This path overrides the default system include path +.Pa /usr/share/mk . +Furthermore, the system include path is appended to the search path used for .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). +The system include path can be referenced via the read-only variable +.Va .SYSPATH . .Pp -If a file or directory name in the +If a directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string -.Qq \&.../ -then +.Ql \&.../ , .Nm -will search for the specified file or directory named in the remaining part +searches for the specified file or directory named in the remaining part of the argument string. -The search starts with the current directory of -the Makefile and then works upward towards the root of the file system. -If the search is successful, then the resulting directory replaces the -.Qq \&.../ +The search starts with the current directory +and then works upward towards the root of the file system. +If the search is successful, the resulting directory replaces the +.Ql \&.../ specification in the .Fl m argument. -If used, this feature allows +This feature allows .Nm -to easily search in the current source tree for customized sys.mk files -(e.g., by using -.Qq \&.../mk/sys.mk +to easily search in the current source tree for customized +.Pa sys.mk +files (e.g., by using +.Ql \&.../mk/sys.mk as an argument). .It Fl n Display the commands that would have been executed, but do not -actually execute them unless the target depends on the .MAKE special -source (see below) or the command is prefixed with -.Sq Ic + . +actually execute them unless the target depends on the +.Va .MAKE +special source (see below) or the command is prefixed with +.Sq Cm + . .It Fl N -Display the commands which would have been executed, but do not -actually execute any of them; useful for debugging top-level makefiles +Display the commands that would have been executed, +but do not actually execute any of them; +useful for debugging top-level makefiles without descending into subdirectories. .It Fl q -Do not execute any commands, but exit 0 if the specified targets are -up-to-date and 1, otherwise. +Do not execute any commands, +instead exit 0 if the specified targets are up to date, and 1 otherwise. .It Fl r Do not use the built-in rules specified in the system makefile. .It Fl S Stop processing if an error is encountered. This is the default behavior and the opposite of .Fl k . .It Fl s Do not echo any commands as they are executed. Equivalent to specifying .Sq Ic @ before each command line in the makefile. .It Fl T Ar tracefile When used with the .Fl j flag, append a trace record to .Ar tracefile for each job started and completed. .It Fl t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up-to-date. .It Fl V Ar variable Print the value of .Ar variable . Do not build any targets. Multiple instances of this option may be specified; -the variables will be printed one per line, +the variables are printed one per line, with a blank line for each null or undefined variable. The value printed is extracted from the global scope after all makefiles have been read. +.Pp By default, the raw variable contents (which may include additional unexpanded variable references) are shown. If .Ar variable contains a -.Ql \&$ -then the value will be recursively expanded to its complete resultant -text before printing. -The expanded value will also be printed if +.Ql \&$ , +it is not interpreted as a variable name but rather as an expression. +Its value is expanded before printing. +The value is also expanded before printing if .Va .MAKE.EXPAND_VARIABLES -is set to true and -the +is set to true and the .Fl dV option has not been used to override it. +.Pp Note that loop-local and target-local variables, as well as values taken temporarily by global variables during makefile processing, are not accessible via this option. The .Fl dv debug mode can be used to see these at the cost of generating substantial extraneous output. .It Fl v Ar variable Like +.Fl V , +but all printed variables are always expanded to their complete value. +The last occurrence of .Fl V -but the variable is always expanded to its complete value. +or +.Fl v +decides whether all variables are expanded or not. .It Fl W Treat any warnings during makefile parsing as errors. .It Fl w Print entering and leaving directory messages, pre and post processing. .It Fl X Don't export variables passed on the command line to the environment individually. -Variables passed on the command line are still exported -via the -.Va MAKEFLAGS +Variables passed on the command line are still exported via the +.Ev MAKEFLAGS environment variable. This option may be useful on systems which have a small limit on the size of command arguments. -.It Ar variable=value +.It Ar variable\| Ns Cm \&= Ns Ar value Set the value of the variable .Ar variable to .Ar value . Normally, all values passed on the command line are also exported to sub-makes in the environment. The .Fl X flag disables this behavior. Variable assignments should follow options for POSIX compatibility but no ordering is enforced. .El .Pp -There are seven different types of lines in a makefile: file dependency +There are several different types of lines in a makefile: dependency specifications, shell commands, variable assignments, include statements, -conditional directives, for loops, and comments. +conditional directives, for loops, other directives, and comments. .Pp -In general, lines may be continued from one line to the next by ending -them with a backslash +Lines may be continued from one line to the next +by ending them with a backslash .Pq Ql \e . The trailing newline character and initial whitespace on the following line are compressed into a single space. .Sh FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets .Dq depend -on the sources -and are customarily created from them. -A target is considered 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 +on the sources and are customarily created from them. +A target is considered 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 is re-created, but not until all sources have been examined and themselves re-created as needed. Three operators may be used: .Bl -tag -width flag .It Ic \&: Many dependency lines may name this target but only one may have attached shell commands. All sources named in all dependency lines are considered together, and if needed the attached shell commands are run to create or re-create the target. If .Nm is interrupted, the target is removed. .It Ic \&! The same, but the target is always re-created whether or not it is out of date. .It Ic \&:: Any 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. Furthermore, unlike -.Ic \&:, +.Ic \&: , for dependency lines with no sources, the attached shell commands are always run. Also unlike -.Ic \&:, -the target will not be removed if +.Ic \&: , +the target is not removed if .Nm is interrupted. .El +.Pp All dependency lines mentioning a particular target must use the same operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , .Ql * , .Ql [] , and .Ql {} . The values .Ql \&? , .Ql * , and .Ql [] -may only be used as part of the final -component of the target or source, and must be used to describe existing -files. +may only be used as part of the final component of the target or source, +and only match existing files. The value .Ql {} need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. .Sh SHELL COMMANDS -Each target may have associated with it one or more lines of shell -commands, normally -used to create the target. +Each target may have associated with it one or more lines of shell commands, +normally used to create the target. Each of the lines in this script .Em must 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 -script. +While targets can occur in many dependency lines if desired, +by default only one of these rules may be followed by a creation script. If the .Sq Ic \&:: -operator is used, however, all rules may include scripts and the -scripts are executed in the order found. +operator is used, however, all rules may include scripts, +and the respective scripts are executed in the order found. .Pp -Each line is treated as a separate shell command, unless the end of -line is escaped with a backslash -.Pq Ql \e +Each line is treated as a separate shell command, +unless the end of line is escaped with a backslash +.Ql \e , in which case that line and the next are combined. -.\" The escaped newline is retained and passed to the shell, which -.\" normally ignores it. -.\" However, the tab at the beginning of the following line is removed. If the first characters of the command are any combination of .Sq Ic @ , .Sq Ic + , or .Sq Ic \- , the command is treated specially. -A -.Sq Ic @ +.Bl -tag -offset indent -width indent +.It Ic @ causes the command not to be echoed before it is executed. -A -.Sq Ic + +.It Ic + causes the command to be executed even when .Fl n is given. -This is similar to the effect of the .MAKE special source, +This is similar to the effect of the +.Va .MAKE +special source, except that the effect can be limited to a single line of a script. -A -.Sq Ic \- +.It Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. +.El .Pp When .Nm is run in jobs mode with .Fl j Ar max_jobs , -the entire script for the target is fed to a -single instance of the shell. +the entire script for the target is fed to a single instance of the shell. In compatibility (non-jobs) mode, each command is run in a separate process. If the command contains any shell meta characters -.Pq Ql #=|^(){};&<>*?[]:$`\e\en -it will be passed to the shell; otherwise +.Pq Ql #=|^(){};&<>*?[]:$`\e\en , +it is passed to the shell; otherwise .Nm -will attempt direct execution. +attempts direct execution. If a line starts with .Sq Ic \- -and the shell has ErrCtl enabled then failure of the command line -will be ignored as in compatibility mode. +and the shell has ErrCtl enabled, +failure of the command line is ignored as in compatibility mode. Otherwise .Sq Ic \- affects the entire job; -the script will stop at the first command line that fails, -but the target will not be deemed to have failed. +the script stops at the first command line that fails, +but the target is not deemed to have failed. .Pp Makefiles should be written so that the mode of .Nm operation does not change their behavior. -For example, any command which needs to use +For example, any command which uses .Dq cd or .Dq chdir -without potentially changing the directory for subsequent commands +without the intention of changing the directory for subsequent 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 +To force the use of a single shell, escape the line breaks so as to make the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: - @echo Building $@ in `pwd` + @echo "Building $@ in $$(pwd)" @(cd ${.CURDIR} && ${MAKE} $@) - @echo Back in `pwd` + @echo "Back in $$(pwd)" ensure-one-shell-regardless-of-mode: - @echo Building $@ in `pwd`; \e + @echo "Building $@ in $$(pwd)"; \e (cd ${.CURDIR} && ${MAKE} $@); \e - echo Back in `pwd` + echo "Back in $$(pwd)" .Ed .Pp Since .Nm -will -.Xr chdir 2 -to +changes the current working directory to .Sq Va .OBJDIR -before executing any targets, each child process -starts with that as its current working directory. +before executing any targets, +each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make behave much like macros in the C preprocessor. .Pp Variable assignments have the form .Sq Ar NAME Ar op Ar value , where: -.Bl -tag -width Ds +.Bl -tag -offset Ds -width Ds .It Ar NAME is a single-word variable name, consisting, by tradition, of all upper-case letters, .It Ar op -is one of the five variable assignment operators described below, and +is one of the variable assignment operators described below, and .It Ar value is interpreted according to the variable assignment operator. .El .Pp Whitespace around .Ar NAME , .Ar op and .Ar value is discarded. .Ss Variable assignment operators -The five operators that can be used to assign values to variables are: +The five operators that assign values to variables are: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overwritten. .It Ic \&+= Append the value to the current value of the variable, separating them by a single space. .It Ic \&?= Assign the value to the variable if it is not already defined. .It Ic \&:= -Assign with expansion, i.e. expand the value before assigning it -to the variable. -Normally, expansion is not done until the variable is referenced. +Expand the value, then assign it to the variable. .Pp .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. +.\" See var-op-expand.mk, the section with LATER and INDIRECT. .It Ic \&!= -Expand the value and pass it to the shell for execution and assign -the result to the variable. +Expand the value and pass it to the shell for execution, +then assign the output from the child's standard output to the variable. Any newlines in the result are replaced with spaces. .El .Ss Expansion of variables -In contexts where variables are expanded, +In most contexts where variables are expanded, .Ql \&$$ expands to a single dollar sign. +In other contexts (most variable modifiers, string literals in conditions), +.Ql \&\e$ +expands to a single dollar sign. +.Pp References to variables have the form -.Ql \&${ Ns Ar name Ns Oo \&: Ns Ar modifiers Oc Ns } +.Cm \&${ Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&} or -.Ql \&$( Ns Ar name Ns Oo \&: Ns Ar modifiers Oc Ns ) . -If the variable name contains only a single character, +.Cm \&$( Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&) . +If the variable name consists of only a single character +and the expression contains no modifiers, the surrounding curly braces or parentheses are not required. This shorter form is not recommended. .Pp -If the variable name contains a dollar, then the name itself is expanded first. +If the variable name contains a dollar, the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parentheses, or whitespace are really best avoided. +braces, parentheses or whitespace are really best avoided. .Pp -If the result of expanding a variable contains a dollar sign +If the result of expanding a nested variable expression contains a dollar sign .Pq Ql \&$ , -the string is expanded again. +the result is subject to further expansion. .Pp Variable substitution occurs at four distinct times, depending on where the variable is being used. .Bl -enum .It Variables in dependency lines are expanded as the line is read. .It Variables in conditionals are expanded individually, but only as far as necessary to determine the result of the conditional. .It Variables in shell commands are expanded when the shell command is executed. .It -.Dq .for +.Ic .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded when composing the body of a loop, so the following example code: .Bd -literal -offset indent - -.Dv .for i in 1 2 3 +\&.for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} -.Dv .endfor +\&.endfor all: @echo ${a} @echo ${b} - .Ed -will print: +.Pp +prints: .Bd -literal -offset indent 1 2 3 3 3 3 - .Ed -Because while ${a} contains -.Dq 1 2 3 -after the loop is executed, ${b} +.Pp +After the loop is executed: +.Bl -tag -offset indent -width indent +.It Va a contains -.Dq ${j} ${j} ${j} +.Ql ${:U1} ${:U2} ${:U3} , which expands to -.Dq 3 3 3 -since after the loop completes ${j} contains -.Dq 3 . +.Ql 1 2 3 . +.It Va j +contains +.Ql ${:U3} , +which expands to +.Ql 3 . +.It Va b +contains +.Ql ${j} ${j} ${j} , +which expands to +.Ql ${:U3} ${:U3} ${:U3} +and further to +.Ql 3 3 3 . +.El .El .Ss Variable classes The four different classes of variables (in order of increasing precedence) are: .Bl -tag -width Ds .It Environment variables Variables defined as part of .Nm Ns 's environment. .It Global variables Variables defined in the makefile or in included makefiles. .It Command line variables Variables defined as part of the command line. .It Local variables Variables that are defined specific to a certain target. .El .Pp -Local variables can be set on a dependency line, if +Local variables can be set on a dependency line, unless .Va .MAKE.TARGET_LOCAL_VARIABLES -is not set to +is set to .Ql false . The rest of the line -(which will already have had global variables expanded) +(which already has had global variables expanded) is the variable value. For example: .Bd -literal -offset indent COMPILER_WRAPPERS= ccache distcc icecc ${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,} .Ed .Pp Only the targets .Ql ${OBJS} -will be impacted by that filter (in "meta" mode) and -simply enabling/disabling any of the compiler wrappers will not render all +are impacted by that filter (in +.Dq meta +mode) and +simply enabling/disabling any of the compiler wrappers does not render all of those targets out-of-date. .Pp .Em NOTE : target-local variable assignments behave differently in that; .Bl -tag -width Ds -offset indent .It Ic \&+= Only appends to a previous local assignment for the same target and variable. .It Ic \&:= Is redundant with respect to global variables, which have already been expanded. .El .Pp -The seven built-in local variables are as follows: -.Bl -tag -width ".ARCHIVE" -offset indent +The seven built-in local variables are: +.Bl -tag -width ".Va .ARCHIVE" -offset indent .It Va .ALLSRC The list of all sources for this target; also known as .Sq Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Sq Va \&! . .It Va .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the .Dq implied source); also known as .Sq Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Sq Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Sq Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Sq Va * . The suffix must be one of the known suffixes declared with -.Ic .SUFFIXES -or it will not be recognized. +.Ic .SUFFIXES , +or it is not recognized. .It Va .TARGET The name of the target; also known as .Sq Va @ . For compatibility with other makes this is an alias for -.Ic .ARCHIVE +.Va .ARCHIVE in archive member rules. .El .Pp The shorter forms -.Ql ( Va > , +.Po +.Sq Va \&> , .Sq Va \&! , -.Sq Va < , -.Sq Va % , +.Sq Va \&< , +.Sq Va \&% , .Sq Va \&? , -.Sq Va * , +.Sq Va \&* , and -.Sq Va @ ) +.Sq Va \&@ +.Pc are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. .Pp Variants of these variables with the punctuation followed immediately by .Ql D or .Ql F , e.g.\& -.Sq Va $(@D) , +.Ql $(@D) , are legacy forms equivalent to using the .Ql :H and .Ql :T modifiers. These forms are accepted for compatibility with .At V makefiles and POSIX but are not recommended. .Pp Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. These variables are .Sq Va .TARGET , .Sq Va .PREFIX , .Sq Va .ARCHIVE , and .Sq Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: -.Bl -tag -width .MAKEOVERRIDES +.Bl -tag +.\" NB: This list is sorted case-insensitive, ignoring punctuation. +.\" NB: To find all built-in variables in make's source code, +.\" NB: search for Var_*, Global_*, SetVarObjdir, GetBooleanExpr, +.\" NB: and the implementation of Var_SetWithFlags. +.\" NB: Last synced on 2023-01-01. .It Va .ALLTARGETS -The list of all targets encountered in the Makefile. -If evaluated during -Makefile parsing, lists only those targets encountered thus far. +The list of all targets encountered in the makefiles. +If evaluated during makefile parsing, +lists only those targets encountered thus far. .It Va .CURDIR A path to the directory where .Nm was executed. Refer to the description of -.Sq Ev PWD +.Sq Va PWD for more details. +.It Va .ERROR_CMD +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_CWD +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_META_FILE +Is used in error handling in +.Dq meta +mode, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_TARGET +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . .It Va .INCLUDEDFROMDIR -The directory of the file this Makefile was included from. +The directory of the file this makefile was included from. .It Va .INCLUDEDFROMFILE -The filename of the file this Makefile was included from. -.It Ev MAKE +The filename of the file this makefile was included from. +.\" .INCLUDES is intentionally undocumented, as it is obsolete. +.\" .LIBS is intentionally undocumented, as it is obsolete. +.It Va MACHINE +The machine hardware name, see +.Xr uname 1 . +.It Va MACHINE_ARCH +The machine processor architecture name, see +.Xr uname 1 . +.It Va MAKE The name that .Nm was executed with .Pq Va argv[0] . -For compatibility -.Nm -also sets -.Va .MAKE -with the same value. +.It Va .MAKE +The same as +.Va MAKE , +for compatibility. The preferred variable to use is the environment variable .Ev MAKE -because it is more compatible with other versions of -.Nm +because it is more compatible with other make variants and cannot be confused with the special target with the same name. +.\" '.MAKE.cmd_filtered' is intentionally undocumented, +.\" as it is an internal implementation detail. .It Va .MAKE.DEPENDFILE Names the makefile (default .Sq Pa .depend ) from which generated dependencies are read. +.It Va .MAKE.DIE_QUIETLY +If set to +.Ql true , +do not print error information at the end. .It Va .MAKE.EXPAND_VARIABLES A boolean that controls the default behavior of the .Fl V option. If true, variable values printed with .Fl V are fully expanded; if false, the raw variable contents (which may include additional unexpanded variable references) are shown. .It Va .MAKE.EXPORTED The list of variables exported by .Nm . -.It Va .MAKE.JOBS -The argument to the -.Fl j -option. +.It Va MAKEFILE +The top-level makefile that is currently read, +as given in the command line. +.It Va .MAKEFLAGS +The environment variable +.Sq Ev MAKEFLAGS +may contain anything that +may be specified on +.Nm Ns 's +command line. +Anything specified on +.Nm Ns 's +command line is appended to the +.Va .MAKEFLAGS +variable, which is then added to the environment for all programs that +.Nm +executes. +.It Va .MAKE.GID +The numeric group ID of the user running +.Nm . +It is read-only. .It Va .MAKE.JOB.PREFIX If .Nm is run with .Fl j , the output for each target is prefixed with a token -.Ql --- target --- +.Dl --- Ar target Li --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. For example, setting .Va .MAKE.JOB.PREFIX to -.Li ${.newline}---${.MAKE:T}[${.MAKE.PID}] +.Ql ${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like -.Ql ---make[1234] target --- +.Dl ---make[1234] Ar target Li --- making it easier to track the degree of parallelism being achieved. -.It .MAKE.TARGET_LOCAL_VARIABLES -If set to -.Ql false , -apparent variable assignments in dependency lines are -treated as normal sources. -.It Ev MAKEFLAGS -The environment variable -.Sq Ev MAKEFLAGS -may contain anything that -may be specified on -.Nm Ns 's -command line. -Anything specified on -.Nm Ns 's -command line is appended to the -.Sq Ev MAKEFLAGS -variable which is then -entered into the environment for all programs which -.Nm -executes. +.It Va .MAKE.JOBS +The argument to the +.Fl j +option. .It Va .MAKE.LEVEL The recursion depth of .Nm . -The initial instance of +The top-level instance of .Nm -will be 0, and an incremented value is put into the environment -to be seen by the next generation. +has level 0, and each child make has its parent level plus 1. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 -to protect things which should only be evaluated in the initial instance of +to protect things which should only be evaluated in the top-level instance of +.Nm . +.It Va .MAKE.LEVEL.ENV +The name of the environment variable that stores the level of nested calls to .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Sq Pa makefile , .Sq Pa Makefile ) that .Nm -will look for. +looks for. .It Va .MAKE.MAKEFILES The list of makefiles read by .Nm , which is useful for tracking dependencies. Each makefile is recorded only once, regardless of the number of times read. +.It Va .MAKE.META.BAILIWICK +In +.Dq meta +mode, provides a list of prefixes which +match the directories controlled by +.Nm . +If a file that was generated outside of +.Va .OBJDIR +but within said bailiwick is missing, +the current target is considered out-of-date. +.It Va .MAKE.META.CMP_FILTER +In +.Dq meta +mode, it can (very rarely!) be useful to filter command +lines before comparison. +This variable can be set to a set of modifiers that are applied to +each line of the old and new command that differ, if the filtered +commands still differ, the target is considered out-of-date. +.It Va .MAKE.META.CREATED +In +.Dq meta +mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In +.Dq meta +mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.IGNORE_FILTER +Provides a list of variable modifiers to apply to each pathname. +Ignore if the expansion is an empty string. +.It Va .MAKE.META.IGNORE_PATHS +Provides a list of path prefixes that should be ignored; +because the contents are expected to change over time. +The default list includes: +.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp +.It Va .MAKE.META.IGNORE_PATTERNS +Provides a list of patterns to match against pathnames. +Ignore any that match. +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in +.Dq meta verbose +mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} .It Va .MAKE.MODE Processed after reading all makefiles. -Can affect the mode that +Affects the mode that .Nm runs in. -It can contain a number of keywords: -.Bl -hang -width missing-filemon=bf. -.It Pa compat +It can contain these keywords: +.Bl -tag -width indent +.It Cm compat Like .Fl B , puts .Nm -into "compat" mode. -.It Pa meta +into +.Dq compat +mode. +.It Cm meta Puts .Nm -into "meta" mode, where meta files are created for each target -to capture the command run, the output generated and if +into +.Dq meta +mode, where meta files are created for each target +to capture the command run, the output generated, and if .Xr filemon 4 is available, the system calls which are of interest to .Nm . -The captured output can be very useful when diagnosing errors. -.It Pa curdirOk= Ar bf -Normally +The captured output can be useful when diagnosing errors. +.It Cm curdirOk= Ns Ar bf +By default, .Nm -will not create .meta files in +does not create +.Pa .meta +files in .Sq Va .CURDIR . This can be overridden by setting -.Va bf -to a value which represents True. -.It Pa missing-meta= Ar bf +.Ar bf +to a value which represents true. +.It Cm missing-meta= Ns Ar bf If -.Va bf -is True, then a missing .meta file makes the target out-of-date. -.It Pa missing-filemon= Ar bf +.Ar bf +is true, a missing +.Pa .meta +file makes the target out-of-date. +.It Cm missing-filemon= Ns Ar bf If -.Va bf -is True, then missing filemon data makes the target out-of-date. -.It Pa nofilemon +.Ar bf +is true, missing filemon data makes the target out-of-date. +.It Cm nofilemon Do not use .Xr filemon 4 . -.It Pa env +.It Cm env For debugging, it can be useful to include the environment -in the .meta file. -.It Pa verbose -If in "meta" mode, print a clue about the target being built. +in the +.Pa .meta +file. +.It Cm verbose +If in +.Dq meta +mode, print a clue about the target being built. This is useful if the build is otherwise running silently. -The message printed the value of: +The message printed is the expanded value of .Va .MAKE.META.PREFIX . -.It Pa ignore-cmd +.It Cm ignore-cmd Some makefiles have commands which are simply not stable. This keyword causes them to be ignored for -determining whether a target is out of date in "meta" mode. +determining whether a target is out of date in +.Dq meta +mode. See also .Ic .NOMETA_CMP . -.It Pa silent= Ar bf +.It Cm silent= Ns Ar bf If -.Va bf -is True, when a .meta file is created, mark the target +.Ar bf +is true, when a .meta file is created, mark the target .Ic .SILENT . -.It Pa randomize-targets +.It Cm randomize-targets In both compat and parallel mode, do not make the targets in the usual order, but instead randomize their order. This mode can be used to detect undeclared dependencies between files. .El -.It Va .MAKE.META.BAILIWICK -In "meta" mode, provides a list of prefixes which -match the directories controlled by -.Nm . -If a file that was generated outside of -.Va .OBJDIR -but within said bailiwick is missing, -the current target is considered out-of-date. -.It Va .MAKE.META.CMP_FILTER -In "meta" mode, it can (very rarely!) be useful to filter command -lines before comparison. -This variable can be set to a set of modifiers that will be applied to -each line of the old and new command that differ, if the filtered -commands still differ, the target is considered out-of-date. -.It Va .MAKE.META.CREATED -In "meta" mode, this variable contains a list of all the meta files -updated. -If not empty, it can be used to trigger processing of -.Va .MAKE.META.FILES . -.It Va .MAKE.META.FILES -In "meta" mode, this variable contains a list of all the meta files -used (updated or not). -This list can be used to process the meta files to extract dependency -information. -.It Va .MAKE.META.IGNORE_PATHS -Provides a list of path prefixes that should be ignored; -because the contents are expected to change over time. -The default list includes: -.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp -.It Va .MAKE.META.IGNORE_PATTERNS -Provides a list of patterns to match against pathnames. -Ignore any that match. -.It Va .MAKE.META.IGNORE_FILTER -Provides a list of variable modifiers to apply to each pathname. -Ignore if the expansion is an empty string. -.It Va .MAKE.META.PREFIX -Defines the message printed for each meta file updated in "meta verbose" mode. -The default value is: -.Dl Building ${.TARGET:H:tA}/${.TARGET:T} +.It Va MAKEOBJDIR +Used to create files in a separate directory, see +.Va .OBJDIR . +.It Va MAKE_OBJDIR_CHECK_WRITABLE +Used to force a separate directory for the created files, +even if that directory is not writable, see +.Va .OBJDIR . +.It Va MAKEOBJDIRPREFIX +Used to create files in a separate directory, see +.Va .OBJDIR . +.It Va .MAKE.OS +The name of the operating system, see +.Xr uname 1 . +It is read-only. .It Va .MAKEOVERRIDES 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 .Sq Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Sq Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Sq Va .MAKEOVERRIDES . .Sq Ev MAKEFLAGS is re-exported whenever .Sq Va .MAKEOVERRIDES is modified. .It Va .MAKE.PATH_FILEMON If .Nm was built with .Xr filemon 4 support, this is set to the path of the device node. This allows makefiles to test for this support. .It Va .MAKE.PID -The process-id of +The process ID of .Nm . +It is read-only. .It Va .MAKE.PPID -The parent process-id of -.Nm . -.It Va .MAKE.SAVE_DOLLARS -value should be a boolean that controls whether -.Ql $$ -are preserved when doing -.Ql := -assignments. -The default is false, for backwards compatibility. -Set to true for compatability with other makes. -If set to false, -.Ql $$ -becomes -.Ql $ -per normal evaluation rules. -.It Va .MAKE.UID -The user-id running -.Nm . -.It Va .MAKE.GID -The group-id running +The parent process ID of .Nm . +It is read-only. .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Sq Va .ERROR_TARGET to the name of the target that failed, .Sq Va .ERROR_CMD to the commands of the failed target, -and in "meta" mode, it also sets +and in +.Dq meta +mode, it also sets .Sq Va .ERROR_CWD to the .Xr getcwd 3 , and .Sq Va .ERROR_META_FILE to the path of the meta file (if any) describing the failed target. It then prints its name and the value of .Sq Va .CURDIR as well as the value of any variables named in .Sq Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .MAKE.SAVE_DOLLARS +If true, +.Ql $$ +are preserved when doing +.Ql := +assignments. +The default is false, for backwards compatibility. +Set to true for compatability with other makes. +If set to false, +.Ql $$ +becomes +.Ql $ +per normal evaluation rules. +.It Va .MAKE.TARGET_LOCAL_VARIABLES +If set to +.Ql false , +apparent variable assignments in dependency lines are +treated as normal sources. +.It Va .MAKE.UID +The numeric ID of the user running +.Nm . +It is read-only. +.\" 'MAKE_VERSION' is intentionally undocumented +.\" since it is only defined in the bmake distribution, +.\" but not in NetBSD's native make. +.\" '.meta.%d.lcwd' is intentionally undocumented +.\" since it is an internal implementation detail. +.\" '.meta.%d.ldir' is intentionally undocumented +.\" since it is an internal implementation detail. +.\" 'MFLAGS' is intentionally undocumented +.\" since it is obsolete. .It Va .newline This variable is simply assigned a newline character as its value. +It is read-only. This allows expansions using the .Cm \&:@ modifier to put a newline between iterations of the loop rather than a space. -For example, the printing of -.Sq Va MAKE_PRINT_VAR_ON_ERROR -could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. +For example, in case of an error, +.Nm +prints the variable names and their values using: +.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Va .OBJDIR A path to the directory where the targets are built. Its value is determined by trying to .Xr chdir 2 to the following directories in order and using the first match: .Bl -enum .It -.Ev ${MAKEOBJDIRPREFIX}${.CURDIR} +.Cm ${MAKEOBJDIRPREFIX} Ns Cm ${.CURDIR} .Pp (Only if .Sq Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It -.Ev ${MAKEOBJDIR} +.Cm ${MAKEOBJDIR} .Pp (Only if .Sq Ev MAKEOBJDIR is set in the environment or on the command line.) .It -.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} +.Cm ${.CURDIR} Ns Pa /obj. Ns Cm ${MACHINE} .It -.Ev ${.CURDIR} Ns Pa /obj +.Cm ${.CURDIR} Ns Pa /obj .It -.Pa /usr/obj/ Ns Ev ${.CURDIR} +.Pa /usr/obj/ Ns Cm ${.CURDIR} .It -.Ev ${.CURDIR} +.Cm ${.CURDIR} .El .Pp -Variable expansion is performed on the value before it's used, +Variable expansion is performed on the value before it is used, so expressions such as -.Dl ${.CURDIR:S,^/usr/src,/var/obj,} +.Cm ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Sq Ev MAKEOBJDIR . .Pp .Sq Va .OBJDIR may be modified in the makefile via the special target .Sq Ic .OBJDIR . In all cases, .Nm -will -.Xr chdir 2 -to the specified directory if it exists, and set +changes to the specified directory if it exists, and sets .Sq Va .OBJDIR and -.Sq Ev PWD +.Sq Va PWD to that directory before executing any targets. .Pp Except in the case of an explicit .Sq Ic .OBJDIR target, .Nm -will check that the specified directory is writable and ignore it if not. +checks that the specified directory is writable and ignores it if not. This check can be skipped by setting the environment variable .Sq Ev MAKE_OBJDIR_CHECK_WRITABLE -to "no". -. +to +.Dq no . .It Va .PARSEDIR -A path to the directory of the current -.Sq Pa Makefile -being parsed. +The directory name of the current makefile being parsed. .It Va .PARSEFILE -The basename of the current -.Sq Pa Makefile -being parsed. +The basename of the current makefile being parsed. This variable and .Sq Va .PARSEDIR -are both set only while the -.Sq Pa Makefiles -are being parsed. -If you want to retain their current values, assign them to a variable -using assignment with expansion +are both set only while the makefiles are being parsed. +To retain their current values, +assign them to a variable using assignment with expansion .Sq Cm \&:= . .It Va .PATH -A variable that represents the list of directories that +The space-separated list of directories that .Nm -will search for files. -The search list should be updated using the target -.Sq Va .PATH -rather than the variable. -.It Ev PWD +searches for files. +To update this search list, use the special target +.Sq Ic .PATH +rather than modifying the variable directly. +.It Va %POSIX +Is set in POSIX mode, see the special +.Ql Va .POSIX +target. +.\" XXX: There is no make variable named 'PWD', +.\" XXX: make only reads and writes the environment variable 'PWD'. +.It Va PWD Alternate path to the current directory. .Nm normally sets .Sq Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Sq Ev PWD -is set and gives a path to the current directory, then +is set and gives a path to the current directory, .Nm sets .Sq Va .CURDIR to the value of .Sq Ev PWD instead. This behavior is disabled if .Sq Ev MAKEOBJDIRPREFIX is set or .Sq Ev MAKEOBJDIR contains a variable transform. -.Sq Ev PWD +.Sq Va PWD is set to the value of .Sq Va .OBJDIR for all programs which .Nm executes. -.It Ev .SHELL +.It Va .SHELL The pathname of the shell used to run target scripts. It is read-only. -.It Ev .SUFFIXES +.It Va .SUFFIXES The list of known suffixes. It is read-only. -.It Ev .TARGETS +.It Va .SYSPATH +The space-separated list of directories that +.Nm +searches for makefiles, referred to as the system include path. +To update this search list, use the special target +.Sq Ic .SYSPATH +rather than modifying the variable which is read-only. +.It Va .TARGETS The list of targets explicitly specified on the command line, if any. -.It Ev VPATH -Colon-separated +.It Va VPATH +The colon-separated .Pq Dq \&: -lists of directories that +list of directories that .Nm -will search for files. -The variable is supported for compatibility with old make programs only, -use +searches for files. +This variable is supported for compatibility with old make programs only, use .Sq Va .PATH instead. .El .Ss Variable modifiers -Variable expansion may be modified to select or modify each word of the -variable (where a -.Dq word -is white-space delimited sequence of characters). -The general format of a variable expansion is as follows: +The general format of a variable expansion is: .Pp -.Dl ${variable[:modifier[:...]]} +.Sm off +.D1 Ic \&${ Ar variable\| Oo Ic \&: Ar modifier\| Oo Ic \&: No ... Oc Oc Ic \&} +.Sm on .Pp -Each modifier begins with a colon, -which may be escaped with a backslash -.Pq Ql \e . +Each modifier begins with a colon. +To escape a colon, precede it with a backslash +.Ql \e . .Pp -A set of modifiers can be specified via a variable, as follows: +A list of indirect modifiers can be specified via a variable, as follows: .Pp -.Dl modifier_variable=modifier[:...] -.Dl ${variable:${modifier_variable}[:...]} +.Bd -literal -offset indent +.Ar modifier_variable\^ Li \&= Ar modifier Ns Oo Ic \&: Ns No ... Oc + +.Sm off +.Ic \&${ Ar variable Ic \&:${ Ar modifier_variable Ic \&} Oo Ic \&: No ... Oc Ic \&} +.Sm on +.Ed .Pp -In this case the first modifier in the modifier_variable does not -start with a colon, since that must appear in the referencing -variable. -If any of the modifiers in the modifier_variable contain a dollar sign +In this case, the first modifier in the +.Ar modifier_variable +does not start with a colon, +since that colon already occurs in the referencing variable. +If any of the modifiers in the +.Ar modifier_variable +contains a dollar sign .Pq Ql $ , these must be doubled to avoid early expansion. .Pp +Some modifiers interpret the expression value as a single string, +others treat the expression value as a whitespace-separated list of words. +When splitting a string into words, +whitespace can be escaped using double quotes, single quotes and backslashes, +like in the shell. +The quotes and backslashes are retained in the words. +.Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E -Replaces each word in the variable with its suffix. +Replaces each word with its suffix. .It Cm \&:H -Replaces each word in the variable with everything but the last component. +Replaces each word with its dirname. .It Cm \&:M\| Ns Ar pattern Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql \&[] ) may be used. The wildcard characters may be escaped with a backslash .Pq Ql \e . As a consequence of the way values are split into words, matched, -and then joined, a construct like -.Dl ${VAR:M*} -will normalize the inter-word spacing, removing all leading and -trailing space, and converting multiple consecutive spaces -to single spaces. -. +and then joined, the construct +.Ql ${VAR:M*} +removes all leading and trailing whitespace +and normalizes the inter-word spacing to a single space. .It Cm \&:N\| Ns Ar pattern -This is identical to +This is the opposite of .Sq Cm \&:M , -but selects all words which do not match +selecting all words which do +.Em not +match .Ar pattern . .It Cm \&:O -Orders every word in variable alphabetically. +Orders the words lexicographically. .It Cm \&:On -Orders every word in variable numerically. +Orders the words numerically. A number followed by one of .Ql k , .Ql M or .Ql G -is multiplied by the appropriate factor (1024 (k), 1048576 (M), or -1073741824 (G)). +is multiplied by the appropriate factor, which is 1024 for +.Ql k , +1048576 for +.Ql M , +or 1073741824 for +.Ql G . Both upper- and lower-case letters are accepted. .It Cm \&:Or -Orders every word in variable in reverse alphabetical order. +Orders the words in reverse lexicographical order. .It Cm \&:Orn -Orders every word in variable in reverse numerical order. +Orders the words in reverse numerical order. .It Cm \&:Ox -Shuffles the words in variable. -The results will be different each time you are referring to the +Shuffles the words. +The results are different each time you are referring to the modified variable; use the assignment with expansion .Sq Cm \&:= to prevent such behavior. For example, .Bd -literal -offset indent LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" .Ed may produce output similar to: .Bd -literal -offset indent quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre .Ed .It Cm \&:Q -Quotes every shell meta-character in the variable, so that it can be passed +Quotes every shell meta-character in the value, so that it can be passed safely to the shell. .It Cm \&:q -Quotes every shell meta-character in the variable, and also doubles +Quotes every shell meta-character in the value, and also doubles .Sq $ characters so that it can be passed safely through recursive invocations of .Nm . -This is equivalent to: -.Sq \&:S/\e\&$/&&/g:Q . +This is equivalent to +.Sq Cm \&:S/\e\&$/&&/g:Q . .It Cm \&:R -Replaces each word in the variable with everything but its suffix. -.It Cm \&:range Ns Oo = Ns Ar count Oc +Replaces each word with everything but its suffix. +.It Cm \&:range Ns Oo Cm = Ns Ar count Oc The value is an integer sequence representing the words of the original value, or the supplied -.Va count . -.It Cm \&:gmtime Ns Oo = Ns Ar utc Oc -The value is a format string for +.Ar count . +.It Cm \&:gmtime Ns Oo Cm = Ns Ar timestamp Oc +The value is interpreted as a format string for .Xr strftime 3 , using -.Xr gmtime 3 . +.Xr gmtime 3 , +producing the formatted timestamp. If a -.Va utc +.Ar timestamp value is not provided or is 0, the current time is used. .It Cm \&:hash -Computes a 32-bit hash of the value and encode it as hex digits. -.It Cm \&:localtime Ns Oo = Ns Ar utc Oc -The value is a format string for +Computes a 32-bit hash of the value and encodes it as 8 hex digits. +.It Cm \&:localtime Ns Oo Cm = Ns Ar timestamp Oc +The value is interpreted as a format string for .Xr strftime 3 , using -.Xr localtime 3 . +.Xr localtime 3 , +producing the formatted timestamp. If a -.Va utc +.Ar timestamp value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempts to convert variable to an absolute path using -.Xr realpath 3 , -if that fails, the value is unchanged. +Attempts to convert the value to an absolute path using +.Xr realpath 3 . +If that fails, the value is unchanged. .It Cm \&:tl -Converts variable to lower-case letters. +Converts the value to lower-case letters. .It Cm \&:ts Ns Ar c -Words in the variable are normally separated by a space on expansion. -This modifier sets the separator to the character +When joining the words after a modifier that treats the value as words, +the words are normally separated by a space. +This modifier changes the separator to the character .Ar c . If .Ar c -is omitted, then no separator is used. +is omitted, no separator is used. The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu -Converts variable to upper-case letters. +Converts the value to upper-case letters. .It Cm \&:tW -Causes the value to be treated as a single word -(possibly containing embedded white space). +Causes subsequent modifiers to treat the value as a single word +(possibly containing embedded whitespace). See also .Sq Cm \&:[*] . .It Cm \&:tw -Causes the value to be treated as a sequence of -words delimited by white space. +Causes the value to be treated as a list of words. See also .Sq Cm \&:[@] . .Sm off .It Cm \&:S\| No \&/ Ar old_string\| No \&/ Ar new_string\| No \&/ Op Cm 1gW .Sm on Modifies the first occurrence of .Ar old_string -in each word of the variable's value, replacing it with +in each word of the value, replacing it with .Ar new_string . If a .Ql g -is appended to the last delimiter of the pattern, all occurrences -in each word are replaced. +is appended to the last delimiter of the pattern, +all occurrences in each word are replaced. If a .Ql 1 -is appended to the last delimiter of the pattern, only the first occurrence -is affected. +is appended to the last delimiter of the pattern, +only the first occurrence is affected. If a .Ql W is appended to the last delimiter of the pattern, -then the value is treated as a single word -(possibly containing embedded white space). +the value is treated as a single word. If .Ar old_string begins with a caret .Pq Ql ^ , .Ar old_string is anchored at the beginning of each word. If .Ar old_string ends with a dollar sign .Pq Ql \&$ , it is anchored at the end of each word. Inside .Ar new_string , an ampersand .Pq Ql & is replaced by .Ar old_string -(without any +(without the anchoring .Ql ^ or .Ql \&$ ) . -Any character may be used as a delimiter for the parts of the modifier +Any character may be used as the delimiter for the parts of the modifier string. -The anchoring, ampersand and delimiter characters may be escaped with a +The anchoring, ampersand and delimiter characters can be escaped with a backslash .Pq Ql \e . .Pp -Variable expansion occurs in the normal fashion inside both +Both .Ar old_string and .Ar new_string -with the single exception that a backslash is used to prevent the expansion -of a dollar sign -.Pq Ql \&$ , -not a preceding dollar sign as is usual. +may contain nested expressions. +To prevent a dollar sign from starting a nested expression, +escape it with a backslash. .Sm off .It Cm \&:C\| No \&/ Ar pattern\| No \&/ Ar replacement\| No \&/ Op Cm 1gW .Sm on The .Cm \&:C -modifier is just like the +modifier works like the .Cm \&:S modifier except that the old and new strings, instead of being -simple strings, are an extended regular expression (see -.Xr regex 3 ) -string +simple strings, are an extended regular expression .Ar pattern +(see +.Xr regex 3 ) and an .Xr ed 1 Ns \-style -string .Ar replacement . Normally, the first occurrence of the pattern .Ar pattern in each word of the value is substituted with .Ar replacement . The .Ql 1 modifier causes the substitution to apply to at most one word; the .Ql g modifier causes the substitution to apply to as many instances of the search pattern .Ar pattern as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word -(possibly containing embedded white space). +(possibly containing embedded whitespace). .Pp As for the .Cm \&:S modifier, the .Ar pattern and .Ar replacement are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last path component. +Replaces each word with its last path component (basename). .It Cm \&:u Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&?\| Ar true_string\| Cm \&: Ar false_string .Sm on -If the variable name (not its value), when parsed as a .if conditional -expression, evaluates to true, return as its value the +If the variable name (not its value), when parsed as a +.Cm .if +conditional expression, evaluates to true, return as its value the .Ar true_string , otherwise return the .Ar false_string . -Since the variable name is used as the expression, \&:\&? must be the -first modifier after the variable name itself - which will, of course, -usually contain variable expansions. +Since the variable name is used as the expression, +\&:\&? must be the first modifier after the variable name +.No itself Ns \^\(em\^ Ns +which, of course, usually contains variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} -which actually tests defined(NUMBERS), -to determine if any words match "42" you need to use something like: +which actually tests defined(NUMBERS). +To determine if any words match +.Dq 42 , +you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Cm :\| Ns Ar old_string\| Ns Cm = Ns Ar new_string This is the .At V -style variable substitution. -It must be the last modifier specified. -If +style substitution. +It can only be the last modifier specified, +as a +.Ql \&: +in either .Ar old_string or .Ar new_string -do not contain the pattern matching character -.Ar % -then it is assumed that they are -anchored at the end of each word, so only suffixes or entire -words may be replaced. -Otherwise -.Ar % -is the substring of -.Ar old_string -to be replaced in -.Ar new_string . -If only +is treated as a regular character, not as the end of the modifier. +.Pp +If .Ar old_string -contains the pattern matching character -.Ar % , -and +does not contain the pattern matching character +.Ql % , +and the word ends with .Ar old_string -matches, then the result is the +or equals it, +that suffix is replaced with .Ar new_string . -If only the -.Ar new_string -contains the pattern matching character -.Ar % , -then it is not treated specially and it is printed as a literal -.Ar % -on match. -If there is more than one pattern matching character -.Ar ( % ) -in either the +.Pp +Otherwise, the first +.Ql % +in +.Ar old_string +matches a possibly empty substring of arbitrary characters, +and if the whole pattern is found in the word, +the matching part is replaced with +.Ar new_string , +and the first occurrence of +.Ql % +in .Ar new_string -or -.Ar old_string , -only the first instance is treated specially (as the pattern character); -all subsequent instances are treated as regular characters. +(if any) is replaced with the substring matched by the +.Ql % . .Pp -Variable expansion occurs in the normal fashion inside both +Both .Ar old_string and .Ar new_string -with the single exception that a backslash is used to prevent the -expansion of a dollar sign -.Pq Ql \&$ , -not a preceding dollar sign as is usual. +may contain nested expressions. +To prevent a dollar sign from starting a nested expression, +escape it with a backslash. .Sm off -.It Cm \&:@ Ar temp\| Cm @ Ar string\| Cm @ +.It Cm \&:@ Ar varname\| Cm @ Ar string\| Cm @ .Sm on This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for loops, expansion occurs at the time of reference. -Assigns -.Ar temp -to each word in the variable and evaluates +For each word in the value, assign the word to the variable named +.Ar varname +and evaluate .Ar string . The ODE convention is that -.Ar temp -should start and end with a period. -For example. +.Ar varname +should start and end with a period, for example: .Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} .Pp -However a single character variable is often more readable: +However, a single-letter variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_ Ns Oo Cm = Ns Ar var Oc Saves the current variable value in .Ql $_ or the named -.Va var +.Ar var for later reference. Example usage: .Bd -literal -offset indent M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \&\\ \\* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .Dv .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} .Ed Here .Ql $_ is used to save the result of the .Ql :S modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U\| Ns Ar newval If the variable is undefined, .Ar newval 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: .Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D\| Ns Ar newval If the variable is defined, .Ar newval is the value. .It Cm \&:L The name of the variable is the value. .It Cm \&:P -The path of the node which has the same name as the variable -is the value. -If no such node exists or its path is null, then the -name of the variable is used. +The path of the node which has the same name as the variable is the value. +If no such node exists or its path is null, the name of the variable is used. In order for this modifier to work, the name (node) must at least have -appeared on the rhs of a dependency. +appeared on the right-hand side of a dependency. .Sm off .It Cm \&:\&! Ar cmd\| Cm \&! .Sm on The output of running .Ar cmd is the value. .It Cm \&:sh -If the variable is non-empty it is run as a command and the output -becomes the new value. +The value is run as a command, and the output becomes the new value. .It Cm \&::= Ns Ar str The variable is assigned the value .Ar str after substitution. -This modifier and its variations are useful in -obscure situations such as wanting to set a variable when shell commands -are being parsed. -These assignment modifiers always expand to -nothing, so if appearing in a rule line by themselves should be -preceded with something to keep -.Nm -happy. +This modifier and its variations are useful in obscure situations +such as wanting to set a variable +at a point where a target's shell commands are being parsed. +These assignment modifiers always expand to nothing. .Pp The .Sq Cm \&:: helps avoid false matches with the .At V style -.Cm \&:= -modifier and since substitution always occurs the -.Cm \&::= +.Ql \&:= +modifier and since substitution always occurs, the +.Ql \&::= form is vaguely appropriate. .It Cm \&::?= Ns Ar str As for .Cm \&::= but only if the variable does not already have a value. .It Cm \&::+= Ns Ar str Append .Ar str to the variable. .It Cm \&::!= Ns Ar cmd Assign the output of .Ar cmd to the variable. .It Cm \&:\&[ Ns Ar range Ns Cm \&] Selects one or more words from the value, or performs other operations related to the way in which the -value is divided into words. +value is split into words. .Pp -Ordinarily, a value is treated as a sequence of words -delimited by white space. -Some modifiers suppress this behavior, -causing a value to be treated as a single word -(possibly containing embedded white space). An empty value, or a value that consists entirely of white-space, is treated as a single word. For the purposes of the .Sq Cm \&:[] modifier, the words are indexed both forwards using positive integers (where index 1 represents the first word), and backwards using negative integers (where index \-1 represents the last word). .Pp The .Ar range is subjected to variable expansion, and the expanded result is then interpreted as follows: .Bl -tag -width index .\" :[n] .It Ar index Selects a single word from the value. .\" :[start..end] .It Ar start Ns Cm \&.. Ns Ar end Selects all words from .Ar start to .Ar end , inclusive. For example, .Sq Cm \&:[2..-1] selects all words from the second word to the last word. If .Ar start is greater than .Ar end , -then the words are output in reverse order. +the words are output in reverse order. For example, .Sq Cm \&:[-1..1] selects all the words from last to first. -If the list is already ordered, then this effectively reverses -the list, but it is more efficient to use +If the list is already ordered, +this effectively reverses the list, +but it is more efficient to use .Sq Cm \&:Or instead of .Sq Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word -(possibly containing embedded white space). +(possibly containing embedded whitespace). Analogous to the effect of -\&"$*\&" +.Li \&$* in Bourne shell. .\" :[0] .It 0 Means the same as .Sq Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words -delimited by white space. +delimited by whitespace. Analogous to the effect of -\&"$@\&" +.Li \&$@ in Bourne shell. .\" :[#] .It Cm \&# Returns the number of words in the value. .El \" :[range] .El -.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent -of the C programming language are provided in -.Nm . -All such structures are identified by a line beginning with a single -dot +.Sh DIRECTIVES +.Nm +offers directives for including makefiles, conditionals and for loops. +All these directives are identified by a line beginning with a single dot .Pq Ql \&. -character. +character, followed by the keyword of the directive, such as +.Cm include +or +.Cm if . +.Ss File inclusion Files are included with either .Cm \&.include \&< Ns Ar file Ns Cm \&> or .Cm \&.include \&\*q Ns Ar file Ns Cm \&\*q . Variables 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 used, the including makefile's directory and any directories specified using the .Fl I -option are searched before the system -makefile directory. -For compatibility with other versions of -.Nm -.Ql include file ... +option are searched before the system makefile directory. +.Pp +For compatibility with other make variants, +.Sq Cm include Ar file No ... +(without leading dot) is also accepted. .Pp If the include statement is written as .Cm .-include or as -.Cm .sinclude -then errors locating and/or opening include files are ignored. +.Cm .sinclude , +errors locating and/or opening include files are ignored. .Pp If the include statement is written as -.Cm .dinclude +.Cm .dinclude , not only are errors locating and/or opening include files ignored, -but stale dependencies within the included file will be ignored -just like +but stale dependencies within the included file are ignored just like in .Va .MAKE.DEPENDFILE . -.Pp -Conditional expressions are also preceded by a single dot as the first -character of a line. -The possible conditionals are as follows: +.Ss Exporting variables +The directives for exporting and unexporting variables are: .Bl -tag -width Ds -.It Ic .error Ar message -The message is printed along with the name of the makefile and line number, -then -.Nm -will exit immediately. -.It Ic .export Ar variable ... +.It Ic .export Ar variable No ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables (those that start with .Ql \&. ) . This is not affected by the .Fl X flag, so should be used with caution. -For compatibility with other -.Nm -programs -.Ql export variable=value -is also accepted. +For compatibility with other make programs, +.Cm export Ar variable\| Ns Cm \&= Ns Ar value +(without leading dot) is also accepted. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. -.It Ic .export-env Ar variable ... +.It Ic .export-env Ar variable No ... The same as .Ql .export , except that the variable is not appended to .Va .MAKE.EXPORTED . This allows exporting a value to the environment which is different from that used by .Nm internally. -.It Ic .export-literal Ar variable ... +.It Ic .export-literal Ar variable No ... The same as .Ql .export-env , 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 variables. -Only global variables can be un-defined. -.It Ic .unexport Ar variable ... +.It Ic .unexport Ar variable No ... The opposite of .Ql .export . The specified global -.Va variable -will be removed from +.Ar variable +is removed from .Va .MAKE.EXPORTED . If no variable list is provided, all globals are unexported, and .Va .MAKE.EXPORTED deleted. .It Ic .unexport-env Unexport all globals previously exported and clear the environment inherited from the parent. -This operation will cause a memory leak of the original environment, +This operation causes a memory leak of the original environment, so should be used sparingly. Testing for .Va .MAKE.LEVEL -being 0, would make sense. +being 0 would make sense. Also note that any variables which originated in the parent environment should be explicitly preserved if desired. For example: .Bd -literal -offset indent .Li .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .Li .unexport-env .Li .export PATH .Li .endif .Pp .Ed Would result in an environment containing only .Sq Ev PATH , which is the minimal useful environment. +.\" TODO: Check the below sentence, environment variables don't start with '.'. Actually -.Sq Ev .MAKE.LEVEL -will also be pushed into the new environment. +.Sq Va .MAKE.LEVEL +is also pushed into the new environment. +.El +.Ss Messages +The directives for printing messages to the output are: +.Bl -tag -width Ds +.It Ic .info Ar message +The message is printed along with the name of the makefile and line number. .It Ic .warning Ar message The message prefixed by -.Sq Pa warning: +.Sq Li warning: is printed along with the name of the makefile and line number. -.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... +.It Ic .error Ar message +The message is printed along with the name of the makefile and line number, +.Nm +exits immediately. +.El +.Ss Conditionals +The directives for conditionals are: +.ds maybenot Oo Ic \&! Oc Ns +.Bl -tag +.It Ic .if \*[maybenot] Ar expression Op Ar operator expression No ... Test the value of an expression. -.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... -Test the value of a variable. -.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... -Test the value of a variable. -.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... -Test the target being built. -.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... -Test the target being built. +.It Ic .ifdef \*[maybenot] Ar variable Op Ar operator variable No ... +Test whether a variable is defined. +.It Ic .ifndef \*[maybenot] Ar variable Op Ar operator variable No ... +Test whether a variable is not defined. +.It Ic .ifmake \*[maybenot] Ar target Op Ar operator target No ... +Test the target being requested. +.It Ic .ifnmake \*[maybenot] Ar target Op Ar operator target No ... +Test the target being requested. .It Ic .else Reverse the sense of the last conditional. -.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... +.It Ic .elif \*[maybenot] Ar expression Op Ar operator expression No ... A combination of .Sq Ic .else followed by .Sq Ic .if . -.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +.It Ic .elifdef \*[maybenot] Ar variable Op Ar operator variable No ... A combination of .Sq Ic .else followed by .Sq Ic .ifdef . -.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +.It Ic .elifndef \*[maybenot] Ar variable Op Ar operator variable No ... A combination of .Sq Ic .else followed by .Sq Ic .ifndef . -.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +.It Ic .elifmake \*[maybenot] Ar target Op Ar operator target No ... A combination of .Sq Ic .else followed by .Sq Ic .ifmake . -.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... +.It Ic .elifnmake \*[maybenot] Ar target Op Ar operator target No ... A combination of .Sq Ic .else followed by .Sq Ic .ifnmake . .It Ic .endif End the body of the conditional. .El .Pp The .Ar operator may be any one of the following: -.Bl -tag -width "Cm XX" -.It Cm \&|\&| +.Bl -tag +.It Ic \&|\&| Logical OR. -.It Cm \&&& -Logical -.Tn AND ; -of higher precedence than -.Dq \&|\&| . +.It Ic \&&& +Logical AND; of higher precedence than +.Sq Ic \&|\&| . .El .Pp -As in C, .Nm -will only evaluate a conditional as far as is necessary to determine -its value. -Parentheses may be used to change the order of evaluation. +only evaluates a conditional as far as is necessary to determine its value. +Parentheses can be used to override the operator precedence. The boolean operator .Sq Ic \&! -may be used to logically negate an entire -conditional. +may be used to logically negate an entire conditional. It is of higher precedence than .Sq Ic \&&& . .Pp The value of .Ar expression -may be any of the following: -.Bl -tag -width defined -.It Ic defined -Takes a variable name as an argument and evaluates to true if the variable +may be any of the following function call expressions: +.Bl -tag +.Sm off +.It Ic defined Li \&( Ar varname Li \&) +.Sm on +Evaluates to true if the variable +.Ar varname has been defined. -.It Ic make -Takes a target name as an argument and evaluates to true if the target -was specified as part of +.Sm off +.It Ic make Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target was specified as part of .Nm Ns 's command line or was declared the default target (either implicitly or explicitly, see .Va .MAIN ) before the line containing the conditional. -.It Ic empty -Takes a variable, with possible modifiers, and evaluates to true if -the expansion of the variable would result in an empty string. -.It Ic exists -Takes a file name as an argument and evaluates to true if the file exists. -The file is searched for on the system search path (see +.Sm off +.It Ic empty Li \&( Ar varname Oo Li : Ar modifiers Oc Li \&) +.Sm on +Evaluates to true if the expansion of the variable, +after applying the modifiers, results in an empty string. +.Sm off +.It Ic exists Li \&( Ar pathname Li \&) +.Sm on +Evaluates to true if the given pathname exists. +If relative, the pathname is searched for on the system search path (see .Va .PATH ) . -.It Ic target -Takes a target name as an argument and evaluates to true if the target -has been defined. -.It Ic commands -Takes a target name as an argument and evaluates to true if the target -has been defined and has commands associated with it. +.Sm off +.It Ic target Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target has been defined. +.Sm off +.It Ic commands Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target has been defined +and has commands associated with it. .El .Pp .Ar Expression 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 supported. -The standard C relational operators are all supported. -If after -variable expansion, either the left or right hand side of a -.Sq Ic == -or -.Sq Ic "!=" -operator is not a numerical value, then -string comparison is performed between the expanded -variables. -If no relational operator is given, it is assumed that the expanded -variable is being compared against 0, or an empty string in the case -of a string comparison. +Variable expansion is performed on both sides of the comparison. +If both sides are numeric and neither is enclosed in quotes, +the comparison is done numerically, otherwise lexicographically. +A string is interpreted as hexadecimal integer if it is preceded by +.Li 0x , +otherwise it is a decimal floating-point number; +octal numbers are not supported. +.Pp +All comparisons may use the operators +.Sq Ic \&== +and +.Sq Ic \&!= . +Numeric comparisons may also use the operators +.Sq Ic \&< , +.Sq Ic \&<= , +.Sq Ic \&> +and +.Sq Ic \&>= . +.Pp +If the comparison has neither a comparison operator nor a right side, +the expression evaluates to true if it is nonempty +and its numeric value (if any) is not zero. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters -a (white-space separated) word it doesn't recognize, either the +a (whitespace separated) word it doesn't recognize, either the .Dq make or .Dq defined -expression is applied to it, depending on the form of the conditional. +function is applied to it, depending on the form of the conditional. If the form is .Sq Ic .ifdef , -.Sq Ic .ifndef , +.Sq Ic .ifndef or -.Sq Ic .if +.Sq Ic .if , the .Dq defined -expression is applied. +function is applied. Similarly, if the form is .Sq Ic .ifmake or .Sq Ic .ifnmake , the .Dq make -expression is applied. +function is applied. .Pp -If the conditional evaluates to true the parsing of the makefile continues -as before. +If the conditional evaluates to true, +parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. -In both cases this continues until a +In both cases, this continues until the corresponding .Sq Ic .else or .Sq Ic .endif is found. -.Pp +.Ss For loops For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds -.It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-lines +.It Ic \&.for Ar variable Oo Ar variable No ... Oc Ic in Ar expression +.It Aq Ar make-lines .It Ic \&.endfor .El .Pp -After the for -.Ic expression -is evaluated, it is split into words. +The +.Ar expression +is expanded and then split into words. On each iteration of the loop, one word is taken and assigned to each -.Ic variable , +.Ar variable , in order, and these -.Ic variables +.Ar variables are substituted into the -.Ic make-lines +.Ar make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. +.Pp +If +.Sq Ic .break +is encountered within a +.Cm \&.for +loop, it causes early termination of the loop, otherwise a parse error. +.\" TODO: Describe limitations with defined/empty. +.Ss Other directives +.Bl -tag -width Ds +.It Ic .undef Ar variable No ... +Un-define the specified global variables. +Only global variables can be un-defined. +.El .Sh COMMENTS Comments begin with a hash .Pq Ql \&# character, anywhere but in a shell command line, and continue to the end of an unescaped new line. .Sh SPECIAL SOURCES (ATTRIBUTES) .Bl -tag -width .IGNOREx .It Ic .EXEC Target is never out of date, but always execute commands anyway. .It Ic .IGNORE Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . .\" .It Ic .INVISIBLE .\" XXX .\" .It Ic .JOIN .\" XXX .It Ic .MADE -Mark all sources of this target as being up-to-date. +Mark all sources of this target as being up to date. .It Ic .MAKE Execute the commands associated with this target even if the .Fl n or .Fl t options were specified. Normally used to mark recursive .Nm Ns s . .It Ic .META Create a meta file for the target, even if it is flagged as .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL . Usage in conjunction with .Ic .MAKE is the most likely case. -In "meta" mode, the target is out-of-date if the meta file is missing. +In +.Dq meta +mode, the target is out-of-date if the meta file is missing. .It Ic .NOMETA Do not create a meta file for the target. Meta files are also not created for .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL targets. .It Ic .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which always changes. -If the number of commands change, though, the target will still be out of date. +If the number of commands change, though, +the target is still considered out of date. The same effect applies to any command line that uses the variable .Va .OODATE , which can be used for that purpose even when not otherwise needed or desired: .Bd -literal -offset indent skip-compare-for-some: - @echo this will be compared - @echo this will not ${.OODATE:M.NOMETA_CMP} - @echo this will also be compared + @echo this is compared + @echo this is not ${.OODATE:M.NOMETA_CMP} + @echo this is also compared .Ed The .Cm \&:M pattern suppresses any expansion of the unwanted variable. .It Ic .NOPATH Do not search for the target in the directories specified by -.Ic .PATH . +.Va .PATH . .It Ic .NOTMAIN Normally .Nm selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .It Ic .OPTIONAL If a target is marked with this attribute and .Nm -can't figure out how to create it, it will ignore this fact and assume +can't figure out how to create it, it ignores this fact and assumes the file isn't needed or already exists. .It Ic .PHONY -The target does not -correspond to an actual file; it is always considered to be out of date, -and will not be created with the +The target does not correspond to an actual file; +it is always considered to be out of date, +and is not created with the .Fl t option. Suffix-transformation rules are not applied to .Ic .PHONY targets. .It Ic .PRECIOUS When .Nm is interrupted, it normally removes any partially made targets. This source prevents the target from being removed. .It Ic .RECURSIVE Synonym for .Ic .MAKE . .It Ic .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign .Pq Ql @ . .It Ic .USE Turn the target into .Nm Ns 's version of a macro. When the target is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .Ic .USE ) of the source. If the target already has commands, the .Ic .USE target's commands are appended to them. .It Ic .USEBEFORE -Exactly like +Like .Ic .USE , -but prepend the +but instead of appending, prepend the .Ic .USEBEFORE target commands to the target. .It Ic .WAIT If .Ic .WAIT appears 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 dependency tree. So given: .Bd -literal x: a .WAIT b echo x a: echo a b: b1 echo b b1: echo b1 .Ed the output is always .Ql a , .Ql b1 , .Ql b , .Ql x . -.br +.Pp The ordering imposed by .Ic .WAIT is only relevant for parallel makes. .El .Sh SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .Bl -tag -width .BEGINx .It Ic .BEGIN Any command lines attached to this target are executed before anything else is done. .It Ic .DEFAULT This is sort of a .Ic .USE -rule for any target (that was used only as a -source) that +rule for any target (that was used only as a source) that .Nm can't figure out any other way to create. Only the shell script is used. The -.Ic .IMPSRC +.Va .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's -commands is set -to the target's own name. +commands is set to the target's own name. .It Ic .DELETE_ON_ERROR If this target is present in the makefile, it globally causes 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. .It Ic .END Any command lines attached to this target are executed after everything -else is done. +else is done successfully. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The -.Ic .ERROR_TARGET +.Va .ERROR_TARGET variable is set to the target that failed. See also -.Ic MAKE_PRINT_VAR_ON_ERROR . +.Va MAKE_PRINT_VAR_ON_ERROR . .It Ic .IGNORE Mark each of the sources with the .Ic .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the .Fl i option. .It Ic .INTERRUPT If .Nm -is interrupted, the commands for this target will be executed. +is interrupted, the commands for this target are executed. .It Ic .MAIN If no target is specified when .Nm -is invoked, this target will be built. +is invoked, this target is built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm -when the makefile is used. +at the time when the makefiles are read. The flags are as if typed to the shell, though the .Fl f -option will have +option has no effect. .\" XXX: NOT YET!!!! .\" .It Ic .NOTPARALLEL .\" The named targets are executed in non parallel mode. .\" If no targets are -.\" specified, then all targets are executed in non parallel mode. +.\" specified, all targets are executed in non parallel mode. .It Ic .NOPATH Apply the .Ic .NOPATH attribute to any specified sources. .It Ic .NOTPARALLEL Disable parallel mode. .It Ic .NO_PARALLEL Synonym for .Ic .NOTPARALLEL , for compatibility with other pmake variants. +.It Ic .NOREADONLY +clear the read-only attribute from the global variables specified as sources. .It Ic .OBJDIR The source is a new value for .Sq Va .OBJDIR . If it exists, .Nm -will -.Xr chdir 2 -to it and update the value of +changes the current working directory to it and updates the value of .Sq Va .OBJDIR . .It Ic .ORDER In parallel mode, the named targets are made in sequence. This ordering does not add targets to the list of targets to be made. .Pp Since the dependents of a target do not get built until the target itself could be built, unless .Ql a is built by another part of the dependency graph, the following is a dependency loop: .Bd -literal \&.ORDER: b a b: a .Ed .Pp .\" XXX: NOT YET!!!! .\" .It Ic .PARALLEL .\" The named targets are executed in parallel mode. .\" If no targets are -.\" specified, then all targets are executed in parallel mode. +.\" specified, all targets are executed in parallel mode. .It Ic .PATH The sources are directories which are to be searched for files not found in the current directory. -If no sources are specified, any previously specified directories are -deleted. +If no sources are specified, +any previously specified directories are removed from the search path. If the source is the special .Ic .DOTLAST -target, then the current working -directory is searched last. -.It Ic .PATH. Ns Va suffix +target, the current working directory is searched last. +.It Ic .PATH. Ns Ar suffix Like .Ic .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .Ic .SUFFIXES . .It Ic .PHONY Apply the .Ic .PHONY attribute to any specified sources. .It Ic .POSIX If this is the first non-comment line in the main makefile, the variable .Va %POSIX is set to the value .Ql 1003.2 and the makefile .Ql is included if it exists, to provide POSIX-compatible default rules. If .Nm is run with the .Fl r -flag, then only +flag, only .Ql posix.mk -will contribute to the default rules. +contributes to the default rules. .It Ic .PRECIOUS Apply the .Ic .PRECIOUS attribute to any specified sources. If no sources are specified, the .Ic .PRECIOUS -attribute is applied to every -target in the file. +attribute is applied to every target in the file. +.It Ic .READONLY +set the read-only attribute on the global variables specified as sources. .It Ic .SHELL Sets the shell that .Nm -will use to execute commands. +uses to execute commands in jobs mode. The sources are a set of -.Ar field=value +.Ar field\| Ns Cm \&= Ns Ar value pairs. -.Bl -tag -width hasErrCtls -.It Ar name +.Bl -tag -width ".Li hasErrCtls" +.It Li name This is the minimal specification, used to select one of the built-in shell specs; -.Ar sh , -.Ar ksh , +.Li sh , +.Li ksh , and -.Ar csh . -.It Ar path -Specifies the path to the shell. -.It Ar hasErrCtl +.Li csh . +.It Li path +Specifies the absolute path to the shell. +.It Li hasErrCtl Indicates whether the shell supports exit on error. -.It Ar check +.It Li check The command to turn on error checking. -.It Ar ignore +.It Li ignore The command to disable error checking. -.It Ar echo +.It Li echo The command to turn on echoing of commands executed. -.It Ar quiet +.It Li quiet The command to turn off echoing of commands executed. -.It Ar filter +.It Li filter The output to filter after issuing the -.Ar quiet +.Li quiet command. It is typically identical to -.Ar quiet . -.It Ar errFlag +.Li quiet . +.It Li errFlag The flag to pass the shell to enable error checking. -.It Ar echoFlag +.It Li echoFlag The flag to pass the shell to enable command echoing. -.It Ar newline +.It Li newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. .El Example: .Bd -literal \&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e check="set \-e" ignore="set +e" \e echo="set \-v" quiet="set +v" filter="set +v" \e echoFlag=v errFlag=e newline="'\en'" .Ed .It Ic .SILENT Apply the .Ic .SILENT attribute to any specified sources. If no sources are specified, the .Ic .SILENT attribute is applied to every command in the file. .It Ic .STALE This target gets run when a dependency file contains stale entries, having .Va .ALLSRC set to the name of that dependency file. .It Ic .SUFFIXES Each source specifies a suffix to .Nm . If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. .Pp Example: .Bd -literal -\&.SUFFIXES: .o +\&.SUFFIXES: .c .o \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed +.It Ic .SYSPATH +The sources are directories which are to be added to the system +include path which +.Nm +searches for makefiles. +If no sources are specified, +any previously specified directories are removed from the system +include path. .El .Sh ENVIRONMENT .Nm uses the following environment variables, if they exist: .Ev MACHINE , .Ev MACHINE_ARCH , .Ev MAKE , .Ev MAKEFLAGS , .Ev MAKEOBJDIR , .Ev MAKEOBJDIRPREFIX , .Ev MAKESYSPATH , .Ev PWD , and .Ev TMPDIR . .Pp .Ev MAKEOBJDIRPREFIX and .Ev MAKEOBJDIR may only be set in the environment or on the command line to .Nm and not as makefile variables; see the description of .Sq Va .OBJDIR for more details. .Sh FILES .Bl -tag -width /usr/share/mk -compact .It .depend list of dependencies -.It Makefile -list of dependencies .It makefile -list of dependencies +first default makefile if no makefile is specified on the command line +.It Makefile +second default makefile if no makefile is specified on the command line .It sys.mk system makefile .It /usr/share/mk system makefile directory .El .Sh COMPATIBILITY -The basic make syntax is compatible between different versions of make; +The basic make syntax is compatible between different make variants; however the special variables, variable modifiers and conditionals are not. .Ss Older versions An incomplete list of changes in older versions of .Nm : .Pp 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. .Pp The way that parallel makes are scheduled changed in NetBSD 4.0 so that .ORDER and .WAIT apply recursively to the dependent nodes. The algorithms used may change again in the future. .Ss Other make dialects Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not support most of the features of .Nm as described in this manual. Most notably: .Bl -bullet -offset indent .It The .Ic .WAIT and .Ic .ORDER declarations and most functionality pertaining to parallelization. -(GNU make supports parallelization but lacks these features needed to +(GNU make supports parallelization but lacks the features needed to control it effectively.) .It Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) +.\" The "less powerful" above means that GNU make does not have the +.\" make(target), target(target) and commands(target) functions. .It All built-in variables that begin with a dot. .It Most of the special sources and targets that begin with a dot, with the notable exception of .Ic .PHONY , .Ic .PRECIOUS , and .Ic .SUFFIXES . .It Variable modifiers, except for the -.Dl :old=new +.Ql :old=new string substitution, which does not portably support globbing with .Ql % and historically only works on declared suffixes. .It The .Ic $> variable even in its short form; most makes support this functionality but its name varies. .El .Pp Some features are somewhat more portable, such as assignment with .Ic += , .Ic ?= , and .Ic != . The -.Ic .PATH +.Va .PATH functionality is based on an older feature .Ic VPATH found in GNU make and many versions of SVR4 make; however, historically its behavior is too ill-defined (and too buggy) to rely upon. .Pp The .Ic $@ and .Ic $< variables are more or less universally portable, as is the .Ic $(MAKE) variable. Basic use of suffix rules (for files only in the current directory, not trying to chain transformations together, etc.) is also reasonably portable. .Sh SEE ALSO .Xr mkdep 1 .Sh HISTORY .Nm is derived from NetBSD .Xr make 1 . It uses autoconf to facilitate portability to other platforms. .Pp A make command appeared in .At v7 . This make -implementation is based on Adam De Boor's pmake program which was written -for Sprite at Berkeley. +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 .Dq customs . .Pp Historically the target/dependency -.Dq FRC +.Ic FRC has been used to FoRCe rebuilding (since the target/dependency -does not exist... unless someone creates an -.Dq FRC +does not exist ... unless someone creates an +.Pa FRC file). .Sh BUGS The make -syntax is difficult to parse without actually acting on the data. +syntax is difficult to parse. For instance, finding the end of a variable's use should involve scanning each of the modifiers, using the correct terminator for each field. In many places make just counts {} and () in order to find the end of a variable expansion. .Pp There is no way of escaping a space character in a filename. diff --git a/contrib/bmake/bmake.cat1 b/contrib/bmake/bmake.cat1 index ff19b554c8c3..7cb47607417f 100644 --- a/contrib/bmake/bmake.cat1 +++ b/contrib/bmake/bmake.cat1 @@ -1,1644 +1,1743 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) NAME - bmake — maintain program dependencies + bmake -- maintain program dependencies SYNOPSIS bmake [-BeikNnqrSstWwX] [-C directory] [-D variable] [-d flags] [-f makefile] [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file] [-V variable] [-v variable] - [variable=value] [target ...] + [variable=value] [target ...] DESCRIPTION - bmake is a program designed to simplify the maintenance of other pro‐ + bmake is a program designed to simplify the maintenance of other pro- grams. Its input is a list of specifications as to the files upon which - programs and other files depend. If no -f makefile makefile option is - given, bmake will try to open ‘makefile’ then ‘Makefile’ in order to find - the specifications. If the file ‘.depend’ exists, it is read (see - mkdep(1)). + programs and other files depend. If no -f makefile option is given, + bmake tries to open `makefile' then `Makefile' in order to find the spec- + ifications. If the file `.depend' exists, it is read, see mkdep(1). This manual page is intended as a reference document only. For a more thorough description of bmake and makefiles, please refer to PMake - A - Tutorial. + Tutorial (from 1993). - bmake will prepend the contents of the MAKEFLAGS environment variable to - the command line arguments before parsing them. + bmake prepends the contents of the MAKEFLAGS environment variable to the + command line arguments before parsing them. The options are as follows: -B Try to be backwards compatible by executing a single shell per - command and by executing the commands to make the sources of a - dependency line in sequence. + command and by making the sources of a dependency line in se- + quence. -C directory - Change to directory before reading the makefiles or doing any‐ - thing else. If multiple -C options are specified, each is inter‐ + Change to directory before reading the makefiles or doing any- + thing else. If multiple -C options are specified, each is inter- preted relative to the previous one: -C / -C etc is equivalent to -C /etc. -D variable Define variable to be 1, in the global scope. - -d [-]flags + -d [-]flags Turn on debugging, and specify which portions of bmake are to print debugging information. Unless the flags are preceded by - ‘-’ they are added to the MAKEFLAGS 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 F 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. Flags is one or more of the following: - - A Print all possible debugging information; equivalent to + `-', they are added to the MAKEFLAGS environment variable and are + passed on to any child make processes. By default, debugging in- + formation is printed to standard error, but this can be changed + using the F 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, the standard output is + line buffered. The available flags are: + + A Print all possible debugging information; equivalent to specifying all of the debugging flags. - a Print debugging information about archive searching and + a Print debugging information about archive searching and caching. - C Print debugging information about current working direc‐ - tory. + C Print debugging information about the current working di- + rectory. - c Print debugging information about conditional evaluation. + c Print debugging information about conditional evaluation. - d Print debugging information about directory searching and + d Print debugging information about directory searching and caching. - e Print debugging information about failed commands and + e Print debugging information about failed commands and targets. - F[+]filename + F[+]filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the - argument. If the character immediately after the ‘F’ - flag is ‘+’, then the file will be opened in append mode; - otherwise the file will be overwritten. If the file name - is ‘stdout’ or ‘stderr’ then debugging output will be - written to the standard output or standard error output - file descriptors respectively (and the ‘+’ option has no - effect). Otherwise, the output will be written to the - named file. If the file name ends ‘.%d’ then the ‘%d’ is - replaced by the pid. + argument. If the character immediately after the F flag + is `+', the file is opened in append mode; otherwise the + file is overwritten. If the file name is `stdout' or + `stderr', debugging output is written to the standard + output or standard error output respectively (and the `+' + option has no effect). Otherwise, the output is written + to the named file. If the file name ends with `.%d', the + `%d' is replaced by the pid. - f Print debugging information about loop evaluation. + f Print debugging information about loop evaluation. - g1 Print the input graph before making anything. + g1 Print the input graph before making anything. - g2 Print the input graph after making everything, or before + g2 Print the input graph after making everything, or before exiting on error. - g3 Print the input graph before exiting on error. + g3 Print the input graph before exiting on error. - h Print debugging information about hash table operations. + h Print debugging information about hash table operations. - j Print debugging information about running multiple + j Print debugging information about running multiple shells. - L Turn on lint checks. This will throw errors for variable - assignments that do not parse correctly, at the time of - assignment so the file and line number are available. + L Turn on lint checks. This throws errors for variable as- + signments that do not parse correctly, at the time of as- + signment, so the file and line number are available. - l Print commands in Makefiles regardless of whether or not - they are prefixed by ‘@’ or other "quiet" flags. Also + l Print commands in Makefiles regardless of whether or not + they are prefixed by `@' or other "quiet" flags. Also known as "loud" behavior. - M Print debugging information about "meta" mode decisions + M Print debugging information about "meta" mode decisions about targets. - m Print debugging information about making targets, includ‐ + m Print debugging information about making targets, includ- ing modification dates. - n Don't delete the temporary command scripts created when + n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in - the directory referred to by the TMPDIR environment vari‐ + the directory referred to by the TMPDIR environment vari- able, or in /tmp if TMPDIR is unset or set to the empty string. The temporary scripts are created by mkstemp(3), and have names of the form makeXXXXXX. NOTE: This can create many files in TMPDIR or /tmp, so use with care. - p Print debugging information about makefile parsing. + p Print debugging information about makefile parsing. - s Print debugging information about suffix-transformation + s Print debugging information about suffix-transformation rules. - t Print debugging information about target list mainte‐ + t Print debugging information about target list mainte- nance. - V Force the -V option to print raw values of variables, + V Force the -V option to print raw values of variables, overriding the default behavior set via .MAKE.EXPAND_VARIABLES. - v Print debugging information about variable assignment. + v Print debugging information about variable assignment and + expansion. - x Run shell commands with -x so the actual commands are + x Run shell commands with -x so the actual commands are printed as they are executed. - -e Specify that environment variables override macro assignments - within makefiles. + -e Let environment variables override global variables within make- + files. -f makefile - Specify a makefile to read instead of the default ‘makefile’. If - makefile is ‘-’, standard input is read. Multiple makefiles may - be specified, and are read in the order specified. + Specify a makefile to read instead of the default makefile or + Makefile. If makefile is `-', standard input is read. Multiple + makefiles may be specified, and are read in the order specified. -I directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the -m option) is automatically included as part of this list. - -i Ignore non-zero exit of shell commands in the makefile. Equiva‐ - lent to specifying ‘-’ before each command line in the makefile. + -i Ignore non-zero exit of shell commands in the makefile. Equiva- + lent to specifying `-' before each command line in the makefile. -J private This option should not be specified by the user. When the -j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. -j max_jobs Specify the maximum number of jobs that bmake may have running at - any one time. The value is saved in .MAKE.JOBS. Turns compati‐ + any one time. The value is saved in .MAKE.JOBS. Turns compati- bility mode off, unless the -B option is also specified. When compatibility mode is off, all commands associated with a target - are executed in a single shell invocation as opposed to the tra‐ - ditional one shell invocation per line. This can break tradi‐ - tional scripts which change directories on each command invoca‐ + are executed in a single shell invocation as opposed to the tra- + ditional one shell invocation per line. This can break tradi- + tional scripts which change directories on each command invoca- tion and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. -k Continue processing after errors are encountered, but only on those targets that do not depend on the target whose creation caused the error. -m directory - Specify a directory in which to search for sys.mk and makefiles + Specify a directory in which to search for sys.mk and makefiles included via the <file>-style include statement. The -m option - 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 "file"-style include statements (see the -I op‐ - tion). - - If a file or directory name in the -m argument (or the - MAKESYSPATH environment variable) starts with the string ".../" - then bmake will search for the specified file or directory named - in the remaining part of the argument string. The search starts - with the current directory of the Makefile and then works upward - towards the root of the file system. If the search is success‐ - ful, then the resulting directory replaces the ".../" specifica‐ - tion in the -m argument. If used, this feature allows bmake to - easily search in the current source tree for customized sys.mk - files (e.g., by using ".../mk/sys.mk" as an argument). + can be used multiple times to form a search path. This path + overrides the default system include path /usr/share/mk. Fur- + thermore, the system include path is appended to the search path + used for "file"-style include statements (see the -I option). + The system include path can be referenced via the read-only vari- + able .SYSPATH. + + If a directory name in the -m argument (or the MAKESYSPATH envi- + ronment variable) starts with the string `.../', bmake searches + for the specified file or directory named in the remaining part + of the argument string. The search starts with the current di- + rectory and then works upward towards the root of the file sys- + tem. If the search is successful, the resulting directory re- + places the `.../' specification in the -m argument. This feature + allows bmake to easily search in the current source tree for cus- + tomized sys.mk files (e.g., by using `.../mk/sys.mk' as an argu- + ment). -n Display the commands that would have been executed, but do not - actually execute them unless the target depends on the .MAKE spe‐ - cial source (see below) or the command is prefixed with ‘+’. + actually execute them unless the target depends on the .MAKE spe- + cial source (see below) or the command is prefixed with `+'. - -N Display the commands which would have been executed, but do not + -N Display the commands that would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles without descending into subdirectories. - -q Do not execute any commands, but exit 0 if the specified targets - are up-to-date and 1, otherwise. + -q Do not execute any commands, instead exit 0 if the specified tar- + gets are up to date, and 1 otherwise. -r Do not use the built-in rules specified in the system makefile. -S Stop processing if an error is encountered. This is the default behavior and the opposite of -k. -s Do not echo any commands as they are executed. Equivalent to - specifying ‘@’ before each command line in the makefile. + specifying `@' before each command line in the makefile. -T tracefile When used with the -j flag, append a trace record to tracefile for each job started and completed. -t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up- to-date. -V variable Print the value of variable. Do not build any targets. Multiple - instances of this option may be specified; the variables will be - printed one per line, with a blank line for each null or unde‐ + instances of this option may be specified; the variables are + printed one per line, with a blank line for each null or unde- fined variable. The value printed is extracted from the global - scope after all makefiles have been read. By default, the raw - variable contents (which may include additional unexpanded vari‐ - able references) are shown. If variable contains a ‘$’ then the - value will be recursively expanded to its complete resultant text - before printing. The expanded value will also be printed if + scope after all makefiles have been read. + + By default, the raw variable contents (which may include addi- + tional unexpanded variable references) are shown. If variable + contains a `$', it is not interpreted as a variable name but + rather as an expression. Its value is expanded before printing. + The value is also expanded before printing if .MAKE.EXPAND_VARIABLES is set to true and the -dV option has not - been used to override it. Note that loop-local and target-local - variables, as well as values taken temporarily by global vari‐ - ables during makefile processing, are not accessible via this op‐ - tion. The -dv debug mode can be used to see these at the cost of - generating substantial extraneous output. + been used to override it. + + Note that loop-local and target-local variables, as well as val- + ues taken temporarily by global variables during makefile pro- + cessing, are not accessible via this option. The -dv debug mode + can be used to see these at the cost of generating substantial + extraneous output. -v variable - Like -V but the variable is always expanded to its complete - value. + Like -V, but all printed variables are always expanded to their + complete value. The last occurrence of -V or -v decides whether + all variables are expanded or not. -W Treat any warnings during makefile parsing as errors. - -w Print entering and leaving directory messages, pre and post pro‐ + -w Print entering and leaving directory messages, pre and post pro- cessing. - -X Don't export variables passed on the command line to the environ‐ + -X Don't export variables passed on the command line to the environ- ment individually. Variables passed on the command line are - still exported via the MAKEFLAGS environment variable. This op‐ + still exported via the MAKEFLAGS environment variable. This op- tion may be useful on systems which have a small limit on the size of command arguments. - variable=value + variable=value Set the value of the variable variable to value. Normally, all values passed on the command line are also exported to sub-makes - in the environment. The -X flag disables this behavior. Vari‐ + in the environment. The -X flag disables this behavior. Vari- able assignments should follow options for POSIX compatibility but no ordering is enforced. - There are seven different types of lines in a makefile: file dependency + There are several different types of lines in a makefile: dependency specifications, shell commands, variable assignments, include statements, - conditional directives, for loops, and comments. + conditional directives, for loops, other directives, and comments. - In general, lines may be continued from one line to the next by ending - them with a backslash (‘\’). The trailing newline character and initial - whitespace on the following line are compressed into a single space. + Lines may be continued from one line to the next by ending them with a + backslash (`\'). The trailing newline character and initial whitespace + on the following line are compressed into a single space. FILE DEPENDENCY SPECIFICATIONS 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 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: + 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 is re-cre- + ated, but not until all sources have been examined and themselves re-cre- + ated as needed. Three operators may be used: : Many dependency lines may name this target but only one may have attached shell commands. All sources named in all dependency lines are considered together, and if needed the attached shell commands - are run to create or re-create the target. If bmake is inter‐ + are run to create or re-create the target. If bmake is inter- rupted, the target is removed. ! The same, but the target is always re-created whether or not it is out of date. :: Any dependency line may have attached shell commands, but each one - 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‐ + 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 :, for dependency lines with no sources, the - attached shell commands are always run. Also unlike :, the target - will not be removed if bmake is interrupted. - All dependency lines mentioning a particular target must use the same op‐ + Furthermore, unlike :, for dependency lines with no sources, the + attached shell commands are always run. Also unlike :, the target + is not removed if bmake is interrupted. + + 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 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. + 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 only match existing files. + The value `{}' need not necessarily be used to describe existing files. + Expansion is in directory order, not alphabetically as done in the shell. SHELL COMMANDS - Each target may have associated with it one or more lines of shell com‐ + Each target may have associated with it one or more lines of shell com- mands, normally used to create the target. Each of the lines in this script must be preceded by a tab. (For historical reasons, spaces are - not accepted.) While targets can appear in many dependency lines if de‐ + not accepted.) While targets can occur in many dependency lines if de- sired, by default only one of these rules may be followed by a creation - script. If the ‘::’ operator is used, however, all rules may include - scripts and the scripts are executed in the order found. + script. If the `::' operator is used, however, all rules may include + scripts, and the respective scripts are executed in the order found. Each line is treated as a separate shell command, unless the end of line - is escaped with a backslash (‘\’) in which case that line and the next - are combined. If the first characters of the command are any combination - of ‘@’, ‘+’, or ‘-’, the command is treated specially. A ‘@’ causes the - command not to be echoed before it is executed. A ‘+’ causes the command - to be executed even when -n is given. This is similar to the effect of - the .MAKE special source, except that the effect can be limited to a sin‐ - gle line of a script. A ‘-’ in compatibility mode causes any non-zero - exit status of the command line to be ignored. + is escaped with a backslash `\', in which case that line and the next are + combined. If the first characters of the command are any combination of + `@', `+', or `-', the command is treated specially. + + @ causes the command not to be echoed before it is executed. + + + causes the command to be executed even when -n is given. + This is similar to the effect of the .MAKE special source, + except that the effect can be limited to a single line of a + script. + + - in compatibility mode causes any non-zero exit status of + the command line to be ignored. When bmake is run in jobs mode with -j max_jobs, the entire script for the target is fed to a single instance of the shell. In compatibility - (non-jobs) mode, each command is run in a separate process. If the com‐ - mand contains any shell meta characters (‘#=|^(){};&<>*?[]:$`\\n’) it - will be passed to the shell; otherwise bmake will attempt direct execu‐ - tion. If a line starts with ‘-’ and the shell has ErrCtl enabled then - failure of the command line will be ignored as in compatibility mode. - Otherwise ‘-’ affects the entire job; the script will stop at the first - command line that fails, but the target will not be deemed to have - failed. + (non-jobs) mode, each command is run in a separate process. If the com- + mand contains any shell meta characters (`#=|^(){};&<>*?[]:$`\\n'), it is + passed to the shell; otherwise bmake attempts direct execution. If a + line starts with `-' and the shell has ErrCtl enabled, failure of the + command line is ignored as in compatibility mode. Otherwise `-' affects + the entire job; the script stops at the first command line that fails, + but the target is not deemed to have failed. Makefiles should be written so that the mode of bmake operation does not - 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 + change their behavior. For example, any command which uses "cd" or + "chdir" without the intention of changing the directory for subsequent + commands should be put in parentheses so it executes in a subshell. To + force the use of a single shell, escape the line breaks so as to make the whole script one command. For example: avoid-chdir-side-effects: - @echo Building $@ in `pwd` + @echo "Building $@ in $$(pwd)" @(cd ${.CURDIR} && ${MAKE} $@) - @echo Back in `pwd` + @echo "Back in $$(pwd)" ensure-one-shell-regardless-of-mode: - @echo Building $@ in `pwd`; \ + @echo "Building $@ in $$(pwd)"; \ (cd ${.CURDIR} && ${MAKE} $@); \ - echo Back in `pwd` + echo "Back in $$(pwd)" - Since bmake will chdir(2) to ‘.OBJDIR’ before executing any targets, each - child process starts with that as its current working directory. + Since bmake changes the current working directory to `.OBJDIR' before ex- + ecuting any targets, each child process starts with that as its current + working directory. VARIABLE ASSIGNMENTS Variables in make behave much like macros in the C preprocessor. - Variable assignments have the form ‘NAME op value’, where: + Variable assignments have the form `NAME op value', where: - NAME is a single-word variable name, consisting, by tradition, of all - upper-case letters, + NAME is a single-word variable name, consisting, by tradition, + of all upper-case letters, - op is one of the five variable assignment operators described below, - and + op is one of the variable assignment operators described be- + low, and - value is interpreted according to the variable assignment operator. + value is interpreted according to the variable assignment opera- + tor. Whitespace around NAME, op and value is discarded. Variable assignment operators - The five operators that can be used to assign values to variables are: + The five operators that assign values to variables are: - = Assign the value to the variable. Any previous value is over‐ + = Assign the value to the variable. Any previous value is over- written. += Append the value to the current value of the variable, separating them by a single space. ?= Assign the value to the variable if it is not already defined. - := Assign with expansion, i.e. expand the value before assigning it - to the variable. Normally, expansion is not done until the vari‐ - able is referenced. + := Expand the value, then assign it to the variable. NOTE: References to undefined variables are not expanded. This can cause problems when variable modifiers are used. - != Expand 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. + != Expand the value and pass it to the shell for execution, then as- + sign the output from the child's standard output to the variable. + Any newlines in the result are replaced with spaces. Expansion of variables - In contexts where variables are expanded, ‘$$’ expands to a single dollar - sign. References to variables have the form ‘${name[:modifiers]}’ or - ‘$(name[:modifiers]’). If the variable name contains only a single char‐ - acter, the surrounding curly braces or parentheses are not required. - This shorter form is not recommended. - - If the variable name contains a dollar, then the name itself is expanded - first. This allows almost arbitrary variable names, however names con‐ - taining dollar, braces, parentheses, or whitespace are really best + In most contexts where variables are expanded, `$$' expands to a single + dollar sign. In other contexts (most variable modifiers, string literals + in conditions), `\$' expands to a single dollar sign. + + References to variables have the form ${name[:modifiers]} or + $(name[:modifiers]). If the variable name consists of only a single + character and the expression contains no modifiers, the surrounding curly + braces or parentheses are not required. This shorter form is not recom- + mended. + + If the variable name contains a dollar, the name itself is expanded + first. This allows almost arbitrary variable names, however names con- + taining dollar, braces, parentheses or whitespace are really best avoided. - If the result of expanding a variable contains a dollar sign (‘$’), the - string is expanded again. + If the result of expanding a nested variable expression contains a dollar + sign (`$'), the result is subject to further expansion. Variable substitution occurs at four distinct times, depending on where the variable is being used. 1. Variables in dependency lines are expanded as the line is read. 2. Variables in conditionals are expanded individually, but only as far as necessary to determine the result of the conditional. 3. Variables in shell commands are expanded when the shell command is executed. - 4. “.for” loop index variables are expanded on each loop iteration. - Note that other variables are not expanded when composing the body - of a loop, so the following example code: - + 4. .for loop index variables are expanded on each loop iteration. Note + that other variables are not expanded when composing the body of a + loop, so the following example code: .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .endfor all: @echo ${a} @echo ${b} - will print: + prints: 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”. + After the loop is executed: + + a contains `${:U1} ${:U2} ${:U3}', which expands to `1 2 + 3'. + + j contains `${:U3}', which expands to `3'. + + b contains `${j} ${j} ${j}', which expands to `${:U3} + ${:U3} ${:U3}' and further to `3 3 3'. Variable classes - The four different classes of variables (in order of increasing prece‐ + The four different classes of variables (in order of increasing prece- dence) are: Environment variables Variables defined as part of bmake's environment. Global variables Variables defined in the makefile or in included makefiles. Command line variables Variables defined as part of the command line. Local variables Variables that are defined specific to a certain target. - Local variables can be set on a dependency line, if - .MAKE.TARGET_LOCAL_VARIABLES is not set to ‘false’. The rest of the line - (which will already have had global variables expanded) is the variable - value. For example: + Local variables can be set on a dependency line, unless + .MAKE.TARGET_LOCAL_VARIABLES is set to `false'. The rest of the line + (which already has had global variables expanded) is the variable value. + For example: COMPILER_WRAPPERS= ccache distcc icecc ${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,} - Only the targets ‘${OBJS}’ will be impacted by that filter (in "meta" - mode) and simply enabling/disabling any of the compiler wrappers will not - render all of those targets out-of-date. + Only the targets `${OBJS}' are impacted by that filter (in "meta" mode) + and simply enabling/disabling any of the compiler wrappers does not ren- + der all of those targets out-of-date. NOTE: target-local variable assignments behave differently in that; += Only appends to a previous local assignment for the same target and variable. := Is redundant with respect to global variables, which have already been expanded. - The seven built-in local variables are as follows: + The seven built-in local variables are: .ALLSRC The list of all sources for this target; also known as - ‘>’. + `>'. - .ARCHIVE The name of the archive file; also known as ‘!’. + .ARCHIVE The name of the archive file; also known as `!'. .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the - “implied” source); also known as ‘<’. It is not defined + "implied" source); also known as `<'. It is not defined in explicit rules. - .MEMBER The name of the archive member; also known as ‘%’. + .MEMBER The name of the archive member; also known as `%'. .OODATE The list of sources for this target that were deemed out- - of-date; also known as ‘?’. + of-date; also known as `?'. .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; - also known as ‘*’. The suffix must be one of the known - suffixes declared with .SUFFIXES or it will not be recog‐ + also known as `*'. The suffix must be one of the known + suffixes declared with .SUFFIXES, or it is not recog- nized. - .TARGET The name of the target; also known as ‘@’. For compati‐ - bility with other makes this is an alias for .ARCHIVE in + .TARGET The name of the target; also known as `@'. For compati- + bility with other makes this is an alias for .ARCHIVE in archive member rules. - The shorter forms (‘>’, ‘!’, ‘<’, ‘%’, ‘?’, ‘*’, and ‘@’) are permitted + The shorter forms (`>', `!', `<', `%', `?', `*', and `@') are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. Variants of these variables with the punctuation followed immediately by - ‘D’ or ‘F’, e.g. ‘$(@D)’, are legacy forms equivalent to using the ‘:H’ - and ‘:T’ modifiers. These forms are accepted for compatibility with AT&T + `D' or `F', e.g. `$(@D)', are legacy forms equivalent to using the `:H' + and `:T' modifiers. These forms are accepted for compatibility with AT&T System V UNIX makefiles and POSIX but are not recommended. Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. - These variables are ‘.TARGET’, ‘.PREFIX’, ‘.ARCHIVE’, and ‘.MEMBER’. + These variables are `.TARGET', `.PREFIX', `.ARCHIVE', and `.MEMBER'. Additional built-in variables In addition, bmake sets or knows about the following variables: - .ALLTARGETS The list of all targets encountered in the Makefile. If - evaluated during Makefile parsing, lists only those tar‐ - gets encountered thus far. + .ALLTARGETS + The list of all targets encountered in the makefiles. If evalu- + ated during makefile parsing, lists only those targets encoun- + tered thus far. + + .CURDIR + A path to the directory where bmake was executed. Refer to the + description of `PWD' for more details. + + .ERROR_CMD + Is used in error handling, see MAKE_PRINT_VAR_ON_ERROR. - .CURDIR A path to the directory where bmake was executed. Refer - to the description of ‘PWD’ for more details. + .ERROR_CWD + Is used in error handling, see MAKE_PRINT_VAR_ON_ERROR. + + .ERROR_META_FILE + Is used in error handling in "meta" mode, see + MAKE_PRINT_VAR_ON_ERROR. + + .ERROR_TARGET + Is used in error handling, see MAKE_PRINT_VAR_ON_ERROR. .INCLUDEDFROMDIR - The directory of the file this Makefile was included - from. + The directory of the file this makefile was included from. .INCLUDEDFROMFILE - The filename of the file this Makefile was included from. + The filename of the file this makefile was included from. + + MACHINE + The machine hardware name, see uname(1). + + MACHINE_ARCH + The machine processor architecture name, see uname(1). - MAKE The name that bmake was executed with (argv[0]). For - compatibility bmake also sets .MAKE with the same value. - The preferred variable to use is the environment variable - MAKE because it is more compatible with other versions of - bmake and cannot be confused with the special target with - the same name. + MAKE The name that bmake was executed with (argv[0]). + + .MAKE The same as MAKE, for compatibility. The preferred variable to + use is the environment variable MAKE because it is more compati- + ble with other make variants and cannot be confused with the spe- + cial target with the same name. .MAKE.DEPENDFILE - Names the makefile (default ‘.depend’) from which gener‐ - ated dependencies are read. + Names the makefile (default `.depend') from which generated de- + pendencies are read. + + .MAKE.DIE_QUIETLY + If set to `true', do not print error information at the end. .MAKE.EXPAND_VARIABLES - A boolean that controls the default behavior of the -V - option. If true, variable values printed with -V are - fully expanded; if false, the raw variable contents - (which may include additional unexpanded variable refer‐ - ences) are shown. + A boolean that controls the default behavior of the -V option. + If true, variable values printed with -V are fully expanded; if + false, the raw variable contents (which may include additional + unexpanded variable references) are shown. + + .MAKE.EXPORTED + The list of variables exported by bmake. + + MAKEFILE + The top-level makefile that is currently read, as given in the + command line. - .MAKE.EXPORTED The list of variables exported by bmake. + .MAKEFLAGS + The environment variable `MAKEFLAGS' may contain anything that + may be specified on bmake's command line. Anything specified on + bmake's command line is appended to the .MAKEFLAGS variable, + which is then added to the environment for all programs that + bmake executes. - .MAKE.JOBS The argument to the -j option. + .MAKE.GID + The numeric group ID of the user running bmake. It is read-only. .MAKE.JOB.PREFIX - If bmake is run with -j, the output for each target is - prefixed with a token ‘--- target ---’ the first part of - which can be controlled via .MAKE.JOB.PREFIX. If - .MAKE.JOB.PREFIX is empty, no token is printed. For ex‐ - ample, setting .MAKE.JOB.PREFIX to - ${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce to‐ - kens like ‘---make[1234] target ---’ making it easier to - track the degree of parallelism being achieved. - - .MAKE.TARGET_LOCAL_VARIABLES - If set to ‘false’, apparent variable assignments in de‐ - pendency lines are treated as normal sources. - - MAKEFLAGS The environment variable ‘MAKEFLAGS’ may contain anything - that may be specified on bmake's command line. Anything - specified on bmake's command line is appended to the - ‘MAKEFLAGS’ variable which is then entered into the envi‐ - ronment for all programs which bmake executes. - - .MAKE.LEVEL The recursion depth of bmake. The initial instance of - bmake will be 0, and an incremented value is put into the - 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 bmake. + If bmake is run with -j, the output for each target is prefixed + with a token + --- target --- + the first part of which can be controlled via .MAKE.JOB.PREFIX. + If .MAKE.JOB.PREFIX is empty, no token is printed. For example, + setting .MAKE.JOB.PREFIX to + `${.newline}---${.MAKE:T}[${.MAKE.PID}]' would produce tokens + like + ---make[1234] target --- + making it easier to track the degree of parallelism being + achieved. + + .MAKE.JOBS + The argument to the -j option. + + .MAKE.LEVEL + The recursion depth of bmake. The top-level instance of bmake + has level 0, and each child make has its parent level plus 1. + This allows tests like: .if ${.MAKE.LEVEL} == 0 to protect things + which should only be evaluated in the top-level instance of + bmake. + + .MAKE.LEVEL.ENV + The name of the environment variable that stores the level of + nested calls to bmake. .MAKE.MAKEFILE_PREFERENCE - The ordered list of makefile names (default ‘makefile’, - ‘Makefile’) that bmake will look for. + The ordered list of makefile names (default `makefile', + `Makefile') that bmake looks for. .MAKE.MAKEFILES - The list of makefiles read by bmake, which is useful for - tracking dependencies. Each makefile is recorded only - once, regardless of the number of times read. - - .MAKE.MODE Processed after reading all makefiles. Can affect the - mode that bmake runs in. It can contain a number of key‐ - words: - - compat Like -B, puts bmake into "compat" - mode. - - meta Puts bmake into "meta" mode, where - meta files are created for each tar‐ - get to capture the command run, the - output generated and if filemon(4) - is available, the system calls which - are of interest to bmake. The cap‐ - tured output can be very useful when - diagnosing errors. - - curdirOk= bf Normally bmake will not create .meta - files in ‘.CURDIR’. This can be - overridden by setting bf to a value - which represents True. - - missing-meta= bf If bf is True, then a missing .meta - file makes the target out-of-date. - - missing-filemon= bf If bf is True, then missing filemon - data makes the target out-of-date. - - nofilemon Do not use filemon(4). - - env For debugging, it can be useful to - include the environment in the .meta - file. - - verbose If in "meta" mode, print a clue - about the target being built. This - is useful if the build is otherwise - running silently. The message - printed the value of: - .MAKE.META.PREFIX. - - ignore-cmd Some makefiles have commands which - are simply not stable. This keyword - causes them to be ignored for deter‐ - mining whether a target is out of - date in "meta" mode. See also - .NOMETA_CMP. - - silent= bf If bf is True, when a .meta file is - created, mark the target .SILENT. - - randomize-targets In both compat and parallel mode, do - not make the targets in the usual - order, but instead randomize their - order. This mode can be used to de‐ - tect undeclared dependencies between - files. + The list of makefiles read by bmake, which is useful for tracking + dependencies. Each makefile is recorded only once, regardless of + the number of times read. .MAKE.META.BAILIWICK - In "meta" mode, provides a list of prefixes which match - the directories controlled by bmake. If a file that was - generated outside of .OBJDIR but within said bailiwick is - missing, the current target is considered out-of-date. + In "meta" mode, provides a list of prefixes which match the di- + rectories controlled by bmake. If a file that was generated out- + side of .OBJDIR but within said bailiwick is missing, the current + target is considered out-of-date. .MAKE.META.CMP_FILTER - In "meta" mode, it can (very rarely!) be useful to filter - command lines before comparison. This variable can be - set to a set of modifiers that will be applied to each - line of the old and new command that differ, if the fil‐ - tered commands still differ, the target is considered - out-of-date. + In "meta" mode, it can (very rarely!) be useful to filter command + lines before comparison. This variable can be set to a set of + modifiers that are applied to each line of the old and new com- + mand that differ, if the filtered commands still differ, the tar- + get is considered out-of-date. .MAKE.META.CREATED - In "meta" mode, this variable contains a list of all the - meta files updated. If not empty, it can be used to - trigger processing of .MAKE.META.FILES. + In "meta" mode, this variable contains a list of all the meta + files updated. If not empty, it can be used to trigger process- + ing of .MAKE.META.FILES. .MAKE.META.FILES - In "meta" mode, this variable contains a list of all the - meta files used (updated or not). This list can be used - to process the meta files to extract dependency informa‐ - tion. + In "meta" mode, this variable contains a list of all the meta + files used (updated or not). This list can be used to process + the meta files to extract dependency information. + + .MAKE.META.IGNORE_FILTER + Provides a list of variable modifiers to apply to each pathname. + Ignore if the expansion is an empty string. .MAKE.META.IGNORE_PATHS - Provides a list of path prefixes that should be ignored; - because the contents are expected to change over time. - The default list includes: ‘/dev /etc /proc /tmp /var/run - /var/tmp’ + Provides a list of path prefixes that should be ignored; because + the contents are expected to change over time. The default list + includes: `/dev /etc /proc /tmp /var/run /var/tmp' .MAKE.META.IGNORE_PATTERNS - Provides a list of patterns to match against pathnames. - Ignore any that match. - - .MAKE.META.IGNORE_FILTER - Provides a list of variable modifiers to apply to each - pathname. Ignore if the expansion is an empty string. + Provides a list of patterns to match against pathnames. Ignore + any that match. .MAKE.META.PREFIX - Defines the message printed for each meta file updated in - "meta verbose" mode. The default value is: - Building ${.TARGET:H:tA}/${.TARGET:T} - - .MAKEOVERRIDES This variable is used to record the names of variables - 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 ‘.MAKEOVERRIDES’ - within a makefile. Extra variables can be exported from - a makefile by appending their names to ‘.MAKEOVERRIDES’. - ‘MAKEFLAGS’ is re-exported whenever ‘.MAKEOVERRIDES’ is - modified. + Defines the message printed for each meta file updated in "meta + verbose" mode. The default value is: + Building ${.TARGET:H:tA}/${.TARGET:T} + + .MAKE.MODE + Processed after reading all makefiles. Affects the mode that + bmake runs in. It can contain these keywords: + + compat Like -B, puts bmake into "compat" mode. + + meta Puts bmake into "meta" mode, where meta files are created + for each target to capture the command run, the output + generated, and if filemon(4) is available, the system + calls which are of interest to bmake. The captured out- + put can be useful when diagnosing errors. + + curdirOk=bf + By default, bmake does not create .meta files in + `.CURDIR'. This can be overridden by setting bf to a + value which represents true. + + missing-meta=bf + If bf is true, a missing .meta file makes the target out- + of-date. + + missing-filemon=bf + If bf is true, missing filemon data makes the target out- + of-date. + + nofilemon + Do not use filemon(4). + + env For debugging, it can be useful to include the environ- + ment in the .meta file. + + verbose + If in "meta" mode, print a clue about the target being + built. This is useful if the build is otherwise running + silently. The message printed is the expanded value of + .MAKE.META.PREFIX. + + ignore-cmd + Some makefiles have commands which are simply not stable. + This keyword causes them to be ignored for determining + whether a target is out of date in "meta" mode. See also + .NOMETA_CMP. + + silent=bf + If bf is true, when a .meta file is created, mark the + target .SILENT. + + randomize-targets + In both compat and parallel mode, do not make the targets + in the usual order, but instead randomize their order. + This mode can be used to detect undeclared dependencies + between files. + + MAKEOBJDIR + Used to create files in a separate directory, see .OBJDIR. + + MAKE_OBJDIR_CHECK_WRITABLE + Used to force a separate directory for the created files, even if + that directory is not writable, see .OBJDIR. + + MAKEOBJDIRPREFIX + Used to create files in a separate directory, see .OBJDIR. + + .MAKE.OS + The name of the operating system, see uname(1). It is read-only. + + .MAKEOVERRIDES + 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 `.MAKEOVERRIDES' within a makefile. Extra variables can + be exported from a makefile by appending their names to + `.MAKEOVERRIDES'. `MAKEFLAGS' is re-exported whenever + `.MAKEOVERRIDES' is modified. .MAKE.PATH_FILEMON - If bmake was built with filemon(4) support, this is set - to the path of the device node. This allows makefiles to - test for this support. + If bmake was built with filemon(4) support, this is set to the + path of the device node. This allows makefiles to test for this + support. - .MAKE.PID The process-id of bmake. + .MAKE.PID + The process ID of bmake. It is read-only. - .MAKE.PPID The parent process-id of bmake. + .MAKE.PPID + The parent process ID of bmake. It is read-only. + + MAKE_PRINT_VAR_ON_ERROR + When bmake stops due to an error, it sets `.ERROR_TARGET' to the + name of the target that failed, `.ERROR_CMD' to the commands of + the failed target, and in "meta" mode, it also sets `.ERROR_CWD' + to the getcwd(3), and `.ERROR_META_FILE' to the path of the meta + file (if any) describing the failed target. It then prints its + name and the value of `.CURDIR' as well as the value of any vari- + ables named in `MAKE_PRINT_VAR_ON_ERROR'. .MAKE.SAVE_DOLLARS - 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, ‘$$’ be‐ - comes ‘$’ per normal evaluation rules. + If true, `$$' are preserved when doing `:=' assignments. The de- + fault is false, for backwards compatibility. Set to true for + compatability with other makes. If set to false, `$$' becomes + `$' per normal evaluation rules. - .MAKE.UID The user-id running bmake. + .MAKE.TARGET_LOCAL_VARIABLES + If set to `false', apparent variable assignments in dependency + lines are treated as normal sources. - .MAKE.GID The group-id running bmake. + .MAKE.UID + The numeric ID of the user running bmake. It is read-only. - MAKE_PRINT_VAR_ON_ERROR - When bmake stops due to an error, it sets ‘.ERROR_TARGET’ - to the name of the target that failed, ‘.ERROR_CMD’ to - the commands of the failed target, and in "meta" mode, it - also sets ‘.ERROR_CWD’ to the getcwd(3), and - ‘.ERROR_META_FILE’ to the path of the meta file (if any) - describing the failed target. It then prints its name - and the value of ‘.CURDIR’ as well as the value of any - variables named in ‘MAKE_PRINT_VAR_ON_ERROR’. + .newline + This variable is simply assigned a newline character as its + value. It is read-only. This allows expansions using the :@ + modifier to put a newline between iterations of the loop rather + than a space. For example, in case of an error, bmake prints the + variable names and their values using: + ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} + + .OBJDIR + A path to the directory where the targets are built. Its value + is determined by trying to chdir(2) to the following directories + in order and using the first match: - .newline This variable is simply assigned a newline character as - its value. This allows expansions using the :@ modifier - to put a newline between iterations of the loop rather - than a space. For example, the printing of - ‘MAKE_PRINT_VAR_ON_ERROR’ could be done as - ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. + 1. ${MAKEOBJDIRPREFIX}${.CURDIR} - .OBJDIR A path to the directory where the targets are built. Its - value is determined by trying to chdir(2) to the follow‐ - ing directories in order and using the first match: + (Only if `MAKEOBJDIRPREFIX' is set in the environment or on + the command line.) - 1. ${MAKEOBJDIRPREFIX}${.CURDIR} + 2. ${MAKEOBJDIR} - (Only if ‘MAKEOBJDIRPREFIX’ is set in the environ‐ - ment or on the command line.) + (Only if `MAKEOBJDIR' is set in the environment or on the + command line.) - 2. ${MAKEOBJDIR} + 3. ${.CURDIR}/obj.${MACHINE} - (Only if ‘MAKEOBJDIR’ is set in the environment or - on the command line.) + 4. ${.CURDIR}/obj - 3. ${.CURDIR}/obj.${MACHINE} + 5. /usr/obj/${.CURDIR} - 4. ${.CURDIR}/obj + 6. ${.CURDIR} - 5. /usr/obj/${.CURDIR} + Variable expansion is performed on the value before it is used, + so expressions such as ${.CURDIR:S,^/usr/src,/var/obj,} may be + used. This is especially useful with `MAKEOBJDIR'. - 6. ${.CURDIR} + `.OBJDIR' may be modified in the makefile via the special target + `.OBJDIR'. In all cases, bmake changes to the specified direc- + tory if it exists, and sets `.OBJDIR' and `PWD' to that directory + before executing any targets. - Variable expansion is performed on the value before it's - used, so expressions such as - ${.CURDIR:S,^/usr/src,/var/obj,} - may be used. This is especially useful with - ‘MAKEOBJDIR’. + Except in the case of an explicit `.OBJDIR' target, bmake checks + that the specified directory is writable and ignores it if not. + This check can be skipped by setting the environment variable + `MAKE_OBJDIR_CHECK_WRITABLE' to "no". - ‘.OBJDIR’ may be modified in the makefile via the special - target ‘.OBJDIR’. In all cases, bmake will chdir(2) to - the specified directory if it exists, and set ‘.OBJDIR’ - and ‘PWD’ to that directory before executing any targets. + .PARSEDIR + The directory name of the current makefile being parsed. - Except in the case of an explicit ‘.OBJDIR’ target, bmake - will check that the specified directory is writable and - ignore it if not. This check can be skipped by setting - the environment variable ‘MAKE_OBJDIR_CHECK_WRITABLE’ to - "no". + .PARSEFILE + The basename of the current makefile being parsed. This variable + and `.PARSEDIR' are both set only while the makefiles are being + parsed. To retain their current values, assign them to a vari- + able using assignment with expansion `:='. - .PARSEDIR A path to the directory of the current ‘Makefile’ being - parsed. + .PATH The space-separated list of directories that bmake searches for + files. To update this search list, use the special target + `.PATH' rather than modifying the variable directly. - .PARSEFILE The basename of the current ‘Makefile’ being parsed. - This variable and ‘.PARSEDIR’ are both set only while the - ‘Makefiles’ are being parsed. If you want to retain - their current values, assign them to a variable using as‐ - signment with expansion ‘:=’. + %POSIX Is set in POSIX mode, see the special `.POSIX' target. - .PATH A variable that represents the list of directories that - bmake will search for files. The search list should be - updated using the target ‘.PATH’ rather than the vari‐ - able. + PWD Alternate path to the current directory. bmake normally sets + `.CURDIR' to the canonical path given by getcwd(3). However, if + the environment variable `PWD' is set and gives a path to the + current directory, bmake sets `.CURDIR' to the value of `PWD' in- + stead. This behavior is disabled if `MAKEOBJDIRPREFIX' is set or + `MAKEOBJDIR' contains a variable transform. `PWD' is set to the + value of `.OBJDIR' for all programs which bmake executes. - PWD Alternate path to the current directory. bmake normally - sets ‘.CURDIR’ to the canonical path given by getcwd(3). - However, if the environment variable ‘PWD’ is set and - gives a path to the current directory, then bmake sets - ‘.CURDIR’ to the value of ‘PWD’ instead. This behavior - is disabled if ‘MAKEOBJDIRPREFIX’ is set or ‘MAKEOBJDIR’ - contains a variable transform. ‘PWD’ is set to the value - of ‘.OBJDIR’ for all programs which bmake executes. + .SHELL The pathname of the shell used to run target scripts. It is + read-only. - .SHELL The pathname of the shell used to run target scripts. It - is read-only. + .SUFFIXES + The list of known suffixes. It is read-only. - .SUFFIXES The list of known suffixes. It is read-only. + .SYSPATH + The space-separated list of directories that bmake searches for + makefiles, referred to as the system include path. To update + this search list, use the special target `.SYSPATH' rather than + modifying the variable which is read-only. - .TARGETS The list of targets explicitly specified on the command - line, if any. + .TARGETS + The list of targets explicitly specified on the command line, if + any. - VPATH Colon-separated (“:”) lists of directories that bmake - will search for files. The variable is supported for - compatibility with old make programs only, use ‘.PATH’ - instead. + VPATH The colon-separated (":") list of directories that bmake searches + for files. This variable is supported for compatibility with old + make programs only, use `.PATH' instead. Variable modifiers - Variable expansion may be modified to select or modify each word of the - variable (where a “word” is white-space delimited sequence of charac‐ - ters). The general format of a variable expansion is as follows: + The general format of a variable expansion is: - ${variable[:modifier[:...]]} + ${variable[:modifier[:...]]} - Each modifier begins with a colon, which may be escaped with a backslash - (‘\’). + Each modifier begins with a colon. To escape a colon, precede it with a + backslash `\'. - A set of modifiers can be specified via a variable, as follows: + A list of indirect modifiers can be specified via a variable, as follows: - modifier_variable=modifier[:...] - ${variable:${modifier_variable}[:...]} + modifier_variable = modifier[:...] - In this case the first modifier in the modifier_variable does not start - with a colon, since that must appear in the referencing variable. If any - of the modifiers in the modifier_variable contain a dollar sign (‘$’), - these must be doubled to avoid early expansion. + ${variable:${modifier_variable}[:...]} + + In this case, the first modifier in the modifier_variable does not start + with a colon, since that colon already occurs in the referencing vari- + able. If any of the modifiers in the modifier_variable contains a dollar + sign (`$'), these must be doubled to avoid early expansion. + + Some modifiers interpret the expression value as a single string, others + treat the expression value as a whitespace-separated list of words. When + splitting a string into words, whitespace can be escaped using double + quotes, single quotes and backslashes, like in the shell. The quotes and + backslashes are retained in the words. The supported modifiers are: - :E Replaces each word in the variable with its suffix. + :E Replaces each word with its suffix. - :H Replaces each word in the variable with everything but the last com‐ - ponent. + :H Replaces each word with its dirname. :Mpattern Selects only those words that match pattern. The standard shell - wildcard characters (‘*’, ‘?’, and ‘[]’) may be used. The wildcard - characters may be escaped with a backslash (‘\’). As a consequence - of the way values are split into words, matched, and then joined, a - construct like - ${VAR:M*} - will normalize the inter-word spacing, removing all leading and - trailing space, and converting multiple consecutive spaces to single - spaces. + wildcard characters (`*', `?', and `[]') may be used. The wildcard + characters may be escaped with a backslash (`\'). As a consequence + of the way values are split into words, matched, and then joined, + the construct `${VAR:M*}' removes all leading and trailing white- + space and normalizes the inter-word spacing to a single space. :Npattern - This is identical to ‘:M’, but selects all words which do not match + This is the opposite of `:M', selecting all words which do not match pattern. - :O Orders every word in variable alphabetically. + :O Orders the words lexicographically. - :On Orders every word in variable numerically. A number followed by one - of ‘k’, ‘M’ or ‘G’ is multiplied by the appropriate factor (1024 - (k), 1048576 (M), or 1073741824 (G)). Both upper- and lower-case - letters are accepted. + :On Orders the words numerically. A number followed by one of `k', `M' + or `G' is multiplied by the appropriate factor, which is 1024 for + `k', 1048576 for `M', or 1073741824 for `G'. Both upper- and lower- + case letters are accepted. - :Or Orders every word in variable in reverse alphabetical order. + :Or Orders the words in reverse lexicographical order. :Orn - Orders every word in variable in reverse numerical order. + Orders the words in reverse numerical order. - :Ox Shuffles the words in variable. The results will be different each - time you are referring to the modified variable; use the assignment - with expansion ‘:=’ to prevent such behavior. For example, + :Ox Shuffles the words. The results are different each time you are re- + ferring to the modified variable; use the assignment with expansion + `:=' to prevent such behavior. For example, LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" may produce output similar to: quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre - :Q Quotes every shell meta-character in the variable, so that it can be + :Q Quotes every shell meta-character in the value, so that it can be passed safely to the shell. - :q Quotes every shell meta-character in the variable, and also doubles - ‘$’ characters so that it can be passed safely through recursive in‐ - vocations of bmake. This is equivalent to: ‘:S/\$/&&/g:Q’. + :q Quotes every shell meta-character in the value, and also doubles `$' + characters so that it can be passed safely through recursive invoca- + tions of bmake. This is equivalent to `:S/\$/&&/g:Q'. - :R Replaces each word in the variable with everything but its suffix. + :R Replaces each word with everything but its suffix. - :range[=count] - The value is an integer sequence representing the words of the orig‐ + :range[=count] + The value is an integer sequence representing the words of the orig- inal value, or the supplied count. - :gmtime[=utc] - The value is a format string for strftime(3), using gmtime(3). If a - utc value is not provided or is 0, the current time is used. + :gmtime[=timestamp] + The value is interpreted as a format string for strftime(3), using + gmtime(3), producing the formatted timestamp. If a timestamp value + is not provided or is 0, the current time is used. :hash - Computes a 32-bit hash of the value and encode it as hex digits. + Computes a 32-bit hash of the value and encodes it as 8 hex digits. - :localtime[=utc] - The value is a format string for strftime(3), using localtime(3). - If a utc value is not provided or is 0, the current time is used. + :localtime[=timestamp] + The value is interpreted as a format string for strftime(3), using + localtime(3), producing the formatted timestamp. If a timestamp + value is not provided or is 0, the current time is used. - :tA Attempts to convert variable to an absolute path using realpath(3), - if that fails, the value is unchanged. + :tA Attempts to convert the value to an absolute path using realpath(3). + If that fails, the value is unchanged. - :tl Converts variable to lower-case letters. + :tl Converts the value to lower-case letters. :tsc - Words in the variable are normally separated by a space on expan‐ - sion. This modifier sets the separator to the character c. If c is - omitted, then no separator is used. The common escapes (including - octal numeric codes) work as expected. + When joining the words after a modifier that treats the value as + words, the words are normally separated by a space. This modifier + changes the separator to the character c. If c is omitted, no sepa- + rator is used. The common escapes (including octal numeric codes) + work as expected. - :tu Converts variable to upper-case letters. + :tu Converts the value to upper-case letters. - :tW Causes the value to be treated as a single word (possibly containing - embedded white space). See also ‘:[*]’. + :tW Causes subsequent modifiers to treat the value as a single word + (possibly containing embedded whitespace). See also `:[*]'. - :tw Causes the value to be treated as a sequence of words delimited by - white space. See also ‘:[@]’. + :tw Causes the value to be treated as a list of words. See also `:[@]'. :S/old_string/new_string/[1gW] Modifies the first occurrence of old_string in each word of the - variable's value, replacing it with new_string. 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 old_string begins with a caret (‘^’), old_string is anchored at - the beginning of each word. If old_string ends with a dollar sign - (‘$’), it is anchored at the end of each word. Inside new_string, - an ampersand (‘&’) is replaced by old_string (without any ‘^’ or - ‘$’). Any character may be used as a delimiter for the parts of the - modifier string. The anchoring, ampersand and delimiter characters - may be escaped with a backslash (‘\’). - - Variable expansion occurs in the normal fashion inside both - old_string and new_string with the single exception that a backslash - is used to prevent the expansion of a dollar sign (‘$’), not a pre‐ - ceding dollar sign as is usual. + value, replacing it with new_string. If a `g' is appended to the + last delimiter of the pattern, all occurrences in each word are re- + placed. 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, the value is treated as a single + word. If old_string begins with a caret (`^'), old_string is an- + chored at the beginning of each word. If old_string ends with a + dollar sign (`$'), it is anchored at the end of each word. Inside + new_string, an ampersand (`&') is replaced by old_string (without + the anchoring `^' or `$'). Any character may be used as the delim- + iter for the parts of the modifier string. The anchoring, ampersand + and delimiter characters can be escaped with a backslash (`\'). + + Both old_string and new_string may contain nested expressions. To + prevent a dollar sign from starting a nested expression, escape it + with a backslash. :C/pattern/replacement/[1gW] - The :C modifier is just like the :S modifier except that the old and - new strings, instead of being simple strings, are an extended regu‐ - lar expression (see regex(3)) string pattern and an ed(1)-style - string replacement. Normally, the first occurrence of the pattern - pattern in each word of the value is substituted with replacement. - The ‘1’ modifier causes the substitution to apply to at most one - word; the ‘g’ modifier causes the substitution to apply to as many - instances of the search pattern pattern as occur in the word or - words it is found in; the ‘W’ modifier causes the value to be - treated as a single word (possibly containing embedded white space). + The :C modifier works like the :S modifier except that the old and + new strings, instead of being simple strings, are an extended regu- + lar expression pattern (see regex(3)) and an ed(1)-style + replacement. Normally, the first occurrence of the pattern pattern + in each word of the value is substituted with replacement. The `1' + modifier causes the substitution to apply to at most one word; the + `g' modifier causes the substitution to apply to as many instances + of the search pattern pattern as occur in the word or words it is + found in; the `W' modifier causes the value to be treated as a sin- + gle word (possibly containing embedded whitespace). As for the :S modifier, the pattern and replacement are subjected to variable expansion before being parsed as regular expressions. - :T Replaces each word in the variable with its last path component. + :T Replaces each word with its last path component (basename). :u Removes adjacent duplicate words (like uniq(1)). :?true_string:false_string - If the variable name (not its value), when parsed as a .if condi‐ + If the variable name (not its value), when parsed as a .if condi- tional expression, evaluates to true, return as its value the true_string, otherwise return the false_string. Since the variable name is used as the expression, :? must be the first modifier after - the variable name itself - which will, of course, usually contain - variable expansions. A common error is trying to use expressions - like + the variable name itself--which, of course, usually contains vari- + able expansions. A common error is trying to use expressions like ${NUMBERS:M42:?match:no} - which actually tests defined(NUMBERS), to determine if any words - match "42" you need to use something like: + which actually tests defined(NUMBERS). To determine if any words + match "42", you need to use something like: ${"${NUMBERS:M42}" != "":?match:no}. :old_string=new_string - This is the AT&T System V UNIX style variable substitution. It must - be the last modifier specified. If old_string or new_string do not - contain the pattern matching character % then it is assumed that - they are anchored at the end of each word, so only suffixes or en‐ - tire words may be replaced. Otherwise % is the substring of - old_string to be replaced in new_string. If only old_string con‐ - tains the pattern matching character %, and old_string matches, then - the result is the new_string. If only the new_string contains the - pattern matching character %, then it is not treated specially and - it is printed as a literal % on match. If there is more than one - pattern matching character (%) in either the new_string or - old_string, only the first instance is treated specially (as the - pattern character); all subsequent instances are treated as regular - characters. - - Variable expansion occurs in the normal fashion inside both - old_string and new_string with the single exception that a backslash - is used to prevent the expansion of a dollar sign (‘$’), not a pre‐ - ceding dollar sign as is usual. - - :@temp@string@ - This is the loop expansion mechanism from the OSF Development Envi‐ + This is the AT&T System V UNIX style substitution. It can only be + the last modifier specified, as a `:' in either old_string or + new_string is treated as a regular character, not as the end of the + modifier. + + If old_string does not contain the pattern matching character `%', + and the word ends with old_string or equals it, that suffix is re- + placed with new_string. + + Otherwise, the first `%' in old_string matches a possibly empty sub- + string of arbitrary characters, and if the whole pattern is found in + the word, the matching part is replaced with new_string, and the + first occurrence of `%' in new_string (if any) is replaced with the + substring matched by the `%'. + + Both old_string and new_string may contain nested expressions. To + prevent a dollar sign from starting a nested expression, escape it + with a backslash. + + :@varname@string@ + This is the loop expansion mechanism from the OSF Development Envi- ronment (ODE) make. Unlike .for loops, expansion occurs at the time - of reference. Assigns temp to each word in the variable and evalu‐ - ates string. The ODE convention is that temp should start and end - with a period. For example. + of reference. For each word in the value, assign the word to the + variable named varname and evaluate string. The ODE convention is + that varname should start and end with a period, for example: ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} - However a single character variable is often more readable: + However, a single-letter variable is often more readable: ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} :_[=var] - Saves the current variable value in ‘$_’ or the named var for later + Saves the current variable value in `$_' or the named var for later reference. Example usage: M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \ \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} - Here ‘$_’ is used to save the result of the ‘:S’ modifier which is - later referenced using the index values from ‘:range’. + Here `$_' is used to save the result of the `:S' modifier which is + later referenced using the index values from `:range'. :Unewval If the variable is undefined, newval 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 in‐ + 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} :Dnewval If the variable is defined, newval is the value. :L The name of the variable is the value. :P The path of the node which has the same name as the variable is the - value. If no such node exists or its path is null, then the name of - the variable is used. In order for this modifier to work, the name - (node) must at least have appeared on the rhs of a dependency. + value. If no such node exists or its path is null, the name of the + variable is used. In order for this modifier to work, the name + (node) must at least have appeared on the right-hand side of a de- + pendency. :!cmd! The output of running cmd is the value. - :sh If the variable is non-empty it is run as a command and the output - becomes the new value. + :sh The value is run as a command, and the output becomes the new value. ::=str The variable is assigned the value str after substitution. This modifier and its variations are useful in obscure situations such as - wanting to set a variable when shell commands are being parsed. - These assignment modifiers always expand to nothing, so if appearing - in a rule line by themselves should be preceded with something to - keep bmake happy. + wanting to set a variable at a point where a target's shell commands + are being parsed. These assignment modifiers always expand to noth- + ing. - The ‘::’ helps avoid false matches with the AT&T System V UNIX style - := modifier and since substitution always occurs the ::= form is - vaguely appropriate. + The `::' helps avoid false matches with the AT&T System V UNIX style + `:=' modifier and since substitution always occurs, the `::=' form + is vaguely appropriate. ::?=str As for ::= but only if the variable does not already have a value. ::+=str Append str to the variable. ::!=cmd Assign the output of cmd to the variable. :[range] - Selects one or more words from the value, or performs other opera‐ - tions related to the way in which the value is divided into words. - - Ordinarily, a value is treated as a sequence of words delimited by - white space. Some modifiers suppress this behavior, causing a value - to be treated as a single word (possibly containing embedded white - space). An empty value, or a value that consists entirely of white- - space, is treated as a single word. For the purposes of the ‘:[]’ - modifier, the words are indexed both forwards using positive inte‐ - gers (where index 1 represents the first word), and backwards using - negative integers (where index -1 represents the last word). - - The range is subjected to variable expansion, and the expanded re‐ + Selects one or more words from the value, or performs other opera- + tions related to the way in which the value is split into words. + + An empty value, or a value that consists entirely of white-space, is + treated as a single word. For the purposes of the `:[]' modifier, + the words are indexed both forwards using positive integers (where + index 1 represents the first word), and backwards using negative in- + tegers (where index -1 represents the last word). + + The range is subjected to variable expansion, and the expanded re- sult is then interpreted as follows: index Selects a single word from the value. start..end Selects all words from start to end, inclusive. For example, - ‘:[2..-1]’ selects all words from the second word to the last - word. If start is greater than end, then the words are out‐ - put in reverse order. For example, ‘:[-1..1]’ selects all - 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 ‘:Or’ instead of ‘:O:[-1..1]’. + `:[2..-1]' selects all words from the second word to the last + word. If start is greater than end, the words are output in + reverse order. For example, `:[-1..1]' selects all the words + from last to first. If the list is already ordered, this ef- + fectively reverses the list, but it is more efficient to use + `:Or' instead of `:O:[-1..1]'. * Causes subsequent modifiers to treat the value as a single - word (possibly containing embedded white space). Analogous - to the effect of "$*" in Bourne shell. + word (possibly containing embedded whitespace). Analogous to + the effect of $* in Bourne shell. - 0 Means the same as ‘:[*]’. + 0 Means the same as `:[*]'. @ Causes subsequent modifiers to treat the value as a sequence - of words delimited by white space. Analogous to the effect - of "$@" in Bourne shell. + of words delimited by whitespace. Analogous to the effect of + $@ in Bourne shell. # Returns the number of words in the value. -INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS - Makefile inclusion, conditional structures and for loops reminiscent of - the C programming language are provided in bmake. All such structures - are identified by a line beginning with a single dot (‘.’) character. - Files are included with either .include <file> or .include "file". Vari‐ +DIRECTIVES + bmake offers directives for including makefiles, conditionals and for + loops. All these directives are identified by a line beginning with a + single dot (`.') character, followed by the keyword of the directive, + such as include or if. + + File inclusion + Files are included with either .include <file> or .include "file". 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 ex‐ + 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 -I option are searched before the system makefile directory. - For compatibility with other versions of bmake ‘include file ...’ is also - accepted. - If the include statement is written as .-include or as .sinclude then er‐ - rors locating and/or opening include files are ignored. + For compatibility with other make variants, `include file ...' (without + leading dot) is also accepted. - If the include statement is written as .dinclude not only are errors lo‐ - cating and/or opening include files ignored, but stale dependencies - within the included file will be ignored just like .MAKE.DEPENDFILE. + If the include statement is written as .-include or as .sinclude, errors + locating and/or opening include files are ignored. - Conditional expressions are also preceded by a single dot as the first - character of a line. The possible conditionals are as follows: + If the include statement is written as .dinclude, not only are errors lo- + cating and/or opening include files ignored, but stale dependencies + within the included file are ignored just like in .MAKE.DEPENDFILE. - .error message - The message is printed along with the name of the makefile and - line number, then bmake will exit immediately. + Exporting variables + The directives for exporting and unexporting variables are: - .export variable ... + .export variable ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables - (those that start with ‘.’). This is not affected by the -X + (those that start with `.'). This is not affected by the -X flag, so should be used with caution. For compatibility with - other bmake programs ‘export variable=value’ is also accepted. + other make programs, export variable=value (without leading dot) + is also accepted. - Appending a variable name to .MAKE.EXPORTED is equivalent to ex‐ + Appending a variable name to .MAKE.EXPORTED is equivalent to ex- porting a variable. - .export-env variable ... - The same as ‘.export’, except that the variable is not appended - to .MAKE.EXPORTED. This allows exporting a value to the environ‐ + .export-env variable ... + The same as `.export', except that the variable is not appended + to .MAKE.EXPORTED. This allows exporting a value to the environ- ment which is different from that used by bmake internally. - .export-literal variable ... - The same as ‘.export-env’, except that variables in the value are + .export-literal variable ... + The same as `.export-env', except that variables in the value are not expanded. - .info message - The message is printed along with the name of the makefile and - line number. - - .undef variable ... - Un-define the specified global variables. Only global variables - can be un-defined. - - .unexport variable ... - The opposite of ‘.export’. The specified global variable will be - removed from .MAKE.EXPORTED. If no variable list is provided, - all globals are unexported, and .MAKE.EXPORTED deleted. + .unexport variable ... + The opposite of `.export'. The specified global variable is re- + moved from .MAKE.EXPORTED. If no variable list is provided, all + globals are unexported, and .MAKE.EXPORTED deleted. .unexport-env - Unexport all globals previously exported and clear the environ‐ - ment inherited from the parent. This operation will cause a mem‐ - ory leak of the original environment, so should be used spar‐ - ingly. Testing for .MAKE.LEVEL being 0, would make sense. Also - note that any variables which originated in the parent environ‐ - ment should be explicitly preserved if desired. For example: + Unexport all globals previously exported and clear the environ- + ment inherited from the parent. This operation causes a memory + leak of the original environment, so should be used sparingly. + Testing for .MAKE.LEVEL being 0 would make sense. Also note that + any variables which originated in the parent environment should + be explicitly preserved if desired. For example: .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .unexport-env .export PATH .endif - Would result in an environment containing only ‘PATH’, which is - the minimal useful environment. Actually ‘.MAKE.LEVEL’ will also - be pushed into the new environment. + Would result in an environment containing only `PATH', which is + the minimal useful environment. Actually `.MAKE.LEVEL' is also + pushed into the new environment. + + Messages + The directives for printing messages to the output are: + + .info message + The message is printed along with the name of the makefile and + line number. .warning message - The message prefixed by ‘warning:’ is printed along with the name + The message prefixed by `warning:' is printed along with the name of the makefile and line number. - .if [!]expression [operator expression ...] + .error message + The message is printed along with the name of the makefile and + line number, bmake exits immediately. + + Conditionals + The directives for conditionals are: + + .if [!]expression [operator expression ...] Test the value of an expression. - .ifdef [!]variable [operator variable ...] - Test the value of a variable. + .ifdef [!]variable [operator variable ...] + Test whether a variable is defined. - .ifndef [!]variable [operator variable ...] - Test the value of a variable. + .ifndef [!]variable [operator variable ...] + Test whether a variable is not defined. - .ifmake [!]target [operator target ...] - Test the target being built. + .ifmake [!]target [operator target ...] + Test the target being requested. - .ifnmake [!] target [operator target ...] - Test the target being built. + .ifnmake [!]target [operator target ...] + Test the target being requested. .else Reverse the sense of the last conditional. - .elif [!] expression [operator expression ...] - A combination of ‘.else’ followed by ‘.if’. + .elif [!]expression [operator expression ...] + A combination of `.else' followed by `.if'. - .elifdef [!]variable [operator variable ...] - A combination of ‘.else’ followed by ‘.ifdef’. + .elifdef [!]variable [operator variable ...] + A combination of `.else' followed by `.ifdef'. - .elifndef [!]variable [operator variable ...] - A combination of ‘.else’ followed by ‘.ifndef’. + .elifndef [!]variable [operator variable ...] + A combination of `.else' followed by `.ifndef'. - .elifmake [!]target [operator target ...] - A combination of ‘.else’ followed by ‘.ifmake’. + .elifmake [!]target [operator target ...] + A combination of `.else' followed by `.ifmake'. - .elifnmake [!]target [operator target ...] - A combination of ‘.else’ followed by ‘.ifnmake’. + .elifnmake [!]target [operator target ...] + A combination of `.else' followed by `.ifnmake'. .endif End the body of the conditional. The operator may be any one of the following: - || Logical OR. + || Logical OR. + + && Logical AND; of higher precedence than `||'. - && Logical AND; of higher precedence than “||”. + bmake only evaluates a conditional as far as is necessary to determine + its value. Parentheses can be used to override the operator precedence. + The boolean operator `!' may be used to logically negate an entire condi- + tional. It is of higher precedence than `&&'. - As in C, bmake will only evaluate a conditional as far as is necessary to - determine its value. Parentheses may be used to change the order of - evaluation. The boolean operator ‘!’ may be used to logically negate an - entire conditional. It is of higher precedence than ‘&&’. + The value of expression may be any of the following function call expres- + sions: - The value of expression may be any of the following: + defined(varname) + Evaluates to true if the variable varname has been defined. - defined Takes a variable name as an argument and evaluates to true if - the variable has been defined. + make(target) + Evaluates to true if the target was specified as part of bmake's + command line or was declared the default target (either implic- + itly or explicitly, see .MAIN) before the line containing the + conditional. - make Takes a target name as an argument and evaluates to true if the - target was specified as part of bmake's command line or was de‐ - clared the default target (either implicitly or explicitly, see - .MAIN) before the line containing the conditional. + empty(varname[:modifiers]) + Evaluates to true if the expansion of the variable, after apply- + ing the modifiers, results in an empty string. - empty Takes a variable, with possible modifiers, and evaluates to true - if the expansion of the variable would result in an empty - string. + exists(pathname) + Evaluates to true if the given pathname exists. If relative, the + pathname is searched for on the system search path (see .PATH). - exists Takes a file name as an argument and evaluates to true if the - file exists. The file is searched for on the system search path - (see .PATH). + target(target) + Evaluates to true if the target has been defined. - target Takes a target name as an argument and evaluates to true if the - target has been defined. + commands(target) + Evaluates to true if the target has been defined and has commands + associated with it. - commands - Takes a target name as an argument and evaluates to true if the - target has been defined and has commands associated with it. + Expression may also be an arithmetic or string comparison. Variable ex- + pansion is performed on both sides of the comparison. If both sides are + numeric and neither is enclosed in quotes, the comparison is done numeri- + cally, otherwise lexicographically. A string is interpreted as hexadeci- + mal integer if it is preceded by 0x, otherwise it is a decimal floating- + point number; octal numbers are not supported. - Expression 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 ‘==’ or ‘!=’ - operator is not a numerical value, then string comparison is performed - between the expanded variables. If no relational operator is given, it - is assumed that the expanded variable is being compared against 0, or an - empty string in the case of a string comparison. + All comparisons may use the operators `==' and `!='. Numeric comparisons + may also use the operators `<', `<=', `>' and `>='. - When bmake is 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 ‘.ifdef’, ‘.ifndef’, or ‘.if’ the - “defined” expression is applied. Similarly, if the form is ‘.ifmake’ or - ‘.ifnmake’, the “make” expression is applied. + If the comparison has neither a comparison operator nor a right side, the + expression evaluates to true if it is nonempty and its numeric value (if + any) is not zero. - If the conditional evaluates to true the parsing of the makefile contin‐ - ues as before. If it evaluates to false, the following lines are - skipped. In both cases this continues until a ‘.else’ or ‘.endif’ is - found. + When bmake is evaluating one of these conditional expressions, and it en- + counters a (whitespace separated) word it doesn't recognize, either the + "make" or "defined" function is applied to it, depending on the form of + the conditional. If the form is `.ifdef', `.ifndef' or `.if', the + "defined" function is applied. Similarly, if the form is `.ifmake' or + `.ifnmake', the "make" function is applied. + If the conditional evaluates to true, parsing of the makefile continues + as before. If it evaluates to false, the following lines are skipped. + In both cases, this continues until the corresponding `.else' or `.endif' + is found. + + For loops For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: - .for variable [variable ...] in expression - ⟨make-lines⟩ + .for variable [variable ...] in expression + <make-lines> .endfor - After the for expression is evaluated, it is split into words. On each - iteration of the loop, one word is taken and assigned to each variable, - in order, and these variables are substituted into the make-lines inside - the body of the for loop. The number of words must come out even; that - is, if there are three iteration variables, the number of words provided - must be a multiple of three. + The expression is expanded and then split into words. On each iteration + of the loop, one word is taken and assigned to each variable, in order, + and these variables are substituted into the make-lines inside the body + of the for loop. The number of words must come out even; that is, if + there are three iteration variables, the number of words provided must be + a multiple of three. + + If `.break' is encountered within a .for loop, it causes early termina- + tion of the loop, otherwise a parse error. + + Other directives + .undef variable ... + Un-define the specified global variables. Only global variables + can be un-defined. COMMENTS - Comments begin with a hash (‘#’) character, anywhere but in a shell com‐ + Comments begin with a hash (`#') character, anywhere but in a shell com- mand line, and continue to the end of an unescaped new line. SPECIAL SOURCES (ATTRIBUTES) - .EXEC Target is never out of date, but always execute commands any‐ + .EXEC Target is never out of date, but always execute commands any- way. - .IGNORE Ignore any errors from the commands associated with this tar‐ - get, exactly as if they all were preceded by a dash (‘-’). + .IGNORE Ignore any errors from the commands associated with this tar- + get, exactly as if they all were preceded by a dash (`-'). - .MADE Mark all sources of this target as being up-to-date. + .MADE Mark all sources of this target as being up to date. .MAKE Execute the commands associated with this target even if the -n or -t options were specified. Normally used to mark recursive bmakes. .META Create a meta file for the target, even if it is flagged as .PHONY, .MAKE, or .SPECIAL. Usage in conjunction with .MAKE is the most likely case. In "meta" mode, the target is out-of- date if the meta file is missing. .NOMETA Do not create a meta file for the target. Meta files are also not created for .PHONY, .MAKE, or .SPECIAL targets. .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which 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 .OODATE, which can be - used for that purpose even when not otherwise needed or de‐ - sired: + target is still considered out of date. The same effect ap- + plies to any command line that uses the variable .OODATE, which + can be used for that purpose even when not otherwise needed or + desired: skip-compare-for-some: - @echo this will be compared - @echo this will not ${.OODATE:M.NOMETA_CMP} - @echo this will also be compared + @echo this is compared + @echo this is not ${.OODATE:M.NOMETA_CMP} + @echo this is also compared - The :M pattern suppresses any expansion of the unwanted vari‐ + The :M pattern suppresses any expansion of the unwanted vari- able. .NOPATH Do not search for the target in the directories specified by - .PATH. + .PATH. .NOTMAIN Normally bmake selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .OPTIONAL - If a target is marked with this attribute and bmake can't fig‐ - ure out how to create it, it will ignore this fact and assume - the file isn't needed or already exists. + If a target is marked with this attribute and bmake can't fig- + ure out how to create it, it ignores this fact and assumes the + file isn't needed or already exists. .PHONY The target does not correspond to an actual file; it is always - considered to be out of date, and will not be created with the - -t option. Suffix-transformation rules are not applied to - .PHONY targets. + considered to be out of date, and is not created with the -t + option. Suffix-transformation rules are not applied to .PHONY + targets. .PRECIOUS When bmake is interrupted, it normally removes any partially - made targets. This source prevents the target from being re‐ + made targets. This source prevents the target from being re- moved. .RECURSIVE Synonym for .MAKE. .SILENT Do not echo any of the commands associated with this target, - exactly as if they all were preceded by an at sign (‘@’). + exactly as if they all were preceded by an at sign (`@'). - .USE Turn the target into bmake's version of a macro. When the tar‐ + .USE Turn the target into bmake's version of a macro. When the tar- get is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .USE) of the source. If the target already has commands, the .USE target's commands are appended to them. .USEBEFORE - Exactly like .USE, but prepend the .USEBEFORE target commands - to the target. + Like .USE, but instead of appending, prepend the .USEBEFORE + target commands to the target. .WAIT If .WAIT appears 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 it‐ + 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 a: echo a b: b1 echo b b1: echo b1 - the output is always ‘a’, ‘b1’, ‘b’, ‘x’. + the output is always `a', `b1', `b', `x'. + The ordering imposed by .WAIT is only relevant for parallel makes. SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .BEGIN Any command lines attached to this target are executed before anything else is done. .DEFAULT This is sort of a .USE rule for any target (that was used only - as a source) that bmake can't figure out any other way to cre‐ - ate. Only the shell script is used. The .IMPSRC variable of a + as a source) that bmake can't figure out any other way to cre- + ate. Only the shell script is used. The .IMPSRC variable of a target that inherits .DEFAULT's commands is set to the target's own name. .DELETE_ON_ERROR If this target is present in the makefile, it globally causes 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 be‐ + used to help prevent half-finished or malformed targets from be- ing left around and corrupting future rebuilds. - .END Any command lines attached to this target are executed after ev‐ - erything else is done. + .END Any command lines attached to this target are executed after ev- + erything else is done successfully. - .ERROR Any command lines attached to this target are executed when an‐ - other target fails. The .ERROR_TARGET variable is set to the - target that failed. See also MAKE_PRINT_VAR_ON_ERROR. + .ERROR Any command lines attached to this target are executed when an- + other target fails. The .ERROR_TARGET variable is set to the + target that failed. See also MAKE_PRINT_VAR_ON_ERROR. .IGNORE Mark each of the sources with the .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the -i option. .INTERRUPT - If bmake is interrupted, the commands for this target will be - executed. + If bmake is interrupted, the commands for this target are exe- + cuted. - .MAIN If no target is specified when bmake is invoked, this target - will be built. + .MAIN If no target is specified when bmake is invoked, this target is + built. .MAKEFLAGS - This target provides a way to specify flags for bmake when the - makefile is used. The flags are as if typed to the shell, - though the -f option will have no effect. + This target provides a way to specify flags for bmake at the + time when the makefiles are read. The flags are as if typed to + the shell, though the -f option has no effect. .NOPATH Apply the .NOPATH attribute to any specified sources. .NOTPARALLEL Disable parallel mode. .NO_PARALLEL Synonym for .NOTPARALLEL, for compatibility with other pmake variants. - .OBJDIR The source is a new value for ‘.OBJDIR’. If it exists, bmake - will chdir(2) to it and update the value of ‘.OBJDIR’. + .NOREADONLY + clear the read-only attribute from the global variables speci- + fied as sources. + + .OBJDIR The source is a new value for `.OBJDIR'. If it exists, bmake + changes the current working directory to it and updates the + value of `.OBJDIR'. .ORDER In parallel mode, the named targets are made in sequence. This ordering does not add targets to the list of targets to be made. - Since the dependents of a target do not get built until the tar‐ - get itself could be built, unless ‘a’ is built by another part + Since the dependents of a target do not get built until the tar- + get itself could be built, unless `a' is built by another part of the dependency graph, the following is a dependency loop: .ORDER: b a b: a .PATH The sources are directories which are to be searched for files - not found in the current directory. If no sources are speci‐ - fied, any previously specified directories are deleted. If the - source is the special .DOTLAST target, then the current working - directory is searched last. + not found in the current directory. If no sources are speci- + fied, any previously specified directories are removed from the + search path. If the source is the special .DOTLAST target, the + current working directory is searched last. .PATH.suffix Like .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .SUFFIXES. .PHONY Apply the .PHONY attribute to any specified sources. .POSIX If this is the first non-comment line in the main makefile, the - variable %POSIX is set to the value ‘1003.2’ and the makefile - ‘’ is included if it exists, to provide POSIX-compati‐ - ble default rules. If bmake is run with the -r flag, then only - ‘posix.mk’ will contribute to the default rules. + variable %POSIX is set to the value `1003.2' and the makefile + `' is included if it exists, to provide POSIX-compati- + ble default rules. If bmake is run with the -r flag, only + `posix.mk' contributes to the default rules. .PRECIOUS Apply the .PRECIOUS attribute to any specified sources. If no - sources are specified, the .PRECIOUS attribute is applied to ev‐ + sources are specified, the .PRECIOUS attribute is applied to ev- ery target in the file. - .SHELL Sets the shell that bmake will use to execute commands. The - sources are a set of field=value pairs. + .READONLY + set the read-only attribute on the global variables specified as + sources. + + .SHELL Sets the shell that bmake uses to execute commands in jobs mode. + The sources are a set of field=value pairs. - name This is the minimal specification, used to select - one of the built-in shell specs; sh, ksh, and csh. + name This is the minimal specification, used to select + one of the built-in shell specs; sh, ksh, and csh. - path Specifies the path to the shell. + path Specifies the absolute path to the shell. - hasErrCtl Indicates whether the shell supports exit on error. + hasErrCtl Indicates whether the shell supports exit on error. - check The command to turn on error checking. + check The command to turn on error checking. - ignore The command to disable error checking. + ignore The command to disable error checking. - echo The command to turn on echoing of commands executed. + echo The command to turn on echoing of commands executed. - quiet The command to turn off echoing of commands exe‐ + quiet The command to turn off echoing of commands exe- cuted. - filter The output to filter after issuing the quiet com‐ - mand. It is typically identical to quiet. + filter The output to filter after issuing the quiet com- + mand. It is typically identical to quiet. - errFlag The flag to pass the shell to enable error checking. + errFlag The flag to pass the shell to enable error checking. - echoFlag The flag to pass the shell to enable command echo‐ + echoFlag The flag to pass the shell to enable command echo- ing. - newline The string literal to pass the shell that results in + newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. Example: .SHELL: name=ksh path=/bin/ksh hasErrCtl=true \ check="set -e" ignore="set +e" \ echo="set -v" quiet="set +v" filter="set +v" \ echoFlag=v errFlag=e newline="'\n'" .SILENT Apply the .SILENT attribute to any specified sources. If no sources are specified, the .SILENT attribute is applied to every command in the file. - .STALE This target gets run when a dependency file contains stale en‐ + .STALE This target gets run when a dependency file contains stale en- tries, having .ALLSRC set to the name of that dependency file. .SUFFIXES Each source specifies a suffix to bmake. If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. Example: - .SUFFIXES: .o + .SUFFIXES: .c .o .c.o: cc -o ${.TARGET} -c ${.IMPSRC} + .SYSPATH + The sources are directories which are to be added to the system + include path which bmake searches for makefiles. If no sources + are specified, any previously specified directories are removed + from the system include path. + ENVIRONMENT bmake uses the following environment variables, if they exist: MACHINE, MACHINE_ARCH, MAKE, MAKEFLAGS, MAKEOBJDIR, MAKEOBJDIRPREFIX, MAKESYSPATH, PWD, and TMPDIR. MAKEOBJDIRPREFIX and MAKEOBJDIR may only be set in the environment or on - the command line to bmake and not as makefile variables; see the descrip‐ - tion of ‘.OBJDIR’ for more details. + the command line to bmake and not as makefile variables; see the descrip- + tion of `.OBJDIR' for more details. FILES .depend list of dependencies - Makefile list of dependencies - makefile list of dependencies + makefile first default makefile if no makefile is specified on the + command line + Makefile second default makefile if no makefile is specified on the + command line sys.mk system makefile /usr/share/mk system makefile directory COMPATIBILITY - The basic make syntax is compatible between different versions of make; - however the special variables, variable modifiers and conditionals are - not. + The basic make syntax is compatible between different make variants; how- + ever the special variables, variable modifiers and conditionals are not. Older versions An incomplete list of changes in older versions of bmake: 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 us‐ + 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‐ + .ORDER and .WAIT apply recursively to the dependent nodes. The algo- rithms used may change again in the future. Other make dialects - Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup‐ - port most of the features of bmake as described in this manual. Most no‐ + Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup- + port most of the features of bmake as described in this manual. Most no- tably: - • The .WAIT and .ORDER declarations and most functionality per‐ + +o The .WAIT and .ORDER declarations and most functionality per- taining to parallelization. (GNU make supports parallelization - but lacks these features needed to control it effectively.) + but lacks the features needed to control it effectively.) - • Directives, including for loops and conditionals and most of + +o Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) - • All built-in variables that begin with a dot. + +o All built-in variables that begin with a dot. - • Most of the special sources and targets that begin with a dot, + +o Most of the special sources and targets that begin with a dot, with the notable exception of .PHONY, .PRECIOUS, and .SUFFIXES. - • Variable modifiers, except for the - :old=new - string substitution, which does not portably support globbing - with ‘%’ and historically only works on declared suffixes. + +o Variable modifiers, except for the `:old=new' string substitu- + tion, which does not portably support globbing with `%' and + historically only works on declared suffixes. - • The $> variable even in its short form; most makes support this + +o The $> variable even in its short form; most makes support this functionality but its name varies. Some features are somewhat more portable, such as assignment with +=, ?=, - and !=. The .PATH functionality is based on an older feature VPATH found - in GNU make and many versions of SVR4 make; however, historically its be‐ + and !=. The .PATH functionality is based on an older feature VPATH found + 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 $@ and $< variables are more or less universally portable, as is the - $(MAKE) variable. Basic use of suffix rules (for files only in the cur‐ + $(MAKE) variable. Basic use of suffix rules (for files only in the cur- rent directory, not trying to chain transformations together, etc.) is also reasonably portable. SEE ALSO mkdep(1) HISTORY bmake is derived from NetBSD make(1). It uses autoconf to facilitate portability to other platforms. A make command appeared in Version 7 AT&T UNIX. This make implementation - is based on Adam De Boor's pmake program which was written for Sprite at + is based on Adam de Boor's pmake program, which was written for Sprite at Berkeley. It was designed to be a parallel distributed make running jobs - 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 rebuild‐ - ing (since the target/dependency does not exist... unless someone creates - an “FRC” file). + Historically the target/dependency FRC has been used to FoRCe rebuilding + (since the target/dependency does not exist ... unless someone creates an + FRC file). BUGS - The make syntax is difficult to parse without actually acting on the - data. For instance, finding the end of a variable's use should involve - scanning each of the modifiers, using the correct terminator for each - field. In many places make just counts {} and () in order to find the - end of a variable expansion. + The make syntax is difficult to parse. For instance, finding the end of + a variable's use should involve scanning each of the modifiers, using the + correct terminator for each field. In many places make just counts {} + and () in order to find the end of a variable expansion. There is no way of escaping a space character in a filename. -FreeBSD 13.0 July 12, 2022 FreeBSD 13.0 +FreeBSD 13.0 January 26, 2023 FreeBSD 13.0 diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c index b1996b91af6d..13063d8abd7e 100644 --- a/contrib/bmake/compat.c +++ b/contrib/bmake/compat.c @@ -1,798 +1,799 @@ -/* $NetBSD: compat.c,v 1.240 2022/05/07 17:49:47 rillig Exp $ */ +/* $NetBSD: compat.c,v 1.244 2023/01/17 21:35:19 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file implements the full-compatibility mode of make, which makes the * targets without parallelism and without a custom shell. * * Interface: * Compat_MakeAll Initialize this module and make the given targets. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "wait.h" #include #include #include "make.h" #include "dir.h" #include "job.h" #include "metachar.h" #include "pathnames.h" /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: compat.c,v 1.240 2022/05/07 17:49:47 rillig Exp $"); +MAKE_RCSID("$NetBSD: compat.c,v 1.244 2023/01/17 21:35:19 christos Exp $"); static GNode *curTarg = NULL; static pid_t compatChild; static int compatSigno; /* - * CompatDeleteTarget -- delete the file of a failed, interrupted, or - * otherwise duffed target if not inhibited by .PRECIOUS. + * Delete the file of a failed, interrupted, or otherwise duffed target, + * unless inhibited by .PRECIOUS. */ static void CompatDeleteTarget(GNode *gn) { if (gn != NULL && !GNode_IsPrecious(gn)) { const char *file = GNode_VarTarget(gn); - if (!opts.noExecute && unlink_file(file)) { + if (!opts.noExecute && unlink_file(file) == 0) { Error("*** %s removed", file); } } } /* * 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. * * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've * left the logic alone for now. - dholland 20160826 */ static void CompatInterrupt(int signo) { CompatDeleteTarget(curTarg); if (curTarg != NULL && !GNode_IsPrecious(curTarg)) { /* * Run .INTERRUPT only if hit with interrupt signal */ if (signo == SIGINT) { GNode *gn = Targ_FindNode(".INTERRUPT"); if (gn != NULL) { Compat_Make(gn, gn); } } } if (signo == SIGQUIT) _exit(signo); /* * If there is a child running, pass the signal on. * We will exist after it has exited. */ compatSigno = signo; if (compatChild > 0) { KILLPG(compatChild, signo); } else { bmake_signal(signo, SIG_DFL); kill(myPid, signo); } } static void DebugFailedTarget(const char *cmd, const 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 bool UseShell(const char *cmd MAKE_ATTR_UNUSED) { #if defined(FORCE_USE_SHELL) || !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 * gn Node from which the command came * ln List node that contains the command * * Results: * true if the command succeeded. */ bool Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) { char *cmdStart; /* Start of expanded command */ char *bp; bool silent; /* Don't print command */ bool doIt; /* Execute even if -n */ volatile bool errCheck; /* Check errors */ WAIT_T reason; /* Reason for child's death */ WAIT_T status; /* Description of child's death */ pid_t cpid; /* Child actually found */ pid_t retstat; /* Result of wait */ const char **volatile av; /* Argument vector for thing to exec */ char **volatile mav; /* Copy of the argument vector for freeing */ bool useShell; /* True if command should be executed using a * shell */ const char *volatile cmd = cmdp; silent = (gn->type & OP_SILENT) != OP_NONE; errCheck = !(gn->type & OP_IGNORE); doIt = false; (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart); /* TODO: handle errors */ if (cmdStart[0] == '\0') { free(cmdStart); return true; } cmd = cmdStart; LstNode_Set(ln, cmdStart); if (gn->type & OP_SAVE_CMDS) { GNode *endNode = Targ_GetEndNode(); if (gn != endNode) { /* * Append the expanded command, to prevent the * local variables from being interpreted in the * scope 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 true; } } if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return true; } for (;;) { if (*cmd == '@') silent = !DEBUG(LOUD); else if (*cmd == '-') errCheck = false; else if (*cmd == '+') { doIt = true; if (shellName == NULL) /* we came here from jobs */ Shell_Init(); - } else + } else if (!ch_isspace(*cmd)) + /* Ignore whitespace for compatibility with gnu make */ break; cmd++; } while (ch_isspace(*cmd)) cmd++; /* * If we did not end up with a command, just skip it. */ if (cmd[0] == '\0') return true; useShell = UseShell(cmd); /* * 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); fflush(stdout); } /* * If we're not supposed to execute any commands, this is as far as * we go... */ if (!doIt && !GNode_ShouldExecute(gn)) return true; DEBUG1(JOB, "Execute: '%s'\n", cmd); if (useShell) { /* * We need to pass the command off to the shell, typically * because the command contains a "meta" character. */ static const char *shargv[5]; /* The following work for any of the builtin shell specs. */ int shargc = 0; shargv[shargc++] = shellPath; if (errCheck && shellErrFlag != NULL) shargv[shargc++] = shellErrFlag; shargv[shargc++] = DEBUG(SHELL) ? "-xc" : "-c"; shargv[shargc++] = cmd; shargv[shargc] = NULL; av = shargv; bp = NULL; 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. */ Words words = Str_Words(cmd, false); mav = words.words; bp = words.freeIt; av = (void *)mav; } #ifdef USE_META if (useMeta) meta_compat_start(); #endif Var_ReexportVars(); compatChild = cpid = vfork(); if (cpid < 0) Fatal("Could not fork"); if (cpid == 0) { #ifdef USE_META if (useMeta) meta_compat_child(); #endif (void)execvp(av[0], (char *const *)UNCONST(av)); execDie("exec", av[0]); } free(mav); free(bp); /* XXX: Memory management looks suspicious here. */ /* XXX: Setting a list item to NULL is unexpected. */ LstNode_SetNull(ln); #ifdef USE_META if (useMeta) meta_compat_parent(cpid); #endif /* * The child is off and running. Now all we can do is wait... */ while ((retstat = wait(&reason)) != cpid) { if (retstat > 0) JobReapChild(retstat, reason, false); /* not ours? */ if (retstat == -1 && errno != EINTR) { break; } } if (retstat < 0) Fatal("error in wait: %d: %s", retstat, strerror(errno)); if (WIFSTOPPED(reason)) { status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { status = WEXITSTATUS(reason); /* exited */ #if defined(USE_META) && defined(USE_FILEMON_ONCE) if (useMeta) meta_cmd_finish(NULL); #endif if (status != 0) { if (DEBUG(ERROR)) DebugFailedTarget(cmd, gn); printf("*** Error code %d", status); } } else { status = WTERMSIG(reason); /* signaled */ printf("*** Signal %d", status); } if (!WIFEXITED(reason) || status != 0) { if (errCheck) { #ifdef USE_META if (useMeta) meta_job_error(NULL, gn, false, status); #endif gn->made = ERROR; if (opts.keepgoing) { /* * Abort the current target, * but let others continue. */ printf(" (continuing)\n"); } else { printf("\n"); } if (deleteOnError) CompatDeleteTarget(gn); } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ printf(" (ignored)\n"); status = 0; } } free(cmdStart); compatChild = 0; if (compatSigno != 0) { bmake_signal(compatSigno, SIG_DFL); kill(myPid, compatSigno); } return status == 0; } static void RunCommands(GNode *gn) { StringListNode *ln; for (ln = gn->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; if (!Compat_RunCommand(cmd, gn, ln)) break; } } static void MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn) { GNode **it; size_t r; for (r = (size_t)(end - gnodes); r >= 2; r--) { /* Biased, but irrelevant in practice. */ size_t i = (size_t)random() % r; GNode *t = gnodes[r - 1]; gnodes[r - 1] = gnodes[i]; gnodes[i] = t; } for (it = gnodes; it != end; it++) Compat_Make(*it, pgn); } static void MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn) { Vector vec; GNodeListNode *ln; GNode **nodes; size_t i, n, start; Vector_Init(&vec, sizeof(GNode *)); for (ln = gnodes->first; ln != NULL; ln = ln->next) *(GNode **)Vector_Push(&vec) = ln->datum; nodes = vec.items; n = vec.len; start = 0; for (i = 0; i < n; i++) { if (nodes[i]->type & OP_WAIT) { MakeInRandomOrder(nodes + start, nodes + i, pgn); Compat_Make(nodes[i], pgn); start = i + 1; } } MakeInRandomOrder(nodes + start, nodes + i, pgn); Vector_Done(&vec); } static void MakeNodes(GNodeList *gnodes, GNode *pgn) { GNodeListNode *ln; if (Lst_IsEmpty(gnodes)) return; if (opts.randomizeTargets) { MakeWaitGroupsInRandomOrder(gnodes, pgn); return; } for (ln = gnodes->first; ln != NULL; ln = ln->next) { GNode *cgn = ln->datum; Compat_Make(cgn, pgn); } } static bool MakeUnmade(GNode *gn, GNode *pgn) { 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 * descend and make all our children. If any of them has an error * but the -k flag was given, our 'make' field will be set to false * again. This is our signal to not attempt to do anything but abort * our parent as well. */ gn->flags.remake = true; gn->made = BEINGMADE; if (!(gn->type & OP_MADE)) Suff_FindDeps(gn); MakeNodes(&gn->children, gn); if (!gn->flags.remake) { gn->made = ABORTED; pgn->flags.remake = false; return false; } if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) Var_Set(pgn, IMPSRC, GNode_VarTarget(gn)); /* * All the children were made ok. Now youngestChild->mtime contains the * modification time of the newest child, we need to find out if we * exist and when we were modified last. The criteria for datedness * are defined by GNode_IsOODate. */ DEBUG1(MAKE, "Examining %s...", gn->name); if (!GNode_IsOODate(gn)) { gn->made = UPTODATE; DEBUG0(MAKE, "up-to-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.query) + if (opts.query && gn != Targ_GetEndNode()) exit(1); /* * We need to be re-made. * Ensure that $? (.OODATE) and $> (.ALLSRC) are both set. */ GNode_SetLocalVars(gn); /* * Alter our type to tell if errors should be ignored or things * should not be printed so Compat_RunCommand knows what to do. */ if (opts.ignoreErrors) gn->type |= OP_IGNORE; if (opts.silent) gn->type |= OP_SILENT; if (Job_CheckCommands(gn, Fatal)) { /* * Our commands are ok, but we still have to worry about * the -t flag. */ if (!opts.touch || (gn->type & OP_MAKE)) { curTarg = gn; #ifdef USE_META if (useMeta && GNode_ShouldExecute(gn)) meta_job_start(NULL, gn); #endif RunCommands(gn); curTarg = NULL; } else { Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE); } } else { gn->made = ERROR; } #ifdef USE_META if (useMeta && GNode_ShouldExecute(gn)) { if (meta_job_finish(NULL) != 0) gn->made = ERROR; } #endif if (gn->made != ERROR) { /* * If the node was made successfully, mark it so, update * its modification time and timestamp all its parents. * This is to keep its state from affecting that of its parent. */ gn->made = MADE; if (Make_Recheck(gn) == 0) pgn->flags.force = true; if (!(gn->type & OP_EXEC)) { pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, gn); } } else if (opts.keepgoing) { pgn->flags.remake = false; } else { PrintOnError(gn, "\nStop.\n"); exit(1); } return true; } static void MakeOther(GNode *gn, GNode *pgn) { if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) { const char *target = GNode_VarTarget(gn); Var_Set(pgn, IMPSRC, target != NULL ? target : ""); } switch (gn->made) { case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->made = ERROR; pgn->flags.remake = false; break; case MADE: if (!(gn->type & OP_EXEC)) { pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, gn); } break; case UPTODATE: if (!(gn->type & OP_EXEC)) GNode_UpdateYoungestChild(pgn, gn); break; default: break; } } /* * 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 * * 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_Make(GNode *gn, GNode *pgn) { 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.remake = false; } 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.\n"); 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) bmake_signal(SIGTERM, CompatInterrupt); if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) bmake_signal(SIGHUP, CompatInterrupt); if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) bmake_signal(SIGQUIT, CompatInterrupt); } void Compat_MakeAll(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 (!opts.query) MakeBeginNode(); /* * Expand .USE nodes right now, because they can modify the structure * of the tree. */ Make_ExpandUse(targs); while (!Lst_IsEmpty(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); } if (GNode_IsError(gn) && errorNode == NULL) errorNode = gn; } /* If the user has defined a .END target, run its commands. */ if (errorNode == NULL) { GNode *endNode = Targ_GetEndNode(); Compat_Make(endNode, endNode); if (GNode_IsError(endNode)) errorNode = endNode; } if (errorNode != NULL) { if (DEBUG(GRAPH2)) Targ_PrintGraph(2); else if (DEBUG(GRAPH3)) Targ_PrintGraph(3); PrintOnError(errorNode, "\nStop.\n"); exit(1); } } diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c index 3dd177d5acb8..fb7f789af0d1 100644 --- a/contrib/bmake/cond.c +++ b/contrib/bmake/cond.c @@ -1,1277 +1,1264 @@ -/* $NetBSD: cond.c,v 1.334 2022/04/15 09:33:20 rillig Exp $ */ +/* $NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Handling of conditionals in a makefile. * * Interface: * Cond_EvalLine Evaluate the conditional directive, such as * '.if ', '.elifnmake ', '.else', '.endif'. * * Cond_EvalCondition * Evaluate the conditional, which is either the argument * of one of the .if directives or the condition in a * ':?then:else' variable modifier. * - * Cond_save_depth - * Cond_restore_depth - * Save and restore the nesting of the conditions, at - * the start and end of including another makefile, to - * ensure that in each makefile the conditional - * directives are well-balanced. + * Cond_EndFile + * At the end of reading a makefile, ensure that the + * conditional directives are well-balanced. */ #include #include "make.h" #include "dir.h" /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: cond.c,v 1.334 2022/04/15 09:33:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $"); /* * Conditional expressions conform to this grammar: * Or -> And ('||' And)* * And -> Term ('&&' Term)* * Term -> Function '(' Argument ')' * Term -> Leaf Operator Leaf * Term -> Leaf * Term -> '(' Or ')' * Term -> '!' Term * Leaf -> "string" * Leaf -> Number * Leaf -> VariableExpression * Leaf -> BareWord * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<=' * * BareWord is an unquoted string literal, its evaluation depends on the kind * of '.if' directive. * * The tokens are scanned by CondParser_Token, which returns: * TOK_AND for '&&' * TOK_OR for '||' * TOK_NOT for '!' * TOK_LPAREN for '(' * TOK_RPAREN for ')' * * Other terminal symbols are evaluated using either the default function or * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE * or TOK_ERROR. */ typedef enum Token { TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR } Token; typedef enum ComparisonOp { LT, LE, GT, GE, EQ, NE } ComparisonOp; typedef struct CondParser { /* * The plain '.if ${VAR}' evaluates to true if the value of the * expression has length > 0. The other '.if' variants delegate * to evalBare instead, for example '.ifdef ${VAR}' is equivalent to * '.if defined(${VAR})', checking whether the variable named by the * expression '${VAR}' is defined. */ bool plain; /* The function to apply on unquoted bare words. */ bool (*evalBare)(const char *); bool negateEvalBare; /* * Whether the left-hand side of a comparison may be an unquoted * string. This is allowed for expressions of the form * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is * expanded before it is evaluated, due to ease of implementation. * This means that at the point where the condition is evaluated, * make cannot know anymore whether the left-hand side had originally * been a variable expression or a plain word. * * In conditional directives like '.if', the left-hand side must * either be a variable expression, a quoted string or a number. */ bool leftUnquotedOK; 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. */ bool printedError; } CondParser; static CondResult CondParser_Or(CondParser *par, bool); -static unsigned int cond_depth = 0; /* current .if nesting level */ -static unsigned int cond_min_depth = 0; /* depth at makefile open */ +unsigned int cond_depth = 0; /* current .if nesting level */ /* Names for ComparisonOp. */ static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; MAKE_INLINE bool skip_string(const char **pp, const char *str) { size_t len = strlen(str); bool ok = strncmp(*pp, str, len) == 0; if (ok) *pp += len; return ok; } static Token ToToken(bool cond) { return cond ? TOK_TRUE : TOK_FALSE; } static void CondParser_SkipWhitespace(CondParser *par) { cpp_skip_whitespace(&par->p); } /* * Parse a single word, taking into account balanced parentheses as well as * embedded expressions. Used for the argument of a built-in function as * well as for bare words, which are then passed to the default function. */ static char * ParseWord(const char **pp, bool doEval) { const char *p = *pp; Buffer word; int paren_depth; Buf_InitSize(&word, 16); paren_depth = 0; for (;;) { char ch = *p; if (ch == '\0' || ch == ' ' || ch == '\t') break; if ((ch == '&' || ch == '|') && paren_depth == 0) break; if (ch == '$') { /* * 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. */ VarEvalMode emode = doEval ? VARE_UNDEFERR : VARE_PARSE_ONLY; FStr nestedVal; (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal); /* TODO: handle errors */ Buf_AddStr(&word, nestedVal.str); FStr_Done(&nestedVal); continue; } if (ch == '(') paren_depth++; else if (ch == ')' && --paren_depth < 0) break; Buf_AddByte(&word, ch); p++; } cpp_skip_hspace(&p); *pp = p; return Buf_DoneData(&word); } /* Parse the function argument, including the surrounding parentheses. */ static char * ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) { const char *p = *pp; char *res; p++; /* Skip opening '(' - verified by caller */ cpp_skip_hspace(&p); res = ParseWord(&p, doEval); cpp_skip_hspace(&p); if (*p++ != ')') { int len = 0; while (ch_isalpha(func[len])) len++; Parse_Error(PARSE_FATAL, "Missing closing parenthesis for %.*s()", len, func); par->printedError = true; free(res); return NULL; } *pp = p; return res; } /* See if the given variable is defined. */ static bool FuncDefined(const char *var) { return Var_Exists(SCOPE_CMDLINE, var); } /* See if a target matching targetPattern is requested to be made. */ static bool FuncMake(const char *targetPattern) { StringListNode *ln; for (ln = opts.create.first; ln != NULL; ln = ln->next) if (Str_Match(ln->datum, targetPattern)) return true; return false; } /* See if the given file exists. */ static bool FuncExists(const char *file) { bool result; char *path; path = Dir_FindFile(file, &dirSearchPath); DEBUG2(COND, "exists(%s) result is \"%s\"\n", file, path != NULL ? path : ""); result = path != NULL; free(path); return result; } /* See if the given node exists and is an actual target. */ static bool FuncTarget(const char *node) { GNode *gn = Targ_FindNode(node); return gn != NULL && GNode_IsTarget(gn); } /* * See if the given node exists and is an actual target with commands * associated with it. */ static bool FuncCommands(const char *node) { GNode *gn = Targ_FindNode(node); return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands); } /* * Convert the string into a floating-point number. Accepted formats are * base-10 integer, base-16 integer and finite floating point numbers. */ static bool TryParseNumber(const char *str, double *out_value) { char *end; unsigned long ul_val; double dbl_val; if (str[0] == '\0') { /* XXX: why is an empty string a number? */ *out_value = 0.0; return true; } errno = 0; ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); if (*end == '\0' && errno != ERANGE) { *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; return true; } if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') return false; /* skip the expensive strtod call */ dbl_val = strtod(str, &end); if (*end != '\0') return false; *out_value = dbl_val; return true; } static bool is_separator(char ch) { return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || ch == '>' || ch == '<' || ch == ')' /* but not '(' */; } /* * In a quoted or unquoted string literal or a number, parse a variable * expression. * * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} */ static bool CondParser_StringExpr(CondParser *par, const char *start, bool doEval, bool quoted, Buffer *buf, FStr *inout_str) { VarEvalMode emode; const char *p; bool atStart; VarParseResult parseResult; emode = doEval && quoted ? VARE_WANTRES : doEval ? VARE_UNDEFERR : VARE_PARSE_ONLY; p = par->p; atStart = p == start; parseResult = Var_Parse(&p, SCOPE_CMDLINE, emode, inout_str); /* TODO: handle errors */ if (inout_str->str == var_Error) { if (parseResult == VPR_ERR) { /* * FIXME: Even if an error occurs, there is no * guarantee that it is reported. * * See cond-token-plain.mk $$$$$$$$. */ par->printedError = true; } /* * XXX: Can there be any situation in which a returned * var_Error needs to be freed? */ FStr_Done(inout_str); /* * Even if !doEval, we still report syntax errors, which is * what getting var_Error back with !doEval means. */ *inout_str = FStr_InitRefer(NULL); return false; } par->p = 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 (atStart && is_separator(par->p[0])) return false; Buf_AddStr(buf, inout_str->str); FStr_Done(inout_str); *inout_str = FStr_InitRefer(NULL); /* not finished yet */ return true; } /* * Parse a string from a variable expression or an optionally quoted string, * on the left-hand and right-hand sides of comparisons. * * Results: * Returns the string without any enclosing quotes, or NULL on error. * Sets out_quoted if the leaf was a quoted string literal. */ static void CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, FStr *out_str, bool *out_quoted) { Buffer buf; FStr str; bool quoted; const char *start; Buf_Init(&buf); str = FStr_InitRefer(NULL); *out_quoted = quoted = par->p[0] == '"'; start = par->p; if (quoted) par->p++; while (par->p[0] != '\0' && str.str == NULL) { switch (par->p[0]) { case '\\': par->p++; if (par->p[0] != '\0') { Buf_AddByte(&buf, par->p[0]); par->p++; } continue; case '"': par->p++; if (quoted) goto return_buf; /* skip the closing quote */ Buf_AddByte(&buf, '"'); continue; case ')': /* see is_separator */ case '!': case '=': case '>': case '<': case ' ': case '\t': if (!quoted) goto return_buf; Buf_AddByte(&buf, par->p[0]); par->p++; continue; case '$': if (!CondParser_StringExpr(par, start, doEval, quoted, &buf, &str)) goto return_str; continue; default: if (!unquotedOK && !quoted && *start != '$' && !ch_isdigit(*start)) { /* * The left-hand side must be quoted, * a variable expression or a number. */ str = FStr_InitRefer(NULL); goto return_str; } Buf_AddByte(&buf, par->p[0]); par->p++; continue; } } return_buf: str = FStr_InitOwn(buf.data); buf.data = NULL; return_str: Buf_Done(&buf); *out_str = str; } /* * Evaluate a "comparison without operator", such as in ".if ${VAR}" or * ".if 0". */ static bool EvalNotEmpty(CondParser *par, const char *value, bool quoted) { double num; /* For .ifxxx "...", check for non-empty string. */ if (quoted) return value[0] != '\0'; /* For .ifxxx , compare against zero */ if (TryParseNumber(value, &num)) return num != 0.0; /* * For .if ${...}, check for non-empty string. This is different * from the evaluation function from that .if variant, which would * test whether a variable of the given name were defined. */ /* * XXX: Whitespace should count as empty, just as in * CondParser_FuncCallEmpty. */ if (par->plain) return value[0] != '\0'; return par->evalBare(value) != par->negateEvalBare; } /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ static bool EvalCompareNum(double lhs, ComparisonOp op, double rhs) { DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); switch (op) { case LT: return lhs < rhs; case LE: return lhs <= rhs; case GT: return lhs > rhs; case GE: return lhs >= rhs; - case NE: - return lhs != rhs; - default: + case EQ: return lhs == rhs; + default: + return lhs != rhs; } } static Token EvalCompareStr(CondParser *par, const char *lhs, ComparisonOp op, const char *rhs) { if (op != EQ && op != NE) { Parse_Error(PARSE_FATAL, - "String comparison operator must be either == or !="); + "Comparison with '%s' requires both operands " + "'%s' and '%s' to be numeric", + opname[op], lhs, rhs); par->printedError = true; return TOK_ERROR; } DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); } /* Evaluate a comparison, such as "${VAR} == 12345". */ static Token EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, ComparisonOp op, const char *rhs, bool rhsQuoted) { double left, right; if (!rhsQuoted && !lhsQuoted) if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) return ToToken(EvalCompareNum(left, op, right)); return EvalCompareStr(par, lhs, op, rhs); } static bool CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) { const char *p = par->p; if (p[0] == '<' && p[1] == '=') return par->p += 2, *out_op = LE, true; if (p[0] == '<') return par->p += 1, *out_op = LT, true; if (p[0] == '>' && p[1] == '=') return par->p += 2, *out_op = GE, true; if (p[0] == '>') return par->p += 1, *out_op = GT, true; if (p[0] == '=' && p[1] == '=') return par->p += 2, *out_op = EQ, true; if (p[0] == '!' && p[1] == '=') return par->p += 2, *out_op = NE, true; return false; } /* * Parse a comparison condition such as: * * 0 * ${VAR:Mpattern} * ${VAR} == value * ${VAR:U0} < 12345 */ static Token CondParser_Comparison(CondParser *par, bool doEval) { Token t = TOK_ERROR; FStr lhs, rhs; ComparisonOp op; bool lhsQuoted, rhsQuoted; CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted); if (lhs.str == NULL) goto done_lhs; CondParser_SkipWhitespace(par); if (!CondParser_ComparisonOp(par, &op)) { /* Unknown operator, compare against an empty string or 0. */ t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted)); goto done_lhs; } CondParser_SkipWhitespace(par); if (par->p[0] == '\0') { Parse_Error(PARSE_FATAL, "Missing right-hand side of operator '%s'", opname[op]); par->printedError = true; goto done_lhs; } CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted); t = rhs.str == NULL ? TOK_ERROR : !doEval ? TOK_FALSE : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); FStr_Done(&rhs); done_lhs: FStr_Done(&lhs); return t; } /* * The argument to empty() is a variable name, optionally followed by * variable modifiers. */ static bool CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) { const char *cp = par->p; Token tok; FStr val; if (!skip_string(&cp, "empty")) return false; cpp_skip_whitespace(&cp); if (*cp != '(') return false; cp--; /* Make cp[1] point to the '('. */ (void)Var_Parse(&cp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val); /* TODO: handle errors */ if (val.str == var_Error) tok = TOK_ERROR; else { cpp_skip_whitespace(&val.str); tok = ToToken(doEval && val.str[0] == '\0'); } FStr_Done(&val); *out_token = tok; par->p = cp; return true; } /* Parse a function call expression, such as 'exists(${file})'. */ static bool CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) { char *arg; const char *p = par->p; bool (*fn)(const char *); const char *fn_name = p; if (skip_string(&p, "defined")) fn = FuncDefined; else if (skip_string(&p, "make")) fn = FuncMake; else if (skip_string(&p, "exists")) fn = FuncExists; else if (skip_string(&p, "target")) fn = FuncTarget; else if (skip_string(&p, "commands")) fn = FuncCommands; else return false; cpp_skip_whitespace(&p); if (*p != '(') return false; arg = ParseFuncArg(par, &p, doEval, fn_name); *out_token = ToToken(doEval && arg != NULL && arg[0] != '\0' && fn(arg)); free(arg); par->p = p; return true; } /* * Parse a comparison that neither starts with '"' nor '$', such as the * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without * operator, which is a number, a variable expression or a string literal. * * TODO: Can this be merged into CondParser_Comparison? */ static Token CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) { Token t; char *arg; const char *cp; /* Push anything numeric through the compare expression */ cp = par->p; if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+') return CondParser_Comparison(par, 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. */ /* * XXX: In edge cases, a variable expression may be evaluated twice, * see cond-token-plain.mk, keyword 'twice'. */ arg = ParseWord(&cp, doEval); assert(arg[0] != '\0'); if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>') return CondParser_Comparison(par, doEval); par->p = cp; /* * Evaluate the argument using the default function. * This path always treats .if as .ifdef. To get here, the character * after .if must have been taken literally, so the argument cannot * be empty - even if it contained a variable expansion. */ t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); free(arg); return t; } /* Return the next token or comparison result from the parser. */ static Token CondParser_Token(CondParser *par, bool doEval) { Token t; t = par->curr; if (t != TOK_NONE) { par->curr = TOK_NONE; return t; } cpp_skip_hspace(&par->p); switch (par->p[0]) { case '(': par->p++; return TOK_LPAREN; case ')': par->p++; return TOK_RPAREN; case '|': par->p++; if (par->p[0] == '|') par->p++; else if (opts.strict) { Parse_Error(PARSE_FATAL, "Unknown operator '|'"); par->printedError = true; return TOK_ERROR; } return TOK_OR; case '&': par->p++; if (par->p[0] == '&') par->p++; else if (opts.strict) { Parse_Error(PARSE_FATAL, "Unknown operator '&'"); par->printedError = true; return TOK_ERROR; } return TOK_AND; case '!': par->p++; return TOK_NOT; case '#': /* XXX: see unit-tests/cond-token-plain.mk */ case '\n': /* XXX: why should this end the condition? */ /* Probably obsolete now, from 1993-03-21. */ case '\0': return TOK_EOF; case '"': case '$': return CondParser_Comparison(par, doEval); default: if (CondParser_FuncCallEmpty(par, doEval, &t)) return t; if (CondParser_FuncCall(par, doEval, &t)) return t; return CondParser_ComparisonOrLeaf(par, doEval); } } /* Skip the next token if it equals t. */ static bool CondParser_Skip(CondParser *par, Token t) { Token actual; actual = CondParser_Token(par, false); if (actual == t) return true; assert(par->curr == TOK_NONE); assert(actual != TOK_NONE); par->curr = actual; return false; } /* * Term -> '(' Or ')' * Term -> '!' Term * Term -> Leaf Operator Leaf * Term -> Leaf */ static CondResult CondParser_Term(CondParser *par, bool doEval) { CondResult res; Token t; t = CondParser_Token(par, doEval); if (t == TOK_TRUE) return CR_TRUE; if (t == TOK_FALSE) return CR_FALSE; if (t == TOK_LPAREN) { res = CondParser_Or(par, doEval); if (res == CR_ERROR) return CR_ERROR; if (CondParser_Token(par, doEval) != TOK_RPAREN) return CR_ERROR; return res; } if (t == TOK_NOT) { res = CondParser_Term(par, doEval); if (res == CR_TRUE) res = CR_FALSE; else if (res == CR_FALSE) res = CR_TRUE; return res; } return CR_ERROR; } /* * And -> Term ('&&' Term)* */ static CondResult CondParser_And(CondParser *par, bool doEval) { CondResult res, rhs; res = CR_TRUE; do { if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) return CR_ERROR; if (rhs == CR_FALSE) { res = CR_FALSE; doEval = false; } } while (CondParser_Skip(par, TOK_AND)); return res; } /* * Or -> And ('||' And)* */ static CondResult CondParser_Or(CondParser *par, bool doEval) { CondResult res, rhs; res = CR_FALSE; do { if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) return CR_ERROR; if (rhs == CR_TRUE) { res = CR_TRUE; doEval = false; } } while (CondParser_Skip(par, TOK_OR)); return res; } static CondResult CondParser_Eval(CondParser *par) { CondResult res; DEBUG1(COND, "CondParser_Eval: %s\n", par->p); res = CondParser_Or(par, true); if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF) return CR_ERROR; return res; } /* * 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. */ static CondResult CondEvalExpression(const char *cond, bool plain, bool (*evalBare)(const char *), bool negate, bool eprint, bool leftUnquotedOK) { CondParser par; CondResult rval; cpp_skip_hspace(&cond); par.plain = plain; par.evalBare = evalBare; par.negateEvalBare = negate; par.leftUnquotedOK = leftUnquotedOK; par.p = cond; par.curr = TOK_NONE; par.printedError = false; rval = CondParser_Eval(&par); if (rval == CR_ERROR && eprint && !par.printedError) Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); return rval; } /* * Evaluate a condition in a :? modifier, such as * ${"${VAR}" == value:?yes:no}. */ CondResult Cond_EvalCondition(const char *cond) { return CondEvalExpression(cond, true, FuncDefined, false, false, true); } static bool IsEndif(const char *p) { return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); } static bool DetermineKindOfConditional(const char **pp, bool *out_plain, bool (**out_evalBare)(const char *), bool *out_negate) { const char *p = *pp + 2; *out_plain = false; *out_evalBare = FuncDefined; *out_negate = skip_string(&p, "n"); if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ *out_evalBare = FuncMake; else if (!*out_negate) /* plain .if */ *out_plain = true; else goto unknown_directive; if (ch_isalpha(*p)) goto unknown_directive; *pp = p; return true; unknown_directive: /* * TODO: Add error message about unknown directive, since there is no * other known directive that starts with 'el' or 'if'. * * Example: .elifx 123 */ return false; } /* * Evaluate the conditional directive in the line, which is one of: * * .if * .ifmake * .ifnmake * .ifdef * .ifndef * .elif * .elifmake * .elifnmake * .elifdef * .elifndef * .else * .endif * * In these directives, consists of &&, ||, !, function(arg), * comparisons, expressions, bare words, numbers and strings, and * parenthetical groupings thereof. * * Results: * CR_TRUE to continue parsing the lines that follow the * conditional (when evaluates to true) * CR_FALSE to skip the lines after the conditional * (when evaluates to false, or when a previous * branch has already been taken) * CR_ERROR if the conditional was not valid, either because of * a syntax error or because some variable was undefined * or because the condition could not be evaluated */ CondResult Cond_EvalLine(const char *line) { typedef enum IfState { /* None of the previous evaluated to true. */ IFS_INITIAL = 0, /* * The previous evaluated to true. The lines following * this condition are interpreted. */ IFS_ACTIVE = 1 << 0, /* The previous directive was an '.else'. */ IFS_SEEN_ELSE = 1 << 1, /* One of the previous evaluated to true. */ IFS_WAS_ACTIVE = 1 << 2 } IfState; static enum IfState *cond_states = NULL; static unsigned int cond_states_cap = 128; bool plain; bool (*evalBare)(const char *); bool negate; bool isElif; CondResult res; IfState state; const char *p = line; if (cond_states == NULL) { cond_states = bmake_malloc( cond_states_cap * sizeof *cond_states); cond_states[0] = IFS_ACTIVE; } p++; /* skip the leading '.' */ cpp_skip_hspace(&p); if (IsEndif(p)) { /* It is an '.endif'. */ if (p[5] != '\0') { Parse_Error(PARSE_FATAL, "The .endif directive does not take arguments"); } - if (cond_depth == cond_min_depth) { + if (cond_depth == CurFile_CondMinDepth()) { Parse_Error(PARSE_FATAL, "if-less endif"); return CR_TRUE; } /* Return state for previous conditional */ cond_depth--; return cond_states[cond_depth] & IFS_ACTIVE ? CR_TRUE : CR_FALSE; } /* 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 '.err.txt', * therefore no error message here. */ return CR_ERROR; } /* Quite likely this is 'else' or 'elif' */ p += 2; if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { if (p[2] != '\0') Parse_Error(PARSE_FATAL, "The .else directive " "does not take arguments"); - if (cond_depth == cond_min_depth) { + if (cond_depth == CurFile_CondMinDepth()) { Parse_Error(PARSE_FATAL, "if-less else"); return CR_TRUE; } state = cond_states[cond_depth]; if (state == IFS_INITIAL) { state = IFS_ACTIVE | IFS_SEEN_ELSE; } else { if (state & IFS_SEEN_ELSE) Parse_Error(PARSE_WARNING, "extra else"); state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; } cond_states[cond_depth] = state; return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; } /* Assume for now it is an elif */ isElif = true; } else 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. */ return CR_ERROR; /* Not an ifxxx or elifxxx line */ } if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) return CR_ERROR; if (isElif) { - if (cond_depth == cond_min_depth) { + if (cond_depth == CurFile_CondMinDepth()) { Parse_Error(PARSE_FATAL, "if-less elif"); return CR_TRUE; } 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; return CR_FALSE; } if (state != IFS_INITIAL) { cond_states[cond_depth] = IFS_WAS_ACTIVE; return CR_FALSE; } } else { /* Normal .if */ if (cond_depth + 1 >= cond_states_cap) { /* * This is rare, but not impossible. * In meta mode, dirdeps.mk (only runs at level 0) * can need more than the default. */ cond_states_cap += 32; cond_states = bmake_realloc(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. */ cond_states[cond_depth] = IFS_WAS_ACTIVE; return CR_FALSE; } } /* And evaluate the conditional expression */ res = CondEvalExpression(p, plain, evalBare, negate, true, false); if (res == CR_ERROR) { /* Syntax error, error message already output. */ /* Skip everything to the matching '.endif'. */ /* An extra '.else' is not detected in this case. */ cond_states[cond_depth] = IFS_WAS_ACTIVE; return CR_FALSE; } cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; return res; } void -Cond_restore_depth(unsigned int saved_depth) +Cond_EndFile(void) { - unsigned int open_conds = cond_depth - cond_min_depth; + unsigned int open_conds = cond_depth - CurFile_CondMinDepth(); - if (open_conds != 0 || saved_depth > cond_depth) { + if (open_conds != 0) { Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds, open_conds == 1 ? "" : "s"); - cond_depth = cond_min_depth; + cond_depth = CurFile_CondMinDepth(); } - - cond_min_depth = saved_depth; -} - -unsigned int -Cond_save_depth(void) -{ - unsigned int depth = cond_min_depth; - - cond_min_depth = cond_depth; - return depth; } diff --git a/contrib/bmake/config.h.in b/contrib/bmake/config.h.in index aebebe783a44..85fc10cf30b6 100644 --- a/contrib/bmake/config.h.in +++ b/contrib/bmake/config.h.in @@ -1,393 +1,444 @@ /* config.h.in. Generated from configure.in by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Path of default shell */ #undef DEFSHELL_CUSTOM /* Shell spec to use by default */ #undef DEFSHELL_INDEX /* Path of default shell */ #undef DEFSHELL_PATH /* Define to 1 if you have the header file. */ #undef HAVE_AR_H /* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you don't. */ #undef HAVE_DECL_SYS_SIGLIST /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the `dirname' function. */ #undef HAVE_DIRNAME /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT /* Define to 1 if you have the `err' function. */ #undef HAVE_ERR /* Define to 1 if you have the `errx' function. */ #undef HAVE_ERRX /* Define to 1 if you have the header file. */ #undef HAVE_ERR_H /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `fork' function. */ #undef HAVE_FORK /* Define to 1 if you have the `getcwd' function. */ #undef HAVE_GETCWD /* Define to 1 if you have the `getenv' function. */ #undef HAVE_GETENV /* Define to 1 if you have the `getopt' function. */ #undef HAVE_GETOPT /* Define to 1 if you have the `getwd' function. */ #undef HAVE_GETWD /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `killpg' function. */ #undef HAVE_KILLPG /* Define to 1 if you have the header file. */ #undef HAVE_LIBGEN_H /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if the system has the type `long long int'. */ #undef HAVE_LONG_LONG_INT -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H +/* Define to 1 if you have the header file. */ +#undef HAVE_MINIX_CONFIG_H /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_PATHS_H /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Define to 1 if you have the `putenv' function. */ #undef HAVE_PUTENV /* Define to 1 if you have the header file. */ #undef HAVE_RANLIB_H /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT /* Define to 1 if you have the `setenv' function. */ #undef HAVE_SETENV /* Define to 1 if you have the `setpgid' function. */ #undef HAVE_SETPGID /* Define to 1 if you have the `setrlimit' function. */ #undef HAVE_SETRLIMIT /* Define to 1 if you have the `setsid' function. */ #undef HAVE_SETSID /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 1 if you have the `sigaddset' function. */ #undef HAVE_SIGADDSET /* Define to 1 if you have the `sigpending' function. */ #undef HAVE_SIGPENDING /* Define to 1 if you have the `sigprocmask' function. */ #undef HAVE_SIGPROCMASK /* Define to 1 if you have the `sigsetmask' function. */ #undef HAVE_SIGSETMASK /* Define to 1 if you have the `sigsuspend' function. */ #undef HAVE_SIGSUSPEND /* Define to 1 if you have the `sigvec' function. */ #undef HAVE_SIGVEC /* Define to 1 if the system has the type `sig_atomic_t'. */ #undef HAVE_SIG_ATOMIC_T /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the `stresep' function. */ #undef HAVE_STRESEP /* Define to 1 if you have the `strftime' function. */ #undef HAVE_STRFTIME /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strsep' function. */ #undef HAVE_STRSEP /* Define to 1 if you have the `strtod' function. */ #undef HAVE_STRTOD /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL /* Define to 1 if you have the `strtoll' function. */ #undef HAVE_STRTOLL /* Define to 1 if you have the `strtoul' function. */ #undef HAVE_STRTOUL -/* Define to 1 if `st_rdev' is a member of `struct stat'. */ -#undef HAVE_STRUCT_STAT_ST_RDEV - -/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use - `HAVE_STRUCT_STAT_ST_RDEV' instead. */ -#undef HAVE_ST_RDEV - /* Define to 1 if you have the `sysctl' function. */ #undef HAVE_SYSCTL /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_DIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV /* Define to 1 if the system has the type `unsigned long long int'. */ #undef HAVE_UNSIGNED_LONG_LONG_INT /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H /* Define to 1 if you have the `vfork' function. */ #undef HAVE_VFORK /* Define to 1 if you have the header file. */ #undef HAVE_VFORK_H /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF /* Define to 1 if you have the `wait3' function. */ #undef HAVE_WAIT3 /* Define to 1 if you have the `wait4' function. */ #undef HAVE_WAIT4 /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID /* Define to 1 if you have the `warn' function. */ #undef HAVE_WARN /* Define to 1 if you have the `warnx' function. */ #undef HAVE_WARNX +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + /* Define to 1 if `fork' works. */ #undef HAVE_WORKING_FORK /* Define to 1 if `vfork' works. */ #undef HAVE_WORKING_VFORK /* define if your compiler has __attribute__ */ #undef HAVE___ATTRIBUTE__ /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION -/* Define as the return type of signal handlers (`int' or `void'). */ -#undef RETSIGTYPE - /* Define to 1 if the `S_IS*' macros in do not work properly. */ #undef STAT_MACROS_BROKEN -/* Define to 1 if you have the ANSI C header files. */ +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ #undef STDC_HEADERS -/* Define to 1 if you can safely include both and . */ -#undef TIME_WITH_SYS_TIME - /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif +/* Enable general extensions on macOS. */ +#ifndef _DARWIN_C_SOURCE +# undef _DARWIN_C_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif -/* Enable threading extensions on Solaris. */ +/* Enable X/Open compliant socket functions that do not require linking + with -lxnet on HP-UX 11.11. */ +#ifndef _HPUX_ALT_XOPEN_SOCKET_API +# undef _HPUX_ALT_XOPEN_SOCKET_API +#endif +/* Identify the host operating system as Minix. + This macro does not affect the system headers' behavior. + A future release of Autoconf may stop defining this macro. */ +#ifndef _MINIX +# undef _MINIX +#endif +/* Enable general extensions on NetBSD. + Enable NetBSD compatibility extensions on Minix. */ +#ifndef _NETBSD_SOURCE +# undef _NETBSD_SOURCE +#endif +/* Enable OpenBSD compatibility extensions on NetBSD. + Oddly enough, this does nothing on OpenBSD. */ +#ifndef _OPENBSD_SOURCE +# undef _OPENBSD_SOURCE +#endif +/* Define to 1 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_SOURCE +# undef _POSIX_SOURCE +#endif +/* Define to 2 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_1_SOURCE +# undef _POSIX_1_SOURCE +#endif +/* Enable POSIX-compatible threading on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif +/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ +#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ +#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ +# undef __STDC_WANT_IEC_60559_BFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ +#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ +# undef __STDC_WANT_IEC_60559_DFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ +#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ +# undef __STDC_WANT_IEC_60559_FUNCS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# undef __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ +#ifndef __STDC_WANT_LIB_EXT2__ +# undef __STDC_WANT_LIB_EXT2__ +#endif +/* Enable extensions specified by ISO/IEC 24747:2009. */ +#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ +# undef __STDC_WANT_MATH_SPEC_FUNCS__ +#endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# undef __EXTENSIONS__ +/* Enable X/Open extensions. Define to 500 only if necessary + to make mbstate_t available. */ +#ifndef _XOPEN_SOURCE +# undef _XOPEN_SOURCE #endif /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif -/* Define to 1 if on MINIX. */ -#undef _MINIX - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -#undef _POSIX_1_SOURCE - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -#undef _POSIX_SOURCE - /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* C99 function name */ #undef __func__ /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef int64_t /* Define to `int' if does not define. */ #undef mode_t /* Define to `long int' if does not define. */ #undef off_t -/* Define to `int' if does not define. */ +/* Define as a signed integer type capable of holding a process identifier. */ #undef pid_t /* type that signal handlers can safely frob */ #undef sig_atomic_t /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define as `fork' if `vfork' does not work. */ #undef vfork diff --git a/contrib/bmake/configure b/contrib/bmake/configure index 42295d1db3e3..44f016b249e1 100755 --- a/contrib/bmake/configure +++ b/contrib/bmake/configure @@ -1,7840 +1,8341 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bmake 20211020. +# Generated by GNU Autoconf 2.71 for bmake 20220909. # # Report bugs to . # # -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else +else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then +if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST -else +else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : -else +else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + if (eval "$as_required") 2>/dev/null +then : as_have_required=yes -else +else $as_nop as_have_required=no fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : -else +else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base + as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : break 2 fi fi done;; esac as_found=false done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi - if test "x$CONFIG_SHELL" != x; then : + if test "x$CONFIG_SHELL" != x +then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else - $as_echo "$0: Please tell bug-autoconf@gnu.org and sjg@NetBSD.org + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and sjg@NetBSD.org $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset + # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | +printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : eval 'as_fn_append () { eval $1+=\$2 }' -else +else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : eval 'as_fn_arith () { as_val=$(( $* )) }' -else +else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $2" >&2 + printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + + rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bmake' PACKAGE_TARNAME='bmake' -PACKAGE_VERSION='20211020' -PACKAGE_STRING='bmake 20211020' +PACKAGE_VERSION='20220909' +PACKAGE_STRING='bmake 20220909' PACKAGE_BUGREPORT='sjg@NetBSD.org' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include +#include +#ifdef HAVE_STDIO_H +# include #endif -#ifdef STDC_HEADERS +#ifdef HAVE_STDLIB_H # include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif #endif #ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif # include #endif -#ifdef HAVE_STRINGS_H -# include -#endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif #ifdef HAVE_UNISTD_H # include #endif" +ac_header_c_list= +ac_func_c_list= ac_subst_vars='LTLIBOBJS UTC_1 _MAKE_VERSION filemon_h use_filemon use_meta diff_u diff GCC INSTALL default_sys_path mksrc force_machine_arch machine_arch force_machine machine +egrep LIBOBJS bmake_path_max ac_exe_suffix INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM -EGREP -GREP -CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_defshell with_makefile with_meta with_filemon with_machine with_force_machine with_force_machine_arch with_machine_arch with_default_sys_path with_path_objdirprefix enable_pwd_override enable_check_make_chdir with_mksrc ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS -CPPFLAGS -CPP' +CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac - # Accept the important Cygnus configure options, so we can diagnose typos. - case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | +printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bmake 20211020 to adapt to many kinds of systems. +\`configure' configures bmake 20220909 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/bmake] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bmake 20211020:";; + short | recursive ) echo "Configuration of bmake 20220909:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-pwd-override disable $PWD overriding getcwd() --disable-check-make-chdir disable make trying to guess when it should automatically cd ${.CURDIR} Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions --without-makefile disable use of generated makefile --without-meta disable use of meta-mode --with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev --with-machine=MACHINE explicitly set MACHINE --with-force-machine=MACHINE set FORCE_MACHINE --with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH MAKESYSPATH is a ':' separated list of directories that bmake will search for system .mk files. _PATH_DEFSYSPATH is its default value. --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX --with-mksrc=PATH tell makefile.boot where to find mk src Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory - CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bmake configure 20211020 -generated by GNU Autoconf 2.69 +bmake configure 20220909 +generated by GNU Autoconf 2.71 -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext + rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then : + } && test -s conftest.$ac_objext +then : ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ----------------------------- ## -## Report this to sjg@NetBSD.org ## -## ----------------------------- ##" - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : eval "$3=yes" -else +else $as_nop eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext - }; then : + } +then : ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that +# executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: program exited with status $ac_status" >&5 + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + # ac_fn_c_find_intX_t LINENO BITS VAR # ----------------------------------- # Finds a signed integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_intX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 -$as_echo_n "checking for int$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +printf %s "checking for int$2_t... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in int$2_t 'int' 'long int' \ 'long long int' 'short int' 'signed char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int -main () +main (void) { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int -main () +main (void) { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : -else +else $as_nop case $ac_type in #( int$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no" +then : -else +else $as_nop break fi done fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_intX_t # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int -main () +main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int -main () +main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : -else +else $as_nop eval "$3=yes" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 -$as_echo_n "checking for uint$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +printf %s "checking for uint$2_t... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int -main () +main (void) { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no" +then : -else +else $as_nop break fi done fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t -# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES -# --------------------------------------------- +# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR +# ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. -ac_fn_c_check_decl () +# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. +ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +printf %s "checking whether $as_decl_name is declared... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -$as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + eval ac_save_FLAGS=\$$6 + as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int -main () +main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : eval "$3=yes" -else +else $as_nop eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + eval $6=\$ac_save_FLAGS + fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno -} # ac_fn_c_check_decl +} # ac_fn_check_decl # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif + which can conflict with char $2 (); below. */ +#include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int -main () +main (void) { return $2 (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : eval "$3=yes" -else +else $as_nop eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$4 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; +esac -} # ac_fn_c_check_member cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bmake $as_me 20211020, which was -generated by GNU Autoconf 2.69. Invocation command line was +It was created by bmake $as_me 20220909, which was +generated by GNU Autoconf 2.71. Invocation command line was - $ $0 $@ + $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo - $as_echo "## ---------------- ## + printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo - $as_echo "## ----------------- ## + printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - $as_echo "$ac_var='\''$ac_val'\''" + printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## + printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - $as_echo "$ac_var='\''$ac_val'\''" + printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then - $as_echo "## ----------- ## + printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h -$as_echo "/* confdefs.h */" > confdefs.h +printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac + ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" + +for ac_site_file in $ac_site_files do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi +as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif + +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' + +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' + +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif + +#include +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +} +' + +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' + +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" +#endif + +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' + +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' + +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" + +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" + +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" + +as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" +as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" +as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" +as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" +as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" +as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" +as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" +as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" +as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H" +as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H" +as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H" +as_fn_append ac_header_c_list " vfork.h vfork_h HAVE_VFORK_H" +as_fn_append ac_func_c_list " fork HAVE_FORK" +as_fn_append ac_func_c_list " vfork HAVE_VFORK" +as_fn_append ac_func_c_list " vprintf HAVE_VPRINTF" + +# Auxiliary files required by this configure script. +ac_aux_files="install-sh" + +# Locations in which to look for auxiliary files. +ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." + +# Search for a directory containing all of the required auxiliary files, +# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. +# If we don't find one directory that contains all the files we need, +# we report the set of missing files from the *first* directory in +# $ac_aux_dir_candidates and give up. +ac_missing_aux_files="" +ac_first_candidate=: +printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in $ac_aux_dir_candidates +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + + printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 + ac_aux_dir_found=yes + ac_install_sh= + for ac_aux in $ac_aux_files + do + # As a special case, if "install-sh" is required, that requirement + # can be satisfied by any of "install-sh", "install.sh", or "shtool", + # and $ac_install_sh is set appropriately for whichever one is found. + if test x"$ac_aux" = x"install-sh" + then + if test -f "${as_dir}install-sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 + ac_install_sh="${as_dir}install-sh -c" + elif test -f "${as_dir}install.sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 + ac_install_sh="${as_dir}install.sh -c" + elif test -f "${as_dir}shtool"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 + ac_install_sh="${as_dir}shtool install -c" + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} install-sh" + else + break + fi + fi + else + if test -f "${as_dir}${ac_aux}"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" + else + break + fi + fi + fi + done + if test "$ac_aux_dir_found" = yes; then + ac_aux_dir="$as_dir" + break + fi + ac_first_candidate=false + + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +fi + + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +if test -f "${ac_aux_dir}config.guess"; then + ac_config_guess="$SHELL ${ac_aux_dir}config.guess" +fi +if test -f "${ac_aux_dir}config.sub"; then + ac_config_sub="$SHELL ${ac_aux_dir}config.sub" +fi +if test -f "$ac_aux_dir/configure"; then + ac_configure="$SHELL ${ac_aux_dir}configure" +fi + # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" case "$srcdir" in /*) ;; *) srcdir=`cd $srcdir && 'pwd'`;; esac . $srcdir/VERSION OS=`uname -s` use_defshell() { case "$defshell_path$DEFSHELL_INDEX" in "") ;; *) return 0;; esac case "$1" in *csh) # we must be desperate DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; *ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; sh|/bin/sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM defshell_path=$1 ;; esac case "$1" in /bin/*) ;; */*) defshell_path=$1;; esac } # Check whether --with-defshell was given. -if test "${with_defshell+set}" = set; then : +if test ${with_defshell+y} +then : withval=$with_defshell; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake DEFSHELL" "$LINENO" 5 ;; no) ;; *) use_defshell $with_defshell;; esac fi case "$OS" in CYGWIN*|MINGW*) use_makefile=no;; *) use_makefile=yes;; esac # Check whether --with-makefile was given. -if test "${with_makefile+set}" = set; then : +if test ${with_makefile+y} +then : withval=$with_makefile; case "${withval}" in yes|no) use_makefile=${withval};; *) as_fn_error $? "bad value ${withval} given for makefile" "$LINENO" 5 ;; esac fi use_meta=yes # Check whether --with-meta was given. -if test "${with_meta+set}" = set; then : +if test ${with_meta+y} +then : withval=$with_meta; case "${withval}" in yes|no) use_meta=${withval};; *) as_fn_error $? "bad value ${withval} given for meta" "$LINENO" 5 ;; esac fi # Check whether --with-filemon was given. -if test "${with_filemon+set}" = set; then : +if test ${with_filemon+y} +then : withval=$with_filemon; case "/${withval}" in /no) use_filemon=no;; /*trace) filemon_h=no use_filemon="${withval}";; */filemon.h) filemon_h="${withval}";; */filemon*) filemon_h="${withval}/filemon.h";; *) as_fn_error $? "bad value ${withval} given for filemon" "$LINENO" 5 ;; esac case "$use_filemon,$filemon_h" in ,*.h) use_filemon=dev;; esac -else +else $as_nop case "$OS" in NetBSD) filemon_h=no use_filemon=ktrace;; *) for d in "/usr/include/dev/filemon" "$prefix/include/dev/filemon" "$srcdir/../../sys/dev/filemon" do for x in "/$OS" "" do filemon_h="$d$x/filemon.h" test -s "$filemon_h" && break done test -s "$filemon_h" && { use_filemon=dev; break; } done ;; esac use_filemon=${use_filemon:-no} case "$use_filemon" in dev) ;; *) filemon_h=no;; esac fi case "$use_meta" in yes) case "$use_filemon" in no) ;; *) echo "Using: filemon_${use_filemon}.c" >&6;; esac ;; esac case "$OS" in Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE" test -x /usr/pkg/bin/clang && CC=${CC:-clang} ;; SCO_SV) # /bin/sh is not usable ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh" CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL" ;; esac echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6 if test -f /usr/share/zoneinfo/Europe/Berlin; then echo yes >&6 # seems a safe bet UTC_1=Europe/Berlin else utcH=`TZ=UTC date +%H 2> /dev/null` utc_1H=`TZ=UTC-1 date +%H 2> /dev/null` if test ${utcH-0} -lt ${utc_1H-0}; then UTC_1=UTC-1 echo no, using UTC-1 >&6 else echo no >&6 fi fi oldPATH=$PATH for d in /usr/gnu/bin do test -d $d || continue PATH=$PATH:$d done export PATH + + + + + + + + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +fi + + +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= -else +else $as_nop ac_file='' fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int -main () +main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done -else - $as_echo "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_compiler_gnu=yes -else +else $as_nop ac_compiler_gnu=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu + if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi -ac_test_CFLAGS=${CFLAGS+set} +ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_g=yes -else +else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : -else +else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_g=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} +$ac_c_conftest_c11_program _ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error +$ac_c_conftest_c99_program _ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC fi -rm -f conftest.err conftest.i conftest.$ac_ext - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include +$ac_c_conftest_c89_program _ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg fi -rm -f conftest.err conftest.i conftest.$ac_ext - +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP +rm -f conftest.$ac_ext +CC=$ac_save_CC fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : -else - # Broken: fails on valid input. -continue +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +ac_header= ac_cache= +for ac_item in $ac_header_c_list do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count + if test $ac_cache; then + ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" + if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then + printf "%s\n" "#define $ac_item 1" >> confdefs.h fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + ac_header= ac_cache= + elif test $ac_header; then + ac_cache=$ac_item + else + ac_header=$ac_item fi -else - ac_cv_path_GREP=$GREP -fi +done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - fi + + + +if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes +then : + +printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else + + + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if test ${ac_cv_safe_to_define___extensions__+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -#include -#include +# define __EXTENSIONS__ 1 + $ac_includes_default int -main () +main (void) { ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_safe_to_define___extensions__=yes +else $as_nop + ac_cv_safe_to_define___extensions__=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; } -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether _XOPEN_SOURCE should be defined" >&5 +printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; } +if test ${ac_cv_should_define__xopen_source+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_should_define__xopen_source=no + if test $ac_cv_header_wchar_h = yes +then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include + #include + mbstate_t x; +int +main (void) +{ + ; + return 0; +} _ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : +if ac_fn_c_try_compile "$LINENO" +then : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) + #define _XOPEN_SOURCE 500 + #include + mbstate_t x; int -main () +main (void) { - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; + + ; return 0; } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_should_define__xopen_source=yes fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi - +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5 +printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; } -$as_echo "#define STDC_HEADERS 1" >>confdefs.h + printf "%s\n" "#define _ALL_SOURCE 1" >>confdefs.h -fi + printf "%s\n" "#define _DARWIN_C_SOURCE 1" >>confdefs.h -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF + printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h -fi + printf "%s\n" "#define _HPUX_ALT_XOPEN_SOCKET_API 1" >>confdefs.h -done + printf "%s\n" "#define _NETBSD_SOURCE 1" >>confdefs.h + printf "%s\n" "#define _OPENBSD_SOURCE 1" >>confdefs.h + printf "%s\n" "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h - ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" -if test "x$ac_cv_header_minix_config_h" = xyes; then : - MINIX=yes -else - MINIX= -fi + printf "%s\n" "#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_IEC_60559_BFP_EXT__ 1" >>confdefs.h - if test "$MINIX" = yes; then + printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h -$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h -$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_LIB_EXT2__ 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_MATH_SPEC_FUNCS__ 1" >>confdefs.h -$as_echo "#define _MINIX 1" >>confdefs.h + printf "%s\n" "#define _TANDEM_SOURCE 1" >>confdefs.h - fi + if test $ac_cv_header_minix_config_h = yes +then : + MINIX=yes + printf "%s\n" "#define _MINIX 1" >>confdefs.h + printf "%s\n" "#define _POSIX_SOURCE 1" >>confdefs.h - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 -$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } -if ${ac_cv_safe_to_define___extensions__+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h -# define __EXTENSIONS__ 1 - $ac_includes_default -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_safe_to_define___extensions__=yes -else - ac_cv_safe_to_define___extensions__=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else $as_nop + MINIX= fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 -$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } - test $ac_cv_safe_to_define___extensions__ = yes && - $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h - - $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + if test $ac_cv_safe_to_define___extensions__ = yes +then : + printf "%s\n" "#define __EXTENSIONS__ 1" >>confdefs.h - $as_echo "#define _GNU_SOURCE 1" >>confdefs.h - - $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h - - $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h +fi + if test $ac_cv_should_define__xopen_source = yes +then : + printf "%s\n" "#define _XOPEN_SOURCE 500" >>confdefs.h +fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi +else + CC="$ac_cv_prog_CC" fi fi -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 -for ac_option in --version -v -V -qversion; do +for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { #ifndef __GNUC__ choke me #endif ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 -$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } -if ${ac_cv_prog_cc_c99+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c99=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include -#include - -// Check varargs macros. These examples are taken from C99 6.10.3.5. -#define debug(...) fprintf (stderr, __VA_ARGS__) -#define showlist(...) puts (#__VA_ARGS__) -#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -static void -test_varargs_macros (void) -{ - int x = 1234; - int y = 5678; - debug ("Flag"); - debug ("X = %d\n", x); - showlist (The first, second, and third items.); - report (x>y, "x is %d but y is %d", x, y); -} - -// Check long long types. -#define BIG64 18446744073709551615ull -#define BIG32 4294967295ul -#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -#if !BIG_OK - your preprocessor is broken; -#endif -#if BIG_OK -#else - your preprocessor is broken; -#endif -static long long int bignum = -9223372036854775807LL; -static unsigned long long int ubignum = BIG64; - -struct incomplete_array -{ - int datasize; - double data[]; -}; - -struct named_init { - int number; - const wchar_t *name; - double average; -}; - -typedef const char *ccp; - -static inline int -test_restrict (ccp restrict text) -{ - // See if C++-style comments work. - // Iterate through items via the restricted pointer. - // Also check for declarations in for loops. - for (unsigned int i = 0; *(text+i) != '\0'; ++i) - continue; - return 0; -} - -// Check varargs and va_copy. -static void -test_varargs (const char *format, ...) -{ - va_list args; - va_start (args, format); - va_list args_copy; - va_copy (args_copy, args); - - const char *str; - int number; - float fnumber; - - while (*format) - { - switch (*format++) - { - case 's': // string - str = va_arg (args_copy, const char *); - break; - case 'd': // int - number = va_arg (args_copy, int); - break; - case 'f': // float - fnumber = va_arg (args_copy, double); - break; - default: - break; - } - } - va_end (args_copy); - va_end (args); + return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu -int -main () -{ - - // Check bool. - _Bool success = false; +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu - // Check restrict. - if (test_restrict ("String literal") == 0) - success = true; - char *restrict newvar = "Another string"; +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - // Check varargs. - test_varargs ("s, d' f .", "string", 65, 34.234); - test_varargs_macros (); +int +main (void) +{ - // Check flexible array members. - struct incomplete_array *ia = - malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); - ia->datasize = 10; - for (int i = 0; i < ia->datasize; ++i) - ia->data[i] = i * 1.234; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - // Check named initializers. - struct named_init ni = { - .number = 34, - .name = L"Test wide string", - .average = 543.34343, - }; +int +main (void) +{ - ni.number = 58; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : - int dynamic_array[ni.number]; - dynamic_array[ni.number - 1] = 543; +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - // work around unused variable warnings - return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' - || dynamic_array[ni.number - 1] != 543); +int +main (void) +{ ; return 0; } _ACEOF -for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : + if ac_fn_c_try_compile "$LINENO" +then : ac_cv_prog_cc_c99=$ac_arg fi -rm -f core conftest.err conftest.$ac_objext +rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC - fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c99" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c99" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c99" != xno; then : +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg fi - - -ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +rm -f conftest.$ac_ext +CC=$ac_save_CC fi -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + -# Find a good install program. We prefer a C program (faster), + # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else +if test ${ac_cv_path_install+y} +then : + printf %s "(cached) " >&6 +else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + # Account for fact that we put trailing slashes in our PATH walk. +case $as_dir in #(( + ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi - if test "${ac_cv_path_install+set}" = set; then + if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' if test -x /usr/bin/getconf; then bmake_path_max=`getconf PATH_MAX / 2> /dev/null` # only a numeric response is useful test ${bmake_path_max:-0} -gt 0 2> /dev/null || bmake_path_max= fi bmake_path_max=${bmake_path_max:-1024} if test $bmake_path_max -gt 1024; then # this is all we expect bmake_path_max=1024 fi echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6 if (type cat) > /dev/null 2>&1; then : which which() { type "$@" | sed 's,[()],,g;s,^[^/][^/]*,,;q' } fi case "$CC" in /*) ;; *) for x in $CC do _cc=`which $x` break done if test -x ${_cc:-/dev/null}; then _cc_dir=`dirname $_cc` case ":$oldPATH:" in *:$_cc_dir:*) ;; *) CC=$_cc_dir/$CC;; esac fi ;; esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 -$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } -if ${ac_cv_header_sys_wait_h+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 +printf %s "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test ${ac_cv_header_sys_wait_h+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int -main () +main (void) { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_header_sys_wait_h=yes -else +else $as_nop ac_cv_header_sys_wait_h=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 -$as_echo "$ac_cv_header_sys_wait_h" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 +printf "%s\n" "$ac_cv_header_sys_wait_h" >&6; } if test $ac_cv_header_sys_wait_h = yes; then -$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h +printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do - as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 -$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } -if eval \${$as_ac_Header+:} false; then : - $as_echo_n "(cached) " >&6 -else + as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 +printf %s "checking for $ac_hdr that defines DIR... " >&6; } +if eval test \${$as_ac_Header+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include <$ac_hdr> int -main () +main (void) { if ((DIR *) 0) return 0; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : eval "$as_ac_Header=yes" -else +else $as_nop eval "$as_ac_Header=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$as_ac_Header - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Header"\" = x"yes" +then : cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF ac_header_dirent=$ac_hdr; break fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 -$as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +printf %s "checking for library containing opendir... " >&6; } +if test ${ac_cv_search_opendir+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char opendir (); int -main () +main (void) { return opendir (); ; return 0; } _ACEOF -for ac_lib in '' dir; do +for ac_lib in '' dir +do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi - if ac_fn_c_try_link "$LINENO"; then : + if ac_fn_c_try_link "$LINENO" +then : ac_cv_search_opendir=$ac_res fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : + if test ${ac_cv_search_opendir+y} +then : break fi done -if ${ac_cv_search_opendir+:} false; then : +if test ${ac_cv_search_opendir+y} +then : -else +else $as_nop ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 -$as_echo "$ac_cv_search_opendir" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +printf "%s\n" "$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir -if test "$ac_res" != no; then : +if test "$ac_res" != no +then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 -$as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +printf %s "checking for library containing opendir... " >&6; } +if test ${ac_cv_search_opendir+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char opendir (); int -main () +main (void) { return opendir (); ; return 0; } _ACEOF -for ac_lib in '' x; do +for ac_lib in '' x +do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi - if ac_fn_c_try_link "$LINENO"; then : + if ac_fn_c_try_link "$LINENO" +then : ac_cv_search_opendir=$ac_res fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : + if test ${ac_cv_search_opendir+y} +then : break fi done -if ${ac_cv_search_opendir+:} false; then : +if test ${ac_cv_search_opendir+y} +then : -else +else $as_nop ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 -$as_echo "$ac_cv_search_opendir" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +printf "%s\n" "$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir -if test "$ac_res" != no; then : +if test "$ac_res" != no +then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi fi -for ac_header in sys/param.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_param_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SYS_PARAM_H 1 -_ACEOF +ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_param_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h fi -done - -for ac_header in sys/sysctl.h -do : - ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "#ifdef HAVE_SYS_PARAM_H +ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "#ifdef HAVE_SYS_PARAM_H # include # endif " -if test "x$ac_cv_header_sys_sysctl_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SYS_SYSCTL_H 1 -_ACEOF +if test "x$ac_cv_header_sys_sysctl_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_SYSCTL_H 1" >>confdefs.h fi -done +ac_fn_c_check_header_compile "$LINENO" "ar.h" "ac_cv_header_ar_h" "$ac_includes_default" +if test "x$ac_cv_header_ar_h" = xyes +then : + printf "%s\n" "#define HAVE_AR_H 1" >>confdefs.h -for ac_header in \ - ar.h \ - err.h \ - fcntl.h \ - libgen.h \ - limits.h \ - paths.h \ - poll.h \ - ranlib.h \ - sys/mman.h \ - sys/select.h \ - sys/socket.h \ - sys/time.h \ - sys/uio.h \ - utime.h \ - -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF +fi +ac_fn_c_check_header_compile "$LINENO" "err.h" "ac_cv_header_err_h" "$ac_includes_default" +if test "x$ac_cv_header_err_h" = xyes +then : + printf "%s\n" "#define HAVE_ERR_H 1" >>confdefs.h fi +ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" +if test "x$ac_cv_header_fcntl_h" = xyes +then : + printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h -done +fi +ac_fn_c_check_header_compile "$LINENO" "libgen.h" "ac_cv_header_libgen_h" "$ac_includes_default" +if test "x$ac_cv_header_libgen_h" = xyes +then : + printf "%s\n" "#define HAVE_LIBGEN_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" +if test "x$ac_cv_header_limits_h" = xyes +then : + printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "paths.h" "ac_cv_header_paths_h" "$ac_includes_default" +if test "x$ac_cv_header_paths_h" = xyes +then : + printf "%s\n" "#define HAVE_PATHS_H 1" >>confdefs.h -ac_fn_c_check_header_mongrel "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_cdefs_h" = xyes; then : +fi +ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" +if test "x$ac_cv_header_poll_h" = xyes +then : + printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h -else - CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`" fi +ac_fn_c_check_header_compile "$LINENO" "ranlib.h" "ac_cv_header_ranlib_h" "$ac_includes_default" +if test "x$ac_cv_header_ranlib_h" = xyes +then : + printf "%s\n" "#define HAVE_RANLIB_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_mman_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_MMAN_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_select_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_socket_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5 -$as_echo_n "checking for __attribute__... " >&6; } -if ${ac_cv___attribute__+:} false; then : - $as_echo_n "(cached) " >&6 -else +fi +ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_time_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "sys/uio.h" "ac_cv_header_sys_uio_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_uio_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_UIO_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default" +if test "x$ac_cv_header_utime_h" = xyes +then : + printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h + +fi + + +ac_fn_c_check_header_compile "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_cdefs_h" = xyes +then : + +else $as_nop + CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`" +fi + + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5 +printf %s "checking for __attribute__... " >&6; } +if test ${ac_cv___attribute__+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static void foo(void) __attribute__ ((noreturn)); static void foo(void) { exit(1); } int main(int argc, char **argv) { foo(); } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : ac_cv___attribute__=yes -else +else $as_nop ac_cv___attribute__=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi if test "$ac_cv___attribute__" = "yes"; then -$as_echo "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h +printf "%s\n" "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv___attribute__" >&5 -$as_echo "$ac_cv___attribute__" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv___attribute__" >&5 +printf "%s\n" "$ac_cv___attribute__" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 -$as_echo_n "checking whether byte ordering is bigendian... " >&6; } -if ${ac_cv_c_bigendian+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +printf %s "checking whether byte ordering is bigendian... " >&6; } +if test ${ac_cv_c_bigendian+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int -main () +main (void) { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int -main () +main (void) { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_c_bigendian=yes -else +else $as_nop ac_cv_c_bigendian=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int -main () +main (void) { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int -main () +main (void) { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_c_bigendian=yes -else +else $as_nop ac_cv_c_bigendian=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. - if test "$cross_compiling" = yes; then : + if test "$cross_compiling" = yes +then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -short int ascii_mm[] = +unsigned short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; - short int ascii_ii[] = + unsigned short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } - short int ebcdic_ii[] = + unsigned short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; - short int ebcdic_mm[] = + unsigned short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int -main () +main (void) { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int -main () +main (void) { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : +if ac_fn_c_try_run "$LINENO" +then : ac_cv_c_bigendian=no -else +else $as_nop ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 -$as_echo "$ac_cv_c_bigendian" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +printf "%s\n" "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) - $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h + printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) -$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h +printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +printf %s "checking for an ANSI C-conforming const... " >&6; } +if test ${ac_cv_c_const+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { #ifndef __cplusplus /* Ultrix mips cc rejects this sort of thing. */ typedef int charset[2]; const charset cs = { 0, 0 }; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. + /* IBM XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this sort of thing. */ char tx; char *t = &tx; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; } bx; struct s *b = &bx; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_c_const=yes -else +else $as_nop ac_cv_c_const=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +printf "%s\n" "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then -$as_echo "#define const /**/" >>confdefs.h +printf "%s\n" "#define const /**/" >>confdefs.h fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +printf %s "checking for inline... " >&6; } +if test ${ac_cv_c_inline+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } +static $ac_kw foo_t static_foo (void) {return 0; } +$ac_kw foo_t foo (void) {return 0; } #endif _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_c_inline=$ac_kw fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" case $ac_cv_c_int64_t in #( no|yes) ;; #( *) -cat >>confdefs.h <<_ACEOF -#define int64_t $ac_cv_c_int64_t -_ACEOF +printf "%s\n" "#define int64_t $ac_cv_c_int64_t" >>confdefs.h ;; esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 -$as_echo_n "checking for unsigned long long int... " >&6; } -if ${ac_cv_type_unsigned_long_long_int+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 +printf %s "checking for unsigned long long int... " >&6; } +if test ${ac_cv_type_unsigned_long_long_int+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_cv_type_unsigned_long_long_int=yes - if test "x${ac_cv_prog_cc_c99-no}" = xno; then - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + case $ac_prog_cc_stdc in + no | c89) ;; + *) + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* For now, do not test the preprocessor; as of 2007 there are too many implementations with broken preprocessors. Perhaps this can be revisited in 2012. In the meantime, code should not expect #if to work with literals wider than 32 bits. */ /* Test literals. */ long long int ll = 9223372036854775807ll; long long int nll = -9223372036854775807LL; unsigned long long int ull = 18446744073709551615ULL; /* Test constant expressions. */ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) ? 1 : -1)]; typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 ? 1 : -1)]; int i = 63; int -main () +main (void) { /* Test availability of runtime routines for shift and division. */ long long int llmax = 9223372036854775807ll; unsigned long long int ullmax = 18446744073709551615ull; return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) | (llmax / ll) | (llmax % ll) | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) | (ullmax / ull) | (ullmax % ull)); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : -else +else $as_nop ac_cv_type_unsigned_long_long_int=no fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext;; + esac fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 -$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 +printf "%s\n" "$ac_cv_type_unsigned_long_long_int" >&6; } if test $ac_cv_type_unsigned_long_long_int = yes; then -$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h +printf "%s\n" "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5 -$as_echo_n "checking for long long int... " >&6; } -if ${ac_cv_type_long_long_int+:} false; then : - $as_echo_n "(cached) " >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5 +printf %s "checking for long long int... " >&6; } +if test ${ac_cv_type_long_long_int+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_cv_type_long_long_int=yes - if test "x${ac_cv_prog_cc_c99-no}" = xno; then - ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int - if test $ac_cv_type_long_long_int = yes; then - if test "$cross_compiling" = yes; then : + case $ac_prog_cc_stdc in + no | c89) ;; + *) + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + if test "$cross_compiling" = yes +then : : -else +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include - #ifndef LLONG_MAX - # define HALF \ - (1LL << (sizeof (long long int) * CHAR_BIT - 2)) - # define LLONG_MAX (HALF - 1 + HALF) - #endif + #ifndef LLONG_MAX + # define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + # define LLONG_MAX (HALF - 1 + HALF) + #endif int -main () +main (void) { long long int n = 1; - int i; - for (i = 0; ; i++) - { - long long int m = n << i; - if (m >> i != n) - return 1; - if (LLONG_MAX / 2 < m) - break; - } - return 0; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0; ; return 0; } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : +if ac_fn_c_try_run "$LINENO" +then : -else +else $as_nop ac_cv_type_long_long_int=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi - fi - fi + fi;; + esac fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 -$as_echo "$ac_cv_type_long_long_int" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 +printf "%s\n" "$ac_cv_type_long_long_int" >&6; } if test $ac_cv_type_long_long_int = yes; then -$as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h +printf "%s\n" "#define HAVE_LONG_LONG_INT 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" -if test "x$ac_cv_type_mode_t" = xyes; then : +if test "x$ac_cv_type_mode_t" = xyes +then : -else +else $as_nop -cat >>confdefs.h <<_ACEOF -#define mode_t int -_ACEOF +printf "%s\n" "#define mode_t int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" -if test "x$ac_cv_type_off_t" = xyes; then : +if test "x$ac_cv_type_off_t" = xyes +then : -else +else $as_nop -cat >>confdefs.h <<_ACEOF -#define off_t long int -_ACEOF +printf "%s\n" "#define off_t long int" >>confdefs.h fi -ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" -if test "x$ac_cv_type_pid_t" = xyes; then : -else + ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default +" +if test "x$ac_cv_type_pid_t" = xyes +then : + +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #if defined _WIN64 && !defined __CYGWIN__ + LLP64 + #endif + +int +main (void) +{ + + ; + return 0; +} -cat >>confdefs.h <<_ACEOF -#define pid_t int _ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_pid_type='int' +else $as_nop + ac_pid_type='__int64' +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h + fi + ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : +if test "x$ac_cv_type_size_t" = xyes +then : -else +else $as_nop -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF +printf "%s\n" "#define size_t unsigned int" >>confdefs.h fi ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) -$as_echo "#define _UINT32_T 1" >>confdefs.h +printf "%s\n" "#define _UINT32_T 1" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define uint32_t $ac_cv_c_uint32_t -_ACEOF +printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h ;; esac -ac_fn_c_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include -/* NetBSD declares sys_siglist in unistd.h. */ -#ifdef HAVE_UNISTD_H -# include -#endif - -" -if test "x$ac_cv_have_decl_sys_siglist" = xyes; then : - ac_have_decl=1 -else - ac_have_decl=0 -fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 +printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } +if test ${ac_cv_c_undeclared_builtin_options+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_CFLAGS=$CFLAGS + ac_cv_c_undeclared_builtin_options='cannot detect' + for ac_arg in '' -fno-builtin; do + CFLAGS="$ac_save_CFLAGS $ac_arg" + # This test program should *not* compile successfully. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -cat >>confdefs.h <<_ACEOF -#define HAVE_DECL_SYS_SIGLIST $ac_have_decl +int +main (void) +{ +(void) strchr; + ; + return 0; +} _ACEOF - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 -$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } -if ${ac_cv_header_time+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + # This test program should compile successfully. + # No library function is consistently available on + # freestanding implementations, so test against a dummy + # declaration. Include always-available headers on the + # off chance that they somehow elicit warnings. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -#include +#include +#include +#include +#include +extern void ac_decl (int, char *); int -main () +main (void) { -if ((struct tm *) 0) -return 0; +(void) ac_decl (0, (char *) 0); + (void) ac_decl; + ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_time=yes -else - ac_cv_header_time=no +if ac_fn_c_try_compile "$LINENO" +then : + if test x"$ac_arg" = x +then : + ac_cv_c_undeclared_builtin_options='none needed' +else $as_nop + ac_cv_c_undeclared_builtin_options=$ac_arg +fi + break fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 -$as_echo "$ac_cv_header_time" >&6; } -if test $ac_cv_header_time = yes; then +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + done + CFLAGS=$ac_save_CFLAGS + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 +printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } + case $ac_cv_c_undeclared_builtin_options in #( + 'cannot detect') : + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot make $CC report undeclared builtins +See \`config.log' for more details" "$LINENO" 5; } ;; #( + 'none needed') : + ac_c_undeclared_builtin_options='' ;; #( + *) : + ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; +esac -$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h +ac_fn_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +# include +#endif +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_sys_siglist" = xyes +then : + ac_have_decl=1 +else $as_nop + ac_have_decl=0 fi +printf "%s\n" "#define HAVE_DECL_SYS_SIGLIST $ac_have_decl" >>confdefs.h -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 -$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } -if ${ac_cv_struct_tm+:} false; then : - $as_echo_n "(cached) " >&6 -else + + + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 +printf %s "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if test ${ac_cv_struct_tm+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int -main () +main (void) { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_struct_tm=time.h -else +else $as_nop ac_cv_struct_tm=sys/time.h fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 -$as_echo "$ac_cv_struct_tm" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 +printf "%s\n" "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then -$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h +printf "%s\n" "#define TM_IN_SYS_TIME 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" " #ifdef HAVE_SYS_TYPES_H #include #endif #include " -if test "x$ac_cv_type_sig_atomic_t" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_SIG_ATOMIC_T 1 -_ACEOF - - -fi +if test "x$ac_cv_type_sig_atomic_t" = xyes +then : +printf "%s\n" "#define HAVE_SIG_ATOMIC_T 1" >>confdefs.h -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 -$as_echo_n "checking return type of signal handlers... " >&6; } -if ${ac_cv_type_signal+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -int -main () -{ -return *(signal (0, 0)) (0) == 1; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_type_signal=int -else - ac_cv_type_signal=void fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 -$as_echo "$ac_cv_type_signal" >&6; } - -cat >>confdefs.h <<_ACEOF -#define RETSIGTYPE $ac_cv_type_signal -_ACEOF - -for ac_header in vfork.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" -if test "x$ac_cv_header_vfork_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_VFORK_H 1 -_ACEOF -fi +ac_func= +for ac_item in $ac_func_c_list +do + if test $ac_func; then + ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func + if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then + echo "#define $ac_item 1" >> confdefs.h + fi + ac_func= + else + ac_func=$ac_item + fi done -for ac_func in fork vfork -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF -fi -done if test "x$ac_cv_func_fork" = xyes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 -$as_echo_n "checking for working fork... " >&6; } -if ${ac_cv_func_fork_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +printf %s "checking for working fork... " >&6; } +if test ${ac_cv_func_fork_works+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "$cross_compiling" = yes +then : ac_cv_func_fork_works=cross -else +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int -main () +main (void) { /* By Ruediger Kuhlmann. */ return fork () < 0; ; return 0; } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : +if ac_fn_c_try_run "$LINENO" +then : ac_cv_func_fork_works=yes -else +else $as_nop ac_cv_func_fork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 -$as_echo "$ac_cv_func_fork_works" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +printf "%s\n" "$ac_cv_func_fork_works" >&6; } else ac_cv_func_fork_works=$ac_cv_func_fork fi if test "x$ac_cv_func_fork_works" = xcross; then case $host in *-*-amigaos* | *-*-msdosdjgpp*) # Override, as these systems have only a dummy fork() stub ac_cv_func_fork_works=no ;; *) ac_cv_func_fork_works=yes ;; esac - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 -$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +printf "%s\n" "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} fi ac_cv_func_vfork_works=$ac_cv_func_vfork if test "x$ac_cv_func_vfork" = xyes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 -$as_echo_n "checking for working vfork... " >&6; } -if ${ac_cv_func_vfork_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +printf %s "checking for working vfork... " >&6; } +if test ${ac_cv_func_vfork_works+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "$cross_compiling" = yes +then : ac_cv_func_vfork_works=cross -else +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Thanks to Paul Eggert for this test. */ $ac_includes_default +#include #include #ifdef HAVE_VFORK_H # include #endif + +static void +do_nothing (int sig) +{ + (void) sig; +} + /* On some sparc systems, changes by the child to local and incoming argument registers are propagated back to the parent. The compiler is told about this with #include , but some compilers (e.g. gcc -O) don't grok . Test for this by using a static variable whose address is put into a register that is clobbered by the vfork. */ static void -#ifdef __cplusplus sparc_address_test (int arg) -# else -sparc_address_test (arg) int arg; -#endif { static pid_t child; if (!child) { child = vfork (); if (child < 0) { perror ("vfork"); _exit(2); } if (!child) { arg = getpid(); write(-1, "", 0); _exit (arg); } } } int -main () +main (void) { pid_t parent = getpid (); pid_t child; sparc_address_test (0); + /* On Solaris 2.4, changes by the child to the signal handler + also munge signal handlers in the parent. To detect this, + start by putting the parent's handler in a known state. */ + signal (SIGTERM, SIG_DFL); + child = vfork (); if (child == 0) { /* Here is another test for sparc vfork register problems. This test uses lots of local variables, at least as many local variables as main has allocated so far including compiler temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should reuse the register of parent for one of the local variables, since it will think that parent can't possibly be used any more in this routine. Assigning to the local variable will thus munge parent in the parent process. */ pid_t p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); /* Convince the compiler that p..p7 are live; otherwise, it might use the same hardware register for all 8 local variables. */ if (p != p1 || p != p2 || p != p3 || p != p4 || p != p5 || p != p6 || p != p7) _exit(1); + /* Alter the child's signal handler. */ + if (signal (SIGTERM, do_nothing) != SIG_DFL) + _exit(1); + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent from child file descriptors. If the child closes a descriptor before it execs or exits, this munges the parent's descriptor as well. Test for this by closing stdout in the child. */ _exit(close(fileno(stdout)) != 0); } else { int status; struct stat st; while (wait(&status) != child) ; return ( /* Was there some problem with vforking? */ child < 0 + /* Did the child munge the parent's signal handler? */ + || signal (SIGTERM, SIG_DFL) != SIG_DFL + /* Did the child fail? (This shouldn't happen.) */ || status /* Did the vfork/compiler bug occur? */ || parent != getpid() /* Did the file descriptor bug occur? */ || fstat(fileno(stdout), &st) != 0 ); } } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : +if ac_fn_c_try_run "$LINENO" +then : ac_cv_func_vfork_works=yes -else +else $as_nop ac_cv_func_vfork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 -$as_echo "$ac_cv_func_vfork_works" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +printf "%s\n" "$ac_cv_func_vfork_works" >&6; } fi; if test "x$ac_cv_func_fork_works" = xcross; then ac_cv_func_vfork_works=$ac_cv_func_vfork - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 -$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +printf "%s\n" "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} fi if test "x$ac_cv_func_vfork_works" = xyes; then -$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h +printf "%s\n" "#define HAVE_WORKING_VFORK 1" >>confdefs.h else -$as_echo "#define vfork fork" >>confdefs.h +printf "%s\n" "#define vfork fork" >>confdefs.h fi if test "x$ac_cv_func_fork_works" = xyes; then -$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h +printf "%s\n" "#define HAVE_WORKING_FORK 1" >>confdefs.h fi -for ac_func in vprintf -do : - ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" -if test "x$ac_cv_func_vprintf" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_VPRINTF 1 -_ACEOF -ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" -if test "x$ac_cv_func__doprnt" = xyes; then : -$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h +if test "x$ac_cv_func_vprintf" = xno +then : + ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" +if test "x$ac_cv_func__doprnt" = xyes +then : -fi +printf "%s\n" "#define HAVE_DOPRNT 1" >>confdefs.h fi -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wait3 that fills in rusage" >&5 -$as_echo_n "checking for wait3 that fills in rusage... " >&6; } -if ${ac_cv_func_wait3_rusage+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wait3 that fills in rusage" >&5 +printf %s "checking for wait3 that fills in rusage... " >&6; } +if test ${ac_cv_func_wait3_rusage+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "$cross_compiling" = yes +then : ac_cv_func_wait3_rusage=no -else +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default #include #include #include /* HP-UX has wait3 but does not fill in rusage at all. */ int -main () +main (void) { struct rusage r; int i; /* Use a field that we can force nonzero -- voluntary context switches. For systems like NeXT and OSF/1 that don't set it, also use the system CPU time. And page faults (I/O) for Linux. */ r.ru_nvcsw = 0; r.ru_stime.tv_sec = 0; r.ru_stime.tv_usec = 0; r.ru_majflt = r.ru_minflt = 0; switch (fork ()) { case 0: /* Child. */ sleep(1); /* Give up the CPU. */ _exit(0); break; case -1: /* What can we do? */ _exit(0); break; default: /* Parent. */ wait3(&i, 0, &r); /* Avoid "text file busy" from rm on fast HP-UX machines. */ sleep(2); return (r.ru_nvcsw == 0 && r.ru_majflt == 0 && r.ru_minflt == 0 && r.ru_stime.tv_sec == 0 && r.ru_stime.tv_usec == 0); } } _ACEOF -if ac_fn_c_try_run "$LINENO"; then : +if ac_fn_c_try_run "$LINENO" +then : ac_cv_func_wait3_rusage=yes -else +else $as_nop ac_cv_func_wait3_rusage=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_wait3_rusage" >&5 -$as_echo "$ac_cv_func_wait3_rusage" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_wait3_rusage" >&5 +printf "%s\n" "$ac_cv_func_wait3_rusage" >&6; } if test $ac_cv_func_wait3_rusage = yes; then -$as_echo "#define HAVE_WAIT3 1" >>confdefs.h - -fi - -for ac_func in \ - err \ - errx \ - getcwd \ - getenv \ - getwd \ - killpg \ - mmap \ - putenv \ - select \ - setenv \ - setpgid \ - setrlimit \ - setsid \ - sigaddset \ - sigpending \ - sigprocmask \ - sigsetmask \ - sigsuspend \ - sigvec \ - snprintf \ - strerror \ - stresep \ - strftime \ - strsep \ - strtod \ - strtol \ - strtoll \ - strtoul \ - sysctl \ - unsetenv \ - vsnprintf \ - wait3 \ - wait4 \ - waitpid \ - warn \ - warnx \ - -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +printf "%s\n" "#define HAVE_WAIT3 1" >>confdefs.h + +fi + +ac_fn_c_check_func "$LINENO" "err" "ac_cv_func_err" +if test "x$ac_cv_func_err" = xyes +then : + printf "%s\n" "#define HAVE_ERR 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "errx" "ac_cv_func_errx" +if test "x$ac_cv_func_errx" = xyes +then : + printf "%s\n" "#define HAVE_ERRX 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" +if test "x$ac_cv_func_getcwd" = xyes +then : + printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "getenv" "ac_cv_func_getenv" +if test "x$ac_cv_func_getenv" = xyes +then : + printf "%s\n" "#define HAVE_GETENV 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "getwd" "ac_cv_func_getwd" +if test "x$ac_cv_func_getwd" = xyes +then : + printf "%s\n" "#define HAVE_GETWD 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "killpg" "ac_cv_func_killpg" +if test "x$ac_cv_func_killpg" = xyes +then : + printf "%s\n" "#define HAVE_KILLPG 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "mmap" "ac_cv_func_mmap" +if test "x$ac_cv_func_mmap" = xyes +then : + printf "%s\n" "#define HAVE_MMAP 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "putenv" "ac_cv_func_putenv" +if test "x$ac_cv_func_putenv" = xyes +then : + printf "%s\n" "#define HAVE_PUTENV 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select" +if test "x$ac_cv_func_select" = xyes +then : + printf "%s\n" "#define HAVE_SELECT 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" +if test "x$ac_cv_func_setenv" = xyes +then : + printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "setpgid" "ac_cv_func_setpgid" +if test "x$ac_cv_func_setpgid" = xyes +then : + printf "%s\n" "#define HAVE_SETPGID 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "setrlimit" "ac_cv_func_setrlimit" +if test "x$ac_cv_func_setrlimit" = xyes +then : + printf "%s\n" "#define HAVE_SETRLIMIT 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "setsid" "ac_cv_func_setsid" +if test "x$ac_cv_func_setsid" = xyes +then : + printf "%s\n" "#define HAVE_SETSID 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigaddset" "ac_cv_func_sigaddset" +if test "x$ac_cv_func_sigaddset" = xyes +then : + printf "%s\n" "#define HAVE_SIGADDSET 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigpending" "ac_cv_func_sigpending" +if test "x$ac_cv_func_sigpending" = xyes +then : + printf "%s\n" "#define HAVE_SIGPENDING 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigprocmask" "ac_cv_func_sigprocmask" +if test "x$ac_cv_func_sigprocmask" = xyes +then : + printf "%s\n" "#define HAVE_SIGPROCMASK 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigsetmask" "ac_cv_func_sigsetmask" +if test "x$ac_cv_func_sigsetmask" = xyes +then : + printf "%s\n" "#define HAVE_SIGSETMASK 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigsuspend" "ac_cv_func_sigsuspend" +if test "x$ac_cv_func_sigsuspend" = xyes +then : + printf "%s\n" "#define HAVE_SIGSUSPEND 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sigvec" "ac_cv_func_sigvec" +if test "x$ac_cv_func_sigvec" = xyes +then : + printf "%s\n" "#define HAVE_SIGVEC 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" +if test "x$ac_cv_func_snprintf" = xyes +then : + printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" +if test "x$ac_cv_func_strerror" = xyes +then : + printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "stresep" "ac_cv_func_stresep" +if test "x$ac_cv_func_stresep" = xyes +then : + printf "%s\n" "#define HAVE_STRESEP 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" +if test "x$ac_cv_func_strftime" = xyes +then : + printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep" +if test "x$ac_cv_func_strsep" = xyes +then : + printf "%s\n" "#define HAVE_STRSEP 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strtod" "ac_cv_func_strtod" +if test "x$ac_cv_func_strtod" = xyes +then : + printf "%s\n" "#define HAVE_STRTOD 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strtol" "ac_cv_func_strtol" +if test "x$ac_cv_func_strtol" = xyes +then : + printf "%s\n" "#define HAVE_STRTOL 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strtoll" "ac_cv_func_strtoll" +if test "x$ac_cv_func_strtoll" = xyes +then : + printf "%s\n" "#define HAVE_STRTOLL 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "strtoul" "ac_cv_func_strtoul" +if test "x$ac_cv_func_strtoul" = xyes +then : + printf "%s\n" "#define HAVE_STRTOUL 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "sysctl" "ac_cv_func_sysctl" +if test "x$ac_cv_func_sysctl" = xyes +then : + printf "%s\n" "#define HAVE_SYSCTL 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv" +if test "x$ac_cv_func_unsetenv" = xyes +then : + printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" +if test "x$ac_cv_func_vsnprintf" = xyes +then : + printf "%s\n" "#define HAVE_VSNPRINTF 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "wait3" "ac_cv_func_wait3" +if test "x$ac_cv_func_wait3" = xyes +then : + printf "%s\n" "#define HAVE_WAIT3 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "wait4" "ac_cv_func_wait4" +if test "x$ac_cv_func_wait4" = xyes +then : + printf "%s\n" "#define HAVE_WAIT4 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "waitpid" "ac_cv_func_waitpid" +if test "x$ac_cv_func_waitpid" = xyes +then : + printf "%s\n" "#define HAVE_WAITPID 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "warn" "ac_cv_func_warn" +if test "x$ac_cv_func_warn" = xyes +then : + printf "%s\n" "#define HAVE_WARN 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "warnx" "ac_cv_func_warnx" +if test "x$ac_cv_func_warnx" = xyes +then : + printf "%s\n" "#define HAVE_WARNX 1" >>confdefs.h fi -done ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt" -if test "x$ac_cv_func_getopt" = xyes; then : - $as_echo "#define HAVE_GETOPT 1" >>confdefs.h +if test "x$ac_cv_func_getopt" = xyes +then : + printf "%s\n" "#define HAVE_GETOPT 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" getopt.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getopt.$ac_objext" ;; esac fi - ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" -if test "x$ac_cv_func_realpath" = xyes; then : - $as_echo "#define HAVE_REALPATH 1" >>confdefs.h +if test "x$ac_cv_func_realpath" = xyes +then : + printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" realpath.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS realpath.$ac_objext" ;; esac fi - ac_fn_c_check_func "$LINENO" "dirname" "ac_cv_func_dirname" -if test "x$ac_cv_func_dirname" = xyes; then : - $as_echo "#define HAVE_DIRNAME 1" >>confdefs.h +if test "x$ac_cv_func_dirname" = xyes +then : + printf "%s\n" "#define HAVE_DIRNAME 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" dirname.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS dirname.$ac_objext" ;; esac fi - ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction" -if test "x$ac_cv_func_sigaction" = xyes; then : - $as_echo "#define HAVE_SIGACTION 1" >>confdefs.h +if test "x$ac_cv_func_sigaction" = xyes +then : + printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" sigaction.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS sigaction.$ac_objext" ;; esac fi - ac_fn_c_check_func "$LINENO" "stresep" "ac_cv_func_stresep" -if test "x$ac_cv_func_stresep" = xyes; then : - $as_echo "#define HAVE_STRESEP 1" >>confdefs.h +if test "x$ac_cv_func_stresep" = xyes +then : + printf "%s\n" "#define HAVE_STRESEP 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" stresep.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS stresep.$ac_objext" ;; esac fi - ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" -if test "x$ac_cv_func_strlcpy" = xyes; then : - $as_echo "#define HAVE_STRLCPY 1" >>confdefs.h +if test "x$ac_cv_func_strlcpy" = xyes +then : + printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h -else +else $as_nop case " $LIBOBJS " in *" strlcpy.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strlcpy.$ac_objext" ;; esac fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for emalloc in -lutil" >&5 -$as_echo_n "checking for emalloc in -lutil... " >&6; } -if ${ac_cv_lib_util_emalloc+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for emalloc in -lutil" >&5 +printf %s "checking for emalloc in -lutil... " >&6; } +if test ${ac_cv_lib_util_emalloc+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char emalloc (); int -main () +main (void) { return emalloc (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_util_emalloc=yes -else +else $as_nop ac_cv_lib_util_emalloc=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_emalloc" >&5 -$as_echo "$ac_cv_lib_util_emalloc" >&6; } -if test "x$ac_cv_lib_util_emalloc" = xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for erealloc in -lutil" >&5 -$as_echo_n "checking for erealloc in -lutil... " >&6; } -if ${ac_cv_lib_util_erealloc+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_emalloc" >&5 +printf "%s\n" "$ac_cv_lib_util_emalloc" >&6; } +if test "x$ac_cv_lib_util_emalloc" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for erealloc in -lutil" >&5 +printf %s "checking for erealloc in -lutil... " >&6; } +if test ${ac_cv_lib_util_erealloc+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char erealloc (); int -main () +main (void) { return erealloc (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_util_erealloc=yes -else +else $as_nop ac_cv_lib_util_erealloc=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_erealloc" >&5 -$as_echo "$ac_cv_lib_util_erealloc" >&6; } -if test "x$ac_cv_lib_util_erealloc" = xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrdup in -lutil" >&5 -$as_echo_n "checking for estrdup in -lutil... " >&6; } -if ${ac_cv_lib_util_estrdup+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_erealloc" >&5 +printf "%s\n" "$ac_cv_lib_util_erealloc" >&6; } +if test "x$ac_cv_lib_util_erealloc" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for estrdup in -lutil" >&5 +printf %s "checking for estrdup in -lutil... " >&6; } +if test ${ac_cv_lib_util_estrdup+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char estrdup (); int -main () +main (void) { return estrdup (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_util_estrdup=yes -else +else $as_nop ac_cv_lib_util_estrdup=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrdup" >&5 -$as_echo "$ac_cv_lib_util_estrdup" >&6; } -if test "x$ac_cv_lib_util_estrdup" = xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrndup in -lutil" >&5 -$as_echo_n "checking for estrndup in -lutil... " >&6; } -if ${ac_cv_lib_util_estrndup+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrdup" >&5 +printf "%s\n" "$ac_cv_lib_util_estrdup" >&6; } +if test "x$ac_cv_lib_util_estrdup" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for estrndup in -lutil" >&5 +printf %s "checking for estrndup in -lutil... " >&6; } +if test ${ac_cv_lib_util_estrndup+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char estrndup (); int -main () +main (void) { return estrndup (); ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO"; then : +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_util_estrndup=yes -else +else $as_nop ac_cv_lib_util_estrndup=no fi -rm -f core conftest.err conftest.$ac_objext \ +rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrndup" >&5 -$as_echo "$ac_cv_lib_util_estrndup" >&6; } -if test "x$ac_cv_lib_util_estrndup" = xyes; then : +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrndup" >&5 +printf "%s\n" "$ac_cv_lib_util_estrndup" >&6; } +if test "x$ac_cv_lib_util_estrndup" = xyes +then : LIBS="$LIBS -lutil" CPPFLAGS="$CPPFLAGS -DUSE_EMALLOC" fi fi fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5 -$as_echo_n "checking whether stat file-mode macros are broken... " >&6; } -if ${ac_cv_header_stat_broken+:} false; then : - $as_echo_n "(cached) " >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5 +printf %s "checking whether stat file-mode macros are broken... " >&6; } +if test ${ac_cv_header_stat_broken+y} +then : + printf %s "(cached) " >&6 +else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if defined S_ISBLK && defined S_IFDIR extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1]; #endif #if defined S_ISBLK && defined S_IFCHR extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1]; #endif #if defined S_ISLNK && defined S_IFREG extern char c3[S_ISLNK (S_IFREG) ? -1 : 1]; #endif #if defined S_ISSOCK && defined S_IFREG extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1]; #endif _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_header_stat_broken=no -else +else $as_nop ac_cv_header_stat_broken=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5 -$as_echo "$ac_cv_header_stat_broken" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5 +printf "%s\n" "$ac_cv_header_stat_broken" >&6; } if test $ac_cv_header_stat_broken = yes; then -$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h - -fi - -ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "$ac_includes_default" -if test "x$ac_cv_member_struct_stat_st_rdev" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_STRUCT_STAT_ST_RDEV 1 -_ACEOF - - -$as_echo "#define HAVE_ST_RDEV 1" >>confdefs.h +printf "%s\n" "#define STAT_MACROS_BROKEN 1" >>confdefs.h fi - echo "checking if compiler supports __func__" >&6 ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { const char *func = __func__; ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_compile "$LINENO" +then : -else +else $as_nop -$as_echo "#define __func__ __FUNCTION__" >>confdefs.h +printf "%s\n" "#define __func__ __FUNCTION__" >>confdefs.h fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -x /usr/gnu/bin/diff; then diff=/usr/gnu/bin/diff diff_u=-u else diff=${diff:-diff} echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6 if $diff -u /dev/null /dev/null > /dev/null 2>&1; then diff_u=-u echo yes >&6 else diff_u= echo no >&6 fi fi echo "checking for MACHINE & MACHINE_ARCH..." >&6 cat > conftest.$ac_ext < #ifdef MACHINE machine=MACHINE #endif #ifdef MACHINE_ARCH machine_arch=MACHINE_ARCH #endif EOF default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 | - egrep machine= | tr -d ' "'` + grep machine= | tr -d ' "'` rm -rf conftest* if test "$default_machine"; then eval "$default_machine" fi machine=${machine:-`$srcdir/machine.sh`} machine_arch=${machine_arch:-`$srcdir/machine.sh arch`} echo "defaults: MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 # Check whether --with-machine was given. -if test "${with_machine+set}" = set; then : +if test ${with_machine+y} +then : withval=$with_machine; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake MACHINE" "$LINENO" 5 ;; no) ;; generic) machine=`$srcdir/machine.sh`;; *) machine=$with_machine;; esac fi force_machine= # Check whether --with-force_machine was given. -if test "${with_force_machine+set}" = set; then : +if test ${with_force_machine+y} +then : withval=$with_force_machine; case "${withval}" in yes) force_machine=FORCE_;; no) ;; *) force_machine=FORCE_; machine=$with_force_machine;; esac fi force_machine_arch= # Check whether --with-force_machine_arch was given. -if test "${with_force_machine_arch+set}" = set; then : +if test ${with_force_machine_arch+y} +then : withval=$with_force_machine_arch; case "${withval}" in yes) force_machine_arch=FORCE_;; no) ;; *) force_machine_arch=FORCE_; machine_arch=$with_force_machine_arch;; esac fi # Check whether --with-machine_arch was given. -if test "${with_machine_arch+set}" = set; then : +if test ${with_machine_arch+y} +then : withval=$with_machine_arch; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake MACHINE_ARCH" "$LINENO" 5 ;; no) ;; *) machine_arch=$with_machine_arch;; esac fi echo "Using: ${force_machine}MACHINE=$machine, ${force_machine_arch}MACHINE_ARCH=$machine_arch" 1>&6 default_sys_path=\${prefix}/share/mk # Check whether --with-default-sys-path was given. -if test "${with_default_sys_path+set}" = set; then : +if test ${with_default_sys_path+y} +then : withval=$with_default_sys_path; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake _PATH_DEFSYSPATH" "$LINENO" 5 ;; no) ;; *) default_sys_path="$with_default_sys_path" ;; esac fi # Check whether --with-path-objdirprefix was given. -if test "${with_path_objdirprefix+set}" = set; then : +if test ${with_path_objdirprefix+y} +then : withval=$with_path_objdirprefix; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" "$LINENO" 5 ;; no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;; *) CPPFLAGS="$CPPFLAGS \"-D_PATH_OBJDIRPREFIX=\\\"$with_path-objdir\\\"\"" ;; esac fi # Check whether --enable-pwd-override was given. -if test "${enable_pwd_override+set}" = set; then : +if test ${enable_pwd_override+y} +then : enableval=$enable_pwd_override; case "${enableval}" in yes) ;; no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;; *) as_fn_error $? "bad value ${enableval} given for pwd-override option" "$LINENO" 5 ;; esac fi # Check whether --enable-check-make-chdir was given. -if test "${enable_check_make_chdir+set}" = set; then : +if test ${enable_check_make_chdir+y} +then : enableval=$enable_check_make_chdir; case "${enableval}" in yes) ;; no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;; *) as_fn_error $? "bad value ${enableval} given for check-make-chdir option" "$LINENO" 5 ;; esac fi # Check whether --with-mksrc was given. -if test "${with_mksrc+set}" = set; then : +if test ${with_mksrc+y} +then : withval=$with_mksrc; case "${withval}" in ""|yes|no) ;; *) test -s $withval/install-mk && mksrc=$withval || as_fn_error $? "bad value ${withval} given for mksrc cannot find install-mk" "$LINENO" 5 ;; esac fi srcdir=`cd $srcdir && pwd` for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk do test -s $mksrc/install-mk || continue mksrc=`cd $mksrc && pwd` break done mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` echo "Using: MKSRC=$mksrc" 1>&6 for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS do test -x $sh || continue use_defshell $sh break done case "$defshell_path$DEFSHELL_INDEX" in "") ;; *DEFSHELL_INDEX_CUSTOM) echo "Using: SHELL=$defshell_path" >&6 -cat >>confdefs.h <<_ACEOF -#define DEFSHELL_CUSTOM "$defshell_path" -_ACEOF +printf "%s\n" "#define DEFSHELL_CUSTOM \"$defshell_path\"" >>confdefs.h ;; /*INDEX*) echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6 -cat >>confdefs.h <<_ACEOF -#define DEFSHELL_INDEX $DEFSHELL_INDEX -_ACEOF +printf "%s\n" "#define DEFSHELL_INDEX $DEFSHELL_INDEX" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define DEFSHELL_PATH "$defshell_path" -_ACEOF +printf "%s\n" "#define DEFSHELL_PATH \"$defshell_path\"" >>confdefs.h ;; *) echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6 -cat >>confdefs.h <<_ACEOF -#define DEFSHELL_INDEX $DEFSHELL_INDEX -_ACEOF +printf "%s\n" "#define DEFSHELL_INDEX $DEFSHELL_INDEX" >>confdefs.h ;; esac +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) egrep=egrep;; +*) egrep='grep -E';; +esac + bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh" if test $use_makefile = yes; then bm_outfiles="makefile $bm_outfiles" fi here=`'pwd'` : srcdir=$srcdir : here= $here case "$here" in $srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place obj=`basename $here` mkdir -p $srcdir/unit-tests/$obj test -d unit-tests || ln -s ../unit-tests/$obj unit-tests ;; esac ac_config_files="$ac_config_files $bm_outfiles" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else +else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then +if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $2" >&2 + printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error + # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset + # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : eval 'as_fn_append () { eval $1+=\$2 }' -else +else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : eval 'as_fn_arith () { as_val=$(( $* )) }' -else +else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | +printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bmake $as_me 20211020, which was -generated by GNU Autoconf 2.69. Invocation command line was +This file was extended by bmake $as_me 20220909, which was +generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -bmake config.status 20211020 -configured by $0, generated by GNU Autoconf 2.69, +bmake config.status 20220909 +configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; + printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; + printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; + printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX - $as_echo "$ac_log" + printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "$bm_outfiles") CONFIG_FILES="$CONFIG_FILES $bm_outfiles" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | + ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | +printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { - $as_echo "/* $configure_input */" \ + printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else - $as_echo "/* $configure_input */" \ + printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi cat <&6 case "$use_meta" in yes) case "$use_filemon" in no) ;; *) echo "Using: filemon_${use_filemon}.c" >&6;; esac ;; esac dnl dnl Check for OS problems dnl dnl Minix 3 at least has bugs in headers where _NETBSD_SOURCE dnl is needed for compilation case "$OS" in Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE" test -x /usr/pkg/bin/clang && CC=${CC:-clang} ;; SCO_SV) # /bin/sh is not usable ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh" CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL" ;; esac dnl dnl Not everyone groks TZ=Europe/Berlin dnl which is used by the localtime tests echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6 if test -f /usr/share/zoneinfo/Europe/Berlin; then echo yes >&6 # seems a safe bet UTC_1=Europe/Berlin else utcH=`TZ=UTC date +%H 2> /dev/null` utc_1H=`TZ=UTC-1 date +%H 2> /dev/null` if test ${utcH-0} -lt ${utc_1H-0}; then UTC_1=UTC-1 echo no, using UTC-1 >&6 else echo no >&6 fi fi dnl dnl Add some places to look for compilers oldPATH=$PATH for d in /usr/gnu/bin do test -d $d || continue PATH=$PATH:$d done export PATH dnl Solaris's signal.h only privides sigset_t etc if one of dnl _EXTENSIONS_ _POSIX_C_SOURCE or _XOPEN_SOURCE are defined. dnl The later two seem to cause more problems than they solve so if we dnl see _EXTENSIONS_ we use it. AC_USE_SYSTEM_EXTENSIONS dnl Checks for programs. AC_PROG_CC -AC_PROG_CC_C99 -dnl AC_PROG_GCC_TRADITIONAL AC_PROG_INSTALL dnl Executable suffix - normally empty; .exe on os2. AC_SUBST(ac_exe_suffix)dnl dnl dnl Hurd refuses to define PATH_MAX or MAXPATHLEN if test -x /usr/bin/getconf; then bmake_path_max=`getconf PATH_MAX / 2> /dev/null` # only a numeric response is useful test ${bmake_path_max:-0} -gt 0 2> /dev/null || bmake_path_max= fi bmake_path_max=${bmake_path_max:-1024} if test $bmake_path_max -gt 1024; then # this is all we expect bmake_path_max=1024 fi echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6 AC_SUBST(bmake_path_max)dnl dnl -dnl AC_C_CROSS -dnl dnl if type does not work which(1) had better! dnl note we cannot rely on type returning non-zero on failure if (type cat) > /dev/null 2>&1; then : which which() { type "$@" | sed 's,[[()]],,g;s,^[[^/]][[^/]]*,,;q' } fi dnl if CC is somewhere that was not in PATH we need its full path dnl watch out for included flags! case "$CC" in /*) ;; *) for x in $CC do _cc=`which $x` break done if test -x ${_cc:-/dev/null}; then _cc_dir=`dirname $_cc` case ":$oldPATH:" in *:$_cc_dir:*) ;; *) CC=$_cc_dir/$CC;; esac fi ;; esac dnl Checks for header files. AC_HEADER_SYS_WAIT AC_HEADER_DIRENT dnl Keep this list sorted AC_CHECK_HEADERS(sys/param.h) dnl On BSDi at least we really need sys/param.h for sys/sysctl.h AC_CHECK_HEADERS([sys/sysctl.h], [], [], [#ifdef HAVE_SYS_PARAM_H # include # endif ]) AC_CHECK_HEADERS( \ ar.h \ err.h \ fcntl.h \ libgen.h \ limits.h \ paths.h \ poll.h \ ranlib.h \ sys/mman.h \ sys/select.h \ sys/socket.h \ sys/time.h \ sys/uio.h \ utime.h \ ) dnl Both *BSD and Linux have sys/cdefs.h, most do not. dnl If it is missing, we add -I${srcdir}/missing to CFLAGS AC_CHECK_HEADER(sys/cdefs.h,, CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`") dnl Checks for typedefs, structures, and compiler characteristics. AC_C___ATTRIBUTE__ AC_C_BIGENDIAN AC_C_CONST AC_C_INLINE AC_TYPE_INT64_T AC_TYPE_LONG_LONG_INT AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_UINT32_T -AC_DECL_SYS_SIGLIST -AC_HEADER_TIME +AC_CHECK_DECLS([sys_siglist],[],[],[#include +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +# include +#endif +]) + +AC_CHECK_HEADERS_ONCE([sys/time.h]) + AC_STRUCT_TM dnl we need sig_atomic_t AH_TEMPLATE([sig_atomic_t],[type that signal handlers can safely frob]) AC_CHECK_TYPES([sig_atomic_t],[],[], [ #ifdef HAVE_SYS_TYPES_H #include #endif #include ]) dnl Checks for library functions. -AC_TYPE_SIGNAL -AC_FUNC_VFORK +AC_FUNC_FORK AC_FUNC_VPRINTF AC_FUNC_WAIT3 dnl Keep this list sorted AC_CHECK_FUNCS( \ err \ errx \ getcwd \ getenv \ getwd \ killpg \ mmap \ putenv \ select \ setenv \ setpgid \ setrlimit \ setsid \ sigaddset \ sigpending \ sigprocmask \ sigsetmask \ sigsuspend \ sigvec \ snprintf \ strerror \ stresep \ strftime \ strsep \ strtod \ strtol \ strtoll \ strtoul \ sysctl \ unsetenv \ vsnprintf \ wait3 \ wait4 \ waitpid \ warn \ warnx \ ) dnl functions which we may need to provide AC_REPLACE_FUNCS( \ getopt \ realpath \ dirname \ sigaction \ stresep \ strlcpy \ ) AC_CHECK_LIB([util], [emalloc], [ AC_CHECK_LIB([util], [erealloc], [ AC_CHECK_LIB([util], [estrdup], [ AC_CHECK_LIB([util], [estrndup], [ LIBS="$LIBS -lutil" CPPFLAGS="$CPPFLAGS -DUSE_EMALLOC" ])])])]) dnl dnl Structures dnl AC_HEADER_STAT -AC_STRUCT_ST_RDEV dnl echo "checking if compiler supports __func__" >&6 AC_LANG(C) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[[const char *func = __func__;]])],, AC_DEFINE(__func__, __FUNCTION__, C99 function name)) dnl dnl we want this for unit-tests/Makefile dnl GNU diff is known to support -u if test -x /usr/gnu/bin/diff; then diff=/usr/gnu/bin/diff diff_u=-u else diff=${diff:-diff} echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6 if $diff -u /dev/null /dev/null > /dev/null 2>&1; then diff_u=-u echo yes >&6 else diff_u= echo no >&6 fi fi dnl dnl AC_* don't quite cut it. dnl echo "checking for MACHINE & MACHINE_ARCH..." >&6 cat > conftest.$ac_ext < #ifdef MACHINE machine=MACHINE #endif #ifdef MACHINE_ARCH machine_arch=MACHINE_ARCH #endif EOF default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 | - egrep machine= | tr -d ' "'` + grep machine= | tr -d ' "'` rm -rf conftest* if test "$default_machine"; then eval "$default_machine" fi machine=${machine:-`$srcdir/machine.sh`} machine_arch=${machine_arch:-`$srcdir/machine.sh arch`} echo "defaults: MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6 dnl dnl now allow overrides dnl AC_ARG_WITH(machine, [ --with-machine=MACHINE explicitly set MACHINE], [case "${withval}" in yes) AC_MSG_ERROR(bad value ${withval} given for bmake MACHINE) ;; no) ;; generic) machine=`$srcdir/machine.sh`;; *) machine=$with_machine;; esac]) force_machine= AC_ARG_WITH(force_machine, [ --with-force-machine=MACHINE set FORCE_MACHINE], [case "${withval}" in yes) force_machine=FORCE_;; no) ;; *) force_machine=FORCE_; machine=$with_force_machine;; esac]) dnl force_machine_arch= AC_ARG_WITH(force_machine_arch, [ --with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH], [case "${withval}" in yes) force_machine_arch=FORCE_;; no) ;; *) force_machine_arch=FORCE_; machine_arch=$with_force_machine_arch;; esac]) dnl AC_ARG_WITH(machine_arch, [ --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH], [case "${withval}" in yes) AC_MSG_ERROR(bad value ${withval} given for bmake MACHINE_ARCH) ;; no) ;; *) machine_arch=$with_machine_arch;; esac]) dnl dnl Tell them what we ended up with dnl echo "Using: ${force_machine}MACHINE=$machine, ${force_machine_arch}MACHINE_ARCH=$machine_arch" 1>&6 dnl dnl Allow folk to control _PATH_DEFSYSPATH dnl default_sys_path=\${prefix}/share/mk AC_ARG_WITH(default-sys-path, [ --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH MAKESYSPATH is a ':' separated list of directories that bmake will search for system .mk files. _PATH_DEFSYSPATH is its default value.], [case "${withval}" in yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_DEFSYSPATH) ;; no) ;; *) default_sys_path="$with_default_sys_path" ;; esac]) dnl dnl Some folk don't like this one dnl AC_ARG_WITH(path-objdirprefix, [ --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX], [case "${withval}" in yes) AC_MSG_ERROR(bad value ${withval} given for bmake _PATH_OBJDIRPREFIX) ;; no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;; *) CPPFLAGS="$CPPFLAGS \"-D_PATH_OBJDIRPREFIX=\\\"$with_path-objdir\\\"\"" ;; esac]) dnl dnl And this can be handy to do with out. dnl AC_ARG_ENABLE(pwd-override, [ --disable-pwd-override disable $PWD overriding getcwd()], [case "${enableval}" in yes) ;; no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;; *) AC_MSG_ERROR(bad value ${enableval} given for pwd-override option) ;; esac]) dnl dnl Just for grins dnl AC_ARG_ENABLE(check-make-chdir, [ --disable-check-make-chdir disable make trying to guess when it should automatically cd ${.CURDIR}], [case "${enableval}" in yes) ;; no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;; *) AC_MSG_ERROR(bad value ${enableval} given for check-make-chdir option) ;; esac]) dnl dnl On non-BSD systems, bootstrap won't work without mk dnl AC_ARG_WITH(mksrc, [ --with-mksrc=PATH tell makefile.boot where to find mk src], [case "${withval}" in ""|yes|no) ;; *) test -s $withval/install-mk && mksrc=$withval || AC_MSG_ERROR(bad value ${withval} given for mksrc cannot find install-mk) ;; esac ]) dnl dnl Now make sure we have a value dnl srcdir=`cd $srcdir && pwd` for mksrc in $mksrc $srcdir/mk $srcdir/../mk mk do test -s $mksrc/install-mk || continue mksrc=`cd $mksrc && pwd` break done mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` echo "Using: MKSRC=$mksrc" 1>&6 dnl On some systems we want a different default shell by default for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS do test -x $sh || continue use_defshell $sh break done case "$defshell_path$DEFSHELL_INDEX" in "") ;; *DEFSHELL_INDEX_CUSTOM) echo "Using: SHELL=$defshell_path" >&6 AC_DEFINE_UNQUOTED(DEFSHELL_CUSTOM, "$defshell_path", Path of default shell) ;; /*INDEX*) echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6 AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) AC_DEFINE_UNQUOTED(DEFSHELL_PATH, "$defshell_path", Path of default shell) ;; *) echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6 AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) ;; esac dnl +dnl Some systems have deprecated egrep in favor of grep -E +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) egrep=egrep;; +*) egrep='grep -E';; +esac +dnl +AC_SUBST(egrep) AC_SUBST(machine) AC_SUBST(force_machine) AC_SUBST(machine_arch) AC_SUBST(force_machine_arch) AC_SUBST(mksrc) AC_SUBST(default_sys_path) AC_SUBST(INSTALL) AC_SUBST(GCC) AC_SUBST(diff) AC_SUBST(diff_u) AC_SUBST(use_meta) AC_SUBST(use_filemon) AC_SUBST(filemon_h) AC_SUBST(_MAKE_VERSION) AC_SUBST(UTC_1) bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh" if test $use_makefile = yes; then bm_outfiles="makefile $bm_outfiles" fi here=`'pwd'` : srcdir=$srcdir : here= $here case "$here" in $srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place obj=`basename $here` mkdir -p $srcdir/unit-tests/$obj test -d unit-tests || ln -s ../unit-tests/$obj unit-tests ;; esac -AC_OUTPUT($bm_outfiles) +AC_CONFIG_FILES([$bm_outfiles]) +AC_OUTPUT cat < #include #include #include #include "make.h" #include "dir.h" #include "job.h" /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: dir.c,v 1.279 2022/05/07 21:19:43 rillig Exp $"); +MAKE_RCSID("$NetBSD: dir.c,v 1.280 2023/01/24 00:24:02 sjg Exp $"); /* * A search path is a list of CachedDir structures. A CachedDir has in it the * name of the directory and the names of all the files in the directory. * This is used to cut down on the number of system calls necessary to find * implicit dependents and their like. Since these searches are made before * any actions are taken, we need not worry about the directory changing due * to creation commands. If this hampers the style of some makefiles, they * must be changed. * * All previously-read directories are kept in openDirs, which is checked * first before a directory is opened. * * The need for the caching of whole directories is brought about by the * multi-level transformation code in suff.c, which tends to search for far * more files than regular make does. In the initial implementation, the * amount of time spent performing "stat" calls was truly astronomical. * The problem with caching at the start is, of course, that pmake doesn't * then detect changes to these directories during the course of the make. * Three possibilities suggest themselves: * * 1) just use stat to test for a file's existence. As mentioned above, * this is very inefficient due to the number of checks engendered by * the multi-level transformation code. * * 2) use readdir() and company to search the directories, keeping them * open between checks. I have tried this and while it didn't slow down * the process too much, it could severely affect the amount of * parallelism available as each directory open would take another file * descriptor out of play for handling I/O for another job. Given that * it is only recently (as of 1993 or earlier) that UNIX OS's have taken * to allowing more than 20 or 32 file descriptors for a process, this * doesn't seem acceptable to me. * * 3) record the mtime of the directory in the CachedDir structure and * verify the directory hasn't changed since the contents were cached. * This will catch the creation or deletion of files, but not the * updating of files. However, since it is the creation and deletion * that is the problem, this could be a good thing to do. Unfortunately, * if the directory (say ".") were fairly large and changed fairly * frequently, the constant reloading could seriously degrade * performance. It might be good in such cases to keep track of the * number of reloadings and if the number goes over a (small) limit, * resort to using stat in its place. * * An additional thing to consider is that pmake is used primarily to create * C programs and until recently (as of 1993 or earlier) pcc-based compilers * refused to allow you to specify where the resulting object file should be * placed. This forced all objects to be created in the current directory. * This isn't meant as a full excuse, just an explanation of some of the * reasons for the caching used here. * * One more note: the location of a target's file is only performed on the * downward traversal of the graph and then only for terminal nodes in the * graph. This could be construed as wrong in some cases, but prevents * inadvertent modification of files when the "installed" directory for a * file is provided in the search path. * * Another data structure maintained by this module is an mtime cache used * when the searching of cached directories fails to find a file. In the past, * Dir_FindFile would simply perform an access() call in such a case to * determine if the file could be found using just the name given. When this * hit, however, all that was gained was the knowledge that the file existed. * Given that an access() is essentially a stat() without the copyout() call, * and that the same filesystem overhead would have to be incurred in * Dir_MTime, it made sense to replace the access() with a stat() and record * the mtime in a cache for when Dir_UpdateMTime was actually called. */ /* A cache for the filenames in a directory. */ struct CachedDir { /* * 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_Var. */ char *name; /* * The number of SearchPaths that refer to this directory. * Plus the number of global variables that refer to this directory. * References from openDirs do not count though. */ int refCount; /* The number of times a file in this directory has been found. */ int hits; /* The names of the directory entries. */ HashSet files; }; typedef List CachedDirList; typedef ListNode CachedDirListNode; typedef ListNode SearchPathNode; /* A list of cached directories, with fast lookup by directory name. */ typedef struct OpenDirs { CachedDirList list; HashTable /* of CachedDirListNode */ table; } OpenDirs; SearchPath dirSearchPath = { LST_INIT }; /* main search path */ static OpenDirs openDirs; /* all cached directories */ /* * Variables for gathering statistics on the efficiency of the caching * mechanism. */ static int hits; /* Found in directory cache */ static int misses; /* Sad, but not evil misses */ static int nearmisses; /* Found under search path */ static int bigmisses; /* Sought by itself */ /* The cached contents of ".", the relative current directory. */ static CachedDir *dot = NULL; /* The cached contents of the absolute current directory. */ static CachedDir *cur = NULL; /* A fake path entry indicating we need to look for '.' last. */ static CachedDir *dotLast = NULL; /* * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to * the system to find the file, we might as well have its mtime on record. * * XXX: If this is done way early, there's a chance other rules will have * already updated the file, in which case we'll update it again. Generally, * there won't be two rules to update a single file, so this should be ok, * but... */ static HashTable mtimes; static HashTable lmtimes; /* same as mtimes but for lstat */ static void OpenDirs_Remove(OpenDirs *, const char *); static CachedDir * CachedDir_New(const char *name) { CachedDir *dir = bmake_malloc(sizeof *dir); dir->name = bmake_strdup(name); dir->refCount = 0; dir->hits = 0; HashSet_Init(&dir->files); #ifdef DEBUG_REFCNT DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name); #endif return dir; } static CachedDir * CachedDir_Ref(CachedDir *dir) { dir->refCount++; #ifdef DEBUG_REFCNT DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n", dir, dir->refCount, dir->name); #endif return dir; } static void CachedDir_Unref(CachedDir *dir) { dir->refCount--; #ifdef DEBUG_REFCNT DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n", dir, dir->refCount, dir->name); #endif if (dir->refCount > 0) return; #ifdef DEBUG_REFCNT DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); #endif OpenDirs_Remove(&openDirs, dir->name); free(dir->name); HashSet_Done(&dir->files); free(dir); } /* Update the value of the CachedDir variable, updating the reference counts. */ static void CachedDir_Assign(CachedDir **var, CachedDir *dir) { CachedDir *prev; prev = *var; *var = dir; if (dir != NULL) CachedDir_Ref(dir); if (prev != NULL) CachedDir_Unref(prev); } static void OpenDirs_Init(OpenDirs *odirs) { Lst_Init(&odirs->list); HashTable_Init(&odirs->table); } #ifdef CLEANUP static void OpenDirs_Done(OpenDirs *odirs) { CachedDirListNode *ln = odirs->list.first; DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n", odirs->table.numEntries); while (ln != NULL) { CachedDirListNode *next = ln->next; CachedDir *dir = ln->datum; DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n", dir->refCount, dir->name); CachedDir_Unref(dir); /* removes the dir from odirs->list */ ln = next; } Lst_Done(&odirs->list); HashTable_Done(&odirs->table); } #endif static CachedDir * OpenDirs_Find(OpenDirs *odirs, const char *name) { CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); return ln != NULL ? ln->datum : NULL; } static void OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir) { if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) return; Lst_Append(&odirs->list, cdir); HashTable_Set(&odirs->table, cdir->name, odirs->list.last); } static void OpenDirs_Remove(OpenDirs *odirs, const char *name) { HashEntry *he = HashTable_FindEntry(&odirs->table, name); CachedDirListNode *ln; if (he == NULL) return; ln = HashEntry_Get(he); HashTable_DeleteEntry(&odirs->table, he); Lst_Remove(&odirs->list, ln); } /* * Returns 0 and the result of stat(2) or lstat(2) in *out_cst, * or -1 on error. */ static int cached_stats(const char *pathname, struct cached_stat *out_cst, bool useLstat, bool forceRefresh) { HashTable *tbl = useLstat ? &lmtimes : &mtimes; struct stat sys_st; struct cached_stat *cst; int rc; if (pathname == NULL || pathname[0] == '\0') return -1; /* This can happen in meta mode. */ cst = HashTable_FindValue(tbl, pathname); if (cst != NULL && !forceRefresh) { *out_cst = *cst; DEBUG2(DIR, "Using cached time %s for %s\n", Targ_FmtTime(cst->cst_mtime), pathname); return 0; } rc = (useLstat ? lstat : stat)(pathname, &sys_st); if (rc == -1) return -1; /* don't cache negative lookups */ if (sys_st.st_mtime == 0) sys_st.st_mtime = 1; /* avoid confusion with missing file */ if (cst == NULL) { cst = bmake_malloc(sizeof *cst); HashTable_Set(tbl, pathname, cst); } cst->cst_mtime = sys_st.st_mtime; cst->cst_mode = sys_st.st_mode; *out_cst = *cst; DEBUG2(DIR, " Caching %s for %s\n", Targ_FmtTime(sys_st.st_mtime), pathname); return 0; } int cached_stat(const char *pathname, struct cached_stat *cst) { return cached_stats(pathname, cst, false, false); } int cached_lstat(const char *pathname, struct cached_stat *cst) { return cached_stats(pathname, cst, true, false); } /* Initialize the directories module. */ void Dir_Init(void) { OpenDirs_Init(&openDirs); HashTable_Init(&mtimes); HashTable_Init(&lmtimes); CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST")); } /* * Called by Dir_InitDir and whenever .CURDIR is assigned to. */ void Dir_InitCur(const char *newCurdir) { CachedDir *dir; if (newCurdir == NULL) return; /* * Our build directory is not the same as our source directory. * Keep this one around too. */ dir = SearchPath_Add(NULL, newCurdir); if (dir == NULL) return; CachedDir_Assign(&cur, dir); } /* * (Re)initialize "dot" (current/object directory) path hash. * Some directories may be cached. */ void Dir_InitDot(void) { CachedDir *dir; dir = SearchPath_Add(NULL, "."); if (dir == NULL) { Error("Cannot open `.' (%s)", strerror(errno)); exit(2); /* Not 1 so -q can distinguish error */ } CachedDir_Assign(&dot, dir); Dir_SetPATH(); /* initialize */ } /* Clean up the directories module. */ void Dir_End(void) { #ifdef CLEANUP CachedDir_Assign(&cur, NULL); CachedDir_Assign(&dot, NULL); CachedDir_Assign(&dotLast, NULL); SearchPath_Clear(&dirSearchPath); OpenDirs_Done(&openDirs); HashTable_Done(&mtimes); HashTable_Done(&lmtimes); #endif } /* * We want ${.PATH} to indicate the order in which we will actually * search, so we rebuild it after any .PATH: target. * This is the simplest way to deal with the effect of .DOTLAST. */ void Dir_SetPATH(void) { CachedDirListNode *ln; bool seenDotLast = false; /* true if we should search '.' last */ Global_Delete(".PATH"); if ((ln = dirSearchPath.dirs.first) != NULL) { CachedDir *dir = ln->datum; if (dir == dotLast) { seenDotLast = true; Global_Append(".PATH", dotLast->name); } } if (!seenDotLast) { if (dot != NULL) Global_Append(".PATH", dot->name); if (cur != NULL) Global_Append(".PATH", cur->name); } for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; if (dir == dot && seenDotLast) continue; Global_Append(".PATH", dir->name); } if (seenDotLast) { if (dot != NULL) Global_Append(".PATH", dot->name); if (cur != NULL) Global_Append(".PATH", cur->name); } } + +void +Dir_SetSYSPATH(void) +{ + CachedDirListNode *ln; + + Var_ReadOnly(".SYSPATH", false); + Global_Delete(".SYSPATH"); + for (ln = sysIncPath->dirs.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + Global_Append(".SYSPATH", dir->name); + } + Var_ReadOnly(".SYSPATH", true); +} + /* * See if the given name has any wildcard characters in it and all braces and * brackets are properly balanced. * * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think * that make(1) should be expanding patterns, because then you have to set a * mechanism for escaping the expansion! * * Return true if the word should be expanded, false otherwise. */ bool Dir_HasWildcards(const char *name) { const char *p; bool wild = false; int braces = 0, brackets = 0; for (p = name; *p != '\0'; p++) { switch (*p) { case '{': braces++; wild = true; break; case '}': braces--; break; case '[': brackets++; wild = true; break; case ']': brackets--; break; case '?': case '*': wild = true; break; default: break; } } return wild && brackets == 0 && braces == 0; } /* * See if any files match the pattern and add their names to the 'expansions' * list if they do. * * This is incomplete -- wildcards are only expanded in the final path * component, but not in directories like src/lib*c/file*.c, but it * will do for now (now being 1993 until at least 2020). To expand these, * delegate the work to the shell, using the '!=' variable assignment * operator, the ':sh' variable modifier or the ':!...!' variable modifier, * such as in ${:!echo src/lib*c/file*.c!}. * * Input: * pattern Pattern to look for * dir Directory to search * expansion Place to store the results */ static void DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) { const char *dirName = dir->name; bool isDot = dirName[0] == '.' && dirName[1] == '\0'; HashIter hi; /* * XXX: Iterating over all hash entries is inefficient. If the * pattern is a plain string without any wildcards, a direct lookup * is faster. */ HashIter_InitSet(&hi, &dir->files); while (HashIter_Next(&hi) != NULL) { const char *base = hi.entry->key; if (!Str_Match(base, pattern)) continue; /* * Follow the UNIX convention that dot files are only found * if the pattern begins with a dot. The pattern '.*' does * not match '.' or '..' since these are not included in the * directory cache. * * This means that the pattern '[a-z.]*' does not find * '.file', which is consistent with NetBSD sh, NetBSD ksh, * bash, dash, csh and probably many other shells as well. */ if (base[0] == '.' && pattern[0] != '.') continue; { char *fullName = isDot ? bmake_strdup(base) : str_concat3(dirName, "/", base); Lst_Append(expansions, fullName); } } } /* * Find the next closing brace in the string, taking nested braces into * account. */ static const char * closing_brace(const char *p) { int nest = 0; while (*p != '\0') { if (*p == '}' && nest == 0) break; if (*p == '{') nest++; if (*p == '}') nest--; p++; } return p; } /* * Find the next closing brace or comma in the string, taking nested braces * into account. */ static const char * separator_comma(const char *p) { int nest = 0; while (*p != '\0') { if ((*p == '}' || *p == ',') && nest == 0) break; if (*p == '{') nest++; if (*p == '}') nest--; p++; } return p; } static bool contains_wildcard(const char *p) { for (; *p != '\0'; p++) { switch (*p) { case '*': case '?': case '{': case '[': return true; } } return false; } static char * concat3(const char *a, size_t a_len, const char *b, size_t b_len, const char *c, size_t c_len) { size_t s_len = a_len + b_len + c_len; char *s = bmake_malloc(s_len + 1); memcpy(s, a, a_len); memcpy(s + a_len, b, b_len); memcpy(s + a_len + b_len, c, c_len); s[s_len] = '\0'; return s; } /* * Expand curly braces like the C shell. Brace expansion by itself is purely * textual, the expansions are not looked up in the file system. But if an * expanded word contains wildcard characters, it is expanded further, * matching only the actually existing files. * * Example: "{a{b,c}}" expands to "ab" and "ac". * Example: "{a}" expands to "a". * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist. * * Input: * word Entire word to expand * brace First curly brace in it * path Search path to use * expansions Place to store the expansions */ static void DirExpandCurly(const char *word, const char *brace, SearchPath *path, StringList *expansions) { const char *prefix, *middle, *piece, *middle_end, *suffix; size_t prefix_len, suffix_len; /* Split the word into prefix '{' middle '}' suffix. */ middle = brace + 1; middle_end = closing_brace(middle); if (*middle_end == '\0') { Error("Unterminated {} clause \"%s\"", middle); return; } prefix = word; prefix_len = (size_t)(brace - prefix); suffix = middle_end + 1; suffix_len = strlen(suffix); /* Split the middle into pieces, separated by commas. */ piece = middle; while (piece < middle_end + 1) { const char *piece_end = separator_comma(piece); size_t piece_len = (size_t)(piece_end - piece); char *file = concat3(prefix, prefix_len, piece, piece_len, suffix, suffix_len); if (contains_wildcard(file)) { SearchPath_Expand(path, file, expansions); free(file); } else { Lst_Append(expansions, file); } /* skip over the comma or closing brace */ piece = piece_end + 1; } } /* Expand the word in each of the directories from the path. */ static void DirExpandPath(const char *word, SearchPath *path, StringList *expansions) { SearchPathNode *ln; for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; DirMatchFiles(word, dir, expansions); } } static void PrintExpansions(StringList *expansions) { const char *sep = ""; StringListNode *ln; for (ln = expansions->first; ln != NULL; ln = ln->next) { const char *word = ln->datum; debug_printf("%s%s", sep, word); sep = " "; } debug_printf("\n"); } /* * The wildcard isn't in the first component. * Find all the components up to the one with the wildcard. */ static void SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, const char *wildcardComponent, StringList *expansions) { char *prefix, *dirpath, *end; SearchPath *partPath; prefix = bmake_strsedup(pattern, wildcardComponent + 1); /* * XXX: Check the "the directory is added to the path" part. * It is probably surprising that the directory before a * wildcard gets added to the path. */ /* * XXX: Only the first match of the prefix in the path is * taken, any others are ignored. The expectation may be * that the pattern is expanded in the whole path. */ dirpath = Dir_FindFile(prefix, path); free(prefix); /* * dirpath is null if can't find the leading component * * XXX: Dir_FindFile won't find internal components. i.e. if the * path contains ../Etc/Object and we're looking for Etc, it won't * be found. Ah well. Probably not important. * * XXX: Check whether the above comment is still true. */ if (dirpath == NULL) return; end = &dirpath[strlen(dirpath) - 1]; /* XXX: What about multiple trailing slashes? */ if (*end == '/') *end = '\0'; partPath = SearchPath_New(); (void)SearchPath_Add(partPath, dirpath); DirExpandPath(wildcardComponent + 1, partPath, expansions); SearchPath_Free(partPath); } /* * Expand the given pattern into a list of existing filenames by globbing it, * looking in each directory from the search path. * * Input: * path the directories in which to find the files * pattern the pattern to expand * expansions the list on which to place the results */ void SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions) { const char *brace, *slash, *wildcard, *wildcardComponent; assert(path != NULL); assert(expansions != NULL); DEBUG1(DIR, "Expanding \"%s\"... ", pattern); brace = strchr(pattern, '{'); if (brace != NULL) { DirExpandCurly(pattern, brace, path, expansions); goto done; } /* At this point, the pattern does not contain '{'. */ slash = strchr(pattern, '/'); if (slash == NULL) { /* The pattern has no directory component. */ /* First the files in dot. */ DirMatchFiles(pattern, dot, expansions); /* Then the files in every other directory on the path. */ DirExpandPath(pattern, path, expansions); goto done; } /* At this point, the pattern has a directory component. */ /* Find the first wildcard in the pattern. */ for (wildcard = pattern; *wildcard != '\0'; wildcard++) if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') break; if (*wildcard == '\0') { /* * No directory component and no wildcard at all -- this * should never happen as in such a simple case there is no * need to expand anything. */ DirExpandPath(pattern, path, expansions); goto done; } /* Back up to the start of the component containing the wildcard. */ /* XXX: This handles '///' and '/' differently. */ wildcardComponent = wildcard; while (wildcardComponent > pattern && *wildcardComponent != '/') wildcardComponent--; if (wildcardComponent == pattern) { /* The first component contains the wildcard. */ /* Start the search from the local directory */ DirExpandPath(pattern, path, expansions); } else { SearchPath_ExpandMiddle(path, pattern, wildcardComponent, expansions); } done: if (DEBUG(DIR)) PrintExpansions(expansions); } /* * Find if the file with the given name exists in the given path. * Return the freshly allocated path to the file, or NULL. */ static char * DirLookup(CachedDir *dir, const char *base) { char *file; /* the current filename to check */ DEBUG1(DIR, " %s ...\n", dir->name); if (!HashSet_Contains(&dir->files, base)) return NULL; file = str_concat3(dir->name, "/", base); DEBUG1(DIR, " returning %s\n", file); dir->hits++; hits++; return file; } /* * Find if the file with the given name exists in the given directory. * Return the freshly allocated path to the file, or NULL. */ static char * DirLookupSubdir(CachedDir *dir, const char *name) { struct cached_stat cst; char *file = dir == dot ? bmake_strdup(name) : str_concat3(dir->name, "/", name); DEBUG1(DIR, "checking %s ...\n", file); if (cached_stat(file, &cst) == 0) { nearmisses++; return file; } free(file); return NULL; } /* * Find if the file with the given name exists in the given path. * Return the freshly allocated path to the file, the empty string, or NULL. * Returning the empty string means that the search should be terminated. */ static char * DirLookupAbs(CachedDir *dir, const char *name, const char *cp) { const char *dnp; /* pointer into dir->name */ const char *np; /* pointer into name */ DEBUG1(DIR, " %s ...\n", dir->name); /* * If the file has a leading path component and that component * exactly matches the entire name of the current search * directory, we can attempt another cache lookup. And if we don't * have a hit, we can safely assume the file does not exist at all. */ for (dnp = dir->name, np = name; *dnp != '\0' && *dnp == *np; dnp++, np++) continue; if (*dnp != '\0' || np != cp - 1) return NULL; if (!HashSet_Contains(&dir->files, cp)) { DEBUG0(DIR, " must be here but isn't -- returning\n"); return bmake_strdup(""); /* to terminate the search */ } dir->hits++; hits++; DEBUG1(DIR, " returning %s\n", name); return bmake_strdup(name); } /* * Find the given file in "." or curdir. * Return the freshly allocated path to the file, or NULL. */ static char * DirFindDot(const char *name, const char *base) { if (HashSet_Contains(&dot->files, base)) { DEBUG0(DIR, " in '.'\n"); hits++; dot->hits++; return bmake_strdup(name); } if (cur != NULL && HashSet_Contains(&cur->files, base)) { DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name); hits++; cur->hits++; return str_concat3(cur->name, "/", base); } return NULL; } static bool FindFileRelative(SearchPath *path, bool seenDotLast, const char *name, char **out_file) { SearchPathNode *ln; bool checkedDot = false; char *file; DEBUG0(DIR, " Trying subdirectories...\n"); if (!seenDotLast) { if (dot != NULL) { checkedDot = true; if ((file = DirLookupSubdir(dot, name)) != NULL) goto found; } if (cur != NULL && (file = DirLookupSubdir(cur, name)) != NULL) goto found; } for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; if (dir == dot) { if (checkedDot) continue; checkedDot = true; } if ((file = DirLookupSubdir(dir, name)) != NULL) goto found; } if (seenDotLast) { if (dot != NULL && !checkedDot) { checkedDot = true; if ((file = DirLookupSubdir(dot, name)) != NULL) goto found; } if (cur != NULL && (file = DirLookupSubdir(cur, name)) != NULL) goto found; } if (checkedDot) { /* * Already checked by the given name, since . was in * the path, so no point in proceeding. */ DEBUG0(DIR, " Checked . already, returning NULL\n"); file = NULL; goto found; } return false; found: *out_file = file; return true; } static bool FindFileAbsolute(SearchPath *path, bool seenDotLast, const char *name, const char *base, char **out_file) { char *file; SearchPathNode *ln; /* * For absolute names, compare directory path prefix against * the the directory path of each member on the search path * for an exact match. If we have an exact match on any member * of the search path, use the cached contents of that member * to lookup the final file component. If that lookup fails we * can safely assume that the file does not exist at all. * This is signified by DirLookupAbs() returning an empty * string. */ DEBUG0(DIR, " Trying exact path matches...\n"); if (!seenDotLast && cur != NULL && ((file = DirLookupAbs(cur, name, base)) != NULL)) goto found; for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; if ((file = DirLookupAbs(dir, name, base)) != NULL) goto found; } if (seenDotLast && cur != NULL && ((file = DirLookupAbs(cur, name, base)) != NULL)) goto found; return false; found: if (file[0] == '\0') { free(file); file = NULL; } *out_file = file; return true; } /* * Find the file with the given name along the given search path. * * If the file is found in a directory that is not on the path * already (either 'name' is absolute or it is a relative path * [ dir1/.../dirn/file ] which exists below one of the directories * already on the search path), its directory is added to the end * of the path, on the assumption that there will be more files in * that directory later on. Sometimes this is true. Sometimes not. * * Input: * name the file to find * path the directories to search, or NULL * * Results: * The freshly allocated path to the file, or NULL. */ char * Dir_FindFile(const char *name, SearchPath *path) { char *file; /* the current filename to check */ bool seenDotLast = false; /* true if we should search dot last */ struct cached_stat cst; /* Buffer for stat, if necessary */ const char *trailing_dot = "."; const char *base = str_basename(name); DEBUG1(DIR, "Searching for %s ...", name); if (path == NULL) { DEBUG0(DIR, "couldn't open path, file not found\n"); misses++; return NULL; } if (path->dirs.first != NULL) { CachedDir *dir = path->dirs.first->datum; if (dir == dotLast) { seenDotLast = true; DEBUG0(DIR, "[dot last]..."); } } DEBUG0(DIR, "\n"); /* * If there's no leading directory components or if the leading * directory component is exactly `./', consult the cached contents * of each of the directories on the search path. */ if (base == name || (base - name == 2 && *name == '.')) { SearchPathNode *ln; /* * We look through all the directories on the path seeking one * which contains the final component of the given name. If * such a file is found, we concatenate the directory name * and the final component and return the resulting string. * If we don't find any such thing, we go on to phase two. * * No matter what, we always look for the file in the current * directory before anywhere else (unless we found the magic * DOTLAST path, in which case we search it last) and we *do * not* add the ./ to it if it exists. * This is so there are no conflicts between what the user * specifies (fish.c) and what pmake finds (./fish.c). */ if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) return file; for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; if ((file = DirLookup(dir, base)) != NULL) return file; } if (seenDotLast && (file = DirFindDot(name, base)) != NULL) return file; } /* * We didn't find the file on any directory in the search path. * If the name doesn't contain a slash, that means it doesn't exist. * If it *does* contain a slash, however, there is still hope: it * could be in a subdirectory of one of the members of the search * path. (eg. /usr/include and sys/types.h. The above search would * fail to turn up types.h in /usr/include, but it *is* in * /usr/include/sys/types.h). * [ This no longer applies: If we find such a file, we assume there * will be more (what else can we assume?) and add all but the last * component of the resulting name onto the search path (at the * end).] * This phase is only performed if the file is *not* absolute. */ if (base == name) { DEBUG0(DIR, " failed.\n"); misses++; return NULL; } if (*base == '\0') { /* we were given a trailing "/" */ base = trailing_dot; } if (name[0] != '/') { if (FindFileRelative(path, seenDotLast, name, &file)) return file; } else { if (FindFileAbsolute(path, seenDotLast, name, base, &file)) return file; } /* * Didn't find it that way, either. Sigh. Phase 3. Add its directory * onto the search path in any case, just in case, then look for the * thing in the hash table. If we find it, grand. We return a new * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. * Note that if the directory holding the file doesn't exist, this * will do an extra search of the final directory on the path. Unless * something weird happens, this search won't succeed and life will * be groovy. * * Sigh. We cannot add the directory onto the search path because * of this amusing case: * $(INSTALLDIR)/$(FILE): $(FILE) * * $(FILE) exists in $(INSTALLDIR) but not in the current one. * When searching for $(FILE), we will find it in $(INSTALLDIR) * b/c we added it here. This is not good... */ #if 0 { CachedDir *dir; char *prefix; if (base == trailing_dot) { base = strrchr(name, '/'); base++; } prefix = bmake_strsedup(name, base - 1); (void)SearchPath_Add(path, prefix); free(prefix); bigmisses++; if (path->last == NULL) return NULL; dir = path->last->datum; if (HashSet_Contains(&dir->files, base)) return bmake_strdup(name); return NULL; } #else DEBUG1(DIR, " Looking for \"%s\" ...\n", name); bigmisses++; if (cached_stat(name, &cst) == 0) { return bmake_strdup(name); } DEBUG0(DIR, " failed. Returning NULL\n"); return NULL; #endif } /* * Search for a path starting at a given directory and then working our way * up towards the root. * * Input: * here starting directory * search_path the relative path we are looking for * * Results: * The found path, or NULL. */ char * Dir_FindHereOrAbove(const char *here, const char *search_path) { struct cached_stat cst; char *dirbase, *dirbase_end; char *try, *try_end; /* copy out our starting point */ dirbase = bmake_strdup(here); dirbase_end = dirbase + strlen(dirbase); /* loop until we determine a result */ for (;;) { /* try and stat(2) it ... */ try = str_concat3(dirbase, "/", search_path); if (cached_stat(try, &cst) != -1) { /* * success! if we found a file, chop off * the filename so we return a directory. */ if ((cst.cst_mode & S_IFMT) != S_IFDIR) { try_end = try + strlen(try); while (try_end > try && *try_end != '/') try_end--; if (try_end > try) *try_end = '\0'; /* chop! */ } free(dirbase); return try; } free(try); /* * nope, we didn't find it. if we used up dirbase we've * reached the root and failed. */ if (dirbase_end == dirbase) break; /* failed! */ /* * truncate dirbase from the end to move up a dir */ while (dirbase_end > dirbase && *dirbase_end != '/') dirbase_end--; *dirbase_end = '\0'; /* chop! */ } free(dirbase); return NULL; } /* * This is an implied source, and it may have moved, * see if we can find it via the current .PATH */ static char * ResolveMovedDepends(GNode *gn) { char *fullName; const char *base = str_basename(gn->name); if (base == gn->name) return NULL; fullName = Dir_FindFile(base, Suff_FindPath(gn)); if (fullName == NULL) return NULL; /* * Put the found file in gn->path so that we give that to the compiler. */ /* * XXX: Better just reset gn->path to NULL; updating it is already done * by Dir_UpdateMTime. */ gn->path = bmake_strdup(fullName); if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, /* XXX: Why stdout? */ "%s: %s, %u: ignoring stale %s for %s, found %s\n", progname, gn->fname, gn->lineno, makeDependfile, gn->name, fullName); return fullName; } static char * ResolveFullName(GNode *gn) { char *fullName; fullName = gn->path; if (fullName == NULL && !(gn->type & OP_NOPATH)) { fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); if (fullName == NULL && gn->flags.fromDepend && !Lst_IsEmpty(&gn->implicitParents)) fullName = ResolveMovedDepends(gn); DEBUG2(DIR, "Found '%s' as '%s'\n", gn->name, fullName != NULL ? fullName : "(not found)"); } if (fullName == NULL) fullName = bmake_strdup(gn->name); /* XXX: Is every piece of memory freed as it should? */ return fullName; } /* * Search gn along dirSearchPath and store its modification time in gn->mtime. * If no file is found, store 0 instead. * * The found file is stored in gn->path, unless the node already had a path. */ void Dir_UpdateMTime(GNode *gn, bool forceRefresh) { char *fullName; struct cached_stat cst; if (gn->type & OP_ARCHV) { Arch_UpdateMTime(gn); return; } if (gn->type & OP_PHONY) { gn->mtime = 0; return; } fullName = ResolveFullName(gn); if (cached_stats(fullName, &cst, false, forceRefresh) < 0) { if (gn->type & OP_MEMBER) { if (fullName != gn->path) free(fullName); Arch_UpdateMemberMTime(gn); return; } cst.cst_mtime = 0; } if (fullName != NULL && gn->path == NULL) gn->path = fullName; /* XXX: else free(fullName)? */ gn->mtime = cst.cst_mtime; } /* * Read the directory and add it to the cache in openDirs. * If a path is given, add the directory to that path as well. */ static CachedDir * CacheNewDir(const char *name, SearchPath *path) { CachedDir *dir = NULL; DIR *d; struct dirent *dp; if ((d = opendir(name)) == NULL) { DEBUG1(DIR, "Caching %s ... not found\n", name); return dir; } DEBUG1(DIR, "Caching %s ...\n", name); dir = CachedDir_New(name); while ((dp = readdir(d)) != NULL) { #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ /* * The sun directory library doesn't check for a 0 inode * (0-inode slots just take up space), so we have to do * it ourselves. */ if (dp->d_fileno == 0) continue; #endif /* sun && d_ino */ (void)HashSet_Add(&dir->files, dp->d_name); } (void)closedir(d); OpenDirs_Add(&openDirs, dir); if (path != NULL) Lst_Append(&path->dirs, CachedDir_Ref(dir)); DEBUG1(DIR, "Caching %s done\n", name); return dir; } /* * Read the list of filenames in the directory and store the result * in openDirs. * * If a path is given, append the directory to that path. * * Input: * path The path to which the directory should be * added, or NULL to only add the directory to openDirs * name The name of the directory to add. * The name is not normalized in any way. * Output: * result If no path is given and the directory exists, the * returned CachedDir has a reference count of 0. It * must either be assigned to a variable using * CachedDir_Assign or be appended to a SearchPath using * Lst_Append and CachedDir_Ref. */ CachedDir * SearchPath_Add(SearchPath *path, const char *name) { if (path != NULL && strcmp(name, ".DOTLAST") == 0) { SearchPathNode *ln; /* XXX: Linear search gets slow with thousands of entries. */ for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *pathDir = ln->datum; if (strcmp(pathDir->name, name) == 0) return pathDir; } Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast)); } if (path != NULL) { /* XXX: Why is OpenDirs only checked if path != NULL? */ CachedDir *dir = OpenDirs_Find(&openDirs, name); if (dir != NULL) { if (Lst_FindDatum(&path->dirs, dir) == NULL) Lst_Append(&path->dirs, CachedDir_Ref(dir)); return dir; } } return CacheNewDir(name, path); } /* * Return a copy of dirSearchPath, incrementing the reference counts for * the contained directories. */ SearchPath * Dir_CopyDirSearchPath(void) { SearchPath *path = SearchPath_New(); SearchPathNode *ln; for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; Lst_Append(&path->dirs, CachedDir_Ref(dir)); } return path; } /* * Make a string by taking all the directories in the given search path and * preceding them by the given flag. Used by the suffix module to create * variables for compilers based on suffix search paths. * * Input: * flag flag which should precede each directory * path list of directories * * Results: * The string mentioned above. Note that there is no space between the * given flag and each directory. The empty string is returned if things * don't go well. */ char * SearchPath_ToFlags(SearchPath *path, const char *flag) { Buffer buf; SearchPathNode *ln; Buf_Init(&buf); if (path != NULL) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; Buf_AddStr(&buf, " "); Buf_AddStr(&buf, flag); Buf_AddStr(&buf, dir->name); } } return Buf_DoneData(&buf); } /* Free the search path and all directories mentioned in it. */ void SearchPath_Free(SearchPath *path) { SearchPathNode *ln; for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; CachedDir_Unref(dir); } Lst_Done(&path->dirs); free(path); } /* * Clear out all elements from the given search path. * The path is set to the empty list but is not destroyed. */ void SearchPath_Clear(SearchPath *path) { while (!Lst_IsEmpty(&path->dirs)) { CachedDir *dir = Lst_Dequeue(&path->dirs); CachedDir_Unref(dir); } } /* * Concatenate two paths, adding the second to the end of the first, * skipping duplicates. */ void SearchPath_AddAll(SearchPath *dst, SearchPath *src) { SearchPathNode *ln; for (ln = src->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (Lst_FindDatum(&dst->dirs, dir) == NULL) Lst_Append(&dst->dirs, CachedDir_Ref(dir)); } } static int percentage(int num, int den) { return den != 0 ? num * 100 / den : 0; } /********** DEBUG INFO **********/ void Dir_PrintDirectories(void) { CachedDirListNode *ln; debug_printf("#*** Directory Cache:\n"); debug_printf( "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", hits, misses, nearmisses, bigmisses, percentage(hits, hits + bigmisses + nearmisses)); debug_printf("# refs hits directory\n"); for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; debug_printf("# %4d %4d %s\n", dir->refCount, dir->hits, dir->name); } } void SearchPath_Print(const SearchPath *path) { SearchPathNode *ln; for (ln = path->dirs.first; ln != NULL; ln = ln->next) { const CachedDir *dir = ln->datum; debug_printf("%s ", dir->name); } } diff --git a/contrib/bmake/dir.h b/contrib/bmake/dir.h index 3d98fb0201b4..09cbca8ec4c1 100644 --- a/contrib/bmake/dir.h +++ b/contrib/bmake/dir.h @@ -1,107 +1,108 @@ -/* $NetBSD: dir.h,v 1.46 2021/12/15 12:08:25 rillig Exp $ */ +/* $NetBSD: dir.h,v 1.47 2023/01/24 00:24:02 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 */ #ifndef MAKE_DIR_H #define MAKE_DIR_H typedef struct CachedDir CachedDir; void Dir_Init(void); void Dir_InitCur(const char *); void Dir_InitDot(void); void Dir_End(void); void Dir_SetPATH(void); +void Dir_SetSYSPATH(void); bool Dir_HasWildcards(const char *) MAKE_ATTR_USE; void SearchPath_Expand(SearchPath *, const char *, StringList *); char *Dir_FindFile(const char *, SearchPath *) MAKE_ATTR_USE; char *Dir_FindHereOrAbove(const char *, const char *) MAKE_ATTR_USE; void Dir_UpdateMTime(GNode *, bool); CachedDir *SearchPath_Add(SearchPath *, const char *); char *SearchPath_ToFlags(SearchPath *, const char *) MAKE_ATTR_USE; void SearchPath_Clear(SearchPath *); void SearchPath_AddAll(SearchPath *, SearchPath *); void Dir_PrintDirectories(void); void SearchPath_Print(const SearchPath *); SearchPath *Dir_CopyDirSearchPath(void) MAKE_ATTR_USE; /* Stripped-down variant of struct stat. */ struct cached_stat { time_t cst_mtime; mode_t cst_mode; }; int cached_lstat(const char *, struct cached_stat *); int cached_stat(const char *, struct cached_stat *); #endif diff --git a/contrib/bmake/find_lib.sh b/contrib/bmake/find_lib.sh index 3c2e4af2f251..f6419bb85da2 100755 --- a/contrib/bmake/find_lib.sh +++ b/contrib/bmake/find_lib.sh @@ -1,13 +1,20 @@ : re=$1; shift +# some Linux systems have deprecated egrep in favor of grep -E +# but not everyone supports that +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) ;; +*) egrep() { grep -E "$@"; } +esac + for lib in $* do found=`nm $lib | egrep "$re"` case "$found" in "") ;; *) echo "$lib: $found";; esac done diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c index 65cb612fdc5b..bbe6171326f4 100644 --- a/contrib/bmake/for.c +++ b/contrib/bmake/for.c @@ -1,506 +1,513 @@ -/* $NetBSD: for.c,v 1.168 2022/06/12 16:09:21 rillig Exp $ */ +/* $NetBSD: for.c,v 1.170 2022/09/03 00:50:07 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Handling of .for/.endfor loops in a makefile. * * For loops have the form: * * .for in * # the body * .endfor * * 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. * * 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...}. After * that, the body is treated like a file from an .include directive. * * Interface: * For_Eval Evaluate the loop in the passed line. * * For_Run Run accumulated loop */ #include "make.h" /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: for.c,v 1.168 2022/06/12 16:09:21 rillig Exp $"); +MAKE_RCSID("$NetBSD: for.c,v 1.170 2022/09/03 00:50:07 rillig Exp $"); typedef struct ForLoop { Vector /* of 'char *' */ vars; /* Iteration variables */ SubstringWords items; /* Substitution items */ Buffer body; /* Unexpanded body of the loop */ unsigned int nextItem; /* Where to continue iterating */ } ForLoop; static ForLoop *accumFor; /* Loop being accumulated */ static ForLoop * ForLoop_New(void) { ForLoop *f = bmake_malloc(sizeof *f); Vector_Init(&f->vars, sizeof(char *)); SubstringWords_Init(&f->items); Buf_Init(&f->body); f->nextItem = 0; return f; } void ForLoop_Free(ForLoop *f) { while (f->vars.len > 0) free(*(char **)Vector_Pop(&f->vars)); Vector_Done(&f->vars); SubstringWords_Free(f->items); Buf_Done(&f->body); free(f); } char * ForLoop_Details(ForLoop *f) { size_t i, n; const char **vars; const Substring *items; Buffer buf; n = f->vars.len; vars = f->vars.items; assert(f->nextItem >= n); items = f->items.words + f->nextItem - n; Buf_Init(&buf); for (i = 0; i < n; i++) { if (i > 0) Buf_AddStr(&buf, ", "); Buf_AddStr(&buf, vars[i]); Buf_AddStr(&buf, " = "); Buf_AddBytesBetween(&buf, items[i].start, items[i].end); } return Buf_DoneData(&buf); } static bool ForLoop_ParseVarnames(ForLoop *f, const char **pp) { const char *p = *pp; for (;;) { size_t len; cpp_skip_whitespace(&p); if (*p == '\0') { Parse_Error(PARSE_FATAL, "missing `in' in for"); return false; } /* * XXX: This allows arbitrary variable names; * see directive-for.mk. */ for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) continue; if (len == 2 && p[0] == 'i' && p[1] == 'n') { p += 2; break; } *(char **)Vector_Push(&f->vars) = bmake_strldup(p, len); p += len; } if (f->vars.len == 0) { Parse_Error(PARSE_FATAL, "no iteration variables in for"); return false; } *pp = p; return true; } static bool ForLoop_ParseItems(ForLoop *f, const char *p) { char *items; cpp_skip_whitespace(&p); if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { Parse_Error(PARSE_FATAL, "Error in .for loop items"); return false; } f->items = Substring_Words(items, false); free(items); if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0])) f->items.len = 0; /* .for var in ${:U} */ if (f->items.len % f->vars.len != 0) { Parse_Error(PARSE_FATAL, "Wrong number of words (%u) in .for " "substitution list with %u variables", (unsigned)f->items.len, (unsigned)f->vars.len); return false; } return true; } static bool IsFor(const char *p) { return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]); } static bool IsEndfor(const char *p) { return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && (p[6] == '\0' || ch_isspace(p[6])); } /* * Evaluate the for loop in the passed line. The line looks like this: * .for in * * Results: * 0 not a .for directive * 1 found a .for directive * -1 erroneous .for directive */ int For_Eval(const char *line) { const char *p; ForLoop *f; p = line + 1; /* skip the '.' */ cpp_skip_whitespace(&p); if (IsFor(p)) { p += 3; f = ForLoop_New(); if (!ForLoop_ParseVarnames(f, &p)) { ForLoop_Free(f); return -1; } if (!ForLoop_ParseItems(f, p)) f->items.len = 0; /* don't iterate */ accumFor = f; return 1; } else if (IsEndfor(p)) { Parse_Error(PARSE_FATAL, "for-less endfor"); return -1; } else return 0; } /* * Add another line to the .for loop that is being built up. * Returns false when the matching .endfor is reached. */ bool For_Accum(const char *line, int *forLevel) { const char *p = line; if (*p == '.') { p++; cpp_skip_whitespace(&p); if (IsEndfor(p)) { DEBUG1(FOR, "For: end for %d\n", *forLevel); if (--*forLevel == 0) return false; } else if (IsFor(p)) { (*forLevel)++; DEBUG1(FOR, "For: new loop %d\n", *forLevel); } } Buf_AddStr(&accumFor->body, line); Buf_AddByte(&accumFor->body, '\n'); return true; } /* * When the body of a '.for i' loop is prepared for an iteration, each * occurrence of $i in the body is replaced with ${:U...}, inserting the * value of the item. If this item contains a '$', it may be the start of a * variable expression. This expression is copied verbatim, its length is * determined here, in a rather naive way, ignoring escape characters and * funny delimiters in modifiers like ':S}from}to}'. */ static size_t ExprLen(const char *s, const char *e) { char expr_open, expr_close; int depth; const char *p; if (s == e) return 0; /* just escape the '$' */ expr_open = s[0]; if (expr_open == '(') expr_close = ')'; else if (expr_open == '{') expr_close = '}'; else return 1; /* Single char variable */ depth = 1; for (p = s + 1; p != e; p++) { if (*p == expr_open) depth++; else if (*p == expr_close && --depth == 0) return (size_t)(p + 1 - s); } /* Expression end not found, escape the $ */ return 0; } /* * The .for loop substitutes the items as ${:U...}, which means * that characters that break this syntax must be backslash-escaped. */ static bool NeedsEscapes(Substring value, char endc) { const char *p; for (p = value.start; p != value.end; p++) { if (*p == ':' || *p == '$' || *p == '\\' || *p == endc || *p == '\n') return true; } return false; } /* * While expanding the body of a .for loop, write the item in the ${:U...} * expression, escaping characters as needed. The result is later unescaped * by ApplyModifier_Defined. */ static void AddEscaped(Buffer *cmds, Substring item, char endc) { const char *p; char ch; if (!NeedsEscapes(item, endc)) { Buf_AddBytesBetween(cmds, item.start, item.end); return; } for (p = item.start; p != item.end;) { ch = *p; if (ch == '$') { size_t len = ExprLen(p + 1, item.end); if (len != 0) { /* * XXX: Should a '\' be added here? * See directive-for-escape.mk, ExprLen. */ Buf_AddBytes(cmds, p, 1 + len); p += 1 + len; continue; } Buf_AddByte(cmds, '\\'); } else if (ch == ':' || ch == '\\' || ch == endc) Buf_AddByte(cmds, '\\'); else if (ch == '\n') { Parse_Error(PARSE_FATAL, "newline in .for value"); ch = ' '; /* prevent newline injection */ } Buf_AddByte(cmds, ch); p++; } } /* * When 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 ForLoop_SubstVarLong(ForLoop *f, unsigned int firstItem, Buffer *body, const char **pp, char endc, const char **inout_mark) { size_t i; const char *start = *pp; const char **vars = Vector_Get(&f->vars, 0); for (i = 0; i < f->vars.len; i++) { const char *p = start; if (!cpp_skip_string(&p, vars[i])) continue; /* XXX: why test for backslash here? */ if (*p != ':' && *p != endc && *p != '\\') continue; /* * Found a variable match. Skip over the variable name and * instead add ':U' to the current body. */ Buf_AddBytesBetween(body, *inout_mark, start); Buf_AddStr(body, ":U"); AddEscaped(body, f->items.words[firstItem + i], endc); *inout_mark = p; *pp = p; return; } } /* * When expanding the body of a .for loop, replace single-character * variable expressions like $i with their ${:U...} expansion. */ static void ForLoop_SubstVarShort(ForLoop *f, unsigned int firstItem, Buffer *body, const char *p, const char **inout_mark) { const char ch = *p; const char **vars; size_t i; /* Skip $$ and stupid ones. */ if (ch == '}' || ch == ')' || ch == ':' || ch == '$') return; vars = Vector_Get(&f->vars, 0); for (i = 0; i < f->vars.len; i++) { const char *varname = vars[i]; if (varname[0] == ch && varname[1] == '\0') goto found; } return; found: Buf_AddBytesBetween(body, *inout_mark, p); *inout_mark = p + 1; /* Replace $ with ${:U} */ Buf_AddStr(body, "{:U"); AddEscaped(body, f->items.words[firstItem + i], '}'); Buf_AddByte(body, '}'); } /* * Compute the body for the current iteration by copying the unexpanded body, * replacing the expressions for the iteration variables on the way. * * Using variable expressions ensures that the .for loop can't generate * syntax, and that the later parsing will still see a variable. * 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 variables is naive. * Many of the modifiers use '\$' instead of '$$' to escape '$', so it is * possible to contrive a makefile where an unwanted substitution happens. */ static void ForLoop_SubstBody(ForLoop *f, unsigned int firstItem, Buffer *body) { const char *p, *end; const char *mark; /* where the last substitution left off */ Buf_Clear(body); mark = f->body.data; end = f->body.data + f->body.len; for (p = mark; (p = strchr(p, '$')) != NULL;) { if (p[1] == '{' || p[1] == '(') { char endc = p[1] == '{' ? '}' : ')'; p += 2; ForLoop_SubstVarLong(f, firstItem, body, &p, endc, &mark); } else if (p[1] != '\0') { ForLoop_SubstVarShort(f, firstItem, body, p + 1, &mark); p += 2; } else break; } Buf_AddBytesBetween(body, mark, end); } /* * Compute the body for the current iteration by copying the unexpanded body, * replacing the expressions for the iteration variables on the way. */ bool For_NextIteration(ForLoop *f, Buffer *body) { if (f->nextItem == f->items.len) return false; f->nextItem += (unsigned int)f->vars.len; ForLoop_SubstBody(f, f->nextItem - (unsigned int)f->vars.len, body); DEBUG1(FOR, "For: loop body:\n%s", body->data); return true; } +/* Break out of the .for loop. */ +void +For_Break(ForLoop *f) +{ + f->nextItem = (unsigned int)f->items.len; +} + /* Run the .for loop, imitating the actions of an include file. */ void For_Run(unsigned headLineno, unsigned bodyReadLines) { Buffer buf; ForLoop *f = accumFor; accumFor = NULL; if (f->items.len > 0) { Buf_Init(&buf); Parse_PushInput(NULL, headLineno, bodyReadLines, buf, f); } else ForLoop_Free(f); } diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c index 01f864176677..b9b5a5d36e47 100644 --- a/contrib/bmake/job.c +++ b/contrib/bmake/job.c @@ -1,3065 +1,3056 @@ -/* $NetBSD: job.c,v 1.453 2022/05/07 08:01:20 rillig Exp $ */ +/* $NetBSD: job.c,v 1.457 2023/01/17 21:35:19 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * job.c -- * handle the creation etc. of our child processes. * * Interface: * Job_Init Called to initialize this module. In addition, * the .BEGIN target is made including all of its * dependencies before this function returns. * Hence, the makefiles must have been parsed * before this function is called. * * Job_End Clean up any memory used. * * Job_Make Start the creation of the given target. * * Job_CatchChildren * Check for and handle the termination of any * children. This must be called reasonably * frequently to keep the whole make going at * a decent clip, since job table entries aren't * removed until their process is caught this way. * * Job_CatchOutput * Print any output our children have produced. * Should also be called fairly frequently to * keep the user informed of what's going on. * If no output is waiting, it will block for * a time given by the SEL_* constants, below, * or until output is ready. * * Job_ParseShell Given a special dependency line with target '.SHELL', * define the shell that is used for the creation * commands in jobs mode. * * Job_Finish Perform any final processing which needs doing. * This includes the execution of any commands * which have been/were attached to the .END * target. It should only be called when the * job table is empty. * * Job_AbortAll Abort all currently running jobs. Do not handle * output or do anything for the jobs, just kill them. * Should only be called in an emergency. * * Job_CheckCommands * Verify that the commands for a target are * ok. Provide them if necessary and possible. * * Job_Touch Update a target without really updating it. * * Job_Wait Wait for all currently-running jobs to finish. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "wait.h" #include #if !defined(USE_SELECT) && defined(HAVE_POLL_H) #include #else #ifndef USE_SELECT /* no poll.h */ # define USE_SELECT #endif #if defined(HAVE_SYS_SELECT_H) # include #endif #endif #include #include #if defined(HAVE_SYS_SOCKET_H) # include #endif #include "make.h" #include "dir.h" #include "job.h" #include "pathnames.h" #include "trace.h" /* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: job.c,v 1.453 2022/05/07 08:01:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: job.c,v 1.457 2023/01/17 21:35:19 christos Exp $"); /* * A shell defines how the commands are run. All commands for a target are * written into a single file, which is then given to the shell to execute * the commands from it. The commands are written to the file using a few * templates for echo control and error control. * * The name of the shell is the basename for the predefined shells, such as * "sh", "csh", "bash". For custom shells, it is the full pathname, and its * basename is used to select the type of shell; the longest match wins. * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh. * * The echoing of command lines is controlled using hasEchoCtl, echoOff, * echoOn, noPrint and noPrintLen. When echoOff is executed by the shell, it * still outputs something, but this something is not interesting, therefore * it is filtered out using noPrint and noPrintLen. * * The error checking for individual commands is controlled using hasErrCtl, * errOn, errOff and runChkTmpl. * * In case a shell doesn't have error control, echoTmpl is a printf template * for echoing the command, should echoing be on; runIgnTmpl is another * printf template for executing the command while ignoring the return * status. Finally runChkTmpl is a printf template for running the command and * causing the shell to exit on error. If any of these strings are empty when * hasErrCtl is false, the command will be executed anyway as is, and if it * causes an error, so be it. Any templates set up to echo the command will * escape any '$ ` \ "' characters in the command string to avoid unwanted * shell code injection, the escaped command is safe to use in double quotes. * * The command-line flags "echo" and "exit" also control the behavior. The * "echo" flag causes the shell to start echoing commands right away. The * "exit" flag causes the shell to exit when an error is detected in one of * the commands. */ typedef struct Shell { /* * The name of the shell. For Bourne and C shells, this is used only * to find the shell description when used as the single source of a * .SHELL target. For user-defined shells, this is the full path of * the shell. */ const char *name; bool hasEchoCtl; /* whether both echoOff and echoOn are there */ const char *echoOff; /* command to turn echoing off */ const char *echoOn; /* command to turn echoing back on */ const char *noPrint; /* text to skip when printing output from the * shell. This is usually the same as echoOff */ size_t noPrintLen; /* length of noPrint command */ bool hasErrCtl; /* whether error checking can be controlled * for individual commands */ const char *errOn; /* command to turn on error checking */ const char *errOff; /* command to turn off error checking */ const char *echoTmpl; /* template to echo a command */ const char *runIgnTmpl; /* template to run a command without error * checking */ const char *runChkTmpl; /* template to run a command with error * checking */ /* * A string literal that results in a newline character when it * occurs outside of any 'quote' or "quote" characters. */ const char *newline; char commentChar; /* character used by shell for comment lines */ const char *echoFlag; /* shell flag to echo commands */ const char *errFlag; /* shell flag to exit on error */ } Shell; typedef struct CommandFlags { /* Whether to echo the command before or instead of running it. */ bool echo; /* Run the command even in -n or -N mode. */ bool always; /* * true if we turned error checking off before writing the command to * the commands file and need to turn it back on */ bool ignerr; } CommandFlags; /* * Write shell commands to a file. * * TODO: keep track of whether commands are echoed. * TODO: keep track of whether error checking is active. */ typedef struct ShellWriter { FILE *f; /* we've sent 'set -x' */ bool xtraced; } ShellWriter; /* * FreeBSD: traditionally .MAKE is not required to * pass jobs queue to sub-makes. * Use .MAKE.ALWAYS_PASS_JOB_QUEUE=no to disable. */ #define MAKE_ALWAYS_PASS_JOB_QUEUE "${.MAKE.ALWAYS_PASS_JOB_QUEUE:U}" static bool Always_pass_job_queue = true; /* * FreeBSD: aborting entire parallel make isn't always * desired. When doing tinderbox for example, failure of * one architecture should not stop all. * We still want to bail on interrupt though. */ #define MAKE_JOB_ERROR_TOKEN "${MAKE_JOB_ERROR_TOKEN:U}" static bool Job_error_token = true; /* * error handling variables */ static int job_errors = 0; /* number of errors reported */ static enum { /* Why is the make aborting? */ ABORT_NONE, ABORT_ERROR, /* Aborted because of an error */ ABORT_INTERRUPT, /* Aborted because it was interrupted */ ABORT_WAIT /* Waiting for jobs to finish */ } aborting = ABORT_NONE; #define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ /* * this tracks the number of tokens currently "out" to build jobs. */ int jobTokensRunning = 0; typedef enum JobStartResult { JOB_RUNNING, /* Job is running */ JOB_ERROR, /* Error in starting the job */ JOB_FINISHED /* The job is already finished */ } JobStartResult; /* * Descriptions for various shells. * * The build environment may set DEFSHELL_INDEX to one of * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to * select one of the predefined shells as the default shell. * * Alternatively, the build environment may set DEFSHELL_CUSTOM to the * name or the full path of a sh-compatible shell, which will be used as * the default shell. * * ".SHELL" lines in Makefiles can choose the default shell from the * set defined here, or add additional shells. */ #ifdef DEFSHELL_CUSTOM #define DEFSHELL_INDEX_CUSTOM 0 #define DEFSHELL_INDEX_SH 1 #define DEFSHELL_INDEX_KSH 2 #define DEFSHELL_INDEX_CSH 3 #else /* !DEFSHELL_CUSTOM */ #define DEFSHELL_INDEX_SH 0 #define DEFSHELL_INDEX_KSH 1 #define DEFSHELL_INDEX_CSH 2 #endif /* !DEFSHELL_CUSTOM */ #ifndef DEFSHELL_INDEX #define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */ #endif /* !DEFSHELL_INDEX */ static Shell shells[] = { #ifdef DEFSHELL_CUSTOM /* * An sh-compatible shell with a non-standard name. * * Keep this in sync with the "sh" description below, but avoid * non-portable features that might not be supplied by all * sh-compatible shells. */ { DEFSHELL_CUSTOM, /* .name */ false, /* .hasEchoCtl */ "", /* .echoOff */ "", /* .echoOn */ "", /* .noPrint */ 0, /* .noPrintLen */ false, /* .hasErrCtl */ "", /* .errOn */ "", /* .errOff */ "echo \"%s\"\n", /* .echoTmpl */ "%s\n", /* .runIgnTmpl */ "{ %s \n} || exit $?\n", /* .runChkTmpl */ "'\n'", /* .newline */ '#', /* .commentChar */ "", /* .echoFlag */ "", /* .errFlag */ }, #endif /* DEFSHELL_CUSTOM */ /* * SH description. Echo control is also possible and, under * sun UNIX anyway, one can even control error checking. */ { "sh", /* .name */ false, /* .hasEchoCtl */ "", /* .echoOff */ "", /* .echoOn */ "", /* .noPrint */ 0, /* .noPrintLen */ false, /* .hasErrCtl */ "", /* .errOn */ "", /* .errOff */ "echo \"%s\"\n", /* .echoTmpl */ "%s\n", /* .runIgnTmpl */ "{ %s \n} || exit $?\n", /* .runChkTmpl */ "'\n'", /* .newline */ '#', /* .commentChar*/ #if defined(MAKE_NATIVE) && defined(__NetBSD__) /* XXX: -q is not really echoFlag, it's more like noEchoInSysFlag. */ "q", /* .echoFlag */ #else "", /* .echoFlag */ #endif "", /* .errFlag */ }, /* * KSH description. */ { "ksh", /* .name */ true, /* .hasEchoCtl */ "set +v", /* .echoOff */ "set -v", /* .echoOn */ "set +v", /* .noPrint */ 6, /* .noPrintLen */ false, /* .hasErrCtl */ "", /* .errOn */ "", /* .errOff */ "echo \"%s\"\n", /* .echoTmpl */ "%s\n", /* .runIgnTmpl */ "{ %s \n} || exit $?\n", /* .runChkTmpl */ "'\n'", /* .newline */ '#', /* .commentChar */ "v", /* .echoFlag */ "", /* .errFlag */ }, /* * CSH description. The csh can do echo control by playing * with the setting of the 'echo' shell variable. Sadly, * however, it is unable to do error control nicely. */ { "csh", /* .name */ true, /* .hasEchoCtl */ "unset verbose", /* .echoOff */ "set verbose", /* .echoOn */ "unset verbose", /* .noPrint */ 13, /* .noPrintLen */ false, /* .hasErrCtl */ "", /* .errOn */ "", /* .errOff */ "echo \"%s\"\n", /* .echoTmpl */ "csh -c \"%s || exit 0\"\n", /* .runIgnTmpl */ "", /* .runChkTmpl */ "'\\\n'", /* .newline */ '#', /* .commentChar */ "v", /* .echoFlag */ "e", /* .errFlag */ } }; /* * This is the shell to which we pass all commands in the Makefile. * It is set by the Job_ParseShell function. */ static Shell *shell = &shells[DEFSHELL_INDEX]; const char *shellPath = NULL; /* full pathname of executable image */ const char *shellName = NULL; /* last component of shellPath */ char *shellErrFlag = NULL; static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */ static Job *job_table; /* The structures that describe them */ static Job *job_table_end; /* job_table + maxJobs */ static unsigned int wantToken; /* we want a token */ static bool lurking_children = false; static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */ /* * Set of descriptors of pipes connected to * the output channels of children */ static struct pollfd *fds = NULL; static Job **jobByFdIndex = NULL; static nfds_t fdsLen = 0; static void watchfd(Job *); static void clearfd(Job *); static bool readyfd(Job *); static char *targPrefix = NULL; /* To identify a job change in the output. */ static Job tokenWaitJob; /* token wait pseudo-job */ static Job childExitJob; /* child exit pseudo-job */ #define CHILD_EXIT "." #define DO_JOB_RESUME "R" enum { npseudojobs = 2 /* number of pseudo-jobs */ }; static sigset_t caught_signals; /* Set of signals we handle */ static volatile sig_atomic_t caught_sigchld; static void CollectOutput(Job *, bool); static void JobInterrupt(bool, int) MAKE_ATTR_DEAD; static void JobRestartJobs(void); static void JobSigReset(void); static void SwitchOutputTo(GNode *gn) { /* The node for which output was most recently produced. */ static GNode *lastNode = NULL; if (gn == lastNode) return; lastNode = gn; if (opts.maxJobs != 1 && targPrefix != NULL && targPrefix[0] != '\0') (void)fprintf(stdout, "%s %s ---\n", targPrefix, gn->name); } static unsigned nfds_per_job(void) { #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) return 2; #endif return 1; } void Job_FlagsToString(const Job *job, char *buf, size_t bufsize) { snprintf(buf, bufsize, "%c%c%c", job->ignerr ? 'i' : '-', !job->echo ? 's' : '-', job->special ? 'S' : '-'); } static void DumpJobs(const char *where) { Job *job; char flags[4]; debug_printf("job table @ %s\n", where); for (job = job_table; job < job_table_end; job++) { Job_FlagsToString(job, flags, sizeof flags); debug_printf("job %d, status %d, flags %s, pid %d\n", (int)(job - job_table), job->status, flags, job->pid); } } /* * Delete the target of a failed, interrupted, or otherwise * unsuccessful job unless inhibited by .PRECIOUS. */ static void JobDeleteTarget(GNode *gn) { const char *file; if (gn->type & OP_JOIN) return; if (gn->type & OP_PHONY) return; if (GNode_IsPrecious(gn)) return; if (opts.noExecute) return; file = GNode_Path(gn); - if (unlink_file(file)) + if (unlink_file(file) == 0) Error("*** %s removed", file); } /* * JobSigLock/JobSigUnlock * * Signal lock routines to get exclusive access. Currently used to * protect `jobs' and `stoppedJobs' list manipulations. */ static void JobSigLock(sigset_t *omaskp) { if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) { Punt("JobSigLock: sigprocmask: %s", strerror(errno)); sigemptyset(omaskp); } } static void JobSigUnlock(sigset_t *omaskp) { (void)sigprocmask(SIG_SETMASK, omaskp, NULL); } static void JobCreatePipe(Job *job, int minfd) { int i, fd, flags; int pipe_fds[2]; if (pipe(pipe_fds) == -1) Punt("Cannot create pipe: %s", strerror(errno)); for (i = 0; i < 2; i++) { /* Avoid using low numbered fds */ fd = fcntl(pipe_fds[i], F_DUPFD, minfd); if (fd != -1) { close(pipe_fds[i]); pipe_fds[i] = fd; } } job->inPipe = pipe_fds[0]; job->outPipe = pipe_fds[1]; /* Set close-on-exec flag for both */ if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1) Punt("Cannot set close-on-exec: %s", strerror(errno)); if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1) Punt("Cannot set close-on-exec: %s", strerror(errno)); /* * We mark the input side of the pipe non-blocking; we poll(2) the * pipe when we're waiting for a job token, but we might lose the * race for the token when a new one becomes available, so the read * from the pipe should not block. */ flags = fcntl(job->inPipe, F_GETFL, 0); if (flags == -1) Punt("Cannot get flags: %s", strerror(errno)); flags |= O_NONBLOCK; if (fcntl(job->inPipe, F_SETFL, flags) == -1) Punt("Cannot set flags: %s", strerror(errno)); } /* Pass the signal to each running job. */ static void JobCondPassSig(int signo) { Job *job; DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo); for (job = job_table; job < job_table_end; job++) { if (job->status != JOB_ST_RUNNING) continue; DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n", signo, job->pid); KILLPG(job->pid, signo); } } /* * SIGCHLD handler. * * Sends a token on the child exit pipe to wake us up from select()/poll(). */ /*ARGSUSED*/ static void JobChildSig(int signo MAKE_ATTR_UNUSED) { caught_sigchld = 1; while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN) continue; } /* Resume all stopped jobs. */ /*ARGSUSED*/ static void JobContinueSig(int signo MAKE_ATTR_UNUSED) { /* * Defer sending SIGCONT to our stopped children until we return * from the signal handler. */ while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 && errno == EAGAIN) continue; } /* * Pass a signal on to all jobs, then resend to ourselves. * We die by the same signal. */ MAKE_ATTR_DEAD static void JobPassSig_int(int signo) { /* Run .INTERRUPT target then exit */ JobInterrupt(true, signo); } /* * Pass a signal on to all jobs, then resend to ourselves. * We die by the same signal. */ MAKE_ATTR_DEAD static void JobPassSig_term(int signo) { /* Dont run .INTERRUPT target then exit */ JobInterrupt(false, signo); } static void JobPassSig_suspend(int signo) { sigset_t nmask, omask; struct sigaction act; /* Suppress job started/continued messages */ make_suspended = true; /* Pass the signal onto every job */ JobCondPassSig(signo); /* * Send ourselves the signal now we've given the message to everyone * else. Note we block everything else possible while we're getting * the signal. This ensures that all our jobs get continued when we * wake up before we take any other signal. */ sigfillset(&nmask); sigdelset(&nmask, signo); (void)sigprocmask(SIG_SETMASK, &nmask, &omask); act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); act.sa_flags = 0; (void)sigaction(signo, &act, NULL); DEBUG1(JOB, "JobPassSig passing signal %d to self.\n", signo); (void)kill(getpid(), signo); /* * We've been continued. * * A whole host of signals continue to happen! * SIGCHLD for any processes that actually suspended themselves. * SIGCHLD for any processes that exited while we were alseep. * The SIGCONT that actually caused us to wakeup. * * Since we defer passing the SIGCONT on to our children until * the main processing loop, we can be sure that all the SIGCHLD * events will have happened by then - and that the waitpid() will * collect the child 'suspended' events. * For correct sequencing we just need to ensure we process the * waitpid() before passing on the SIGCONT. * * In any case nothing else is needed here. */ /* Restore handler and signal mask */ act.sa_handler = JobPassSig_suspend; (void)sigaction(signo, &act, NULL); (void)sigprocmask(SIG_SETMASK, &omask, NULL); } static Job * JobFindPid(int pid, JobStatus status, bool isJobs) { Job *job; for (job = job_table; job < job_table_end; job++) { if (job->status == status && job->pid == pid) return job; } if (DEBUG(JOB) && isJobs) DumpJobs("no pid"); return NULL; } /* Parse leading '@', '-' and '+', which control the exact execution mode. */ static void ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags) { char *p = *pp; out_cmdFlags->echo = true; out_cmdFlags->ignerr = false; out_cmdFlags->always = false; for (;;) { if (*p == '@') out_cmdFlags->echo = DEBUG(LOUD); else if (*p == '-') out_cmdFlags->ignerr = true; else if (*p == '+') out_cmdFlags->always = true; - else + else if (!ch_isspace(*p)) + /* Ignore whitespace for compatibility with gnu make */ break; p++; } pp_skip_whitespace(&p); *pp = p; } /* Escape a string for a double-quoted string literal in sh, csh and ksh. */ static char * EscapeShellDblQuot(const char *cmd) { size_t i, j; /* Worst that could happen is every char needs escaping. */ char *esc = bmake_malloc(strlen(cmd) * 2 + 1); for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) { if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || cmd[i] == '"') esc[j++] = '\\'; esc[j] = cmd[i]; } esc[j] = '\0'; return esc; } static void ShellWriter_WriteFmt(ShellWriter *wr, const char *fmt, const char *arg) { DEBUG1(JOB, fmt, arg); (void)fprintf(wr->f, fmt, arg); if (wr->f == stdout) (void)fflush(wr->f); } static void ShellWriter_WriteLine(ShellWriter *wr, const char *line) { ShellWriter_WriteFmt(wr, "%s\n", line); } static void ShellWriter_EchoOff(ShellWriter *wr) { if (shell->hasEchoCtl) ShellWriter_WriteLine(wr, shell->echoOff); } static void ShellWriter_EchoCmd(ShellWriter *wr, const char *escCmd) { ShellWriter_WriteFmt(wr, shell->echoTmpl, escCmd); } static void ShellWriter_EchoOn(ShellWriter *wr) { if (shell->hasEchoCtl) ShellWriter_WriteLine(wr, shell->echoOn); } static void ShellWriter_TraceOn(ShellWriter *wr) { if (!wr->xtraced) { ShellWriter_WriteLine(wr, "set -x"); wr->xtraced = true; } } static void ShellWriter_ErrOff(ShellWriter *wr, bool echo) { if (echo) ShellWriter_EchoOff(wr); ShellWriter_WriteLine(wr, shell->errOff); if (echo) ShellWriter_EchoOn(wr); } static void ShellWriter_ErrOn(ShellWriter *wr, bool echo) { if (echo) ShellWriter_EchoOff(wr); ShellWriter_WriteLine(wr, shell->errOn); if (echo) ShellWriter_EchoOn(wr); } /* * The shell has no built-in error control, so emulate error control by * enclosing each shell command in a template like "{ %s \n } || exit $?" * (configurable per shell). */ static void JobWriteSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags, const char *escCmd, const char **inout_cmdTemplate) { /* XXX: Why is the whole job modified at this point? */ job->ignerr = true; if (job->echo && inout_cmdFlags->echo) { ShellWriter_EchoOff(wr); ShellWriter_EchoCmd(wr, escCmd); /* * Leave echoing off so the user doesn't see the commands * for toggling the error checking. */ inout_cmdFlags->echo = false; } *inout_cmdTemplate = shell->runIgnTmpl; /* * The template runIgnTmpl already takes care of ignoring errors, * so pretend error checking is still on. * XXX: What effects does this have, and why is it necessary? */ inout_cmdFlags->ignerr = false; } static void JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run, CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate) { if (!run) { /* * If there is no command to run, there is no need to switch * error checking off and on again for nothing. */ inout_cmdFlags->ignerr = false; } else if (shell->hasErrCtl) ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo); else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') { JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd, inout_cmdTemplate); } else inout_cmdFlags->ignerr = false; } /* * Write a shell command to the job's commands file, to be run later. * * If the command starts with '@' and neither the -s nor the -n flag was * given to make, stick a shell-specific echoOff command in the script. * * If the command starts with '-' and the shell has no error control (none * of the predefined shells has that), ignore errors for the entire job. * * XXX: Why ignore errors for the entire job? This is even documented in the * manual page, but without any rationale since there is no known rationale. * * XXX: The manual page says the '-' "affects the entire job", but that's not * accurate. The '-' does not affect the commands before the '-'. * * If the command is just "...", skip all further commands of this job. These * commands are attached to the .END node instead and will be run by * Job_Finish after all other targets have been made. */ static void JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd) { bool run; CommandFlags cmdFlags; /* Template for writing a command to the shell file */ const char *cmdTemplate; char *xcmd; /* The expanded command */ char *xcmdStart; char *escCmd; /* xcmd escaped to be used in double quotes */ run = GNode_ShouldExecute(job->node); (void)Var_Subst(ucmd, job->node, VARE_WANTRES, &xcmd); /* TODO: handle errors */ xcmdStart = xcmd; cmdTemplate = "%s\n"; ParseCommandFlags(&xcmd, &cmdFlags); /* The '+' command flag overrides the -n or -N options. */ if (cmdFlags.always && !run) { /* * We're not actually executing anything... * but this one needs to be - use compat mode just for it. */ (void)Compat_RunCommand(ucmd, job->node, ln); free(xcmdStart); return; } /* * If the shell doesn't have error control, the alternate echoing * will be done (to avoid showing additional error checking code) * and this needs some characters escaped. */ escCmd = shell->hasErrCtl ? NULL : EscapeShellDblQuot(xcmd); if (!cmdFlags.echo) { if (job->echo && run && shell->hasEchoCtl) { ShellWriter_EchoOff(wr); } else { if (shell->hasErrCtl) cmdFlags.echo = true; } } if (cmdFlags.ignerr) { JobWriteSpecials(job, wr, escCmd, run, &cmdFlags, &cmdTemplate); } else { /* * If errors are being checked and the shell doesn't have * error control but does supply an runChkTmpl template, then * set up commands to run through it. */ if (!shell->hasErrCtl && shell->runChkTmpl != NULL && shell->runChkTmpl[0] != '\0') { if (job->echo && cmdFlags.echo) { ShellWriter_EchoOff(wr); ShellWriter_EchoCmd(wr, escCmd); cmdFlags.echo = false; } /* * If it's a comment line or blank, avoid the possible * syntax error generated by "{\n} || exit $?". */ cmdTemplate = escCmd[0] == shell->commentChar || escCmd[0] == '\0' ? shell->runIgnTmpl : shell->runChkTmpl; cmdFlags.ignerr = false; } } if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0) ShellWriter_TraceOn(wr); ShellWriter_WriteFmt(wr, cmdTemplate, xcmd); free(xcmdStart); free(escCmd); if (cmdFlags.ignerr) ShellWriter_ErrOn(wr, cmdFlags.echo && job->echo); if (!cmdFlags.echo) ShellWriter_EchoOn(wr); } /* * Write all commands to the shell file that is later executed. * * The special command "..." stops writing and saves the remaining commands * to be executed later, when the target '.END' is made. * * Return whether at least one command was written to the shell file. */ static bool JobWriteCommands(Job *job) { StringListNode *ln; bool seen = false; ShellWriter wr; wr.f = job->cmdFILE; wr.xtraced = false; for (ln = job->node->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; if (strcmp(cmd, "...") == 0) { job->node->type |= OP_SAVE_CMDS; job->tailCmds = ln->next; break; } JobWriteCommand(job, &wr, ln, ln->datum); seen = true; } return seen; } /* * Save the delayed commands (those after '...'), to be executed later in * the '.END' node, when everything else is done. */ static void JobSaveCommands(Job *job) { StringListNode *ln; for (ln = job->tailCmds; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; char *expanded_cmd; /* * XXX: This Var_Subst is only intended to expand the dynamic * variables such as .TARGET, .IMPSRC. It is not intended to * expand the other variables as well; see deptgt-end.mk. */ (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd); /* TODO: handle errors */ Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd); } } /* Called to close both input and output pipes when a job is finished. */ static void JobClosePipes(Job *job) { clearfd(job); (void)close(job->outPipe); job->outPipe = -1; CollectOutput(job, true); (void)close(job->inPipe); job->inPipe = -1; } static void DebugFailedJob(const Job *job) { const StringListNode *ln; if (!DEBUG(ERROR)) return; debug_printf("\n"); debug_printf("*** Failed target: %s\n", job->node->name); debug_printf("*** Failed commands:\n"); for (ln = job->node->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; debug_printf("\t%s\n", cmd); if (strchr(cmd, '$') != NULL) { char *xcmd; (void)Var_Subst(cmd, job->node, VARE_WANTRES, &xcmd); debug_printf("\t=> %s\n", xcmd); free(xcmd); } } } static void JobFinishDoneExitedError(Job *job, WAIT_T *inout_status) { SwitchOutputTo(job->node); #ifdef USE_META if (useMeta) { meta_job_error(job, job->node, job->ignerr, WEXITSTATUS(*inout_status)); } #endif if (!shouldDieQuietly(job->node, -1)) { DebugFailedJob(job); (void)printf("*** [%s] Error code %d%s\n", job->node->name, WEXITSTATUS(*inout_status), job->ignerr ? " (ignored)" : ""); } if (job->ignerr) WAIT_STATUS(*inout_status) = 0; else { if (deleteOnError) JobDeleteTarget(job->node); PrintOnError(job->node, "\n"); } } static void JobFinishDoneExited(Job *job, WAIT_T *inout_status) { DEBUG2(JOB, "Process %d [%s] exited.\n", job->pid, job->node->name); if (WEXITSTATUS(*inout_status) != 0) JobFinishDoneExitedError(job, inout_status); else if (DEBUG(JOB)) { SwitchOutputTo(job->node); (void)printf("*** [%s] Completed successfully\n", job->node->name); } } static void JobFinishDoneSignaled(Job *job, WAIT_T status) { SwitchOutputTo(job->node); DebugFailedJob(job); (void)printf("*** [%s] Signal %d\n", job->node->name, WTERMSIG(status)); if (deleteOnError) JobDeleteTarget(job->node); } static void JobFinishDone(Job *job, WAIT_T *inout_status) { if (WIFEXITED(*inout_status)) JobFinishDoneExited(job, inout_status); else JobFinishDoneSignaled(job, *inout_status); (void)fflush(stdout); } /* * Do final processing for the given job including updating parent nodes and * starting new jobs as available/necessary. * * Deferred commands for the job are placed on the .END node. * * If there was a serious error (job_errors != 0; not an ignored one), no more * jobs will be started. * * Input: * job job to finish * status sub-why job went away */ static void JobFinish (Job *job, WAIT_T status) { bool done, return_job_token; DEBUG3(JOB, "JobFinish: %d [%s], status %d\n", job->pid, job->node->name, status); if ((WIFEXITED(status) && ((WEXITSTATUS(status) != 0 && !job->ignerr))) || WIFSIGNALED(status)) { /* Finished because of an error. */ JobClosePipes(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { if (fclose(job->cmdFILE) != 0) Punt("Cannot write shell script for '%s': %s", job->node->name, strerror(errno)); job->cmdFILE = NULL; } done = true; } else if (WIFEXITED(status)) { /* * Deal with ignored errors in -B mode. We need to print a * message telling of the ignored error as well as to run * the next command. */ done = WEXITSTATUS(status) != 0; JobClosePipes(job); } else { /* No need to close things down or anything. */ done = false; } if (done) JobFinishDone(job, &status); #ifdef USE_META if (useMeta) { int meta_status = meta_job_finish(job); if (meta_status != 0 && status == 0) status = meta_status; } #endif return_job_token = false; Trace_Log(JOBEND, job); if (!job->special) { if (WAIT_STATUS(status) != 0 || (aborting == ABORT_ERROR) || aborting == ABORT_INTERRUPT) return_job_token = true; } if (aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT && (WAIT_STATUS(status) == 0)) { /* * As long as we aren't aborting and the job didn't return a * non-zero status that we shouldn't ignore, we call * Make_Update to update the parents. */ JobSaveCommands(job); job->node->made = MADE; if (!job->special) return_job_token = true; Make_Update(job->node); job->status = JOB_ST_FREE; } else if (status != 0) { job_errors++; job->status = JOB_ST_FREE; } if (job_errors > 0 && !opts.keepgoing && aborting != ABORT_INTERRUPT) { /* Prevent more jobs from getting started. */ aborting = ABORT_ERROR; } if (return_job_token) Job_TokenReturn(); if (aborting == ABORT_ERROR && jobTokensRunning == 0) Finish(job_errors); } static void TouchRegular(GNode *gn) { const char *file = GNode_Path(gn); struct utimbuf times; int fd; char c; times.actime = now; times.modtime = now; if (utime(file, ×) >= 0) return; fd = open(file, O_RDWR | O_CREAT, 0666); if (fd < 0) { (void)fprintf(stderr, "*** couldn't touch %s: %s\n", file, strerror(errno)); (void)fflush(stderr); return; /* XXX: What about propagating the error? */ } /* * Last resort: update the file's time stamps in the traditional way. * XXX: This doesn't work for empty files, which are sometimes used * as marker files. */ if (read(fd, &c, 1) == 1) { (void)lseek(fd, 0, SEEK_SET); while (write(fd, &c, 1) == -1 && errno == EAGAIN) continue; } (void)close(fd); /* XXX: What about propagating the error? */ } /* * Touch the given target. Called by JobStart when the -t flag was given. * * The modification date of the file is changed. * If the file did not exist, it is created. */ void Job_Touch(GNode *gn, bool echo) { if (gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC | OP_OPTIONAL | OP_SPECIAL | OP_PHONY)) { /* * These are "virtual" targets and should not really be * created. */ return; } if (echo || !GNode_ShouldExecute(gn)) { (void)fprintf(stdout, "touch %s\n", gn->name); (void)fflush(stdout); } if (!GNode_ShouldExecute(gn)) return; if (gn->type & OP_ARCHV) Arch_Touch(gn); else if (gn->type & OP_LIB) Arch_TouchLib(gn); else TouchRegular(gn); } /* * Make sure the given node has all the commands it needs. * * The node will have commands from the .DEFAULT rule added to it if it * needs them. * * Input: * gn The target whose commands need verifying * abortProc Function to abort with message * * Results: * true if the commands list is/was ok. */ bool Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) { if (GNode_IsTarget(gn)) return true; if (!Lst_IsEmpty(&gn->commands)) return true; if ((gn->type & OP_LIB) && !Lst_IsEmpty(&gn->children)) return true; /* * No commands. Look for .DEFAULT rule from which we might infer * commands. */ if (defaultNode != NULL && !Lst_IsEmpty(&defaultNode->commands) && !(gn->type & OP_SPECIAL)) { /* * The traditional Make only looks for a .DEFAULT if the node * was never the target of an operator, so that's what we do * too. * * The .DEFAULT node acts like a transformation rule, in that * gn also inherits any attributes or sources attached to * .DEFAULT itself. */ Make_HandleUse(defaultNode, gn); Var_Set(gn, IMPSRC, GNode_VarTarget(gn)); return true; } Dir_UpdateMTime(gn, false); if (gn->mtime != 0 || (gn->type & OP_SPECIAL)) return true; /* * The node wasn't the target of an operator. We have no .DEFAULT * rule to go on and the target doesn't already exist. There's * nothing more we can do for this branch. If the -k flag wasn't * given, we stop in our tracks, otherwise we just don't update * this node's parents so they never get examined. */ if (gn->flags.fromDepend) { if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, "%s: %s, %u: ignoring stale %s for %s\n", progname, gn->fname, gn->lineno, makeDependfile, gn->name); return true; } if (gn->type & OP_OPTIONAL) { (void)fprintf(stdout, "%s: don't know how to make %s (%s)\n", progname, gn->name, "ignored"); (void)fflush(stdout); return true; } if (opts.keepgoing) { (void)fprintf(stdout, "%s: don't know how to make %s (%s)\n", progname, gn->name, "continuing"); (void)fflush(stdout); return false; } abortProc("%s: don't know how to make %s. Stop", progname, gn->name); return false; } /* * Execute the shell for the given job. * * See Job_CatchOutput for handling the output of the shell. */ static void JobExec(Job *job, char **argv) { int cpid; /* ID of new child */ sigset_t mask; if (DEBUG(JOB)) { int i; debug_printf("Running %s\n", job->node->name); debug_printf("\tCommand: "); for (i = 0; argv[i] != NULL; i++) { debug_printf("%s ", argv[i]); } debug_printf("\n"); } /* * Some jobs produce no output and it's disconcerting to have * no feedback of their running (since they produce no output, the * banner with their name in it never appears). This is an attempt to * provide that feedback, even if nothing follows it. */ if (job->echo) SwitchOutputTo(job->node); /* No interruptions until this job is on the `jobs' list */ JobSigLock(&mask); /* Pre-emptively mark job running, pid still zero though */ job->status = JOB_ST_RUNNING; Var_ReexportVars(); cpid = vfork(); if (cpid == -1) Punt("Cannot vfork: %s", strerror(errno)); if (cpid == 0) { /* Child */ sigset_t tmask; #ifdef USE_META if (useMeta) meta_job_child(job); #endif /* * Reset all signal handlers; this is necessary because we * also need to unblock signals before we exec(2). */ JobSigReset(); /* Now unblock signals */ sigemptyset(&tmask); JobSigUnlock(&tmask); /* * Must duplicate the input stream down to the child's input * and reset it to the beginning (again). Since the stream * was marked close-on-exec, we must clear that bit in the * new input. */ if (dup2(fileno(job->cmdFILE), 0) == -1) execDie("dup2", "job->cmdFILE"); if (fcntl(0, F_SETFD, 0) == -1) execDie("fcntl clear close-on-exec", "stdin"); if (lseek(0, 0, SEEK_SET) == -1) execDie("lseek to 0", "stdin"); if (Always_pass_job_queue || (job->node->type & (OP_MAKE | OP_SUBMAKE))) { /* * Pass job token pipe to submakes. */ if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1) execDie("clear close-on-exec", "tokenWaitJob.inPipe"); if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1) execDie("clear close-on-exec", "tokenWaitJob.outPipe"); } /* * Set up the child's output to be routed through the pipe * we've created for it. */ if (dup2(job->outPipe, 1) == -1) execDie("dup2", "job->outPipe"); /* * The output channels are marked close on exec. This bit * was duplicated by the dup2(on some systems), so we have * to clear it before routing the shell's error output to * the same place as its standard output. */ if (fcntl(1, F_SETFD, 0) == -1) execDie("clear close-on-exec", "stdout"); if (dup2(1, 2) == -1) execDie("dup2", "1, 2"); /* * We want to switch the child into a different process * family so we can kill it and all its descendants in * one fell swoop, by killing its process family, but not * commit suicide. */ #if defined(HAVE_SETPGID) (void)setpgid(0, getpid()); #else # if defined(HAVE_SETSID) /* XXX: dsl - I'm sure this should be setpgrp()... */ (void)setsid(); # else (void)setpgrp(0, getpid()); # endif #endif (void)execv(shellPath, argv); execDie("exec", shellPath); } /* Parent, continuing after the child exec */ job->pid = cpid; Trace_Log(JOBSTART, job); #ifdef USE_META if (useMeta) meta_job_parent(job, cpid); #endif /* * Set the current position in the buffer to the beginning * and mark another stream to watch in the outputs mask */ job->curPos = 0; watchfd(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { if (fclose(job->cmdFILE) != 0) Punt("Cannot write shell script for '%s': %s", job->node->name, strerror(errno)); job->cmdFILE = NULL; } /* Now that the job is actually running, add it to the table. */ if (DEBUG(JOB)) { debug_printf("JobExec(%s): pid %d added to jobs table\n", job->node->name, job->pid); DumpJobs("job started"); } JobSigUnlock(&mask); } /* Create the argv needed to execute the shell for a given job. */ static void JobMakeArgv(Job *job, char **argv) { int argc; static char args[10]; /* For merged arguments */ argv[0] = UNCONST(shellName); argc = 1; if ((shell->errFlag != NULL && shell->errFlag[0] != '-') || (shell->echoFlag != NULL && shell->echoFlag[0] != '-')) { /* * At least one of the flags doesn't have a minus before it, * so merge them together. Have to do this because the Bourne * shell thinks its second argument is a file to source. * Grrrr. Note the ten-character limitation on the combined * arguments. * * TODO: Research until when the above comments were * practically relevant. */ (void)snprintf(args, sizeof args, "-%s%s", (job->ignerr ? "" : (shell->errFlag != NULL ? shell->errFlag : "")), (!job->echo ? "" : (shell->echoFlag != NULL ? shell->echoFlag : ""))); if (args[1] != '\0') { argv[argc] = args; argc++; } } else { if (!job->ignerr && shell->errFlag != NULL) { argv[argc] = UNCONST(shell->errFlag); argc++; } if (job->echo && shell->echoFlag != NULL) { argv[argc] = UNCONST(shell->echoFlag); argc++; } } argv[argc] = NULL; } static void JobWriteShellCommands(Job *job, GNode *gn, bool *out_run) { /* * tfile is the name of a file into which all shell commands * are put. It is removed before the child shell is executed, * unless DEBUG(SCRIPT) is set. */ char tfile[MAXPATHLEN]; int tfd; /* File descriptor to the temp file */ tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile); job->cmdFILE = fdopen(tfd, "w+"); if (job->cmdFILE == NULL) Punt("Could not fdopen %s", tfile); (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC); #ifdef USE_META if (useMeta) { meta_job_start(job, gn); if (gn->type & OP_SILENT) /* might have changed */ job->echo = false; } #endif *out_run = JobWriteCommands(job); } /* * Start a target-creation process going for the target described by gn. * * Results: * JOB_ERROR if there was an error in the commands, JOB_FINISHED * if there isn't actually anything left to do for the job and * JOB_RUNNING if the job has been started. * * Details: * A new Job node is created and added to the list of running * jobs. PMake is forked and a child shell created. * * NB: The return value is ignored by everyone. */ static JobStartResult JobStart(GNode *gn, bool special) { Job *job; /* new job descriptor */ char *argv[10]; /* Argument vector to shell */ bool cmdsOK; /* true if the nodes commands were all right */ bool run; for (job = job_table; job < job_table_end; job++) { if (job->status == JOB_ST_FREE) break; } if (job >= job_table_end) Punt("JobStart no job slots vacant"); memset(job, 0, sizeof *job); job->node = gn; job->tailCmds = NULL; job->status = JOB_ST_SET_UP; job->special = special || gn->type & OP_SPECIAL; job->ignerr = opts.ignoreErrors || gn->type & OP_IGNORE; job->echo = !(opts.silent || gn->type & OP_SILENT); /* * Check the commands now so any attributes from .DEFAULT have a * chance to migrate to the node. */ cmdsOK = Job_CheckCommands(gn, Error); job->inPollfd = NULL; if (Lst_IsEmpty(&gn->commands)) { job->cmdFILE = stdout; run = false; /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) { PrintOnError(gn, "\n"); /* provide some clue */ DieHorribly(); } } else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) || (!opts.noExecute && !opts.touch)) { /* * The above condition looks very similar to * GNode_ShouldExecute but is subtly different. It prevents * that .MAKE targets are touched since these are usually * virtual targets. */ /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) { PrintOnError(gn, "\n"); /* provide some clue */ DieHorribly(); } JobWriteShellCommands(job, gn, &run); (void)fflush(job->cmdFILE); } else if (!GNode_ShouldExecute(gn)) { /* * Just write all the commands to stdout in one fell swoop. * This still sets up job->tailCmds correctly. */ SwitchOutputTo(gn); job->cmdFILE = stdout; if (cmdsOK) JobWriteCommands(job); run = false; (void)fflush(job->cmdFILE); } else { Job_Touch(gn, job->echo); run = false; } /* If we're not supposed to execute a shell, don't. */ if (!run) { if (!job->special) Job_TokenReturn(); /* Unlink and close the command file if we opened one */ if (job->cmdFILE != NULL && job->cmdFILE != stdout) { (void)fclose(job->cmdFILE); job->cmdFILE = NULL; } /* * We only want to work our way up the graph if we aren't * here because the commands for the job were no good. */ if (cmdsOK && aborting == ABORT_NONE) { JobSaveCommands(job); job->node->made = MADE; Make_Update(job->node); } job->status = JOB_ST_FREE; return cmdsOK ? JOB_FINISHED : JOB_ERROR; } /* * Set up the control arguments to the shell. This is based on the * flags set earlier for this job. */ JobMakeArgv(job, argv); /* Create the pipe by which we'll get the shell's output. */ JobCreatePipe(job, 3); JobExec(job, argv); return JOB_RUNNING; } /* * If the shell has an output filter (which only csh and ksh have by default), * print the output of the child process, skipping the noPrint text of the * shell. * * Return the part of the output that the calling function needs to output by * itself. */ static char * PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */ { char *ecp; /* XXX: should be const */ if (shell->noPrint == NULL || shell->noPrint[0] == '\0') return cp; /* * XXX: What happens if shell->noPrint occurs on the boundary of * the buffer? To work correctly in all cases, this should rather * be a proper stream filter instead of doing string matching on * selected chunks of the output. */ while ((ecp = strstr(cp, shell->noPrint)) != NULL) { if (ecp != cp) { *ecp = '\0'; /* XXX: avoid writing to the buffer */ /* * The only way there wouldn't be a newline after * this line is if it were the last in the buffer. * however, since the noPrint output comes after it, * there must be a newline, so we don't print one. */ /* XXX: What about null bytes in the output? */ (void)fprintf(stdout, "%s", cp); (void)fflush(stdout); } cp = ecp + shell->noPrintLen; if (cp == endp) break; cp++; /* skip over the (XXX: assumed) newline */ pp_skip_whitespace(&cp); } return cp; } /* * This function is called whenever there is something to read on the pipe. * We collect more output from the given job and store it in the job's * outBuf. If this makes up a line, we print it tagged by the job's * identifier, as necessary. * * In the output of the shell, the 'noPrint' lines are removed. If the * command is not alone on the line (the character after it is not \0 or * \n), we do print whatever follows it. * * Input: * job the job whose output needs printing * finish true if this is the last time we'll be called * for this job */ static void CollectOutput(Job *job, bool finish) { bool gotNL; /* true if got a newline */ bool fbuf; /* true if our buffer filled up */ size_t nr; /* number of bytes read */ size_t i; /* auxiliary index into outBuf */ size_t max; /* limit for i (end of current data) */ ssize_t nRead; /* (Temporary) number of bytes read */ /* Read as many bytes as will fit in the buffer. */ again: gotNL = false; fbuf = false; nRead = read(job->inPipe, &job->outBuf[job->curPos], JOB_BUFSIZE - job->curPos); if (nRead < 0) { if (errno == EAGAIN) return; - if (DEBUG(JOB)) { + if (DEBUG(JOB)) perror("CollectOutput(piperead)"); - } nr = 0; - } else { + } else nr = (size_t)nRead; - } + + if (nr == 0) + finish = false; /* stop looping */ /* * If we hit the end-of-file (the job is dead), we must flush its * remaining output, so pretend we read a newline if there's any * output remaining in the buffer. - * Also clear the 'finish' flag so we stop looping. */ if (nr == 0 && job->curPos != 0) { job->outBuf[job->curPos] = '\n'; nr = 1; - finish = false; - } else if (nr == 0) { - finish = false; } - /* - * Look for the last newline in the bytes we just got. If there is - * one, break out of the loop with 'i' as its index and gotNL set - * true. - */ max = job->curPos + nr; + for (i = job->curPos; i < max; i++) + if (job->outBuf[i] == '\0') + job->outBuf[i] = ' '; + + /* Look for the last newline in the bytes we just got. */ for (i = job->curPos + nr - 1; i >= job->curPos && i != (size_t)-1; i--) { if (job->outBuf[i] == '\n') { gotNL = true; break; - } else if (job->outBuf[i] == '\0') { - /* - * FIXME: The null characters are only replaced with - * space _after_ the last '\n'. Everywhere else they - * hide the rest of the command output. - */ - job->outBuf[i] = ' '; } } if (!gotNL) { job->curPos += nr; if (job->curPos == JOB_BUFSIZE) { /* * If we've run out of buffer space, we have no choice * but to print the stuff. sigh. */ fbuf = true; i = job->curPos; } } if (gotNL || fbuf) { /* * Need to send the output to the screen. Null terminate it * first, overwriting the newline character if there was one. * So long as the line isn't one we should filter (according * to the shell description), we print the line, preceded * by a target banner if this target isn't the same as the * one for which we last printed something. * The rest of the data in the buffer are then shifted down * to the start of the buffer and curPos is set accordingly. */ job->outBuf[i] = '\0'; if (i >= job->curPos) { char *cp; /* * FIXME: SwitchOutputTo should be here, according to * the comment above. But since PrintOutput does not * do anything in the default shell, this bug has gone * unnoticed until now. */ cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]); /* * There's still more in the output buffer. This time, * though, we know there's no newline at the end, so * we add one of our own free will. */ if (*cp != '\0') { if (!opts.silent) SwitchOutputTo(job->node); #ifdef USE_META if (useMeta) { meta_job_output(job, cp, gotNL ? "\n" : ""); } #endif (void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); (void)fflush(stdout); } } /* * max is the last offset still in the buffer. Move any * remaining characters to the start of the buffer and * update the end marker curPos. */ if (i < max) { (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1)); job->curPos = max - (i + 1); } else { assert(i == max); job->curPos = 0; } } if (finish) { /* * If the finish flag is true, we must loop until we hit * end-of-file on the pipe. This is guaranteed to happen * eventually since the other end of the pipe is now closed * (we closed it explicitly and the child has exited). When * we do get an EOF, finish will be set false and we'll fall * through and out. */ goto again; } } static void JobRun(GNode *targ) { #if 0 /* * Unfortunately it is too complicated to run .BEGIN, .END, and * .INTERRUPT job in the parallel job module. As of 2020-09-25, * unit-tests/deptgt-end-jobs.mk hangs in an endless loop. * * Running these jobs in compat mode also guarantees that these * jobs do not overlap with other unrelated jobs. */ GNodeList lst = LST_INIT; Lst_Append(&lst, targ); (void)Make_Run(&lst); Lst_Done(&lst); JobStart(targ, true); while (jobTokensRunning != 0) { Job_CatchOutput(); } #else Compat_Make(targ, targ); /* XXX: Replace with GNode_IsError(gn) */ if (targ->made == ERROR) { PrintOnError(targ, "\n\nStop.\n"); exit(1); } #endif } /* * Handle the exit of a child. Called from Make_Make. * * The job descriptor is removed from the list of children. * * Notes: * We do waits, blocking or not, according to the wisdom of our * caller, until there are no more children to report. For each * job, call JobFinish to finish things off. */ void Job_CatchChildren(void) { int pid; /* pid of dead child */ WAIT_T status; /* Exit/termination status */ /* Don't even bother if we know there's no one around. */ if (jobTokensRunning == 0) return; /* Have we received SIGCHLD since last call? */ if (caught_sigchld == 0) return; caught_sigchld = 0; while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) { DEBUG2(JOB, "Process %d exited/stopped status %x.\n", pid, WAIT_STATUS(status)); JobReapChild(pid, status, true); } } /* * It is possible that wait[pid]() was called from elsewhere, * this lets us reap jobs regardless. */ void JobReapChild(pid_t pid, WAIT_T status, bool isJobs) { Job *job; /* job descriptor for dead child */ /* Don't even bother if we know there's no one around. */ if (jobTokensRunning == 0) return; job = JobFindPid(pid, JOB_ST_RUNNING, isJobs); if (job == NULL) { if (isJobs) { if (!lurking_children) Error("Child (%d) status %x not in table?", pid, status); } return; /* not ours */ } if (WIFSTOPPED(status)) { DEBUG2(JOB, "Process %d (%s) stopped.\n", job->pid, job->node->name); if (!make_suspended) { switch (WSTOPSIG(status)) { case SIGTSTP: (void)printf("*** [%s] Suspended\n", job->node->name); break; case SIGSTOP: (void)printf("*** [%s] Stopped\n", job->node->name); break; default: (void)printf("*** [%s] Stopped -- signal %d\n", job->node->name, WSTOPSIG(status)); } job->suspended = true; } (void)fflush(stdout); return; } job->status = JOB_ST_FINISHED; job->exit_status = WAIT_STATUS(status); JobFinish(job, status); } /* * Catch the output from our children, if we're using pipes do so. Otherwise * just block time until we get a signal(most likely a SIGCHLD) since there's * no point in just spinning when there's nothing to do and the reaping of a * child can wait for a while. */ void Job_CatchOutput(void) { int nready; Job *job; unsigned int i; (void)fflush(stdout); /* The first fd in the list is the job token pipe */ do { nready = poll(fds + 1 - wantToken, fdsLen - 1 + wantToken, POLL_MSEC); } while (nready < 0 && errno == EINTR); if (nready < 0) Punt("poll: %s", strerror(errno)); if (nready > 0 && readyfd(&childExitJob)) { char token = 0; ssize_t count; count = read(childExitJob.inPipe, &token, 1); if (count == 1) { if (token == DO_JOB_RESUME[0]) /* * Complete relay requested from our SIGCONT * handler */ JobRestartJobs(); } else if (count == 0) Punt("unexpected eof on token pipe"); else if (errno != EAGAIN) Punt("token pipe read: %s", strerror(errno)); nready--; } Job_CatchChildren(); if (nready == 0) return; for (i = npseudojobs * nfds_per_job(); i < fdsLen; i++) { if (fds[i].revents == 0) continue; job = jobByFdIndex[i]; if (job->status == JOB_ST_RUNNING) CollectOutput(job, false); #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) /* * With meta mode, we may have activity on the job's filemon * descriptor too, which at the moment is any pollfd other * than job->inPollfd. */ if (useMeta && job->inPollfd != &fds[i]) { if (meta_job_event(job) <= 0) fds[i].events = 0; /* never mind */ } #endif if (--nready == 0) return; } } /* * Start the creation of a target. Basically a front-end for JobStart used by * the Make module. */ void Job_Make(GNode *gn) { (void)JobStart(gn, false); } static void InitShellNameAndPath(void) { shellName = shell->name; #ifdef DEFSHELL_CUSTOM if (shellName[0] == '/') { shellPath = shellName; shellName = str_basename(shellPath); return; } #endif #ifdef DEFSHELL_PATH shellPath = DEFSHELL_PATH; #else shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName); #endif } void Shell_Init(void) { if (shellPath == NULL) InitShellNameAndPath(); Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY); if (shell->errFlag == NULL) shell->errFlag = ""; if (shell->echoFlag == NULL) shell->echoFlag = ""; if (shell->hasErrCtl && shell->errFlag[0] != '\0') { if (shellErrFlag != NULL && strcmp(shell->errFlag, &shellErrFlag[1]) != 0) { free(shellErrFlag); shellErrFlag = NULL; } if (shellErrFlag == NULL) shellErrFlag = str_concat2("-", shell->errFlag); } else if (shellErrFlag != NULL) { free(shellErrFlag); shellErrFlag = NULL; } } /* * Return the string literal that is used in the current command shell * to produce a newline character. */ const char * Shell_GetNewline(void) { return shell->newline; } void Job_SetPrefix(void) { if (targPrefix != NULL) { free(targPrefix); } else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) { Global_Set(MAKE_JOB_PREFIX, "---"); } (void)Var_Subst("${" MAKE_JOB_PREFIX "}", SCOPE_GLOBAL, VARE_WANTRES, &targPrefix); /* TODO: handle errors */ } static void AddSig(int sig, SignalProc handler) { if (bmake_signal(sig, SIG_IGN) != SIG_IGN) { sigaddset(&caught_signals, sig); (void)bmake_signal(sig, handler); } } /* Initialize the process module. */ void Job_Init(void) { Job_SetPrefix(); /* Allocate space for all the job info */ job_table = bmake_malloc((size_t)opts.maxJobs * sizeof *job_table); memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table); job_table_end = job_table + opts.maxJobs; wantToken = 0; caught_sigchld = 0; aborting = ABORT_NONE; job_errors = 0; Always_pass_job_queue = GetBooleanExpr(MAKE_ALWAYS_PASS_JOB_QUEUE, Always_pass_job_queue); Job_error_token = GetBooleanExpr(MAKE_JOB_ERROR_TOKEN, Job_error_token); /* * There is a non-zero chance that we already have children. * eg after 'make -f- < 0) continue; if (rval == 0) lurking_children = true; break; } Shell_Init(); JobCreatePipe(&childExitJob, 3); { /* Preallocate enough for the maximum number of jobs. */ size_t nfds = (npseudojobs + (size_t)opts.maxJobs) * nfds_per_job(); fds = bmake_malloc(sizeof *fds * nfds); jobByFdIndex = bmake_malloc(sizeof *jobByFdIndex * nfds); } /* These are permanent entries and take slots 0 and 1 */ watchfd(&tokenWaitJob); watchfd(&childExitJob); sigemptyset(&caught_signals); /* * Install a SIGCHLD handler. */ (void)bmake_signal(SIGCHLD, JobChildSig); sigaddset(&caught_signals, SIGCHLD); /* * Catch the four signals that POSIX specifies if they aren't ignored. * JobPassSig will take care of calling JobInterrupt if appropriate. */ AddSig(SIGINT, JobPassSig_int); AddSig(SIGHUP, JobPassSig_term); AddSig(SIGTERM, JobPassSig_term); AddSig(SIGQUIT, JobPassSig_term); /* * There are additional signals that need to be caught and passed if * either the export system wants to be told directly of signals or if * we're giving each job its own process group (since then it won't get * signals from the terminal driver as we own the terminal) */ AddSig(SIGTSTP, JobPassSig_suspend); AddSig(SIGTTOU, JobPassSig_suspend); AddSig(SIGTTIN, JobPassSig_suspend); AddSig(SIGWINCH, JobCondPassSig); AddSig(SIGCONT, JobContinueSig); (void)Job_RunTarget(".BEGIN", NULL); /* * Create the .END node now, even though no code in the unit tests * depends on it. See also Targ_GetEndNode in Compat_MakeAll. */ (void)Targ_GetEndNode(); } static void DelSig(int sig) { if (sigismember(&caught_signals, sig) != 0) (void)bmake_signal(sig, SIG_DFL); } static void JobSigReset(void) { DelSig(SIGINT); DelSig(SIGHUP); DelSig(SIGQUIT); DelSig(SIGTERM); DelSig(SIGTSTP); DelSig(SIGTTOU); DelSig(SIGTTIN); DelSig(SIGWINCH); DelSig(SIGCONT); (void)bmake_signal(SIGCHLD, SIG_DFL); } /* Find a shell in 'shells' given its name, or return NULL. */ static Shell * FindShellByName(const char *name) { Shell *sh = shells; const Shell *shellsEnd = sh + sizeof shells / sizeof shells[0]; for (sh = shells; sh < shellsEnd; sh++) { if (strcmp(name, sh->name) == 0) return sh; } return NULL; } /* * Parse a shell specification and set up 'shell', shellPath and * shellName appropriately. * * Input: * line The shell spec * * Results: * false if the specification was incorrect. * * Side Effects: * 'shell' points to a Shell structure (either predefined or * created from the shell spec), shellPath is the full path of the * shell described by 'shell', while shellName is just the * final component of shellPath. * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * newline String literal to represent a newline char * check Command to turn on error checking if hasErrCtl * is true or template of command to echo a command * for which error checking is off if hasErrCtl is * false. * ignore Command to turn off error checking if hasErrCtl * is true or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is false. */ bool Job_ParseShell(char *line) { Words wordsList; char **words; char **argv; size_t argc; char *path; Shell newShell; bool fullSpec = false; Shell *sh; /* XXX: don't use line as an iterator variable */ pp_skip_whitespace(&line); free(shell_freeIt); memset(&newShell, 0, sizeof newShell); /* * Parse the specification by keyword */ wordsList = Str_Words(line, true); words = wordsList.words; argc = wordsList.len; path = wordsList.freeIt; if (words == NULL) { Error("Unterminated quoted string [%s]", line); return false; } shell_freeIt = path; for (path = NULL, argv = words; argc != 0; argc--, argv++) { char *arg = *argv; if (strncmp(arg, "path=", 5) == 0) { path = arg + 5; } else if (strncmp(arg, "name=", 5) == 0) { newShell.name = arg + 5; } else { if (strncmp(arg, "quiet=", 6) == 0) { newShell.echoOff = arg + 6; } else if (strncmp(arg, "echo=", 5) == 0) { newShell.echoOn = arg + 5; } else if (strncmp(arg, "filter=", 7) == 0) { newShell.noPrint = arg + 7; newShell.noPrintLen = strlen(newShell.noPrint); } else if (strncmp(arg, "echoFlag=", 9) == 0) { newShell.echoFlag = arg + 9; } else if (strncmp(arg, "errFlag=", 8) == 0) { newShell.errFlag = arg + 8; } else if (strncmp(arg, "hasErrCtl=", 10) == 0) { char c = arg[10]; newShell.hasErrCtl = c == 'Y' || c == 'y' || c == 'T' || c == 't'; } else if (strncmp(arg, "newline=", 8) == 0) { newShell.newline = arg + 8; } else if (strncmp(arg, "check=", 6) == 0) { /* * Before 2020-12-10, these two variables had * been a single variable. */ newShell.errOn = arg + 6; newShell.echoTmpl = arg + 6; } else if (strncmp(arg, "ignore=", 7) == 0) { /* * Before 2020-12-10, these two variables had * been a single variable. */ newShell.errOff = arg + 7; newShell.runIgnTmpl = arg + 7; } else if (strncmp(arg, "errout=", 7) == 0) { newShell.runChkTmpl = arg + 7; } else if (strncmp(arg, "comment=", 8) == 0) { newShell.commentChar = arg[8]; } else { Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", arg); free(words); return false; } fullSpec = true; } } if (path == NULL) { /* * If no path was given, the user wants one of the * pre-defined shells, yes? So we find the one s/he wants * with the help of FindShellByName and set things up the * right way. shellPath will be set up by Shell_Init. */ if (newShell.name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); free(words); return false; } else { if ((sh = FindShellByName(newShell.name)) == NULL) { Parse_Error(PARSE_WARNING, "%s: No matching shell", newShell.name); free(words); return false; } shell = sh; shellName = newShell.name; if (shellPath != NULL) { /* * Shell_Init has already been called! * Do it again. */ free(UNCONST(shellPath)); shellPath = NULL; Shell_Init(); } } } else { /* * The user provided a path. If s/he gave nothing else * (fullSpec is false), try and find a matching shell in the * ones we know of. Else we just take the specification at * its word and copy it to a new location. In either case, * we need to record the path the user gave for the shell. */ shellPath = path; path = strrchr(path, '/'); if (path == NULL) { path = UNCONST(shellPath); } else { path++; } if (newShell.name != NULL) { shellName = newShell.name; } else { shellName = path; } if (!fullSpec) { if ((sh = FindShellByName(shellName)) == NULL) { Parse_Error(PARSE_WARNING, "%s: No matching shell", shellName); free(words); return false; } shell = sh; } else { shell = bmake_malloc(sizeof *shell); *shell = newShell; } /* this will take care of shellErrFlag */ Shell_Init(); } if (shell->echoOn != NULL && shell->echoOff != NULL) shell->hasEchoCtl = true; if (!shell->hasErrCtl) { if (shell->echoTmpl == NULL) shell->echoTmpl = ""; if (shell->runIgnTmpl == NULL) shell->runIgnTmpl = "%s\n"; } /* * Do not free up the words themselves, since they might be in use * by the shell specification. */ free(words); return true; } /* * Handle the receipt of an interrupt. * * All children are killed. Another job will be started if the .INTERRUPT * target is defined. * * Input: * runINTERRUPT Non-zero if commands for the .INTERRUPT target * should be executed * signo signal received */ static void JobInterrupt(bool runINTERRUPT, int signo) { Job *job; /* job descriptor in that element */ GNode *interrupt; /* the node describing the .INTERRUPT target */ sigset_t mask; GNode *gn; aborting = ABORT_INTERRUPT; JobSigLock(&mask); for (job = job_table; job < job_table_end; job++) { if (job->status != JOB_ST_RUNNING) continue; gn = job->node; JobDeleteTarget(gn); if (job->pid != 0) { DEBUG2(JOB, "JobInterrupt passing signal %d to child %d.\n", signo, job->pid); KILLPG(job->pid, signo); } } JobSigUnlock(&mask); if (runINTERRUPT && !opts.touch) { interrupt = Targ_FindNode(".INTERRUPT"); if (interrupt != NULL) { opts.ignoreErrors = false; JobRun(interrupt); } } Trace_Log(MAKEINTR, NULL); exit(signo); /* XXX: why signo? */ } /* * Do the final processing, i.e. run the commands attached to the .END target. * * Return the number of errors reported. */ int Job_Finish(void) { GNode *endNode = Targ_GetEndNode(); if (!Lst_IsEmpty(&endNode->commands) || !Lst_IsEmpty(&endNode->children)) { if (job_errors != 0) { Error("Errors reported so .END ignored"); } else { JobRun(endNode); } } return job_errors; } /* Clean up any memory used by the jobs module. */ void Job_End(void) { #ifdef CLEANUP free(shell_freeIt); #endif } /* * Waits for all running jobs to finish and returns. * Sets 'aborting' to ABORT_WAIT to prevent other jobs from starting. */ void Job_Wait(void) { aborting = ABORT_WAIT; while (jobTokensRunning != 0) { Job_CatchOutput(); } aborting = ABORT_NONE; } /* * Abort all currently running jobs without handling output or anything. * This function is to be called only in the event of a major error. * Most definitely NOT to be called from JobInterrupt. * * All children are killed, not just the firstborn. */ void Job_AbortAll(void) { Job *job; /* the job descriptor in that element */ WAIT_T foo; aborting = ABORT_ERROR; if (jobTokensRunning != 0) { for (job = job_table; job < job_table_end; job++) { if (job->status != JOB_ST_RUNNING) continue; /* * kill the child process with increasingly drastic * signals to make darn sure it's dead. */ KILLPG(job->pid, SIGINT); KILLPG(job->pid, SIGKILL); } } /* * Catch as many children as want to report in at first, then give up */ while (waitpid((pid_t)-1, &foo, WNOHANG) > 0) continue; } /* * Tries to restart stopped jobs if there are slots available. * Called in process context in response to a SIGCONT. */ static void JobRestartJobs(void) { Job *job; for (job = job_table; job < job_table_end; job++) { if (job->status == JOB_ST_RUNNING && (make_suspended || job->suspended)) { DEBUG1(JOB, "Restarting stopped job pid %d.\n", job->pid); if (job->suspended) { (void)printf("*** [%s] Continued\n", job->node->name); (void)fflush(stdout); } job->suspended = false; if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { debug_printf("Failed to send SIGCONT to %d\n", job->pid); } } if (job->status == JOB_ST_FINISHED) { /* * Job exit deferred after calling waitpid() in a * signal handler */ JobFinish(job, job->exit_status); } } make_suspended = false; } static void watchfd(Job *job) { if (job->inPollfd != NULL) Punt("Watching watched job"); fds[fdsLen].fd = job->inPipe; fds[fdsLen].events = POLLIN; jobByFdIndex[fdsLen] = job; job->inPollfd = &fds[fdsLen]; fdsLen++; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { fds[fdsLen].fd = meta_job_fd(job); fds[fdsLen].events = fds[fdsLen].fd == -1 ? 0 : POLLIN; jobByFdIndex[fdsLen] = job; fdsLen++; } #endif } static void clearfd(Job *job) { size_t i; if (job->inPollfd == NULL) Punt("Unwatching unwatched job"); i = (size_t)(job->inPollfd - fds); fdsLen--; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { /* * Sanity check: there should be two fds per job, so the job's * pollfd number should be even. */ assert(nfds_per_job() == 2); if (i % 2 != 0) Punt("odd-numbered fd with meta"); fdsLen--; } #endif /* * Move last job in table into hole made by dead job. */ if (fdsLen != i) { fds[i] = fds[fdsLen]; jobByFdIndex[i] = jobByFdIndex[fdsLen]; jobByFdIndex[i]->inPollfd = &fds[i]; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { fds[i + 1] = fds[fdsLen + 1]; jobByFdIndex[i + 1] = jobByFdIndex[fdsLen + 1]; } #endif } job->inPollfd = NULL; } static bool readyfd(Job *job) { if (job->inPollfd == NULL) Punt("Polling unwatched job"); return (job->inPollfd->revents & POLLIN) != 0; } /* * Put a token (back) into the job pipe. * This allows a make process to start a build job. */ static void JobTokenAdd(void) { char tok = JOB_TOKENS[aborting], tok1; if (!Job_error_token && aborting == ABORT_ERROR) { if (jobTokensRunning == 0) return; tok = '+'; /* no error token */ } /* If we are depositing an error token flush everything else */ while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1) continue; DEBUG3(JOB, "(%d) aborting %d, deposit token %c\n", getpid(), aborting, tok); while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; } /* Get a temp file */ int Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz) { int fd; sigset_t mask; JobSigLock(&mask); fd = mkTempFile(pattern, tfile, tfile_sz); if (tfile != NULL && !DEBUG(SCRIPT)) unlink(tfile); JobSigUnlock(&mask); return fd; } /* Prep the job token pipe in the root make process. */ void Job_ServerStart(int max_tokens, int jp_0, int jp_1) { int i; char jobarg[64]; if (jp_0 >= 0 && jp_1 >= 0) { /* Pipe passed in from parent */ tokenWaitJob.inPipe = jp_0; tokenWaitJob.outPipe = jp_1; (void)fcntl(jp_0, F_SETFD, FD_CLOEXEC); (void)fcntl(jp_1, F_SETFD, FD_CLOEXEC); return; } JobCreatePipe(&tokenWaitJob, 15); snprintf(jobarg, sizeof jobarg, "%d,%d", tokenWaitJob.inPipe, tokenWaitJob.outPipe); Global_Append(MAKEFLAGS, "-J"); Global_Append(MAKEFLAGS, jobarg); /* * Preload the job pipe with one token per job, save the one * "extra" token for the primary job. * * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is * larger than the write buffer size of the pipe, we will * deadlock here. */ for (i = 1; i < max_tokens; i++) JobTokenAdd(); } /* Return a withdrawn token to the pool. */ void Job_TokenReturn(void) { jobTokensRunning--; if (jobTokensRunning < 0) Punt("token botch"); if (jobTokensRunning != 0 || JOB_TOKENS[aborting] != '+') JobTokenAdd(); } /* * Attempt to withdraw a token from the pool. * * If pool is empty, set wantToken so that we wake up when a token is * released. * * Returns true if a token was withdrawn, and false if the pool is currently * empty. */ bool Job_TokenWithdraw(void) { char tok, tok1; ssize_t count; wantToken = 0; DEBUG3(JOB, "Job_TokenWithdraw(%d): aborting %d, running %d\n", getpid(), aborting, jobTokensRunning); if (aborting != ABORT_NONE || (jobTokensRunning >= opts.maxJobs)) return false; count = read(tokenWaitJob.inPipe, &tok, 1); if (count == 0) Fatal("eof on job pipe!"); if (count < 0 && jobTokensRunning != 0) { if (errno != EAGAIN) { Fatal("job pipe read: %s", strerror(errno)); } DEBUG1(JOB, "(%d) blocked for token\n", getpid()); wantToken = 1; return false; } if (count == 1 && tok != '+') { /* make being aborted - remove any other job tokens */ DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok); while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) continue; /* And put the stopper back */ while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; if (shouldDieQuietly(NULL, 1)) exit(6); /* we aborted */ Fatal("A failure has been detected " "in another branch of the parallel make"); } if (count == 1 && jobTokensRunning == 0) /* We didn't want the token really */ while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; jobTokensRunning++; DEBUG1(JOB, "(%d) withdrew token\n", getpid()); return true; } /* * Run the named target if found. If a filename is specified, then set that * to the sources. * * Exits if the target fails. */ bool Job_RunTarget(const char *target, const char *fname) { GNode *gn = Targ_FindNode(target); if (gn == NULL) return false; if (fname != NULL) Var_Set(gn, ALLSRC, fname); JobRun(gn); /* XXX: Replace with GNode_IsError(gn) */ if (gn->made == ERROR) { PrintOnError(gn, "\n\nStop.\n"); exit(1); } return true; } #ifdef USE_SELECT int emul_poll(struct pollfd *fd, int nfd, int timeout) { fd_set rfds, wfds; int i, maxfd, nselect, npoll; struct timeval tv, *tvp; long usecs; FD_ZERO(&rfds); FD_ZERO(&wfds); maxfd = -1; for (i = 0; i < nfd; i++) { fd[i].revents = 0; if (fd[i].events & POLLIN) FD_SET(fd[i].fd, &rfds); if (fd[i].events & POLLOUT) FD_SET(fd[i].fd, &wfds); if (fd[i].fd > maxfd) maxfd = fd[i].fd; } if (maxfd >= FD_SETSIZE) { Punt("Ran out of fd_set slots; " "recompile with a larger FD_SETSIZE."); } if (timeout < 0) { tvp = NULL; } else { usecs = timeout * 1000; tv.tv_sec = usecs / 1000000; tv.tv_usec = usecs % 1000000; tvp = &tv; } nselect = select(maxfd + 1, &rfds, &wfds, NULL, tvp); if (nselect <= 0) return nselect; npoll = 0; for (i = 0; i < nfd; i++) { if (FD_ISSET(fd[i].fd, &rfds)) fd[i].revents |= POLLIN; if (FD_ISSET(fd[i].fd, &wfds)) fd[i].revents |= POLLOUT; if (fd[i].revents) npoll++; } return npoll; } #endif /* USE_SELECT */ diff --git a/contrib/bmake/machine.sh b/contrib/bmake/machine.sh index 0cf9daeeef3a..14fc5269ad40 100755 --- a/contrib/bmake/machine.sh +++ b/contrib/bmake/machine.sh @@ -1,106 +1,49 @@ : -# derrived from /etc/rc_d/os.sh +# This is mostly redundant. +# These days I use the pseudo machine "host" when building for host +# and $TARGET_HOST for its objdir # RCSid: -# $Id: machine.sh,v 1.18 2017/08/13 19:11:28 sjg Exp $ +# $Id: machine.sh,v 1.19 2023/01/17 18:30:21 sjg Exp $ # -# @(#) Copyright (c) 1994-2002 Simon J. Gerraty +# @(#) Copyright (c) 1994-2023 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # -OS=`uname` -OSREL=`uname -r` -OSMAJOR=`IFS=.; set $OSREL; echo $1` -machine=`uname -p 2>/dev/null || uname -m` -MACHINE= - -# there is at least one case of `uname -p` outputting -# a bunch of usless drivel -case "$machine" in -unknown|*[!A-Za-z0-9_-]*) - machine=`uname -m` - ;; -esac - -# Great! Solaris keeps moving arch(1) -# we need this here, and it is not always available... -Which() { - # some shells cannot correctly handle `IFS` - # in conjunction with the for loop. - _dirs=`IFS=:; echo ${2:-$PATH}` - for d in $_dirs - do - test -x $d/$1 && { echo $d/$1; break; } - done -} +# leverage os.sh +Mydir=`dirname $0` +. $Mydir/os.sh +# some further overrides - mostly for MACHINE_ACH case $OS in AIX) # from http://gnats.netbsd.org/29386 - OSMAJOR=`uname -v` - OSMINOR=`uname -r` - MACHINE=$OS$OSMAJOR.$OSMINOR MACHINE_ARCH=`bootinfo -T` ;; -OpenBSD) - MACHINE=$OS$OSMAJOR.$machine - arch=`Which arch /usr/bin:/usr/ucb:$PATH` - MACHINE_ARCH=`$arch -s`; - ;; Bitrig) - MACHINE=$OS$OSMAJOR.$machine - MACHINE_ARCH=`uname -m`; - ;; -*BSD) - MACHINE=$OS$OSMAJOR.$machine - ;; -SunOS) - arch=`Which arch /usr/bin:/usr/ucb:$PATH` - test "$arch" && machine_arch=`$arch` - - case "$OSREL" in - 4.0*) MACHINE_ARCH=$machine_arch MACHINE=$machine_arch;; - 4*) MACHINE_ARCH=$machine_arch;; - esac + MACHINE_ARCH=$MACHINE; ;; HP-UX) - MACHINE_ARCH=`IFS="/-."; set $machine; echo $1` - ;; -Interix) - MACHINE=i386 - MACHINE_ARCH=i386 + MACHINE_ARCH=`IFS="/-."; set $MACHINE; echo $1` ;; -UnixWare) - OSREL=`uname -v` - OSMAJOR=`IFS=.; set $OSREL; echo $1` - MACHINE_ARCH=`uname -m` - ;; -Linux) - case "$machine" in - i?86) MACHINE_ARCH=i386;;# does anyone really care about 686 vs 586? - esac - ;; esac -MACHINE=${MACHINE:-$OS$OSMAJOR} -MACHINE_ARCH=${MACHINE_ARCH:-$machine} - ( case "$0" in arch*) echo $MACHINE_ARCH;; *) case "$1" in "") echo $MACHINE;; *) echo $MACHINE_ARCH;; esac ;; esac -) | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz +) | toLower diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c index b6401ee39959..23350b49e536 100644 --- a/contrib/bmake/main.c +++ b/contrib/bmake/main.c @@ -1,2195 +1,2206 @@ -/* $NetBSD: main.c,v 1.582 2022/05/07 17:49:47 rillig Exp $ */ +/* $NetBSD: main.c,v 1.589 2023/01/26 20:48:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * The main file for this entire program. Exit routines etc. reside here. * * Utility functions defined in this file: * * Main_ParseArgLine * Parse and process command line arguments from a * single string. Used to implement the special targets * .MFLAGS and .MAKEFLAGS. * * Error Print a tagged error message. * * Fatal Print an error message and exit. * * Punt Abort all jobs and exit with a message. * * Finish Finish things up by printing the number of errors * that occurred, and exit. */ #include #include #include #include #include #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) #include #endif #include #include "wait.h" #include #include #include #include #include "make.h" #include "dir.h" #include "job.h" #include "pathnames.h" #include "trace.h" /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: main.c,v 1.582 2022/05/07 17:49:47 rillig Exp $"); +MAKE_RCSID("$NetBSD: main.c,v 1.589 2023/01/26 20:48:17 sjg 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 __arraycount # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) #endif CmdOpts opts; time_t now; /* Time at start of make */ GNode *defaultNode; /* .DEFAULT node */ bool allPrecious; /* .PRECIOUS given on line by itself */ bool deleteOnError; /* .DELETE_ON_ERROR: set */ static int maxJobTokens; /* -j argument */ bool enterFlagObj; /* -w and objdir != srcdir */ static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ bool doing_depend; /* Set while reading .depend */ static bool jobsRunning; /* true if the jobs might be running */ static const char *tracefile; static bool ReadMakefile(const char *); static void purge_relative_cached_realpaths(void); static bool ignorePWD; /* if we use -C, PWD is meaningless */ static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ char curdir[MAXPATHLEN + 1]; /* Startup directory */ const char *progname; char *makeDependfile; pid_t myPid; int makelevel; bool forceJobs = false; static int main_errors = 0; static HashTable cached_realpaths; /* * For compatibility with the POSIX version of MAKEFLAGS that includes * all the options without '-', convert 'flags' to '-f -l -a -g -s'. */ static char * explode(const char *flags) { char *exploded, *ep; const char *p; if (flags == NULL) return NULL; for (p = flags; *p != '\0'; p++) if (!ch_isalpha(*p)) return bmake_strdup(flags); exploded = bmake_malloc((size_t)(p - flags) * 3 + 1); for (p = flags, ep = exploded; *p != '\0'; p++) { *ep++ = '-'; *ep++ = *p; *ep++ = ' '; } *ep = '\0'; return exploded; } MAKE_ATTR_DEAD static void usage(void) { size_t prognameLen = strcspn(progname, "["); (void)fprintf(stderr, "usage: %.*s [-BeikNnqrSstWwX]\n" " [-C directory] [-D variable] [-d flags] [-f makefile]\n" " [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" " [-V variable] [-v variable] [variable=value] [target ...]\n", (int)prognameLen, progname); exit(2); } static void MainParseArgDebugFile(const char *arg) { const char *mode; size_t len; char *fname; if (opts.debug_file != stdout && opts.debug_file != stderr) fclose(opts.debug_file); if (*arg == '+') { arg++; mode = "a"; } else mode = "w"; if (strcmp(arg, "stdout") == 0) { opts.debug_file = stdout; return; } if (strcmp(arg, "stderr") == 0) { opts.debug_file = stderr; return; } len = strlen(arg); fname = bmake_malloc(len + 20); memcpy(fname, arg, len + 1); /* Replace the trailing '%d' after '.%d' with the pid. */ if (len >= 3 && memcmp(fname + len - 3, ".%d", 3) == 0) snprintf(fname + len - 2, 20, "%d", getpid()); opts.debug_file = fopen(fname, mode); if (opts.debug_file == NULL) { fprintf(stderr, "Cannot open debug file \"%s\"\n", fname); exit(2); } free(fname); } static void MainParseArgDebug(const char *argvalue) { const char *modules; DebugFlags debug = opts.debug; for (modules = argvalue; *modules != '\0'; modules++) { switch (*modules) { case '0': /* undocumented, only intended for tests */ memset(&debug, 0, sizeof(debug)); break; case 'A': memset(&debug, ~0, sizeof(debug)); break; case 'a': debug.DEBUG_ARCH = true; break; case 'C': debug.DEBUG_CWD = true; break; case 'c': debug.DEBUG_COND = true; break; case 'd': debug.DEBUG_DIR = true; break; case 'e': debug.DEBUG_ERROR = true; break; case 'f': debug.DEBUG_FOR = true; break; case 'g': if (modules[1] == '1') { debug.DEBUG_GRAPH1 = true; modules++; } else if (modules[1] == '2') { debug.DEBUG_GRAPH2 = true; modules++; } else if (modules[1] == '3') { debug.DEBUG_GRAPH3 = true; modules++; } break; case 'h': debug.DEBUG_HASH = true; break; case 'j': debug.DEBUG_JOB = true; break; case 'L': opts.strict = true; break; case 'l': debug.DEBUG_LOUD = true; break; case 'M': debug.DEBUG_META = true; break; case 'm': debug.DEBUG_MAKE = true; break; case 'n': debug.DEBUG_SCRIPT = true; break; case 'p': debug.DEBUG_PARSE = true; break; case 's': debug.DEBUG_SUFF = true; break; case 't': debug.DEBUG_TARG = true; break; case 'V': opts.debugVflag = true; break; case 'v': debug.DEBUG_VAR = true; break; case 'x': debug.DEBUG_SHELL = true; break; case 'F': MainParseArgDebugFile(modules + 1); goto finish; default: (void)fprintf(stderr, "%s: illegal argument to d option -- %c\n", progname, *modules); usage(); } } finish: opts.debug = debug; setvbuf(opts.debug_file, NULL, _IONBF, 0); if (opts.debug_file != stdout) setvbuf(stdout, NULL, _IOLBF, 0); } /* Is path relative or does it contain any relative component "." or ".."? */ static bool IsRelativePath(const char *path) { const char *p; if (path[0] != '/') return true; p = path; while ((p = strstr(p, "/.")) != NULL) { p += 2; if (*p == '.') p++; if (*p == '/' || *p == '\0') return true; } return false; } static void MainParseArgChdir(const char *argvalue) { struct stat sa, sb; if (chdir(argvalue) == -1) { (void)fprintf(stderr, "%s: chdir %s: %s\n", progname, argvalue, strerror(errno)); exit(2); /* Not 1 so -q can distinguish error */ } if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } if (!IsRelativePath(argvalue) && stat(argvalue, &sa) != -1 && stat(curdir, &sb) != -1 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) strncpy(curdir, argvalue, MAXPATHLEN); ignorePWD = true; } static void MainParseArgJobsInternal(const char *argvalue) { char end; if (sscanf(argvalue, "%d,%d%c", &jp_0, &jp_1, &end) != 2) { (void)fprintf(stderr, "%s: internal error -- J option malformed (%s)\n", progname, argvalue); usage(); } if ((fcntl(jp_0, F_GETFD, 0) < 0) || (fcntl(jp_1, F_GETFD, 0) < 0)) { jp_0 = -1; jp_1 = -1; opts.compatMake = true; } else { Global_Append(MAKEFLAGS, "-J"); Global_Append(MAKEFLAGS, argvalue); } } static void MainParseArgJobs(const char *argvalue) { char *p; forceJobs = true; opts.maxJobs = (int)strtol(argvalue, &p, 0); if (*p != '\0' || opts.maxJobs < 1) { (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", progname); exit(2); /* Not 1 so -q can distinguish error */ } Global_Append(MAKEFLAGS, "-j"); Global_Append(MAKEFLAGS, argvalue); Global_Set(".MAKE.JOBS", argvalue); maxJobTokens = opts.maxJobs; } static void MainParseArgSysInc(const char *argvalue) { /* look for magic parent directory search string */ if (strncmp(".../", argvalue, 4) == 0) { char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); if (found_path == NULL) return; (void)SearchPath_Add(sysIncPath, found_path); free(found_path); } else { (void)SearchPath_Add(sysIncPath, argvalue); } Global_Append(MAKEFLAGS, "-m"); Global_Append(MAKEFLAGS, argvalue); + Dir_SetSYSPATH(); } static bool MainParseArg(char c, const char *argvalue) { switch (c) { case '\0': break; case 'B': opts.compatMake = true; Global_Append(MAKEFLAGS, "-B"); Global_Set(MAKE_MODE, "compat"); break; case 'C': MainParseArgChdir(argvalue); break; case 'D': if (argvalue[0] == '\0') return false; Var_SetExpand(SCOPE_GLOBAL, argvalue, "1"); Global_Append(MAKEFLAGS, "-D"); Global_Append(MAKEFLAGS, argvalue); break; case 'I': Parse_AddIncludeDir(argvalue); Global_Append(MAKEFLAGS, "-I"); Global_Append(MAKEFLAGS, argvalue); break; case 'J': MainParseArgJobsInternal(argvalue); break; case 'N': opts.noExecute = true; opts.noRecursiveExecute = true; Global_Append(MAKEFLAGS, "-N"); break; case 'S': opts.keepgoing = false; Global_Append(MAKEFLAGS, "-S"); break; case 'T': tracefile = bmake_strdup(argvalue); Global_Append(MAKEFLAGS, "-T"); Global_Append(MAKEFLAGS, argvalue); break; case 'V': case 'v': opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; Lst_Append(&opts.variables, bmake_strdup(argvalue)); /* XXX: Why always -V? */ Global_Append(MAKEFLAGS, "-V"); Global_Append(MAKEFLAGS, argvalue); break; case 'W': opts.parseWarnFatal = true; /* XXX: why no Global_Append? */ break; case 'X': opts.varNoExportEnv = true; Global_Append(MAKEFLAGS, "-X"); break; case 'd': /* If '-d-opts' don't pass to children */ if (argvalue[0] == '-') argvalue++; else { Global_Append(MAKEFLAGS, "-d"); Global_Append(MAKEFLAGS, argvalue); } MainParseArgDebug(argvalue); break; case 'e': opts.checkEnvFirst = true; Global_Append(MAKEFLAGS, "-e"); break; case 'f': Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); break; case 'i': opts.ignoreErrors = true; Global_Append(MAKEFLAGS, "-i"); break; case 'j': MainParseArgJobs(argvalue); break; case 'k': opts.keepgoing = true; Global_Append(MAKEFLAGS, "-k"); break; case 'm': MainParseArgSysInc(argvalue); /* XXX: why no Var_Append? */ break; case 'n': opts.noExecute = true; Global_Append(MAKEFLAGS, "-n"); break; case 'q': opts.query = true; /* Kind of nonsensical, wot? */ Global_Append(MAKEFLAGS, "-q"); break; case 'r': opts.noBuiltins = true; Global_Append(MAKEFLAGS, "-r"); break; case 's': opts.silent = true; Global_Append(MAKEFLAGS, "-s"); break; case 't': opts.touch = true; Global_Append(MAKEFLAGS, "-t"); break; case 'w': opts.enterFlag = true; Global_Append(MAKEFLAGS, "-w"); break; default: usage(); } return true; } /* * 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 */ static void MainParseArgs(int argc, char **argv) { char c; int arginc; char *argvalue; char *optscan; bool inOption, dashDash = false; const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; /* Can't actually use getopt(3) because rescanning is not portable */ rearg: inOption = false; optscan = NULL; while (argc > 1) { const char *optspec; if (!inOption) optscan = argv[1]; c = *optscan++; arginc = 0; if (inOption) { if (c == '\0') { argv++; argc--; inOption = false; continue; } } else { if (c != '-' || dashDash) break; inOption = true; c = *optscan++; } /* '-' found at some earlier point */ optspec = strchr(optspecs, c); if (c != '\0' && optspec != NULL && optspec[1] == ':') { /* * - found, and should have an * argument */ inOption = false; arginc = 1; argvalue = optscan; if (*argvalue == '\0') { if (argc < 3) goto noarg; argvalue = argv[2]; arginc = 2; } } else { argvalue = NULL; } switch (c) { case '\0': arginc = 1; inOption = false; break; case '-': dashDash = true; break; default: if (!MainParseArg(c, argvalue)) goto noarg; } argv += arginc; argc -= arginc; } /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (; argc > 1; argv++, argc--) { if (!Parse_VarAssign(argv[1], false, SCOPE_CMDLINE)) { if (argv[1][0] == '\0') Punt("illegal (null) argument."); if (argv[1][0] == '-' && !dashDash) goto rearg; Lst_Append(&opts.create, bmake_strdup(argv[1])); } } return; noarg: (void)fprintf(stderr, "%s: option requires an argument -- %c\n", progname, c); usage(); } /* * 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. */ void Main_ParseArgLine(const char *line) { Words words; char *buf; if (line == NULL) return; /* XXX: don't use line as an iterator variable */ for (; *line == ' '; line++) continue; if (line[0] == '\0') return; #ifndef POSIX { /* * $MAKE may simply be naming the make(1) binary */ char *cp; if (!(cp = strrchr(line, '/'))) cp = line; if ((cp = strstr(cp, "make")) && strcmp(cp, "make") == 0) return; } #endif { FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE"); buf = str_concat3(argv0.str, " ", line); FStr_Done(&argv0); } words = Str_Words(buf, true); if (words.words == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); MainParseArgs((int)words.len, words.words); Words_Free(words); } bool Main_SetObjdir(bool writable, const char *fmt, ...) { struct stat sb; char *path; char buf[MAXPATHLEN + 1]; char buf2[MAXPATHLEN + 1]; va_list ap; va_start(ap, fmt); vsnprintf(path = buf, MAXPATHLEN, fmt, ap); va_end(ap); if (path[0] != '/') { - snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path); - path = buf2; + if (snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path) <= MAXPATHLEN) + path = buf2; + else + return false; } /* look for the directory and try to chdir there */ if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode)) return false; if ((writable && access(path, W_OK) != 0) || chdir(path) != 0) { (void)fprintf(stderr, "%s: warning: %s: %s.\n", progname, path, strerror(errno)); return false; } snprintf(objdir, sizeof objdir, "%s", path); Global_Set(".OBJDIR", objdir); setenv("PWD", objdir, 1); Dir_InitDot(); purge_relative_cached_realpaths(); if (opts.enterFlag && strcmp(objdir, curdir) != 0) enterFlagObj = true; return true; } static bool SetVarObjdir(bool writable, const char *var, const char *suffix) { FStr path = Var_Value(SCOPE_CMDLINE, var); if (path.str == NULL || path.str[0] == '\0') { FStr_Done(&path); return false; } Var_Expand(&path, SCOPE_GLOBAL, VARE_WANTRES); (void)Main_SetObjdir(writable, "%s%s", path.str, suffix); 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. */ int str2Lst_Append(StringList *lp, char *str) { char *cp; int n; const char *sep = " \t"; for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) { Lst_Append(lp, cp); n++; } return n; } #ifdef SIGINFO /*ARGSUSED*/ static void siginfo(int signo MAKE_ATTR_UNUSED) { char dir[MAXPATHLEN]; char str[2 * MAXPATHLEN]; int len; if (getcwd(dir, sizeof dir) == NULL) return; len = snprintf(str, sizeof str, "%s: Working in: %s\n", progname, dir); if (len > 0) (void)write(STDERR_FILENO, str, (size_t)len); } #endif /* Allow makefiles some control over the mode we run in. */ static void MakeMode(void) { char *mode; (void)Var_Subst("${" MAKE_MODE ":tl}", SCOPE_GLOBAL, VARE_WANTRES, &mode); /* TODO: handle errors */ if (mode[0] != '\0') { if (strstr(mode, "compat") != NULL) { opts.compatMake = true; forceJobs = false; } #if USE_META if (strstr(mode, "meta") != NULL) meta_mode_init(mode); #endif if (strstr(mode, "randomize-targets") != NULL) opts.randomizeTargets = true; } free(mode); } static void PrintVar(const char *varname, bool expandVars) { if (strchr(varname, '$') != NULL) { char *evalue; (void)Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES, &evalue); /* TODO: handle errors */ printf("%s\n", evalue); free(evalue); } else if (expandVars) { char *expr = str_concat3("${", varname, "}"); char *evalue; (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &evalue); /* TODO: handle errors */ free(expr); printf("%s\n", evalue); free(evalue); } else { FStr value = Var_Value(SCOPE_GLOBAL, varname); printf("%s\n", value.str != NULL ? value.str : ""); FStr_Done(&value); } } /* * Return a bool based on a variable. * * If the knob is not set, return the fallback. * If set, anything that looks or smells like "No", "False", "Off", "0", etc. * is false, otherwise true. */ bool GetBooleanExpr(const char *expr, bool fallback) { char *value; bool res; (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value); /* TODO: handle errors */ res = ParseBoolean(value, fallback); free(value); return res; } static void doPrintVars(void) { StringListNode *ln; bool expandVars; if (opts.printVars == PVM_EXPANDED) expandVars = true; else if (opts.debugVflag) expandVars = false; else expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}", false); for (ln = opts.variables.first; ln != NULL; ln = ln->next) { const char *varname = ln->datum; PrintVar(varname, expandVars); } } static bool runTargets(void) { GNodeList targs = LST_INIT; /* target nodes to create */ bool outOfDate; /* false if all targets up to date */ /* * Have now read the entire graph and need to make a list of * targets to create. If none was given on the command line, * we consult the parsing module to find the main target(s) * to create. */ if (Lst_IsEmpty(&opts.create)) Parse_MainName(&targs); else Targ_FindList(&targs, &opts.create); if (!opts.compatMake) { /* * Initialize job module before traversing the graph * now that any .BEGIN and .END targets have been read. * This is done only if the -q flag wasn't given * (to prevent the .BEGIN from being executed should * it exist). */ if (!opts.query) { Job_Init(); jobsRunning = true; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(&targs); } else { Compat_MakeAll(&targs); outOfDate = false; } Lst_Done(&targs); /* Don't free the targets themselves. */ return outOfDate; } /* * Set up the .TARGETS variable to contain the list of targets to be created. * If none specified, make the variable empty for now, the parser will fill * in the default or .MAIN target later. */ static void InitVarTargets(void) { StringListNode *ln; if (Lst_IsEmpty(&opts.create)) { Global_Set(".TARGETS", ""); return; } for (ln = opts.create.first; ln != NULL; ln = ln->next) { const char *name = ln->datum; Global_Append(".TARGETS", name); } } static void InitRandom(void) { struct timeval tv; gettimeofday(&tv, NULL); srandom((unsigned int)(tv.tv_sec + tv.tv_usec)); } static const char * InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED) { #ifdef FORCE_MACHINE return FORCE_MACHINE; #else const char *machine = getenv("MACHINE"); if (machine != NULL) return machine; #if defined(MAKE_NATIVE) return utsname->machine; #elif defined(MAKE_MACHINE) return MAKE_MACHINE; #else return "unknown"; #endif #endif } static const char * InitVarMachineArch(void) { #ifdef FORCE_MACHINE_ARCH return FORCE_MACHINE_ARCH; #else const char *env = getenv("MACHINE_ARCH"); if (env != NULL) return env; #if defined(MAKE_NATIVE) && defined(CTL_HW) { struct utsname utsname; static char machine_arch_buf[sizeof utsname.machine]; const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; size_t len = sizeof machine_arch_buf; if (sysctl(mib, (unsigned int)__arraycount(mib), machine_arch_buf, &len, NULL, 0) < 0) { (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, strerror(errno)); exit(2); } return machine_arch_buf; } #elif defined(MACHINE_ARCH) return MACHINE_ARCH; #elif defined(MAKE_MACHINE_ARCH) return MAKE_MACHINE_ARCH; #else return "unknown"; #endif #endif } #ifndef NO_PWD_OVERRIDE /* * All this code is so that we know where we are when we start up * on a different machine with pmake. * * XXX: Make no longer has "local" and "remote" mode. Is this code still * necessary? * * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can vary depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) * or via subdir.mk in which case its likely a shell which does * not provide it. * * So, to stop it breaking this case only, we ignore PWD if * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression. */ static void HandlePWD(const struct stat *curdir_st) { char *pwd; FStr makeobjdir; struct stat pwd_st; if (ignorePWD || (pwd = getenv("PWD")) == NULL) return; if (Var_Exists(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX")) return; makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR"); if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) goto ignore_pwd; if (stat(pwd, &pwd_st) == 0 && curdir_st->st_ino == pwd_st.st_ino && curdir_st->st_dev == pwd_st.st_dev) (void)strncpy(curdir, pwd, MAXPATHLEN); ignore_pwd: FStr_Done(&makeobjdir); } #endif /* * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, MAKEOBJDIR is set * in the environment, try only that value and fall back to .CURDIR if it * does not exist. * * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, * and finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none of these * paths exist, just use .CURDIR. */ static void InitObjdir(const char *machine, const char *machine_arch) { bool writable; Dir_InitCur(curdir); writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true); (void)Main_SetObjdir(false, "%s", curdir); if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) && !SetVarObjdir(writable, "MAKEOBJDIR", "") && !Main_SetObjdir(writable, "%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && !Main_SetObjdir(writable, "%s.%s", _PATH_OBJDIR, machine) && !Main_SetObjdir(writable, "%s", _PATH_OBJDIR)) (void)Main_SetObjdir(writable, "%s%s", _PATH_OBJDIRPREFIX, curdir); } /* get rid of resource limit on file descriptors */ static void UnlimitFiles(void) { #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; (void)setrlimit(RLIMIT_NOFILE, &rl); } #endif } static void CmdOpts_Init(void) { opts.compatMake = false; memset(&opts.debug, 0, sizeof(opts.debug)); /* opts.debug_file has already been initialized earlier */ opts.strict = false; opts.debugVflag = false; opts.checkEnvFirst = false; Lst_Init(&opts.makefiles); opts.ignoreErrors = false; /* Pay attention to non-zero returns */ opts.maxJobs = 1; opts.keepgoing = false; /* Stop on error */ opts.noRecursiveExecute = false; /* Execute all .MAKE targets */ opts.noExecute = false; /* Execute all commands */ opts.query = false; opts.noBuiltins = false; /* Read the built-in rules */ opts.silent = false; /* Print commands as executed */ opts.touch = false; opts.printVars = PVM_NONE; Lst_Init(&opts.variables); opts.parseWarnFatal = false; opts.enterFlag = false; opts.varNoExportEnv = false; Lst_Init(&opts.create); } /* * 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. */ static void InitVarMake(const char *argv0) { const char *make = argv0; if (argv0[0] != '/' && strchr(argv0, '/') != NULL) { char pathbuf[MAXPATHLEN]; const char *abspath = cached_realpath(argv0, pathbuf); struct stat st; if (abspath != NULL && abspath[0] == '/' && stat(make, &st) == 0) make = abspath; } Global_Set("MAKE", make); Global_Set(".MAKE", make); } /* * Add the directories from the colon-separated syspath to defSysIncPath. * After returning, the contents of syspath is unspecified. */ static void InitDefSysIncPath(char *syspath) { static char defsyspath[] = _PATH_DEFSYSPATH; char *start, *cp; /* * If no user-supplied system path was given (through the -m option) * add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ if (syspath == NULL || syspath[0] == '\0') syspath = defsyspath; else syspath = bmake_strdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; if (*cp == ':') *cp++ = '\0'; /* look for magic parent directory search string */ if (strncmp(start, ".../", 4) == 0) { char *dir = Dir_FindHereOrAbove(curdir, start + 4); if (dir != NULL) { (void)SearchPath_Add(defSysIncPath, dir); free(dir); } } else { (void)SearchPath_Add(defSysIncPath, start); } } if (syspath != defsyspath) free(syspath); } static void ReadBuiltinRules(void) { StringListNode *ln; StringList sysMkFiles = LST_INIT; SearchPath_Expand( Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath, _PATH_DEFSYSMK, &sysMkFiles); if (Lst_IsEmpty(&sysMkFiles)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); for (ln = sysMkFiles.first; ln != NULL; ln = ln->next) if (ReadMakefile(ln->datum)) break; if (ln == NULL) Fatal("%s: cannot open %s.", progname, (const char *)sysMkFiles.first->datum); Lst_DoneCall(&sysMkFiles, free); } static void InitMaxJobs(void) { char *value; int n; if (forceJobs || opts.compatMake || !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS")) return; (void)Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES, &value); /* TODO: handle errors */ n = (int)strtol(value, NULL, 0); if (n < 1) { (void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS " "-- must be positive integer!\n", progname); exit(2); /* Not 1 so -q can distinguish error */ } if (n != opts.maxJobs) { Global_Append(MAKEFLAGS, "-j"); Global_Append(MAKEFLAGS, value); } opts.maxJobs = n; maxJobTokens = opts.maxJobs; forceJobs = true; free(value); } /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH environment * variable, i.e. ::... */ static void InitVpath(void) { char *vpath, savec, *path; if (!Var_Exists(SCOPE_CMDLINE, "VPATH")) return; (void)Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES, &vpath); /* TODO: handle errors */ path = vpath; do { char *cp; /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++) continue; /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ (void)SearchPath_Add(&dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); free(vpath); } static void ReadAllMakefiles(const StringList *makefiles) { StringListNode *ln; for (ln = makefiles->first; ln != NULL; ln = ln->next) { const char *fname = ln->datum; if (!ReadMakefile(fname)) Fatal("%s: cannot open %s.", progname, fname); } } static void ReadFirstDefaultMakefile(void) { StringList makefiles = LST_INIT; StringListNode *ln; char *prefs; (void)Var_Subst("${" MAKE_MAKEFILE_PREFERENCE "}", SCOPE_CMDLINE, VARE_WANTRES, &prefs); /* TODO: handle errors */ (void)str2Lst_Append(&makefiles, prefs); for (ln = makefiles.first; ln != NULL; ln = ln->next) if (ReadMakefile(ln->datum)) break; Lst_Done(&makefiles); free(prefs); } /* * Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. * Initialize a few modules. * Parse the arguments from MAKEFLAGS and the command line. */ static void main_Init(int argc, char **argv) { struct stat sa; const char *machine; const char *machine_arch; char *syspath = getenv("MAKESYSPATH"); struct utsname utsname; /* default to writing debug to stderr */ opts.debug_file = stderr; Str_Intern_Init(); HashTable_Init(&cached_realpaths); #ifdef SIGINFO (void)bmake_signal(SIGINFO, siginfo); #endif InitRandom(); progname = str_basename(argv[0]); UnlimitFiles(); if (uname(&utsname) == -1) { (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, strerror(errno)); exit(2); } /* * Get the name of this type of MACHINE from utsname * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * * Note that both MACHINE and MACHINE_ARCH are decided at * run-time. */ machine = InitVarMachine(&utsname); machine_arch = InitVarMachineArch(); myPid = getpid(); /* remember this for vFork() */ /* * Just in case MAKEOBJDIR wants us to do something tricky. */ Targ_Init(); Var_Init(); - Global_Set(".MAKE.OS", utsname.sysname); + Global_Set_ReadOnly(".MAKE.OS", utsname.sysname); Global_Set("MACHINE", machine); Global_Set("MACHINE_ARCH", machine_arch); #ifdef MAKE_VERSION Global_Set("MAKE_VERSION", MAKE_VERSION); #endif - Global_Set(".newline", "\n"); /* handy for :@ loops */ + Global_Set_ReadOnly(".newline", "\n"); /* handy for :@ loops */ #ifndef MAKEFILE_PREFERENCE_LIST /* This is the traditional preference for makefiles. */ # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" #endif Global_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST); - Global_Set(MAKE_DEPENDFILE, ".depend"); + Global_Set(".MAKE.DEPENDFILE", ".depend"); CmdOpts_Init(); allPrecious = false; /* Remove targets when interrupted */ deleteOnError = false; /* Historical default behavior */ jobsRunning = false; maxJobTokens = opts.maxJobs; ignorePWD = false; /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ /* * Initialize various variables. * MAKE also gets this name, for compatibility * .MAKEFLAGS gets set to the empty string just in case. * MFLAGS also gets initialized empty, for compatibility. */ Parse_Init(); InitVarMake(argv[0]); Global_Set(MAKEFLAGS, ""); Global_Set(MAKEOVERRIDES, ""); Global_Set("MFLAGS", ""); Global_Set(".ALLTARGETS", ""); - Var_Set(SCOPE_CMDLINE, MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV); + Var_Set(SCOPE_CMDLINE, ".MAKE.LEVEL.ENV", MAKE_LEVEL_ENV); /* Set some other useful variables. */ { char buf[64], *ep = getenv(MAKE_LEVEL_ENV); makelevel = ep != NULL && ep[0] != '\0' ? atoi(ep) : 0; if (makelevel < 0) makelevel = 0; snprintf(buf, sizeof buf, "%d", makelevel); Global_Set(MAKE_LEVEL, buf); snprintf(buf, sizeof buf, "%u", myPid); - Global_Set(".MAKE.PID", buf); + Global_Set_ReadOnly(".MAKE.PID", buf); snprintf(buf, sizeof buf, "%u", getppid()); - Global_Set(".MAKE.PPID", buf); + Global_Set_ReadOnly(".MAKE.PPID", buf); snprintf(buf, sizeof buf, "%u", getuid()); - Global_Set(".MAKE.UID", buf); + Global_Set_ReadOnly(".MAKE.UID", buf); snprintf(buf, sizeof buf, "%u", getgid()); - Global_Set(".MAKE.GID", buf); + Global_Set_ReadOnly(".MAKE.GID", buf); } if (makelevel > 0) { char pn[1024]; snprintf(pn, sizeof pn, "%s[%d]", progname, makelevel); progname = bmake_strdup(pn); } #ifdef USE_META meta_init(); #endif Dir_Init(); #ifdef POSIX { char *makeflags = explode(getenv("MAKEFLAGS")); Main_ParseArgLine(makeflags); free(makeflags); } #else /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ Main_ParseArgLine(getenv("MAKE")); #endif if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: getcwd: %s.\n", progname, strerror(errno)); exit(2); } MainParseArgs(argc, argv); if (opts.enterFlag) printf("%s: Entering directory `%s'\n", progname, curdir); if (stat(curdir, &sa) == -1) { (void)fprintf(stderr, "%s: %s: %s.\n", progname, curdir, strerror(errno)); exit(2); } #ifndef NO_PWD_OVERRIDE HandlePWD(&sa); #endif Global_Set(".CURDIR", curdir); InitObjdir(machine, machine_arch); Arch_Init(); Suff_Init(); Trace_Init(tracefile); defaultNode = NULL; (void)time(&now); Trace_Log(MAKESTART, NULL); InitVarTargets(); InitDefSysIncPath(syspath); } /* * 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) { + if (Lst_IsEmpty(&sysIncPath->dirs)) + SearchPath_AddAll(sysIncPath, defSysIncPath); + + Dir_SetSYSPATH(); if (!opts.noBuiltins) ReadBuiltinRules(); posix_state = PS_MAYBE_NEXT_LINE; if (!Lst_IsEmpty(&opts.makefiles)) ReadAllMakefiles(&opts.makefiles); else ReadFirstDefaultMakefile(); } /* Compute the dependency graph. */ static void main_PrepareMaking(void) { /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!opts.noBuiltins || opts.printVars == PVM_NONE) { (void)Var_Subst("${.MAKE.DEPENDFILE}", SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile); if (makeDependfile[0] != '\0') { /* TODO: handle errors */ doing_depend = true; (void)ReadMakefile(makeDependfile); doing_depend = false; } } if (enterFlagObj) printf("%s: Entering directory `%s'\n", progname, objdir); MakeMode(); { FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS); Global_Append("MFLAGS", makeflags.str); FStr_Done(&makeflags); } InitMaxJobs(); if (!opts.compatMake && !forceJobs) opts.compatMake = true; if (!opts.compatMake) Job_ServerStart(maxJobTokens, jp_0, jp_1); DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0); if (opts.printVars == PVM_NONE) Main_ExportMAKEFLAGS(true); /* initial export */ InitVpath(); /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_ExtendPaths(); /* * Propagate attributes through :: dependency lists. */ Targ_Propagate(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); } /* * Make the targets. * If the -v or -V options are given, print variables instead. * Return whether any of the targets is out-of-date. */ static bool main_Run(void) { if (opts.printVars != PVM_NONE) { /* print the values of any variables requested by the user */ doPrintVars(); return false; } else { return runTargets(); } } /* Clean up after making the targets. */ static void main_CleanUp(void) { #ifdef CLEANUP Lst_DoneCall(&opts.variables, free); Lst_DoneCall(&opts.makefiles, free); Lst_DoneCall(&opts.create, free); #endif if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEEND, NULL); if (enterFlagObj) printf("%s: Leaving directory `%s'\n", progname, objdir); if (opts.enterFlag) printf("%s: Leaving directory `%s'\n", progname, curdir); #ifdef USE_META meta_finish(); #endif Suff_End(); Targ_End(); Arch_End(); Var_End(); Parse_End(); Dir_End(); Job_End(); Trace_End(); Str_Intern_End(); } /* Determine the exit code. */ static int main_Exit(bool outOfDate) { if (opts.strict && (main_errors > 0 || Parse_NumErrors() > 0)) return 2; /* Not 1 so -q can distinguish error */ return outOfDate ? 1 : 0; } int main(int argc, char **argv) { bool outOfDate; main_Init(argc, argv); main_ReadFiles(); main_PrepareMaking(); outOfDate = main_Run(); main_CleanUp(); return main_Exit(outOfDate); } /* * Open and parse the given makefile, with all its side effects. * Return false if the file could not be opened. */ static bool ReadMakefile(const char *fname) { int fd; char *name, *path = NULL; if (strcmp(fname, "-") == 0) { Parse_File("(stdin)", -1); Var_Set(SCOPE_INTERNAL, "MAKEFILE", ""); } else { /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) != 0 && *fname != '/') { path = str_concat3(curdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } free(path); /* If curdir failed, try objdir (ala .depend) */ path = str_concat3(objdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } } else { fd = open(fname, O_RDONLY); if (fd != -1) goto found; } /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (name == NULL) { SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath; name = Dir_FindFile(fname, sysInc); } if (name == NULL || (fd = open(name, O_RDONLY)) == -1) { free(name); free(path); return false; } fname = name; /* * set the MAKEFILE variable desired by System V fans -- the * placement of the setting here means it gets set to the last * makefile specified, as it is set by SysV make. */ found: if (!doing_depend) Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname); Parse_File(fname, fd); } free(path); return true; } /* * Execute the command in cmd, and return its output (only stdout, not * stderr, possibly empty). In the output, replace newlines with spaces. */ char * Cmd_Exec(const char *cmd, char **error) { const char *args[4]; /* Arguments for invoking the shell */ int pipefds[2]; int cpid; /* Child PID */ int pid; /* PID from wait() */ int status; /* command exit status */ Buffer buf; /* buffer to store the result */ ssize_t bytes_read; char *output; char *cp; int saved_errno; if (shellName == NULL) Shell_Init(); args[0] = shellName; args[1] = "-c"; args[2] = cmd; args[3] = NULL; DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd); if (pipe(pipefds) == -1) { *error = str_concat3( "Couldn't create pipe for \"", cmd, "\""); return bmake_strdup(""); } Var_ReexportVars(); switch (cpid = vfork()) { case 0: (void)close(pipefds[0]); (void)dup2(pipefds[1], STDOUT_FILENO); (void)close(pipefds[1]); (void)execv(shellPath, UNCONST(args)); _exit(1); /* NOTREACHED */ case -1: *error = str_concat3("Couldn't exec \"", cmd, "\""); return bmake_strdup(""); } (void)close(pipefds[1]); /* No need for the writing half */ saved_errno = 0; Buf_Init(&buf); do { char result[BUFSIZ]; bytes_read = read(pipefds[0], result, sizeof result); if (bytes_read > 0) Buf_AddBytes(&buf, result, (size_t)bytes_read); } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); if (bytes_read == -1) saved_errno = errno; (void)close(pipefds[0]); /* Close the input side of the pipe. */ while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) JobReapChild(pid, status, false); if (Buf_EndsWith(&buf, '\n')) buf.data[buf.len - 1] = '\0'; output = Buf_DoneData(&buf); for (cp = output; *cp != '\0'; cp++) if (*cp == '\n') *cp = ' '; if (WIFSIGNALED(status)) *error = str_concat3("\"", cmd, "\" exited on a signal"); else if (WEXITSTATUS(status) != 0) *error = str_concat3( "\"", cmd, "\" returned non-zero status"); else if (saved_errno != 0) *error = str_concat3( "Couldn't read shell's output for \"", cmd, "\""); else *error = NULL; return output; } /* * Print a printf-style error message. * * In default mode, this error message has no consequences, for compatibility * reasons, in particular it does not affect the exit status. Only in lint * mode (-dL) it does. */ void Error(const char *fmt, ...) { va_list ap; FILE *f; f = opts.debug_file; if (f == stdout) f = stderr; (void)fflush(stdout); for (;;) { fprintf(f, "%s: ", progname); va_start(ap, fmt); (void)vfprintf(f, fmt, ap); va_end(ap); (void)fprintf(f, "\n"); (void)fflush(f); if (f == stderr) break; f = stderr; } main_errors++; } /* * 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. */ void Fatal(const char *fmt, ...) { va_list ap; if (jobsRunning) Job_Wait(); (void)fflush(stdout); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintStackTrace(true); PrintOnError(NULL, "\n"); if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, NULL); exit(2); /* Not 1 so -q can distinguish error */ } /* * Major exception once jobs are being created. * Kills all jobs, prints a message and exits. */ void Punt(const char *fmt, ...) { va_list ap; (void)fflush(stdout); (void)fprintf(stderr, "%s: ", progname); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintOnError(NULL, "\n"); DieHorribly(); } /* Exit without giving a message. */ void DieHorribly(void) { if (jobsRunning) Job_AbortAll(); if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, NULL); exit(2); /* Not 1 so -q can distinguish error */ } /* * 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. */ void Finish(int errs) { if (shouldDieQuietly(NULL, -1)) exit(2); Fatal("%d error%s", errs, errs == 1 ? "" : "s"); } -bool +int unlink_file(const char *file) { struct stat st; if (lstat(file, &st) == -1) - return false; + return -1; if (S_ISDIR(st.st_mode)) { + /* + * POSIX says for unlink: "The path argument shall not name + * a directory unless [...]". + */ errno = EISDIR; - return false; + return -1; } - return unlink(file) == 0; + return unlink(file); } static void write_all(int fd, const void *data, size_t n) { const char *mem = data; while (n > 0) { ssize_t written = write(fd, mem, n); /* XXX: Should this EAGAIN be EINTR? */ if (written == -1 && errno == EAGAIN) continue; if (written == -1) break; mem += written; n -= (size_t)written; } } /* Print why exec failed, avoiding stdio. */ void MAKE_ATTR_DEAD execDie(const char *af, const char *av) { Buffer buf; Buf_Init(&buf); Buf_AddStr(&buf, progname); Buf_AddStr(&buf, ": "); Buf_AddStr(&buf, af); Buf_AddStr(&buf, "("); Buf_AddStr(&buf, av); Buf_AddStr(&buf, ") failed ("); Buf_AddStr(&buf, strerror(errno)); Buf_AddStr(&buf, ")\n"); write_all(STDERR_FILENO, buf.data, buf.len); Buf_Done(&buf); _exit(1); } static void purge_relative_cached_realpaths(void) { HashEntry *he, *nhe; HashIter hi; HashIter_Init(&hi, &cached_realpaths); he = HashIter_Next(&hi); while (he != NULL) { nhe = HashIter_Next(&hi); if (he->key[0] != '/') { DEBUG1(DIR, "cached_realpath: purging %s\n", he->key); HashTable_DeleteEntry(&cached_realpaths, he); /* * XXX: What about the allocated he->value? Either * free them or document why they cannot be freed. */ } he = nhe; } } const char * cached_realpath(const char *pathname, char *resolved) { const char *rp; if (pathname == NULL || pathname[0] == '\0') return NULL; rp = HashTable_FindValue(&cached_realpaths, pathname); if (rp != NULL) { /* a hit */ strncpy(resolved, rp, MAXPATHLEN); resolved[MAXPATHLEN - 1] = '\0'; return resolved; } rp = realpath(pathname, resolved); if (rp != NULL) { HashTable_Set(&cached_realpaths, pathname, bmake_strdup(rp)); DEBUG2(DIR, "cached_realpath: %s -> %s\n", pathname, rp); return resolved; } /* should we negative-cache? */ return NULL; } /* * Return true if we should die without noise. * For example our failing child was a sub-make or failure happened elsewhere. */ bool shouldDieQuietly(GNode *gn, int bf) { static int quietly = -1; if (quietly < 0) { if (DEBUG(JOB) || !GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true)) quietly = 0; else if (bf >= 0) quietly = bf; else quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0; } return quietly != 0; } static void SetErrorVars(GNode *gn) { StringListNode *ln; /* * We can print this even if there is no .ERROR target. */ Global_Set(".ERROR_TARGET", gn->name); Global_Delete(".ERROR_CMD"); for (ln = gn->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; if (cmd == NULL) break; Global_Append(".ERROR_CMD", cmd); } } /* * 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) { static GNode *errorNode = NULL; if (DEBUG(HASH)) { Targ_Stats(); Var_Stats(); } if (errorNode != NULL) return; /* we've been here! */ printf("%s%s: stopped in %s\n", msg, progname, curdir); /* we generally want to keep quiet if a sub-make died */ if (shouldDieQuietly(gn, -1)) return; if (gn != NULL) SetErrorVars(gn); { char *errorVarsValues; (void)Var_Subst("${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", SCOPE_GLOBAL, VARE_WANTRES, &errorVarsValues); /* TODO: handle errors */ printf("%s", errorVarsValues); free(errorVarsValues); } fflush(stdout); /* * Finally, see if there is a .ERROR target, and run it if so. */ errorNode = Targ_FindNode(".ERROR"); if (errorNode != NULL) { errorNode->type |= OP_SPECIAL; Compat_Make(errorNode, errorNode); } } void Main_ExportMAKEFLAGS(bool first) { static bool once = true; char *flags; if (once != first) return; once = false; (void)Var_Subst( "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", SCOPE_CMDLINE, VARE_WANTRES, &flags); /* TODO: handle errors */ if (flags[0] != '\0') { #ifdef POSIX setenv("MAKEFLAGS", flags, 1); #else setenv("MAKE", flags, 1); #endif } } char * getTmpdir(void) { static char *tmpdir = NULL; struct stat st; if (tmpdir != NULL) return tmpdir; /* Honor $TMPDIR if it is valid, strip a trailing '/'. */ (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/", SCOPE_GLOBAL, VARE_WANTRES, &tmpdir); /* TODO: handle errors */ if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { free(tmpdir); tmpdir = bmake_strdup(_PATH_TMP); } return tmpdir; } /* * Create and open a temp file using "pattern". * If out_fname is provided, set it to a copy of the filename created. * Otherwise unlink the file once open. */ int mkTempFile(const char *pattern, char *tfile, size_t tfile_sz) { static char *tmpdir = NULL; char tbuf[MAXPATHLEN]; int fd; if (pattern == NULL) pattern = TMPPAT; if (tmpdir == NULL) tmpdir = getTmpdir(); if (tfile == NULL) { tfile = tbuf; tfile_sz = sizeof tbuf; } if (pattern[0] == '/') snprintf(tfile, tfile_sz, "%s", pattern); else snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern); if ((fd = mkstemp(tfile)) < 0) Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); if (tfile == tbuf) unlink(tfile); /* we just want the descriptor */ return fd; } /* * Convert a string representation of a boolean into a boolean value. * Anything that looks like "No", "False", "Off", "0" etc. is false, * the empty string is the fallback, everything else is true. */ bool ParseBoolean(const char *s, bool fallback) { char ch = ch_tolower(s[0]); if (ch == '\0') return fallback; if (ch == '0' || ch == 'f' || ch == 'n') return false; if (ch == 'o') return ch_tolower(s[1]) != 'f'; return true; } diff --git a/contrib/bmake/make.1 b/contrib/bmake/make.1 index 9accbaae9f61..5a28d2f83ca3 100644 --- a/contrib/bmake/make.1 +++ b/contrib/bmake/make.1 @@ -1,2565 +1,2725 @@ -.\" $NetBSD: make.1,v 1.315 2022/07/12 23:47:00 rillig Exp $ +.\" $NetBSD: make.1,v 1.360 2023/01/26 20:48:17 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd July 12, 2022 +.Dd January 26, 2023 .Dt MAKE 1 .Os .Sh NAME .Nm make .Nd maintain program dependencies .Sh SYNOPSIS .Nm .Op Fl BeikNnqrSstWwX .Op Fl C Ar directory .Op Fl D Ar variable .Op Fl d Ar flags .Op Fl f Ar makefile .Op Fl I Ar directory .Op Fl J Ar private .Op Fl j Ar max_jobs .Op Fl m Ar directory .Op Fl T Ar file .Op Fl V Ar variable .Op Fl v Ar variable -.Op Ar variable=value -.Op Ar target ... +.Op Ar variable\| Ns Cm \&= Ns Ar value +.Op Ar target No ... .Sh DESCRIPTION .Nm is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. If no .Fl f Ar makefile -makefile option is given, +option is given, .Nm -will try to open +tries to open .Sq Pa makefile then .Sq Pa Makefile in order to find the specifications. If the file .Sq Pa .depend -exists, it is read (see -.Xr mkdep 1 ) . +exists, it is read, see +.Xr mkdep 1 . .Pp This manual page is intended as a reference document only. For a more thorough description of .Nm and makefiles, please refer to -.%T "PMake \- A Tutorial" . +.%T "PMake \- A Tutorial" +(from 1993). .Pp .Nm -will prepend the contents of the -.Va MAKEFLAGS +prepends the contents of the +.Ev MAKEFLAGS environment variable to the command line arguments before parsing them. .Pp The options are as follows: .Bl -tag -width Ds .It Fl B Try to be backwards compatible by executing a single shell per command and -by executing the commands to make the sources of a dependency line in sequence. +by making the sources of a dependency line in sequence. .It Fl C Ar directory Change to .Ar directory before reading the makefiles or doing anything else. If multiple .Fl C options are specified, each is interpreted relative to the previous one: .Fl C Pa / Fl C Pa etc is equivalent to .Fl C Pa /etc . .It Fl D Ar variable Define .Ar variable to be 1, in the global scope. -.It Fl d Ar [-]flags +.It Fl d Oo Cm \- Oc Ns Ar flags Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by -.Ql \- +.Ql \- , they are added to the -.Va MAKEFLAGS -environment variable and will be processed by any child make processes. +.Ev MAKEFLAGS +environment variable and are passed on to any child make processes. By default, debugging information is printed to standard error, but this can be changed using the -.Ar F +.Cm F 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 output is line buffered. -.Ar Flags -is one or more of the following: +the standard output is line buffered. +The available +.Ar flags +are: .Bl -tag -width Ds -.It Ar A +.It Cm A Print all possible debugging information; equivalent to specifying all of the debugging flags. -.It Ar a +.It Cm a Print debugging information about archive searching and caching. -.It Ar C -Print debugging information about current working directory. -.It Ar c +.It Cm C +Print debugging information about the current working directory. +.It Cm c Print debugging information about conditional evaluation. -.It Ar d +.It Cm d Print debugging information about directory searching and caching. -.It Ar e +.It Cm e Print debugging information about failed commands and targets. -.It Ar F Ns Oo Sy \&+ Oc Ns Ar filename +.It Cm F Ns Oo Cm \&+ Oc Ns Ar filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the argument. If the character immediately after the -.Ql F +.Cm F flag is .Ql \&+ , -then the file will be opened in append mode; -otherwise the file will be overwritten. +the file is opened in append mode; +otherwise the file is overwritten. If the file name is .Ql stdout or -.Ql stderr -then debugging output will be written to the -standard output or standard error output file descriptors respectively -(and the +.Ql stderr , +debugging output is written to the standard output or standard error output +respectively (and the .Ql \&+ option has no effect). -Otherwise, the output will be written to the named file. -If the file name ends -.Ql .%d -then the +Otherwise, the output is written to the named file. +If the file name ends with +.Ql .%d , +the .Ql %d is replaced by the pid. -.It Ar f +.It Cm f Print debugging information about loop evaluation. -.It Ar "g1" +.It Cm g1 Print the input graph before making anything. -.It Ar "g2" +.It Cm g2 Print the input graph after making everything, or before exiting on error. -.It Ar "g3" +.It Cm g3 Print the input graph before exiting on error. -.It Ar h +.It Cm h Print debugging information about hash table operations. -.It Ar j +.It Cm j Print debugging information about running multiple shells. -.It Ar L +.It Cm L Turn on lint checks. -This will throw errors for variable assignments that do not parse -correctly, at the time of assignment so the file and line number -are available. -.It Ar l +This throws errors for variable assignments that do not parse correctly, +at the time of assignment, so the file and line number are available. +.It Cm l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ -or other "quiet" flags. -Also known as "loud" behavior. -.It Ar M -Print debugging information about "meta" mode decisions about targets. -.It Ar m +or other +.Dq quiet +flags. +Also known as +.Dq loud +behavior. +.It Cm M +Print debugging information about +.Dq meta +mode decisions about targets. +.It Cm m Print debugging information about making targets, including modification dates. -.It Ar n +.It Cm n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in the directory referred to by the .Ev TMPDIR environment variable, or in .Pa /tmp if .Ev TMPDIR is unset or set to the empty string. The temporary scripts are created by .Xr mkstemp 3 , and have names of the form .Pa makeXXXXXX . .Em NOTE : This can create many files in .Ev TMPDIR or .Pa /tmp , so use with care. -.It Ar p +.It Cm p Print debugging information about makefile parsing. -.It Ar s +.It Cm s Print debugging information about suffix-transformation rules. -.It Ar t +.It Cm t Print debugging information about target list maintenance. -.It Ar V +.It Cm V Force the .Fl V -option to print raw values of variables, overriding the default behavior -set via +option to print raw values of variables, +overriding the default behavior set via .Va .MAKE.EXPAND_VARIABLES . -.It Ar v -Print debugging information about variable assignment. -.It Ar x +.It Cm v +Print debugging information about variable assignment and expansion. +.It Cm x Run shell commands with .Fl x so the actual commands are printed as they are executed. .El .It Fl e -Specify that environment variables override macro assignments within -makefiles. +Let environment variables override global variables within makefiles. .It Fl f Ar makefile Specify a makefile to read instead of the default -.Sq Pa makefile . +.Pa makefile +or +.Pa Makefile . If .Ar makefile is -.Sq Fl , +.Ql \&- , standard input is read. Multiple makefiles may be specified, and are read in the order specified. .It Fl I Ar directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the .Fl m option) is automatically included as part of this list. .It Fl i Ignore non-zero exit of shell commands in the makefile. Equivalent to specifying -.Sq Fl +.Ql \&- before each command line in the makefile. .It Fl J Ar private This option should .Em not be specified by the user. .Pp When the .Fl j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. .It Fl j Ar max_jobs Specify the maximum number of jobs that .Nm may have running at any one time. The value is saved in .Va .MAKE.JOBS . Turns compatibility mode off, unless the .Fl B option is also specified. When compatibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the traditional one shell invocation per line. This can break traditional scripts which change directories on each command invocation and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. .It Fl k Continue processing after errors are encountered, but only on those targets that do not depend on the target whose creation caused the error. .It Fl m Ar directory -Specify a directory in which to search for sys.mk and makefiles included -via the +Specify a directory in which to search for +.Pa sys.mk +and makefiles included via the .Li \&< Ns Ar file Ns Li \&> Ns -style include statement. The .Fl m option can be used multiple times to form a search path. -This path will override the default system include path: /usr/share/mk. -Furthermore the system include path will be appended to the search path used -for +This path overrides the default system include path +.Pa /usr/share/mk . +Furthermore, the system include path is appended to the search path used for .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). +The system include path can be referenced via the read-only variable +.Va .SYSPATH . .Pp -If a file or directory name in the +If a directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string -.Qq \&.../ -then +.Ql \&.../ , .Nm -will search for the specified file or directory named in the remaining part +searches for the specified file or directory named in the remaining part of the argument string. -The search starts with the current directory of -the Makefile and then works upward towards the root of the file system. -If the search is successful, then the resulting directory replaces the -.Qq \&.../ +The search starts with the current directory +and then works upward towards the root of the file system. +If the search is successful, the resulting directory replaces the +.Ql \&.../ specification in the .Fl m argument. -If used, this feature allows +This feature allows .Nm -to easily search in the current source tree for customized sys.mk files -(e.g., by using -.Qq \&.../mk/sys.mk +to easily search in the current source tree for customized +.Pa sys.mk +files (e.g., by using +.Ql \&.../mk/sys.mk as an argument). .It Fl n Display the commands that would have been executed, but do not -actually execute them unless the target depends on the .MAKE special -source (see below) or the command is prefixed with -.Sq Ic + . +actually execute them unless the target depends on the +.Va .MAKE +special source (see below) or the command is prefixed with +.Sq Cm + . .It Fl N -Display the commands which would have been executed, but do not -actually execute any of them; useful for debugging top-level makefiles +Display the commands that would have been executed, +but do not actually execute any of them; +useful for debugging top-level makefiles without descending into subdirectories. .It Fl q -Do not execute any commands, but exit 0 if the specified targets are -up-to-date and 1, otherwise. +Do not execute any commands, +instead exit 0 if the specified targets are up to date, and 1 otherwise. .It Fl r Do not use the built-in rules specified in the system makefile. .It Fl S Stop processing if an error is encountered. This is the default behavior and the opposite of .Fl k . .It Fl s Do not echo any commands as they are executed. Equivalent to specifying .Sq Ic @ before each command line in the makefile. .It Fl T Ar tracefile When used with the .Fl j flag, append a trace record to .Ar tracefile for each job started and completed. .It Fl t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up-to-date. .It Fl V Ar variable Print the value of .Ar variable . Do not build any targets. Multiple instances of this option may be specified; -the variables will be printed one per line, +the variables are printed one per line, with a blank line for each null or undefined variable. The value printed is extracted from the global scope after all makefiles have been read. +.Pp By default, the raw variable contents (which may include additional unexpanded variable references) are shown. If .Ar variable contains a -.Ql \&$ -then the value will be recursively expanded to its complete resultant -text before printing. -The expanded value will also be printed if +.Ql \&$ , +it is not interpreted as a variable name but rather as an expression. +Its value is expanded before printing. +The value is also expanded before printing if .Va .MAKE.EXPAND_VARIABLES -is set to true and -the +is set to true and the .Fl dV option has not been used to override it. +.Pp Note that loop-local and target-local variables, as well as values taken temporarily by global variables during makefile processing, are not accessible via this option. The .Fl dv debug mode can be used to see these at the cost of generating substantial extraneous output. .It Fl v Ar variable Like +.Fl V , +but all printed variables are always expanded to their complete value. +The last occurrence of .Fl V -but the variable is always expanded to its complete value. +or +.Fl v +decides whether all variables are expanded or not. .It Fl W Treat any warnings during makefile parsing as errors. .It Fl w Print entering and leaving directory messages, pre and post processing. .It Fl X Don't export variables passed on the command line to the environment individually. -Variables passed on the command line are still exported -via the -.Va MAKEFLAGS +Variables passed on the command line are still exported via the +.Ev MAKEFLAGS environment variable. This option may be useful on systems which have a small limit on the size of command arguments. -.It Ar variable=value +.It Ar variable\| Ns Cm \&= Ns Ar value Set the value of the variable .Ar variable to .Ar value . Normally, all values passed on the command line are also exported to sub-makes in the environment. The .Fl X flag disables this behavior. Variable assignments should follow options for POSIX compatibility but no ordering is enforced. .El .Pp -There are seven different types of lines in a makefile: file dependency +There are several different types of lines in a makefile: dependency specifications, shell commands, variable assignments, include statements, -conditional directives, for loops, and comments. +conditional directives, for loops, other directives, and comments. .Pp -In general, lines may be continued from one line to the next by ending -them with a backslash +Lines may be continued from one line to the next +by ending them with a backslash .Pq Ql \e . The trailing newline character and initial whitespace on the following line are compressed into a single space. .Sh FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets .Dq depend -on the sources -and are customarily created from them. -A target is considered 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 +on the sources and are customarily created from them. +A target is considered 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 is re-created, but not until all sources have been examined and themselves re-created as needed. Three operators may be used: .Bl -tag -width flag .It Ic \&: Many dependency lines may name this target but only one may have attached shell commands. All sources named in all dependency lines are considered together, and if needed the attached shell commands are run to create or re-create the target. If .Nm is interrupted, the target is removed. .It Ic \&! The same, but the target is always re-created whether or not it is out of date. .It Ic \&:: Any 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. Furthermore, unlike -.Ic \&:, +.Ic \&: , for dependency lines with no sources, the attached shell commands are always run. Also unlike -.Ic \&:, -the target will not be removed if +.Ic \&: , +the target is not removed if .Nm is interrupted. .El +.Pp All dependency lines mentioning a particular target must use the same operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , .Ql * , .Ql [] , and .Ql {} . The values .Ql \&? , .Ql * , and .Ql [] -may only be used as part of the final -component of the target or source, and must be used to describe existing -files. +may only be used as part of the final component of the target or source, +and only match existing files. The value .Ql {} need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. .Sh SHELL COMMANDS -Each target may have associated with it one or more lines of shell -commands, normally -used to create the target. +Each target may have associated with it one or more lines of shell commands, +normally used to create the target. Each of the lines in this script .Em must 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 -script. +While targets can occur in many dependency lines if desired, +by default only one of these rules may be followed by a creation script. If the .Sq Ic \&:: -operator is used, however, all rules may include scripts and the -scripts are executed in the order found. +operator is used, however, all rules may include scripts, +and the respective scripts are executed in the order found. .Pp -Each line is treated as a separate shell command, unless the end of -line is escaped with a backslash -.Pq Ql \e +Each line is treated as a separate shell command, +unless the end of line is escaped with a backslash +.Ql \e , in which case that line and the next are combined. -.\" The escaped newline is retained and passed to the shell, which -.\" normally ignores it. -.\" However, the tab at the beginning of the following line is removed. If the first characters of the command are any combination of .Sq Ic @ , .Sq Ic + , or .Sq Ic \- , the command is treated specially. -A -.Sq Ic @ +.Bl -tag -offset indent -width indent +.It Ic @ causes the command not to be echoed before it is executed. -A -.Sq Ic + +.It Ic + causes the command to be executed even when .Fl n is given. -This is similar to the effect of the .MAKE special source, +This is similar to the effect of the +.Va .MAKE +special source, except that the effect can be limited to a single line of a script. -A -.Sq Ic \- +.It Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. +.El .Pp When .Nm is run in jobs mode with .Fl j Ar max_jobs , -the entire script for the target is fed to a -single instance of the shell. +the entire script for the target is fed to a single instance of the shell. In compatibility (non-jobs) mode, each command is run in a separate process. If the command contains any shell meta characters -.Pq Ql #=|^(){};&<>*?[]:$`\e\en -it will be passed to the shell; otherwise +.Pq Ql #=|^(){};&<>*?[]:$`\e\en , +it is passed to the shell; otherwise .Nm -will attempt direct execution. +attempts direct execution. If a line starts with .Sq Ic \- -and the shell has ErrCtl enabled then failure of the command line -will be ignored as in compatibility mode. +and the shell has ErrCtl enabled, +failure of the command line is ignored as in compatibility mode. Otherwise .Sq Ic \- affects the entire job; -the script will stop at the first command line that fails, -but the target will not be deemed to have failed. +the script stops at the first command line that fails, +but the target is not deemed to have failed. .Pp Makefiles should be written so that the mode of .Nm operation does not change their behavior. -For example, any command which needs to use +For example, any command which uses .Dq cd or .Dq chdir -without potentially changing the directory for subsequent commands +without the intention of changing the directory for subsequent 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 +To force the use of a single shell, escape the line breaks so as to make the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: - @echo Building $@ in `pwd` + @echo "Building $@ in $$(pwd)" @(cd ${.CURDIR} && ${MAKE} $@) - @echo Back in `pwd` + @echo "Back in $$(pwd)" ensure-one-shell-regardless-of-mode: - @echo Building $@ in `pwd`; \e + @echo "Building $@ in $$(pwd)"; \e (cd ${.CURDIR} && ${MAKE} $@); \e - echo Back in `pwd` + echo "Back in $$(pwd)" .Ed .Pp Since .Nm -will -.Xr chdir 2 -to +changes the current working directory to .Sq Va .OBJDIR -before executing any targets, each child process -starts with that as its current working directory. +before executing any targets, +each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make behave much like macros in the C preprocessor. .Pp Variable assignments have the form .Sq Ar NAME Ar op Ar value , where: -.Bl -tag -width Ds +.Bl -tag -offset Ds -width Ds .It Ar NAME is a single-word variable name, consisting, by tradition, of all upper-case letters, .It Ar op -is one of the five variable assignment operators described below, and +is one of the variable assignment operators described below, and .It Ar value is interpreted according to the variable assignment operator. .El .Pp Whitespace around .Ar NAME , .Ar op and .Ar value is discarded. .Ss Variable assignment operators -The five operators that can be used to assign values to variables are: +The five operators that assign values to variables are: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overwritten. .It Ic \&+= Append the value to the current value of the variable, separating them by a single space. .It Ic \&?= Assign the value to the variable if it is not already defined. .It Ic \&:= -Assign with expansion, i.e. expand the value before assigning it -to the variable. -Normally, expansion is not done until the variable is referenced. +Expand the value, then assign it to the variable. .Pp .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. +.\" See var-op-expand.mk, the section with LATER and INDIRECT. .It Ic \&!= -Expand the value and pass it to the shell for execution and assign -the result to the variable. +Expand the value and pass it to the shell for execution, +then assign the output from the child's standard output to the variable. Any newlines in the result are replaced with spaces. .El .Ss Expansion of variables -In contexts where variables are expanded, +In most contexts where variables are expanded, .Ql \&$$ expands to a single dollar sign. +In other contexts (most variable modifiers, string literals in conditions), +.Ql \&\e$ +expands to a single dollar sign. +.Pp References to variables have the form -.Ql \&${ Ns Ar name Ns Oo \&: Ns Ar modifiers Oc Ns } +.Cm \&${ Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&} or -.Ql \&$( Ns Ar name Ns Oo \&: Ns Ar modifiers Oc Ns ) . -If the variable name contains only a single character, +.Cm \&$( Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&) . +If the variable name consists of only a single character +and the expression contains no modifiers, the surrounding curly braces or parentheses are not required. This shorter form is not recommended. .Pp -If the variable name contains a dollar, then the name itself is expanded first. +If the variable name contains a dollar, the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parentheses, or whitespace are really best avoided. +braces, parentheses or whitespace are really best avoided. .Pp -If the result of expanding a variable contains a dollar sign +If the result of expanding a nested variable expression contains a dollar sign .Pq Ql \&$ , -the string is expanded again. +the result is subject to further expansion. .Pp Variable substitution occurs at four distinct times, depending on where the variable is being used. .Bl -enum .It Variables in dependency lines are expanded as the line is read. .It Variables in conditionals are expanded individually, but only as far as necessary to determine the result of the conditional. .It Variables in shell commands are expanded when the shell command is executed. .It -.Dq .for +.Ic .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded when composing the body of a loop, so the following example code: .Bd -literal -offset indent - -.Dv .for i in 1 2 3 +\&.for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} -.Dv .endfor +\&.endfor all: @echo ${a} @echo ${b} - .Ed -will print: +.Pp +prints: .Bd -literal -offset indent 1 2 3 3 3 3 - .Ed -Because while ${a} contains -.Dq 1 2 3 -after the loop is executed, ${b} +.Pp +After the loop is executed: +.Bl -tag -offset indent -width indent +.It Va a contains -.Dq ${j} ${j} ${j} +.Ql ${:U1} ${:U2} ${:U3} , which expands to -.Dq 3 3 3 -since after the loop completes ${j} contains -.Dq 3 . +.Ql 1 2 3 . +.It Va j +contains +.Ql ${:U3} , +which expands to +.Ql 3 . +.It Va b +contains +.Ql ${j} ${j} ${j} , +which expands to +.Ql ${:U3} ${:U3} ${:U3} +and further to +.Ql 3 3 3 . +.El .El .Ss Variable classes The four different classes of variables (in order of increasing precedence) are: .Bl -tag -width Ds .It Environment variables Variables defined as part of .Nm Ns 's environment. .It Global variables Variables defined in the makefile or in included makefiles. .It Command line variables Variables defined as part of the command line. .It Local variables Variables that are defined specific to a certain target. .El .Pp -Local variables can be set on a dependency line, if +Local variables can be set on a dependency line, unless .Va .MAKE.TARGET_LOCAL_VARIABLES -is not set to +is set to .Ql false . The rest of the line -(which will already have had global variables expanded) +(which already has had global variables expanded) is the variable value. For example: .Bd -literal -offset indent COMPILER_WRAPPERS= ccache distcc icecc ${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,} .Ed .Pp Only the targets .Ql ${OBJS} -will be impacted by that filter (in "meta" mode) and -simply enabling/disabling any of the compiler wrappers will not render all +are impacted by that filter (in +.Dq meta +mode) and +simply enabling/disabling any of the compiler wrappers does not render all of those targets out-of-date. .Pp .Em NOTE : target-local variable assignments behave differently in that; .Bl -tag -width Ds -offset indent .It Ic \&+= Only appends to a previous local assignment for the same target and variable. .It Ic \&:= Is redundant with respect to global variables, which have already been expanded. .El .Pp -The seven built-in local variables are as follows: -.Bl -tag -width ".ARCHIVE" -offset indent +The seven built-in local variables are: +.Bl -tag -width ".Va .ARCHIVE" -offset indent .It Va .ALLSRC The list of all sources for this target; also known as .Sq Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Sq Va \&! . .It Va .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the .Dq implied source); also known as .Sq Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Sq Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Sq Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Sq Va * . The suffix must be one of the known suffixes declared with -.Ic .SUFFIXES -or it will not be recognized. +.Ic .SUFFIXES , +or it is not recognized. .It Va .TARGET The name of the target; also known as .Sq Va @ . For compatibility with other makes this is an alias for -.Ic .ARCHIVE +.Va .ARCHIVE in archive member rules. .El .Pp The shorter forms -.Ql ( Va > , +.Po +.Sq Va \&> , .Sq Va \&! , -.Sq Va < , -.Sq Va % , +.Sq Va \&< , +.Sq Va \&% , .Sq Va \&? , -.Sq Va * , +.Sq Va \&* , and -.Sq Va @ ) +.Sq Va \&@ +.Pc are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. .Pp Variants of these variables with the punctuation followed immediately by .Ql D or .Ql F , e.g.\& -.Sq Va $(@D) , +.Ql $(@D) , are legacy forms equivalent to using the .Ql :H and .Ql :T modifiers. These forms are accepted for compatibility with .At V makefiles and POSIX but are not recommended. .Pp Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. These variables are .Sq Va .TARGET , .Sq Va .PREFIX , .Sq Va .ARCHIVE , and .Sq Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: -.Bl -tag -width .MAKEOVERRIDES +.Bl -tag +.\" NB: This list is sorted case-insensitive, ignoring punctuation. +.\" NB: To find all built-in variables in make's source code, +.\" NB: search for Var_*, Global_*, SetVarObjdir, GetBooleanExpr, +.\" NB: and the implementation of Var_SetWithFlags. +.\" NB: Last synced on 2023-01-01. .It Va .ALLTARGETS -The list of all targets encountered in the Makefile. -If evaluated during -Makefile parsing, lists only those targets encountered thus far. +The list of all targets encountered in the makefiles. +If evaluated during makefile parsing, +lists only those targets encountered thus far. .It Va .CURDIR A path to the directory where .Nm was executed. Refer to the description of -.Sq Ev PWD +.Sq Va PWD for more details. +.It Va .ERROR_CMD +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_CWD +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_META_FILE +Is used in error handling in +.Dq meta +mode, see +.Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .ERROR_TARGET +Is used in error handling, see +.Va MAKE_PRINT_VAR_ON_ERROR . .It Va .INCLUDEDFROMDIR -The directory of the file this Makefile was included from. +The directory of the file this makefile was included from. .It Va .INCLUDEDFROMFILE -The filename of the file this Makefile was included from. -.It Ev MAKE +The filename of the file this makefile was included from. +.\" .INCLUDES is intentionally undocumented, as it is obsolete. +.\" .LIBS is intentionally undocumented, as it is obsolete. +.It Va MACHINE +The machine hardware name, see +.Xr uname 1 . +.It Va MACHINE_ARCH +The machine processor architecture name, see +.Xr uname 1 . +.It Va MAKE The name that .Nm was executed with .Pq Va argv[0] . -For compatibility -.Nm -also sets -.Va .MAKE -with the same value. +.It Va .MAKE +The same as +.Va MAKE , +for compatibility. The preferred variable to use is the environment variable .Ev MAKE -because it is more compatible with other versions of -.Nm +because it is more compatible with other make variants and cannot be confused with the special target with the same name. .It Va .MAKE.ALWAYS_PASS_JOB_QUEUE Tells .Nm whether to pass the descriptors of the job token queue even if the target is not tagged with .Ic .MAKE The default is .Ql Pa yes for backwards compatability with .Fx 9.0 and earlier. +.\" '.MAKE.cmd_filtered' is intentionally undocumented, +.\" as it is an internal implementation detail. .It Va .MAKE.DEPENDFILE Names the makefile (default .Sq Pa .depend ) from which generated dependencies are read. +.It Va .MAKE.DIE_QUIETLY +If set to +.Ql true , +do not print error information at the end. .It Va .MAKE.EXPAND_VARIABLES A boolean that controls the default behavior of the .Fl V option. If true, variable values printed with .Fl V are fully expanded; if false, the raw variable contents (which may include additional unexpanded variable references) are shown. .It Va .MAKE.EXPORTED The list of variables exported by .Nm . -.It Va .MAKE.JOBS -The argument to the -.Fl j -option. +.It Va MAKEFILE +The top-level makefile that is currently read, +as given in the command line. +.It Va .MAKEFLAGS +The environment variable +.Sq Ev MAKEFLAGS +may contain anything that +may be specified on +.Nm Ns 's +command line. +Anything specified on +.Nm Ns 's +command line is appended to the +.Va .MAKEFLAGS +variable, which is then added to the environment for all programs that +.Nm +executes. +.It Va .MAKE.GID +The numeric group ID of the user running +.Nm . +It is read-only. .It Va .MAKE.JOB.PREFIX If .Nm is run with .Fl j , the output for each target is prefixed with a token -.Ql --- target --- +.Dl --- Ar target Li --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. For example, setting .Va .MAKE.JOB.PREFIX to -.Li ${.newline}---${.MAKE:T}[${.MAKE.PID}] +.Ql ${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like -.Ql ---make[1234] target --- +.Dl ---make[1234] Ar target Li --- making it easier to track the degree of parallelism being achieved. -.It .MAKE.TARGET_LOCAL_VARIABLES -If set to -.Ql false , -apparent variable assignments in dependency lines are -treated as normal sources. -.It Ev MAKEFLAGS -The environment variable -.Sq Ev MAKEFLAGS -may contain anything that -may be specified on -.Nm Ns 's -command line. -Anything specified on -.Nm Ns 's -command line is appended to the -.Sq Ev MAKEFLAGS -variable which is then -entered into the environment for all programs which -.Nm -executes. +.It Va .MAKE.JOBS +The argument to the +.Fl j +option. .It Va .MAKE.LEVEL The recursion depth of .Nm . -The initial instance of +The top-level instance of .Nm -will be 0, and an incremented value is put into the environment -to be seen by the next generation. +has level 0, and each child make has its parent level plus 1. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 -to protect things which should only be evaluated in the initial instance of +to protect things which should only be evaluated in the top-level instance of +.Nm . +.It Va .MAKE.LEVEL.ENV +The name of the environment variable that stores the level of nested calls to .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Sq Pa makefile , .Sq Pa Makefile ) that .Nm -will look for. +looks for. .It Va .MAKE.MAKEFILES The list of makefiles read by .Nm , which is useful for tracking dependencies. Each makefile is recorded only once, regardless of the number of times read. +.It Va .MAKE.META.BAILIWICK +In +.Dq meta +mode, provides a list of prefixes which +match the directories controlled by +.Nm . +If a file that was generated outside of +.Va .OBJDIR +but within said bailiwick is missing, +the current target is considered out-of-date. +.It Va .MAKE.META.CMP_FILTER +In +.Dq meta +mode, it can (very rarely!) be useful to filter command +lines before comparison. +This variable can be set to a set of modifiers that are applied to +each line of the old and new command that differ, if the filtered +commands still differ, the target is considered out-of-date. +.It Va .MAKE.META.CREATED +In +.Dq meta +mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In +.Dq meta +mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.IGNORE_FILTER +Provides a list of variable modifiers to apply to each pathname. +Ignore if the expansion is an empty string. +.It Va .MAKE.META.IGNORE_PATHS +Provides a list of path prefixes that should be ignored; +because the contents are expected to change over time. +The default list includes: +.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp +.It Va .MAKE.META.IGNORE_PATTERNS +Provides a list of patterns to match against pathnames. +Ignore any that match. +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in +.Dq meta verbose +mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} .It Va .MAKE.MODE Processed after reading all makefiles. -Can affect the mode that +Affects the mode that .Nm runs in. -It can contain a number of keywords: -.Bl -hang -width missing-filemon=bf. -.It Pa compat +It can contain these keywords: +.Bl -tag -width indent +.It Cm compat Like .Fl B , puts .Nm -into "compat" mode. -.It Pa meta +into +.Dq compat +mode. +.It Cm meta Puts .Nm -into "meta" mode, where meta files are created for each target -to capture the command run, the output generated and if +into +.Dq meta +mode, where meta files are created for each target +to capture the command run, the output generated, and if .Xr filemon 4 is available, the system calls which are of interest to .Nm . -The captured output can be very useful when diagnosing errors. -.It Pa curdirOk= Ar bf -Normally +The captured output can be useful when diagnosing errors. +.It Cm curdirOk= Ns Ar bf +By default, .Nm -will not create .meta files in +does not create +.Pa .meta +files in .Sq Va .CURDIR . This can be overridden by setting -.Va bf -to a value which represents True. -.It Pa missing-meta= Ar bf +.Ar bf +to a value which represents true. +.It Cm missing-meta= Ns Ar bf If -.Va bf -is True, then a missing .meta file makes the target out-of-date. -.It Pa missing-filemon= Ar bf +.Ar bf +is true, a missing +.Pa .meta +file makes the target out-of-date. +.It Cm missing-filemon= Ns Ar bf If -.Va bf -is True, then missing filemon data makes the target out-of-date. -.It Pa nofilemon +.Ar bf +is true, missing filemon data makes the target out-of-date. +.It Cm nofilemon Do not use .Xr filemon 4 . -.It Pa env +.It Cm env For debugging, it can be useful to include the environment -in the .meta file. -.It Pa verbose -If in "meta" mode, print a clue about the target being built. +in the +.Pa .meta +file. +.It Cm verbose +If in +.Dq meta +mode, print a clue about the target being built. This is useful if the build is otherwise running silently. -The message printed the value of: +The message printed is the expanded value of .Va .MAKE.META.PREFIX . -.It Pa ignore-cmd +.It Cm ignore-cmd Some makefiles have commands which are simply not stable. This keyword causes them to be ignored for -determining whether a target is out of date in "meta" mode. +determining whether a target is out of date in +.Dq meta +mode. See also .Ic .NOMETA_CMP . -.It Pa silent= Ar bf +.It Cm silent= Ns Ar bf If -.Va bf -is True, when a .meta file is created, mark the target +.Ar bf +is true, when a .meta file is created, mark the target .Ic .SILENT . -.It Pa randomize-targets +.It Cm randomize-targets In both compat and parallel mode, do not make the targets in the usual order, but instead randomize their order. This mode can be used to detect undeclared dependencies between files. .El -.It Va .MAKE.META.BAILIWICK -In "meta" mode, provides a list of prefixes which -match the directories controlled by -.Nm . -If a file that was generated outside of -.Va .OBJDIR -but within said bailiwick is missing, -the current target is considered out-of-date. -.It Va .MAKE.META.CMP_FILTER -In "meta" mode, it can (very rarely!) be useful to filter command -lines before comparison. -This variable can be set to a set of modifiers that will be applied to -each line of the old and new command that differ, if the filtered -commands still differ, the target is considered out-of-date. -.It Va .MAKE.META.CREATED -In "meta" mode, this variable contains a list of all the meta files -updated. -If not empty, it can be used to trigger processing of -.Va .MAKE.META.FILES . -.It Va .MAKE.META.FILES -In "meta" mode, this variable contains a list of all the meta files -used (updated or not). -This list can be used to process the meta files to extract dependency -information. -.It Va .MAKE.META.IGNORE_PATHS -Provides a list of path prefixes that should be ignored; -because the contents are expected to change over time. -The default list includes: -.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp -.It Va .MAKE.META.IGNORE_PATTERNS -Provides a list of patterns to match against pathnames. -Ignore any that match. -.It Va .MAKE.META.IGNORE_FILTER -Provides a list of variable modifiers to apply to each pathname. -Ignore if the expansion is an empty string. -.It Va .MAKE.META.PREFIX -Defines the message printed for each meta file updated in "meta verbose" mode. -The default value is: -.Dl Building ${.TARGET:H:tA}/${.TARGET:T} +.It Va MAKEOBJDIR +Used to create files in a separate directory, see +.Va .OBJDIR . +.It Va MAKE_OBJDIR_CHECK_WRITABLE +Used to force a separate directory for the created files, +even if that directory is not writable, see +.Va .OBJDIR . +.It Va MAKEOBJDIRPREFIX +Used to create files in a separate directory, see +.Va .OBJDIR . +.It Va .MAKE.OS +The name of the operating system, see +.Xr uname 1 . +It is read-only. .It Va .MAKEOVERRIDES 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 .Sq Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Sq Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Sq Va .MAKEOVERRIDES . .Sq Ev MAKEFLAGS is re-exported whenever .Sq Va .MAKEOVERRIDES is modified. .It Va .MAKE.PATH_FILEMON If .Nm was built with .Xr filemon 4 support, this is set to the path of the device node. This allows makefiles to test for this support. .It Va .MAKE.PID -The process-id of +The process ID of .Nm . +It is read-only. .It Va .MAKE.PPID -The parent process-id of -.Nm . -.It Va .MAKE.SAVE_DOLLARS -value should be a boolean that controls whether -.Ql $$ -are preserved when doing -.Ql := -assignments. -The default is false, for backwards compatibility. -Set to true for compatability with other makes. -If set to false, -.Ql $$ -becomes -.Ql $ -per normal evaluation rules. -.It Va .MAKE.UID -The user-id running -.Nm . -.It Va .MAKE.GID -The group-id running +The parent process ID of .Nm . +It is read-only. .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Sq Va .ERROR_TARGET to the name of the target that failed, .Sq Va .ERROR_CMD to the commands of the failed target, -and in "meta" mode, it also sets +and in +.Dq meta +mode, it also sets .Sq Va .ERROR_CWD to the .Xr getcwd 3 , and .Sq Va .ERROR_META_FILE to the path of the meta file (if any) describing the failed target. It then prints its name and the value of .Sq Va .CURDIR as well as the value of any variables named in .Sq Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .MAKE.SAVE_DOLLARS +If true, +.Ql $$ +are preserved when doing +.Ql := +assignments. +The default is false, for backwards compatibility. +Set to true for compatability with other makes. +If set to false, +.Ql $$ +becomes +.Ql $ +per normal evaluation rules. +.It Va .MAKE.TARGET_LOCAL_VARIABLES +If set to +.Ql false , +apparent variable assignments in dependency lines are +treated as normal sources. +.It Va .MAKE.UID +The numeric ID of the user running +.Nm . +It is read-only. +.\" 'MAKE_VERSION' is intentionally undocumented +.\" since it is only defined in the bmake distribution, +.\" but not in NetBSD's native make. +.\" '.meta.%d.lcwd' is intentionally undocumented +.\" since it is an internal implementation detail. +.\" '.meta.%d.ldir' is intentionally undocumented +.\" since it is an internal implementation detail. +.\" 'MFLAGS' is intentionally undocumented +.\" since it is obsolete. .It Va .newline This variable is simply assigned a newline character as its value. +It is read-only. This allows expansions using the .Cm \&:@ modifier to put a newline between iterations of the loop rather than a space. -For example, the printing of -.Sq Va MAKE_PRINT_VAR_ON_ERROR -could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. +For example, in case of an error, +.Nm +prints the variable names and their values using: +.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Va .OBJDIR A path to the directory where the targets are built. Its value is determined by trying to .Xr chdir 2 to the following directories in order and using the first match: .Bl -enum .It -.Ev ${MAKEOBJDIRPREFIX}${.CURDIR} +.Cm ${MAKEOBJDIRPREFIX} Ns Cm ${.CURDIR} .Pp (Only if .Sq Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It -.Ev ${MAKEOBJDIR} +.Cm ${MAKEOBJDIR} .Pp (Only if .Sq Ev MAKEOBJDIR is set in the environment or on the command line.) .It -.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} +.Cm ${.CURDIR} Ns Pa /obj. Ns Cm ${MACHINE} .It -.Ev ${.CURDIR} Ns Pa /obj +.Cm ${.CURDIR} Ns Pa /obj .It -.Pa /usr/obj/ Ns Ev ${.CURDIR} +.Pa /usr/obj/ Ns Cm ${.CURDIR} .It -.Ev ${.CURDIR} +.Cm ${.CURDIR} .El .Pp -Variable expansion is performed on the value before it's used, +Variable expansion is performed on the value before it is used, so expressions such as -.Dl ${.CURDIR:S,^/usr/src,/var/obj,} +.Cm ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Sq Ev MAKEOBJDIR . .Pp .Sq Va .OBJDIR may be modified in the makefile via the special target .Sq Ic .OBJDIR . In all cases, .Nm -will -.Xr chdir 2 -to the specified directory if it exists, and set +changes to the specified directory if it exists, and sets .Sq Va .OBJDIR and -.Sq Ev PWD +.Sq Va PWD to that directory before executing any targets. .Pp Except in the case of an explicit .Sq Ic .OBJDIR target, .Nm -will check that the specified directory is writable and ignore it if not. +checks that the specified directory is writable and ignores it if not. This check can be skipped by setting the environment variable .Sq Ev MAKE_OBJDIR_CHECK_WRITABLE -to "no". -. +to +.Dq no . .It Va .PARSEDIR -A path to the directory of the current -.Sq Pa Makefile -being parsed. +The directory name of the current makefile being parsed. .It Va .PARSEFILE -The basename of the current -.Sq Pa Makefile -being parsed. +The basename of the current makefile being parsed. This variable and .Sq Va .PARSEDIR -are both set only while the -.Sq Pa Makefiles -are being parsed. -If you want to retain their current values, assign them to a variable -using assignment with expansion +are both set only while the makefiles are being parsed. +To retain their current values, +assign them to a variable using assignment with expansion .Sq Cm \&:= . .It Va .PATH -A variable that represents the list of directories that +The space-separated list of directories that .Nm -will search for files. -The search list should be updated using the target -.Sq Va .PATH -rather than the variable. -.It Ev PWD +searches for files. +To update this search list, use the special target +.Sq Ic .PATH +rather than modifying the variable directly. +.It Va %POSIX +Is set in POSIX mode, see the special +.Ql Va .POSIX +target. +.\" XXX: There is no make variable named 'PWD', +.\" XXX: make only reads and writes the environment variable 'PWD'. +.It Va PWD Alternate path to the current directory. .Nm normally sets .Sq Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Sq Ev PWD -is set and gives a path to the current directory, then +is set and gives a path to the current directory, .Nm sets .Sq Va .CURDIR to the value of .Sq Ev PWD instead. This behavior is disabled if .Sq Ev MAKEOBJDIRPREFIX is set or .Sq Ev MAKEOBJDIR contains a variable transform. -.Sq Ev PWD +.Sq Va PWD is set to the value of .Sq Va .OBJDIR for all programs which .Nm executes. -.It Ev .SHELL +.It Va .SHELL The pathname of the shell used to run target scripts. It is read-only. -.It Ev .SUFFIXES +.It Va .SUFFIXES The list of known suffixes. It is read-only. -.It Ev .TARGETS +.It Va .SYSPATH +The space-separated list of directories that +.Nm +searches for makefiles, referred to as the system include path. +To update this search list, use the special target +.Sq Ic .SYSPATH +rather than modifying the variable which is read-only. +.It Va .TARGETS The list of targets explicitly specified on the command line, if any. -.It Ev VPATH -Colon-separated +.It Va VPATH +The colon-separated .Pq Dq \&: -lists of directories that +list of directories that .Nm -will search for files. -The variable is supported for compatibility with old make programs only, -use +searches for files. +This variable is supported for compatibility with old make programs only, use .Sq Va .PATH instead. .El .Ss Variable modifiers -Variable expansion may be modified to select or modify each word of the -variable (where a -.Dq word -is white-space delimited sequence of characters). -The general format of a variable expansion is as follows: +The general format of a variable expansion is: .Pp -.Dl ${variable[:modifier[:...]]} +.Sm off +.D1 Ic \&${ Ar variable\| Oo Ic \&: Ar modifier\| Oo Ic \&: No ... Oc Oc Ic \&} +.Sm on .Pp -Each modifier begins with a colon, -which may be escaped with a backslash -.Pq Ql \e . +Each modifier begins with a colon. +To escape a colon, precede it with a backslash +.Ql \e . .Pp -A set of modifiers can be specified via a variable, as follows: +A list of indirect modifiers can be specified via a variable, as follows: .Pp -.Dl modifier_variable=modifier[:...] -.Dl ${variable:${modifier_variable}[:...]} +.Bd -literal -offset indent +.Ar modifier_variable\^ Li \&= Ar modifier Ns Oo Ic \&: Ns No ... Oc + +.Sm off +.Ic \&${ Ar variable Ic \&:${ Ar modifier_variable Ic \&} Oo Ic \&: No ... Oc Ic \&} +.Sm on +.Ed .Pp -In this case the first modifier in the modifier_variable does not -start with a colon, since that must appear in the referencing -variable. -If any of the modifiers in the modifier_variable contain a dollar sign +In this case, the first modifier in the +.Ar modifier_variable +does not start with a colon, +since that colon already occurs in the referencing variable. +If any of the modifiers in the +.Ar modifier_variable +contains a dollar sign .Pq Ql $ , these must be doubled to avoid early expansion. .Pp +Some modifiers interpret the expression value as a single string, +others treat the expression value as a whitespace-separated list of words. +When splitting a string into words, +whitespace can be escaped using double quotes, single quotes and backslashes, +like in the shell. +The quotes and backslashes are retained in the words. +.Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E -Replaces each word in the variable with its suffix. +Replaces each word with its suffix. .It Cm \&:H -Replaces each word in the variable with everything but the last component. +Replaces each word with its dirname. .It Cm \&:M\| Ns Ar pattern Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql \&[] ) may be used. The wildcard characters may be escaped with a backslash .Pq Ql \e . As a consequence of the way values are split into words, matched, -and then joined, a construct like -.Dl ${VAR:M*} -will normalize the inter-word spacing, removing all leading and -trailing space, and converting multiple consecutive spaces -to single spaces. -. +and then joined, the construct +.Ql ${VAR:M*} +removes all leading and trailing whitespace +and normalizes the inter-word spacing to a single space. .It Cm \&:N\| Ns Ar pattern -This is identical to +This is the opposite of .Sq Cm \&:M , -but selects all words which do not match +selecting all words which do +.Em not +match .Ar pattern . .It Cm \&:O -Orders every word in variable alphabetically. +Orders the words lexicographically. .It Cm \&:On -Orders every word in variable numerically. +Orders the words numerically. A number followed by one of .Ql k , .Ql M or .Ql G -is multiplied by the appropriate factor (1024 (k), 1048576 (M), or -1073741824 (G)). +is multiplied by the appropriate factor, which is 1024 for +.Ql k , +1048576 for +.Ql M , +or 1073741824 for +.Ql G . Both upper- and lower-case letters are accepted. .It Cm \&:Or -Orders every word in variable in reverse alphabetical order. +Orders the words in reverse lexicographical order. .It Cm \&:Orn -Orders every word in variable in reverse numerical order. +Orders the words in reverse numerical order. .It Cm \&:Ox -Shuffles the words in variable. -The results will be different each time you are referring to the +Shuffles the words. +The results are different each time you are referring to the modified variable; use the assignment with expansion .Sq Cm \&:= to prevent such behavior. For example, .Bd -literal -offset indent LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" .Ed may produce output similar to: .Bd -literal -offset indent quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre .Ed .It Cm \&:Q -Quotes every shell meta-character in the variable, so that it can be passed +Quotes every shell meta-character in the value, so that it can be passed safely to the shell. .It Cm \&:q -Quotes every shell meta-character in the variable, and also doubles +Quotes every shell meta-character in the value, and also doubles .Sq $ characters so that it can be passed safely through recursive invocations of .Nm . -This is equivalent to: -.Sq \&:S/\e\&$/&&/g:Q . +This is equivalent to +.Sq Cm \&:S/\e\&$/&&/g:Q . .It Cm \&:R -Replaces each word in the variable with everything but its suffix. -.It Cm \&:range Ns Oo = Ns Ar count Oc +Replaces each word with everything but its suffix. +.It Cm \&:range Ns Oo Cm = Ns Ar count Oc The value is an integer sequence representing the words of the original value, or the supplied -.Va count . -.It Cm \&:gmtime Ns Oo = Ns Ar utc Oc -The value is a format string for +.Ar count . +.It Cm \&:gmtime Ns Oo Cm = Ns Ar timestamp Oc +The value is interpreted as a format string for .Xr strftime 3 , using -.Xr gmtime 3 . +.Xr gmtime 3 , +producing the formatted timestamp. If a -.Va utc +.Ar timestamp value is not provided or is 0, the current time is used. .It Cm \&:hash -Computes a 32-bit hash of the value and encode it as hex digits. -.It Cm \&:localtime Ns Oo = Ns Ar utc Oc -The value is a format string for +Computes a 32-bit hash of the value and encodes it as 8 hex digits. +.It Cm \&:localtime Ns Oo Cm = Ns Ar timestamp Oc +The value is interpreted as a format string for .Xr strftime 3 , using -.Xr localtime 3 . +.Xr localtime 3 , +producing the formatted timestamp. If a -.Va utc +.Ar timestamp value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempts to convert variable to an absolute path using -.Xr realpath 3 , -if that fails, the value is unchanged. +Attempts to convert the value to an absolute path using +.Xr realpath 3 . +If that fails, the value is unchanged. .It Cm \&:tl -Converts variable to lower-case letters. +Converts the value to lower-case letters. .It Cm \&:ts Ns Ar c -Words in the variable are normally separated by a space on expansion. -This modifier sets the separator to the character +When joining the words after a modifier that treats the value as words, +the words are normally separated by a space. +This modifier changes the separator to the character .Ar c . If .Ar c -is omitted, then no separator is used. +is omitted, no separator is used. The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu -Converts variable to upper-case letters. +Converts the value to upper-case letters. .It Cm \&:tW -Causes the value to be treated as a single word -(possibly containing embedded white space). +Causes subsequent modifiers to treat the value as a single word +(possibly containing embedded whitespace). See also .Sq Cm \&:[*] . .It Cm \&:tw -Causes the value to be treated as a sequence of -words delimited by white space. +Causes the value to be treated as a list of words. See also .Sq Cm \&:[@] . .Sm off .It Cm \&:S\| No \&/ Ar old_string\| No \&/ Ar new_string\| No \&/ Op Cm 1gW .Sm on Modifies the first occurrence of .Ar old_string -in each word of the variable's value, replacing it with +in each word of the value, replacing it with .Ar new_string . If a .Ql g -is appended to the last delimiter of the pattern, all occurrences -in each word are replaced. +is appended to the last delimiter of the pattern, +all occurrences in each word are replaced. If a .Ql 1 -is appended to the last delimiter of the pattern, only the first occurrence -is affected. +is appended to the last delimiter of the pattern, +only the first occurrence is affected. If a .Ql W is appended to the last delimiter of the pattern, -then the value is treated as a single word -(possibly containing embedded white space). +the value is treated as a single word. If .Ar old_string begins with a caret .Pq Ql ^ , .Ar old_string is anchored at the beginning of each word. If .Ar old_string ends with a dollar sign .Pq Ql \&$ , it is anchored at the end of each word. Inside .Ar new_string , an ampersand .Pq Ql & is replaced by .Ar old_string -(without any +(without the anchoring .Ql ^ or .Ql \&$ ) . -Any character may be used as a delimiter for the parts of the modifier +Any character may be used as the delimiter for the parts of the modifier string. -The anchoring, ampersand and delimiter characters may be escaped with a +The anchoring, ampersand and delimiter characters can be escaped with a backslash .Pq Ql \e . .Pp -Variable expansion occurs in the normal fashion inside both +Both .Ar old_string and .Ar new_string -with the single exception that a backslash is used to prevent the expansion -of a dollar sign -.Pq Ql \&$ , -not a preceding dollar sign as is usual. +may contain nested expressions. +To prevent a dollar sign from starting a nested expression, +escape it with a backslash. .Sm off .It Cm \&:C\| No \&/ Ar pattern\| No \&/ Ar replacement\| No \&/ Op Cm 1gW .Sm on The .Cm \&:C -modifier is just like the +modifier works like the .Cm \&:S modifier except that the old and new strings, instead of being -simple strings, are an extended regular expression (see -.Xr regex 3 ) -string +simple strings, are an extended regular expression .Ar pattern +(see +.Xr regex 3 ) and an .Xr ed 1 Ns \-style -string .Ar replacement . Normally, the first occurrence of the pattern .Ar pattern in each word of the value is substituted with .Ar replacement . The .Ql 1 modifier causes the substitution to apply to at most one word; the .Ql g modifier causes the substitution to apply to as many instances of the search pattern .Ar pattern as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word -(possibly containing embedded white space). +(possibly containing embedded whitespace). .Pp As for the .Cm \&:S modifier, the .Ar pattern and .Ar replacement are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last path component. +Replaces each word with its last path component (basename). .It Cm \&:u Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&?\| Ar true_string\| Cm \&: Ar false_string .Sm on -If the variable name (not its value), when parsed as a .if conditional -expression, evaluates to true, return as its value the +If the variable name (not its value), when parsed as a +.Cm .if +conditional expression, evaluates to true, return as its value the .Ar true_string , otherwise return the .Ar false_string . -Since the variable name is used as the expression, \&:\&? must be the -first modifier after the variable name itself - which will, of course, -usually contain variable expansions. +Since the variable name is used as the expression, +\&:\&? must be the first modifier after the variable name +.No itself Ns \^\(em\^ Ns +which, of course, usually contains variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} -which actually tests defined(NUMBERS), -to determine if any words match "42" you need to use something like: +which actually tests defined(NUMBERS). +To determine if any words match +.Dq 42 , +you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Cm :\| Ns Ar old_string\| Ns Cm = Ns Ar new_string This is the .At V -style variable substitution. -It must be the last modifier specified. -If +style substitution. +It can only be the last modifier specified, +as a +.Ql \&: +in either .Ar old_string or .Ar new_string -do not contain the pattern matching character -.Ar % -then it is assumed that they are -anchored at the end of each word, so only suffixes or entire -words may be replaced. -Otherwise -.Ar % -is the substring of -.Ar old_string -to be replaced in -.Ar new_string . -If only +is treated as a regular character, not as the end of the modifier. +.Pp +If .Ar old_string -contains the pattern matching character -.Ar % , -and +does not contain the pattern matching character +.Ql % , +and the word ends with .Ar old_string -matches, then the result is the +or equals it, +that suffix is replaced with .Ar new_string . -If only the -.Ar new_string -contains the pattern matching character -.Ar % , -then it is not treated specially and it is printed as a literal -.Ar % -on match. -If there is more than one pattern matching character -.Ar ( % ) -in either the +.Pp +Otherwise, the first +.Ql % +in +.Ar old_string +matches a possibly empty substring of arbitrary characters, +and if the whole pattern is found in the word, +the matching part is replaced with +.Ar new_string , +and the first occurrence of +.Ql % +in .Ar new_string -or -.Ar old_string , -only the first instance is treated specially (as the pattern character); -all subsequent instances are treated as regular characters. +(if any) is replaced with the substring matched by the +.Ql % . .Pp -Variable expansion occurs in the normal fashion inside both +Both .Ar old_string and .Ar new_string -with the single exception that a backslash is used to prevent the -expansion of a dollar sign -.Pq Ql \&$ , -not a preceding dollar sign as is usual. +may contain nested expressions. +To prevent a dollar sign from starting a nested expression, +escape it with a backslash. .Sm off -.It Cm \&:@ Ar temp\| Cm @ Ar string\| Cm @ +.It Cm \&:@ Ar varname\| Cm @ Ar string\| Cm @ .Sm on This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for loops, expansion occurs at the time of reference. -Assigns -.Ar temp -to each word in the variable and evaluates +For each word in the value, assign the word to the variable named +.Ar varname +and evaluate .Ar string . The ODE convention is that -.Ar temp -should start and end with a period. -For example. +.Ar varname +should start and end with a period, for example: .Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} .Pp -However a single character variable is often more readable: +However, a single-letter variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_ Ns Oo Cm = Ns Ar var Oc Saves the current variable value in .Ql $_ or the named -.Va var +.Ar var for later reference. Example usage: .Bd -literal -offset indent M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \&\\ \\* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .Dv .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} .Ed Here .Ql $_ is used to save the result of the .Ql :S modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U\| Ns Ar newval If the variable is undefined, .Ar newval 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: .Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D\| Ns Ar newval If the variable is defined, .Ar newval is the value. .It Cm \&:L The name of the variable is the value. .It Cm \&:P -The path of the node which has the same name as the variable -is the value. -If no such node exists or its path is null, then the -name of the variable is used. +The path of the node which has the same name as the variable is the value. +If no such node exists or its path is null, the name of the variable is used. In order for this modifier to work, the name (node) must at least have -appeared on the rhs of a dependency. +appeared on the right-hand side of a dependency. .Sm off .It Cm \&:\&! Ar cmd\| Cm \&! .Sm on The output of running .Ar cmd is the value. .It Cm \&:sh -If the variable is non-empty it is run as a command and the output -becomes the new value. +The value is run as a command, and the output becomes the new value. .It Cm \&::= Ns Ar str The variable is assigned the value .Ar str after substitution. -This modifier and its variations are useful in -obscure situations such as wanting to set a variable when shell commands -are being parsed. -These assignment modifiers always expand to -nothing, so if appearing in a rule line by themselves should be -preceded with something to keep -.Nm -happy. +This modifier and its variations are useful in obscure situations +such as wanting to set a variable +at a point where a target's shell commands are being parsed. +These assignment modifiers always expand to nothing. .Pp The .Sq Cm \&:: helps avoid false matches with the .At V style -.Cm \&:= -modifier and since substitution always occurs the -.Cm \&::= +.Ql \&:= +modifier and since substitution always occurs, the +.Ql \&::= form is vaguely appropriate. .It Cm \&::?= Ns Ar str As for .Cm \&::= but only if the variable does not already have a value. .It Cm \&::+= Ns Ar str Append .Ar str to the variable. .It Cm \&::!= Ns Ar cmd Assign the output of .Ar cmd to the variable. .It Cm \&:\&[ Ns Ar range Ns Cm \&] Selects one or more words from the value, or performs other operations related to the way in which the -value is divided into words. +value is split into words. .Pp -Ordinarily, a value is treated as a sequence of words -delimited by white space. -Some modifiers suppress this behavior, -causing a value to be treated as a single word -(possibly containing embedded white space). An empty value, or a value that consists entirely of white-space, is treated as a single word. For the purposes of the .Sq Cm \&:[] modifier, the words are indexed both forwards using positive integers (where index 1 represents the first word), and backwards using negative integers (where index \-1 represents the last word). .Pp The .Ar range is subjected to variable expansion, and the expanded result is then interpreted as follows: .Bl -tag -width index .\" :[n] .It Ar index Selects a single word from the value. .\" :[start..end] .It Ar start Ns Cm \&.. Ns Ar end Selects all words from .Ar start to .Ar end , inclusive. For example, .Sq Cm \&:[2..-1] selects all words from the second word to the last word. If .Ar start is greater than .Ar end , -then the words are output in reverse order. +the words are output in reverse order. For example, .Sq Cm \&:[-1..1] selects all the words from last to first. -If the list is already ordered, then this effectively reverses -the list, but it is more efficient to use +If the list is already ordered, +this effectively reverses the list, +but it is more efficient to use .Sq Cm \&:Or instead of .Sq Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word -(possibly containing embedded white space). +(possibly containing embedded whitespace). Analogous to the effect of -\&"$*\&" +.Li \&$* in Bourne shell. .\" :[0] .It 0 Means the same as .Sq Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words -delimited by white space. +delimited by whitespace. Analogous to the effect of -\&"$@\&" +.Li \&$@ in Bourne shell. .\" :[#] .It Cm \&# Returns the number of words in the value. .El \" :[range] .El -.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent -of the C programming language are provided in -.Nm . -All such structures are identified by a line beginning with a single -dot +.Sh DIRECTIVES +.Nm +offers directives for including makefiles, conditionals and for loops. +All these directives are identified by a line beginning with a single dot .Pq Ql \&. -character. +character, followed by the keyword of the directive, such as +.Cm include +or +.Cm if . +.Ss File inclusion Files are included with either .Cm \&.include \&< Ns Ar file Ns Cm \&> or .Cm \&.include \&\*q Ns Ar file Ns Cm \&\*q . Variables 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 used, the including makefile's directory and any directories specified using the .Fl I -option are searched before the system -makefile directory. -For compatibility with other versions of -.Nm -.Ql include file ... +option are searched before the system makefile directory. +.Pp +For compatibility with other make variants, +.Sq Cm include Ar file No ... +(without leading dot) is also accepted. .Pp If the include statement is written as .Cm .-include or as -.Cm .sinclude -then errors locating and/or opening include files are ignored. +.Cm .sinclude , +errors locating and/or opening include files are ignored. .Pp If the include statement is written as -.Cm .dinclude +.Cm .dinclude , not only are errors locating and/or opening include files ignored, -but stale dependencies within the included file will be ignored -just like +but stale dependencies within the included file are ignored just like in .Va .MAKE.DEPENDFILE . -.Pp -Conditional expressions are also preceded by a single dot as the first -character of a line. -The possible conditionals are as follows: +.Ss Exporting variables +The directives for exporting and unexporting variables are: .Bl -tag -width Ds -.It Ic .error Ar message -The message is printed along with the name of the makefile and line number, -then -.Nm -will exit immediately. -.It Ic .export Ar variable ... +.It Ic .export Ar variable No ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables (those that start with .Ql \&. ) . This is not affected by the .Fl X flag, so should be used with caution. -For compatibility with other -.Nm -programs -.Ql export variable=value -is also accepted. +For compatibility with other make programs, +.Cm export Ar variable\| Ns Cm \&= Ns Ar value +(without leading dot) is also accepted. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. -.It Ic .export-env Ar variable ... +.It Ic .export-env Ar variable No ... The same as .Ql .export , except that the variable is not appended to .Va .MAKE.EXPORTED . This allows exporting a value to the environment which is different from that used by .Nm internally. -.It Ic .export-literal Ar variable ... +.It Ic .export-literal Ar variable No ... The same as .Ql .export-env , 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 variables. -Only global variables can be un-defined. -.It Ic .unexport Ar variable ... +.It Ic .unexport Ar variable No ... The opposite of .Ql .export . The specified global -.Va variable -will be removed from +.Ar variable +is removed from .Va .MAKE.EXPORTED . If no variable list is provided, all globals are unexported, and .Va .MAKE.EXPORTED deleted. .It Ic .unexport-env Unexport all globals previously exported and clear the environment inherited from the parent. -This operation will cause a memory leak of the original environment, +This operation causes a memory leak of the original environment, so should be used sparingly. Testing for .Va .MAKE.LEVEL -being 0, would make sense. +being 0 would make sense. Also note that any variables which originated in the parent environment should be explicitly preserved if desired. For example: .Bd -literal -offset indent .Li .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .Li .unexport-env .Li .export PATH .Li .endif .Pp .Ed Would result in an environment containing only .Sq Ev PATH , which is the minimal useful environment. +.\" TODO: Check the below sentence, environment variables don't start with '.'. Actually -.Sq Ev .MAKE.LEVEL -will also be pushed into the new environment. +.Sq Va .MAKE.LEVEL +is also pushed into the new environment. +.El +.Ss Messages +The directives for printing messages to the output are: +.Bl -tag -width Ds +.It Ic .info Ar message +The message is printed along with the name of the makefile and line number. .It Ic .warning Ar message The message prefixed by -.Sq Pa warning: +.Sq Li warning: is printed along with the name of the makefile and line number. -.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... +.It Ic .error Ar message +The message is printed along with the name of the makefile and line number, +.Nm +exits immediately. +.El +.Ss Conditionals +The directives for conditionals are: +.ds maybenot Oo Ic \&! Oc Ns +.Bl -tag +.It Ic .if \*[maybenot] Ar expression Op Ar operator expression No ... Test the value of an expression. -.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... -Test the value of a variable. -.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... -Test the value of a variable. -.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... -Test the target being built. -.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... -Test the target being built. +.It Ic .ifdef \*[maybenot] Ar variable Op Ar operator variable No ... +Test whether a variable is defined. +.It Ic .ifndef \*[maybenot] Ar variable Op Ar operator variable No ... +Test whether a variable is not defined. +.It Ic .ifmake \*[maybenot] Ar target Op Ar operator target No ... +Test the target being requested. +.It Ic .ifnmake \*[maybenot] Ar target Op Ar operator target No ... +Test the target being requested. .It Ic .else Reverse the sense of the last conditional. -.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... +.It Ic .elif \*[maybenot] Ar expression Op Ar operator expression No ... A combination of .Sq Ic .else followed by .Sq Ic .if . -.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +.It Ic .elifdef \*[maybenot] Ar variable Op Ar operator variable No ... A combination of .Sq Ic .else followed by .Sq Ic .ifdef . -.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +.It Ic .elifndef \*[maybenot] Ar variable Op Ar operator variable No ... A combination of .Sq Ic .else followed by .Sq Ic .ifndef . -.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +.It Ic .elifmake \*[maybenot] Ar target Op Ar operator target No ... A combination of .Sq Ic .else followed by .Sq Ic .ifmake . -.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... +.It Ic .elifnmake \*[maybenot] Ar target Op Ar operator target No ... A combination of .Sq Ic .else followed by .Sq Ic .ifnmake . .It Ic .endif End the body of the conditional. .El .Pp The .Ar operator may be any one of the following: -.Bl -tag -width "Cm XX" -.It Cm \&|\&| +.Bl -tag +.It Ic \&|\&| Logical OR. -.It Cm \&&& -Logical -.Tn AND ; -of higher precedence than -.Dq \&|\&| . +.It Ic \&&& +Logical AND; of higher precedence than +.Sq Ic \&|\&| . .El .Pp -As in C, .Nm -will only evaluate a conditional as far as is necessary to determine -its value. -Parentheses may be used to change the order of evaluation. +only evaluates a conditional as far as is necessary to determine its value. +Parentheses can be used to override the operator precedence. The boolean operator .Sq Ic \&! -may be used to logically negate an entire -conditional. +may be used to logically negate an entire conditional. It is of higher precedence than .Sq Ic \&&& . .Pp The value of .Ar expression -may be any of the following: -.Bl -tag -width defined -.It Ic defined -Takes a variable name as an argument and evaluates to true if the variable +may be any of the following function call expressions: +.Bl -tag +.Sm off +.It Ic defined Li \&( Ar varname Li \&) +.Sm on +Evaluates to true if the variable +.Ar varname has been defined. -.It Ic make -Takes a target name as an argument and evaluates to true if the target -was specified as part of +.Sm off +.It Ic make Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target was specified as part of .Nm Ns 's command line or was declared the default target (either implicitly or explicitly, see .Va .MAIN ) before the line containing the conditional. -.It Ic empty -Takes a variable, with possible modifiers, and evaluates to true if -the expansion of the variable would result in an empty string. -.It Ic exists -Takes a file name as an argument and evaluates to true if the file exists. -The file is searched for on the system search path (see +.Sm off +.It Ic empty Li \&( Ar varname Oo Li : Ar modifiers Oc Li \&) +.Sm on +Evaluates to true if the expansion of the variable, +after applying the modifiers, results in an empty string. +.Sm off +.It Ic exists Li \&( Ar pathname Li \&) +.Sm on +Evaluates to true if the given pathname exists. +If relative, the pathname is searched for on the system search path (see .Va .PATH ) . -.It Ic target -Takes a target name as an argument and evaluates to true if the target -has been defined. -.It Ic commands -Takes a target name as an argument and evaluates to true if the target -has been defined and has commands associated with it. +.Sm off +.It Ic target Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target has been defined. +.Sm off +.It Ic commands Li \&( Ar target Li \&) +.Sm on +Evaluates to true if the target has been defined +and has commands associated with it. .El .Pp .Ar Expression 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 supported. -The standard C relational operators are all supported. -If after -variable expansion, either the left or right hand side of a -.Sq Ic == -or -.Sq Ic "!=" -operator is not a numerical value, then -string comparison is performed between the expanded -variables. -If no relational operator is given, it is assumed that the expanded -variable is being compared against 0, or an empty string in the case -of a string comparison. +Variable expansion is performed on both sides of the comparison. +If both sides are numeric and neither is enclosed in quotes, +the comparison is done numerically, otherwise lexicographically. +A string is interpreted as hexadecimal integer if it is preceded by +.Li 0x , +otherwise it is a decimal floating-point number; +octal numbers are not supported. +.Pp +All comparisons may use the operators +.Sq Ic \&== +and +.Sq Ic \&!= . +Numeric comparisons may also use the operators +.Sq Ic \&< , +.Sq Ic \&<= , +.Sq Ic \&> +and +.Sq Ic \&>= . +.Pp +If the comparison has neither a comparison operator nor a right side, +the expression evaluates to true if it is nonempty +and its numeric value (if any) is not zero. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters -a (white-space separated) word it doesn't recognize, either the +a (whitespace separated) word it doesn't recognize, either the .Dq make or .Dq defined -expression is applied to it, depending on the form of the conditional. +function is applied to it, depending on the form of the conditional. If the form is .Sq Ic .ifdef , -.Sq Ic .ifndef , +.Sq Ic .ifndef or -.Sq Ic .if +.Sq Ic .if , the .Dq defined -expression is applied. +function is applied. Similarly, if the form is .Sq Ic .ifmake or .Sq Ic .ifnmake , the .Dq make -expression is applied. +function is applied. .Pp -If the conditional evaluates to true the parsing of the makefile continues -as before. +If the conditional evaluates to true, +parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. -In both cases this continues until a +In both cases, this continues until the corresponding .Sq Ic .else or .Sq Ic .endif is found. -.Pp +.Ss For loops For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds -.It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-lines +.It Ic \&.for Ar variable Oo Ar variable No ... Oc Ic in Ar expression +.It Aq Ar make-lines .It Ic \&.endfor .El .Pp -After the for -.Ic expression -is evaluated, it is split into words. +The +.Ar expression +is expanded and then split into words. On each iteration of the loop, one word is taken and assigned to each -.Ic variable , +.Ar variable , in order, and these -.Ic variables +.Ar variables are substituted into the -.Ic make-lines +.Ar make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. +.Pp +If +.Sq Ic .break +is encountered within a +.Cm \&.for +loop, it causes early termination of the loop, otherwise a parse error. +.\" TODO: Describe limitations with defined/empty. +.Ss Other directives +.Bl -tag -width Ds +.It Ic .undef Ar variable No ... +Un-define the specified global variables. +Only global variables can be un-defined. +.El .Sh COMMENTS Comments begin with a hash .Pq Ql \&# character, anywhere but in a shell command line, and continue to the end of an unescaped new line. .Sh SPECIAL SOURCES (ATTRIBUTES) .Bl -tag -width .IGNOREx .It Ic .EXEC Target is never out of date, but always execute commands anyway. .It Ic .IGNORE Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . .\" .It Ic .INVISIBLE .\" XXX .\" .It Ic .JOIN .\" XXX .It Ic .MADE -Mark all sources of this target as being up-to-date. +Mark all sources of this target as being up to date. .It Ic .MAKE Execute the commands associated with this target even if the .Fl n or .Fl t options were specified. Normally used to mark recursive .Nm Ns s . .It Ic .META Create a meta file for the target, even if it is flagged as .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL . Usage in conjunction with .Ic .MAKE is the most likely case. -In "meta" mode, the target is out-of-date if the meta file is missing. +In +.Dq meta +mode, the target is out-of-date if the meta file is missing. .It Ic .NOMETA Do not create a meta file for the target. Meta files are also not created for .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL targets. .It Ic .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which always changes. -If the number of commands change, though, the target will still be out of date. +If the number of commands change, though, +the target is still considered out of date. The same effect applies to any command line that uses the variable .Va .OODATE , which can be used for that purpose even when not otherwise needed or desired: .Bd -literal -offset indent skip-compare-for-some: - @echo this will be compared - @echo this will not ${.OODATE:M.NOMETA_CMP} - @echo this will also be compared + @echo this is compared + @echo this is not ${.OODATE:M.NOMETA_CMP} + @echo this is also compared .Ed The .Cm \&:M pattern suppresses any expansion of the unwanted variable. .It Ic .NOPATH Do not search for the target in the directories specified by -.Ic .PATH . +.Va .PATH . .It Ic .NOTMAIN Normally .Nm selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .It Ic .OPTIONAL If a target is marked with this attribute and .Nm -can't figure out how to create it, it will ignore this fact and assume +can't figure out how to create it, it ignores this fact and assumes the file isn't needed or already exists. .It Ic .PHONY -The target does not -correspond to an actual file; it is always considered to be out of date, -and will not be created with the +The target does not correspond to an actual file; +it is always considered to be out of date, +and is not created with the .Fl t option. Suffix-transformation rules are not applied to .Ic .PHONY targets. .It Ic .PRECIOUS When .Nm is interrupted, it normally removes any partially made targets. This source prevents the target from being removed. .It Ic .RECURSIVE Synonym for .Ic .MAKE . .It Ic .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign .Pq Ql @ . .It Ic .USE Turn the target into .Nm Ns 's version of a macro. When the target is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .Ic .USE ) of the source. If the target already has commands, the .Ic .USE target's commands are appended to them. .It Ic .USEBEFORE -Exactly like +Like .Ic .USE , -but prepend the +but instead of appending, prepend the .Ic .USEBEFORE target commands to the target. .It Ic .WAIT If .Ic .WAIT appears 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 dependency tree. So given: .Bd -literal x: a .WAIT b echo x a: echo a b: b1 echo b b1: echo b1 .Ed the output is always .Ql a , .Ql b1 , .Ql b , .Ql x . -.br +.Pp The ordering imposed by .Ic .WAIT is only relevant for parallel makes. .El .Sh SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .Bl -tag -width .BEGINx .It Ic .BEGIN Any command lines attached to this target are executed before anything else is done. .It Ic .DEFAULT This is sort of a .Ic .USE -rule for any target (that was used only as a -source) that +rule for any target (that was used only as a source) that .Nm can't figure out any other way to create. Only the shell script is used. The -.Ic .IMPSRC +.Va .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's -commands is set -to the target's own name. +commands is set to the target's own name. .It Ic .DELETE_ON_ERROR If this target is present in the makefile, it globally causes 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. .It Ic .END Any command lines attached to this target are executed after everything -else is done. +else is done successfully. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The -.Ic .ERROR_TARGET +.Va .ERROR_TARGET variable is set to the target that failed. See also -.Ic MAKE_PRINT_VAR_ON_ERROR . +.Va MAKE_PRINT_VAR_ON_ERROR . .It Ic .IGNORE Mark each of the sources with the .Ic .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the .Fl i option. .It Ic .INTERRUPT If .Nm -is interrupted, the commands for this target will be executed. +is interrupted, the commands for this target are executed. .It Ic .MAIN If no target is specified when .Nm -is invoked, this target will be built. +is invoked, this target is built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm -when the makefile is used. +at the time when the makefiles are read. The flags are as if typed to the shell, though the .Fl f -option will have +option has no effect. .\" XXX: NOT YET!!!! .\" .It Ic .NOTPARALLEL .\" The named targets are executed in non parallel mode. .\" If no targets are -.\" specified, then all targets are executed in non parallel mode. +.\" specified, all targets are executed in non parallel mode. .It Ic .NOPATH Apply the .Ic .NOPATH attribute to any specified sources. .It Ic .NOTPARALLEL Disable parallel mode. .It Ic .NO_PARALLEL Synonym for .Ic .NOTPARALLEL , for compatibility with other pmake variants. +.It Ic .NOREADONLY +clear the read-only attribute from the global variables specified as sources. .It Ic .OBJDIR The source is a new value for .Sq Va .OBJDIR . If it exists, .Nm -will -.Xr chdir 2 -to it and update the value of +changes the current working directory to it and updates the value of .Sq Va .OBJDIR . .It Ic .ORDER In parallel mode, the named targets are made in sequence. This ordering does not add targets to the list of targets to be made. .Pp Since the dependents of a target do not get built until the target itself could be built, unless .Ql a is built by another part of the dependency graph, the following is a dependency loop: .Bd -literal \&.ORDER: b a b: a .Ed .Pp .\" XXX: NOT YET!!!! .\" .It Ic .PARALLEL .\" The named targets are executed in parallel mode. .\" If no targets are -.\" specified, then all targets are executed in parallel mode. +.\" specified, all targets are executed in parallel mode. .It Ic .PATH The sources are directories which are to be searched for files not found in the current directory. -If no sources are specified, any previously specified directories are -deleted. +If no sources are specified, +any previously specified directories are removed from the search path. If the source is the special .Ic .DOTLAST -target, then the current working -directory is searched last. -.It Ic .PATH. Ns Va suffix +target, the current working directory is searched last. +.It Ic .PATH. Ns Ar suffix Like .Ic .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .Ic .SUFFIXES . .It Ic .PHONY Apply the .Ic .PHONY attribute to any specified sources. .It Ic .POSIX If this is the first non-comment line in the main makefile, the variable .Va %POSIX is set to the value .Ql 1003.2 and the makefile .Ql is included if it exists, to provide POSIX-compatible default rules. If .Nm is run with the .Fl r -flag, then only +flag, only .Ql posix.mk -will contribute to the default rules. +contributes to the default rules. .It Ic .PRECIOUS Apply the .Ic .PRECIOUS attribute to any specified sources. If no sources are specified, the .Ic .PRECIOUS -attribute is applied to every -target in the file. +attribute is applied to every target in the file. +.It Ic .READONLY +set the read-only attribute on the global variables specified as sources. .It Ic .SHELL Sets the shell that .Nm -will use to execute commands. +uses to execute commands in jobs mode. The sources are a set of -.Ar field=value +.Ar field\| Ns Cm \&= Ns Ar value pairs. -.Bl -tag -width hasErrCtls -.It Ar name +.Bl -tag -width ".Li hasErrCtls" +.It Li name This is the minimal specification, used to select one of the built-in shell specs; -.Ar sh , -.Ar ksh , +.Li sh , +.Li ksh , and -.Ar csh . -.It Ar path -Specifies the path to the shell. -.It Ar hasErrCtl +.Li csh . +.It Li path +Specifies the absolute path to the shell. +.It Li hasErrCtl Indicates whether the shell supports exit on error. -.It Ar check +.It Li check The command to turn on error checking. -.It Ar ignore +.It Li ignore The command to disable error checking. -.It Ar echo +.It Li echo The command to turn on echoing of commands executed. -.It Ar quiet +.It Li quiet The command to turn off echoing of commands executed. -.It Ar filter +.It Li filter The output to filter after issuing the -.Ar quiet +.Li quiet command. It is typically identical to -.Ar quiet . -.It Ar errFlag +.Li quiet . +.It Li errFlag The flag to pass the shell to enable error checking. -.It Ar echoFlag +.It Li echoFlag The flag to pass the shell to enable command echoing. -.It Ar newline +.It Li newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. .El Example: .Bd -literal \&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e check="set \-e" ignore="set +e" \e echo="set \-v" quiet="set +v" filter="set +v" \e echoFlag=v errFlag=e newline="'\en'" .Ed .It Ic .SILENT Apply the .Ic .SILENT attribute to any specified sources. If no sources are specified, the .Ic .SILENT attribute is applied to every command in the file. .It Ic .STALE This target gets run when a dependency file contains stale entries, having .Va .ALLSRC set to the name of that dependency file. .It Ic .SUFFIXES Each source specifies a suffix to .Nm . If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. .Pp Example: .Bd -literal -\&.SUFFIXES: .o +\&.SUFFIXES: .c .o \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed +.It Ic .SYSPATH +The sources are directories which are to be added to the system +include path which +.Nm +searches for makefiles. +If no sources are specified, +any previously specified directories are removed from the system +include path. .El .Sh ENVIRONMENT .Nm uses the following environment variables, if they exist: .Ev MACHINE , .Ev MACHINE_ARCH , .Ev MAKE , .Ev MAKEFLAGS , .Ev MAKEOBJDIR , .Ev MAKEOBJDIRPREFIX , .Ev MAKESYSPATH , .Ev PWD , and .Ev TMPDIR . .Pp .Ev MAKEOBJDIRPREFIX and .Ev MAKEOBJDIR may only be set in the environment or on the command line to .Nm and not as makefile variables; see the description of .Sq Va .OBJDIR for more details. .Sh FILES .Bl -tag -width /usr/share/mk -compact .It .depend list of dependencies -.It Makefile -list of dependencies .It makefile -list of dependencies +first default makefile if no makefile is specified on the command line +.It Makefile +second default makefile if no makefile is specified on the command line .It sys.mk system makefile .It /usr/share/mk system makefile directory .El .Sh COMPATIBILITY -The basic make syntax is compatible between different versions of make; +The basic make syntax is compatible between different make variants; however the special variables, variable modifiers and conditionals are not. .Ss Older versions An incomplete list of changes in older versions of .Nm : .Pp The way that .for loop variables are substituted changed after .Nx 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. .Pp The way that parallel makes are scheduled changed in .Nx 4.0 so that .ORDER and .WAIT apply recursively to the dependent nodes. The algorithms used may change again in the future. .Ss Other make dialects Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not support most of the features of .Nm as described in this manual. Most notably: .Bl -bullet -offset indent .It The .Ic .WAIT and .Ic .ORDER declarations and most functionality pertaining to parallelization. -(GNU make supports parallelization but lacks these features needed to +(GNU make supports parallelization but lacks the features needed to control it effectively.) .It Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) +.\" The "less powerful" above means that GNU make does not have the +.\" make(target), target(target) and commands(target) functions. .It All built-in variables that begin with a dot. .It Most of the special sources and targets that begin with a dot, with the notable exception of .Ic .PHONY , .Ic .PRECIOUS , and .Ic .SUFFIXES . .It Variable modifiers, except for the -.Dl :old=new +.Ql :old=new string substitution, which does not portably support globbing with .Ql % and historically only works on declared suffixes. .It The .Ic $> variable even in its short form; most makes support this functionality but its name varies. .El .Pp Some features are somewhat more portable, such as assignment with .Ic += , .Ic ?= , and .Ic != . The -.Ic .PATH +.Va .PATH functionality is based on an older feature .Ic VPATH found in GNU make and many versions of SVR4 make; however, historically its behavior is too ill-defined (and too buggy) to rely upon. .Pp The .Ic $@ and .Ic $< variables are more or less universally portable, as is the .Ic $(MAKE) variable. Basic use of suffix rules (for files only in the current directory, not trying to chain transformations together, etc.) is also reasonably portable. .Sh SEE ALSO .Xr mkdep 1 , .Xr style.Makefile 5 .Sh HISTORY A .Nm command appeared in .At v7 . This .Nm -implementation is based on Adam De Boor's pmake program which was written -for Sprite at Berkeley. +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 .Dq customs . .Pp Historically the target/dependency -.Dq FRC +.Ic FRC has been used to FoRCe rebuilding (since the target/dependency -does not exist... unless someone creates an -.Dq FRC +does not exist ... unless someone creates an +.Pa FRC file). .Sh BUGS The .Nm -syntax is difficult to parse without actually acting on the data. +syntax is difficult to parse. For instance, finding the end of a variable's use should involve scanning each of the modifiers, using the correct terminator for each field. In many places .Nm just counts {} and () in order to find the end of a variable expansion. .Pp There is no way of escaping a space character in a filename. diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c index fa1e594a1b16..c9e59c40a033 100644 --- a/contrib/bmake/make.c +++ b/contrib/bmake/make.c @@ -1,1496 +1,1494 @@ -/* $NetBSD: make.c,v 1.255 2022/05/07 17:49:47 rillig Exp $ */ +/* $NetBSD: make.c,v 1.258 2022/12/05 23:28:08 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Examination of targets and their suitability for creation. * * Interface: * Make_Run Initialize things for the module. Returns true if * work was (or would have been) done. * * Make_Update After a target is made, update all its parents. * Perform various bookkeeping chores like the updating * of the youngestChild field of the parent, filling * of the IMPSRC variable, etc. Place the parent on the * toBeMade queue if it should be. * * GNode_UpdateYoungestChild * Update the node's youngestChild field based on the * child's modification time. * * GNode_SetLocalVars * Set up the various local variables for a * target, including the .ALLSRC variable, making * sure that any variable that needs to exist * at the very least has the empty value. * * GNode_IsOODate Determine if a target is out-of-date. * * Make_HandleUse See if a child is a .USE node for a parent * and perform the .USE actions if so. * * Make_ExpandUse Expand .USE nodes */ #include "make.h" #include "dir.h" #include "job.h" /* "@(#)make.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: make.c,v 1.255 2022/05/07 17:49:47 rillig Exp $"); +MAKE_RCSID("$NetBSD: make.c,v 1.258 2022/12/05 23:28:08 rillig Exp $"); /* Sequence # to detect recursion. */ static unsigned int checked_seqno = 1; /* * The current fringe of the graph. * These are nodes which await examination by MakeOODate. * It is added to by Make_Update and subtracted from by MakeStartJobs */ static GNodeList toBeMade = LST_INIT; void debug_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(opts.debug_file, fmt, ap); va_end(ap); } static const char * GNodeType_ToString(GNodeType type, void **freeIt) { Buffer buf; Buf_InitSize(&buf, 32); #define ADD(flag) Buf_AddFlag(&buf, (type & (flag)) != OP_NONE, #flag) ADD(OP_DEPENDS); ADD(OP_FORCE); ADD(OP_DOUBLEDEP); ADD(OP_OPTIONAL); ADD(OP_USE); ADD(OP_EXEC); ADD(OP_IGNORE); ADD(OP_PRECIOUS); ADD(OP_SILENT); ADD(OP_MAKE); ADD(OP_JOIN); ADD(OP_MADE); ADD(OP_SPECIAL); ADD(OP_USEBEFORE); ADD(OP_INVISIBLE); ADD(OP_NOTMAIN); ADD(OP_PHONY); ADD(OP_NOPATH); ADD(OP_WAIT); ADD(OP_NOMETA); ADD(OP_META); ADD(OP_NOMETA_CMP); ADD(OP_SUBMAKE); ADD(OP_TRANSFORM); ADD(OP_MEMBER); ADD(OP_LIB); ADD(OP_ARCHV); ADD(OP_HAS_COMMANDS); ADD(OP_SAVE_CMDS); ADD(OP_DEPS_FOUND); ADD(OP_MARK); #undef ADD return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf)); } static const char * GNodeFlags_ToString(GNodeFlags flags, void **freeIt) { Buffer buf; Buf_InitSize(&buf, 32); -#define ADD(flag, name) Buf_AddFlag(&buf, flags.flag, name) - ADD(remake, "REMAKE"); - ADD(childMade, "CHILDMADE"); - ADD(force, "FORCE"); - ADD(doneWait, "DONE_WAIT"); - ADD(doneOrder, "DONE_ORDER"); - ADD(fromDepend, "FROM_DEPEND"); - ADD(doneAllsrc, "DONE_ALLSRC"); - ADD(cycle, "CYCLE"); - ADD(doneCycle, "DONECYCLE"); -#undef ADD + Buf_AddFlag(&buf, flags.remake, "REMAKE"); + Buf_AddFlag(&buf, flags.childMade, "CHILDMADE"); + Buf_AddFlag(&buf, flags.force, "FORCE"); + Buf_AddFlag(&buf, flags.doneWait, "DONE_WAIT"); + Buf_AddFlag(&buf, flags.doneOrder, "DONE_ORDER"); + Buf_AddFlag(&buf, flags.fromDepend, "FROM_DEPEND"); + Buf_AddFlag(&buf, flags.doneAllsrc, "DONE_ALLSRC"); + Buf_AddFlag(&buf, flags.cycle, "CYCLE"); + Buf_AddFlag(&buf, flags.doneCycle, "DONECYCLE"); return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf)); } void GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, const char *suffix) { void *type_freeIt = NULL; void *flags_freeIt = NULL; fprintf(f, "%s%s, type %s, flags %s%s", prefix, GNodeMade_Name(gn->made), GNodeType_ToString(gn->type, &type_freeIt), GNodeFlags_ToString(gn->flags, &flags_freeIt), suffix); free(type_freeIt); free(flags_freeIt); } bool GNode_ShouldExecute(GNode *gn) { return !((gn->type & OP_MAKE) ? opts.noRecursiveExecute : opts.noExecute); } /* Update the youngest child of the node, according to the given child. */ void GNode_UpdateYoungestChild(GNode *gn, GNode *cgn) { if (gn->youngestChild == NULL || cgn->mtime > gn->youngestChild->mtime) gn->youngestChild = cgn; } static bool IsOODateRegular(GNode *gn) { /* These rules are inherited from the original Make. */ if (gn->youngestChild != NULL) { if (gn->mtime < gn->youngestChild->mtime) { DEBUG1(MAKE, "modified before source \"%s\"...", GNode_Path(gn->youngestChild)); return true; } return false; } if (gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) { DEBUG0(MAKE, "nonexistent and no sources..."); return true; } if (gn->type & OP_DOUBLEDEP) { DEBUG0(MAKE, ":: operator and no sources..."); return true; } return false; } /* * See if the node is out of date with respect to its sources. * * Used by Make_Run when deciding which nodes to place on the * toBeMade queue initially and by Make_Update to screen out .USE and * .EXEC nodes. In the latter case, however, any other sort of node * must be considered out-of-date since at least one of its children * will have been recreated. * * The mtime field of the node and the youngestChild field of its parents * may be changed. */ bool GNode_IsOODate(GNode *gn) { bool oodate; /* * Certain types of targets needn't even be sought as their datedness * doesn't depend on their modification time... */ if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) { Dir_UpdateMTime(gn, true); if (DEBUG(MAKE)) { if (gn->mtime != 0) debug_printf("modified %s...", Targ_FmtTime(gn->mtime)); else debug_printf("nonexistent..."); } } /* * A target is remade in one of the following circumstances: * * its modification time is smaller than that of its youngest * child and it would actually be run (has commands or is not * GNode_IsTarget) * * it's the object of a force operator * * it has no children, was on the lhs of an operator and doesn't * exist already. * * Libraries are only considered out-of-date if the archive module * says they are. * * These weird rules are brought to you by Backward-Compatibility * and the strange people who wrote 'Make'. */ if (gn->type & (OP_USE | OP_USEBEFORE)) { /* * If the node is a USE node it is *never* out of date * no matter *what*. */ DEBUG0(MAKE, ".USE node..."); oodate = false; } else if ((gn->type & OP_LIB) && (gn->mtime == 0 || Arch_IsLib(gn))) { DEBUG0(MAKE, "library..."); /* * always out of date if no children and :: target * or nonexistent. */ oodate = (gn->mtime == 0 || Arch_LibOODate(gn) || (gn->youngestChild == NULL && (gn->type & OP_DOUBLEDEP))); } else if (gn->type & OP_JOIN) { /* * A target with the .JOIN attribute is only considered * out-of-date if any of its children was out-of-date. */ DEBUG0(MAKE, ".JOIN node..."); DEBUG1(MAKE, "source %smade...", gn->flags.childMade ? "" : "not "); oodate = gn->flags.childMade; } else if (gn->type & (OP_FORCE | OP_EXEC | OP_PHONY)) { /* * A node which is the object of the force (!) operator or * which has the .EXEC attribute is always considered * out-of-date. */ if (DEBUG(MAKE)) { if (gn->type & OP_FORCE) { debug_printf("! operator..."); } else if (gn->type & OP_PHONY) { debug_printf(".PHONY node..."); } else { debug_printf(".EXEC node..."); } } oodate = true; } else if (IsOODateRegular(gn)) { oodate = true; } else { /* * When a nonexistent child with no sources * (such as a typically used FORCE source) has been made and * the target of the child (usually a directory) has the same * timestamp as the timestamp just given to the nonexistent * child after it was considered made. */ if (DEBUG(MAKE)) { if (gn->flags.force) debug_printf("non existing child..."); } oodate = gn->flags.force; } #ifdef USE_META if (useMeta) oodate = meta_oodate(gn, oodate); #endif /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date * but aren't, because they have no commands and are GNode_IsTarget, * have their mtime stay below their children's mtime to keep parents * from thinking they're out-of-date. */ if (!oodate) { GNodeListNode *ln; for (ln = gn->parents.first; ln != NULL; ln = ln->next) GNode_UpdateYoungestChild(ln->datum, gn); } return oodate; } static void PretendAllChildrenAreMade(GNode *pgn) { GNodeListNode *ln; for (ln = pgn->children.first; ln != NULL; ln = ln->next) { GNode *cgn = ln->datum; /* This may also update cgn->path. */ Dir_UpdateMTime(cgn, false); GNode_UpdateYoungestChild(pgn, cgn); pgn->unmade--; } } /* * Called by Make_Run and SuffApplyTransform on the downward pass to handle * .USE and transformation nodes, by copying the child node's commands, type * flags and children to the parent node. * * A .USE node is much like an explicit transformation rule, except its * commands are always added to the target node, even if the target already * has commands. * * Input: * cgn The source node, which is either a .USE/.USEBEFORE * node or a transformation node (OP_TRANSFORM). * pgn The target node */ void Make_HandleUse(GNode *cgn, GNode *pgn) { GNodeListNode *ln; /* An element in the children list */ #ifdef DEBUG_SRC if (!(cgn->type & (OP_USE | OP_USEBEFORE | OP_TRANSFORM))) { debug_printf("Make_HandleUse: called for plain node %s\n", cgn->name); /* XXX: debug mode should not affect control flow */ return; } #endif if ((cgn->type & (OP_USE | OP_USEBEFORE)) || Lst_IsEmpty(&pgn->commands)) { if (cgn->type & OP_USEBEFORE) { /* .USEBEFORE */ Lst_PrependAll(&pgn->commands, &cgn->commands); } else { /* .USE, or target has no commands */ Lst_AppendAll(&pgn->commands, &cgn->commands); } } for (ln = cgn->children.first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; /* * Expand variables in the .USE node's name * and save the unexpanded form. * We don't need to do this for commands. * They get expanded properly when we execute. */ if (gn->uname == NULL) { gn->uname = gn->name; } else { free(gn->name); } (void)Var_Subst(gn->uname, pgn, VARE_WANTRES, &gn->name); /* TODO: handle errors */ if (gn->uname != NULL && strcmp(gn->name, gn->uname) != 0) { /* See if we have a target for this node. */ GNode *tgn = Targ_FindNode(gn->name); if (tgn != NULL) gn = tgn; } Lst_Append(&pgn->children, gn); Lst_Append(&gn->parents, pgn); pgn->unmade++; } pgn->type |= - cgn->type & ~(OP_OPMASK | OP_USE | OP_USEBEFORE | OP_TRANSFORM); + cgn->type & (unsigned)~(OP_OPMASK | OP_USE | OP_USEBEFORE | OP_TRANSFORM); } /* * Used by Make_Run on the downward pass to handle .USE nodes. Should be * called before the children are enqueued to be looked at by MakeAddChild. * * For a .USE child, the commands, type flags and children are copied to the * parent node, and since the relation to the .USE node is then no longer * needed, that relation is removed. * * Input: * cgn the child, which may be a .USE node * pgn the current parent */ static void MakeHandleUse(GNode *cgn, GNode *pgn, GNodeListNode *ln) { bool unmarked; unmarked = !(cgn->type & OP_MARK); cgn->type |= OP_MARK; if (!(cgn->type & (OP_USE | OP_USEBEFORE))) return; if (unmarked) Make_HandleUse(cgn, pgn); /* * This child node is now "made", so we decrement the count of * unmade children in the parent... We also remove the child * from the parent's list to accurately reflect the number of decent * children the parent has. This is used by Make_Run to decide * whether to queue the parent or examine its children... */ Lst_Remove(&pgn->children, ln); pgn->unmade--; } static void HandleUseNodes(GNode *gn) { GNodeListNode *ln, *nln; for (ln = gn->children.first; ln != NULL; ln = nln) { nln = ln->next; MakeHandleUse(ln->datum, gn, ln); } } /* * Check the modification time of a gnode, and update it if necessary. * Return 0 if the gnode does not exist, or its filesystem time if it does. */ time_t Make_Recheck(GNode *gn) { time_t mtime; Dir_UpdateMTime(gn, true); mtime = gn->mtime; #ifndef RECHECK /* * We can't re-stat the thing, but we can at least take care of rules * where a target depends on a source that actually creates the * target, but only if it has changed, e.g. * * parse.h : parse.o * * parse.o : parse.y * yacc -d parse.y * cc -c y.tab.c * mv y.tab.o parse.o * cmp -s y.tab.h parse.h || mv y.tab.h parse.h * * In this case, if the definitions produced by yacc haven't changed * from before, parse.h won't have been updated and gn->mtime will * reflect the current modification time for parse.h. This is * something of a kludge, I admit, but it's a useful one. * * XXX: People like to use a rule like "FRC:" to force things that * depend on FRC to be made, so we have to check for gn->children * being empty as well. */ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { gn->mtime = now; } #else /* * This is what Make does and it's actually a good thing, as it * allows rules like * * cmp -s y.tab.h parse.h || cp y.tab.h parse.h * * to function as intended. Unfortunately, thanks to the stateless * nature of NFS (by which I mean the loose coupling of two clients * using the same file from a common server), there are times when * the modification time of a file created on a remote machine * will not be modified before the local stat() implied by the * Dir_UpdateMTime occurs, thus leading us to believe that the file * is unchanged, wreaking havoc with files that depend on this one. * * I have decided it is better to make too much than to make too * little, so this stuff is commented out unless you're sure it's ok. * -- ardeb 1/12/88 */ /* * Christos, 4/9/92: If we are saving commands, pretend that * the target is made now. Otherwise archives with '...' rules * don't work! */ if (!GNode_ShouldExecute(gn) || (gn->type & OP_SAVE_CMDS) || (mtime == 0 && !(gn->type & OP_WAIT))) { DEBUG2(MAKE, " recheck(%s): update time from %s to now\n", gn->name, gn->mtime == 0 ? "nonexistent" : Targ_FmtTime(gn->mtime)); gn->mtime = now; } else { DEBUG2(MAKE, " recheck(%s): current update time: %s\n", gn->name, Targ_FmtTime(gn->mtime)); } #endif /* * XXX: The returned mtime may differ from gn->mtime. Intentionally? */ return mtime; } /* * Set the .PREFIX and .IMPSRC variables for all the implied parents * of this node. */ static void UpdateImplicitParentsVars(GNode *cgn, const char *cname) { GNodeListNode *ln; const char *cpref = GNode_VarPrefix(cgn); for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) { GNode *pgn = ln->datum; if (pgn->flags.remake) { Var_Set(pgn, IMPSRC, cname); if (cpref != NULL) Var_Set(pgn, PREFIX, cpref); } } } /* See if a .ORDER rule stops us from building this node. */ static bool IsWaitingForOrder(GNode *gn) { GNodeListNode *ln; for (ln = gn->order_pred.first; ln != NULL; ln = ln->next) { GNode *ogn = ln->datum; if (GNode_IsDone(ogn) || !ogn->flags.remake) continue; DEBUG2(MAKE, "IsWaitingForOrder: Waiting for .ORDER node \"%s%s\"\n", ogn->name, ogn->cohort_num); return true; } return false; } static bool MakeBuildChild(GNode *, GNodeListNode *); static void ScheduleOrderSuccessors(GNode *gn) { GNodeListNode *toBeMadeNext = toBeMade.first; GNodeListNode *ln; for (ln = gn->order_succ.first; ln != NULL; ln = ln->next) { GNode *succ = ln->datum; if (succ->made == DEFERRED && !MakeBuildChild(succ, toBeMadeNext)) succ->flags.doneOrder = true; } } /* * Perform update on the parents of a node. Used by JobFinish once * a node has been dealt with and by MakeStartJobs if it finds an * up-to-date node. * * The unmade field of pgn is decremented and pgn may be placed on * the toBeMade queue if this field becomes 0. * * If the child was made, the parent's flag CHILDMADE field will be * set true. * * If the child is not up-to-date and still does not exist, * set the FORCE flag on the parents. * * If the child wasn't made, the youngestChild field of the parent will be * altered if the child's mtime is big enough. * * Finally, if the child is the implied source for the parent, the * parent's IMPSRC variable is set appropriately. */ void Make_Update(GNode *cgn) { const char *cname; /* the child's name */ time_t mtime = -1; GNodeList *parents; GNodeListNode *ln; GNode *centurion; /* It is save to re-examine any nodes again */ checked_seqno++; cname = GNode_VarTarget(cgn); DEBUG2(MAKE, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); /* * If the child was actually made, see what its modification time is * now -- some rules won't actually update the file. If the file * still doesn't exist, make its mtime now. */ if (cgn->made != UPTODATE) { mtime = Make_Recheck(cgn); } /* * If this is a `::' node, we must consult its first instance * which is where all parents are linked. */ if ((centurion = cgn->centurion) != NULL) { if (!Lst_IsEmpty(&cgn->parents)) Punt("%s%s: cohort has parents", cgn->name, cgn->cohort_num); centurion->unmade_cohorts--; if (centurion->unmade_cohorts < 0) Error("Graph cycles through centurion %s", centurion->name); } else { centurion = cgn; } parents = ¢urion->parents; /* If this was a .ORDER node, schedule the RHS */ ScheduleOrderSuccessors(centurion); /* Now mark all the parents as having one less unmade child */ for (ln = parents->first; ln != NULL; ln = ln->next) { GNode *pgn = ln->datum; if (DEBUG(MAKE)) { debug_printf("inspect parent %s%s: ", pgn->name, pgn->cohort_num); GNode_FprintDetails(opts.debug_file, "", pgn, ""); debug_printf(", unmade %d ", pgn->unmade - 1); } if (!pgn->flags.remake) { /* This parent isn't needed */ DEBUG0(MAKE, "- not needed\n"); continue; } if (mtime == 0 && !(cgn->type & OP_WAIT)) pgn->flags.force = true; /* * If the parent has the .MADE attribute, its timestamp got * updated to that of its newest child, and its unmade * child count got set to zero in Make_ExpandUse(). * However other things might cause us to build one of its * children - and so we mustn't do any processing here when * the child build finishes. */ if (pgn->type & OP_MADE) { DEBUG0(MAKE, "- .MADE\n"); continue; } if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE))) { if (cgn->made == MADE) pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, cgn); } /* * A parent must wait for the completion of all instances * of a `::' dependency. */ if (centurion->unmade_cohorts != 0 || !GNode_IsDone(centurion)) { DEBUG2(MAKE, "- centurion made %d, %d unmade cohorts\n", centurion->made, centurion->unmade_cohorts); continue; } /* One more child of this parent is now made */ pgn->unmade--; if (pgn->unmade < 0) { if (DEBUG(MAKE)) { debug_printf("Graph cycles through %s%s\n", pgn->name, pgn->cohort_num); Targ_PrintGraph(2); } Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); } /* * We must always rescan the parents of .WAIT and .ORDER * nodes. */ if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) && !centurion->flags.doneOrder) { DEBUG0(MAKE, "- unmade children\n"); continue; } if (pgn->made != DEFERRED) { /* * Either this parent is on a different branch of * the tree, or it on the RHS of a .WAIT directive * or it is already on the toBeMade list. */ DEBUG0(MAKE, "- not deferred\n"); continue; } if (IsWaitingForOrder(pgn)) continue; if (DEBUG(MAKE)) { debug_printf("- %s%s made, schedule %s%s (made %d)\n", cgn->name, cgn->cohort_num, pgn->name, pgn->cohort_num, pgn->made); Targ_PrintNode(pgn, 2); } /* Ok, we can schedule the parent again */ pgn->made = REQUESTED; Lst_Enqueue(&toBeMade, pgn); } UpdateImplicitParentsVars(cgn, cname); } static void UnmarkChildren(GNode *gn) { GNodeListNode *ln; for (ln = gn->children.first; ln != NULL; ln = ln->next) { GNode *child = ln->datum; - child->type &= ~OP_MARK; + child->type &= (unsigned)~OP_MARK; } } /* * Add a child's name to the ALLSRC and OODATE variables of the given * node, but only if it has not been given the .EXEC, .USE or .INVISIBLE * attributes. .EXEC and .USE children are very rarely going to be files, * so... * * If the child is a .JOIN node, its ALLSRC is propagated to the parent. * * A child is added to the OODATE variable if its modification time is * later than that of its parent, as defined by Make, except if the * parent is a .JOIN node. In that case, it is only added to the OODATE * variable if it was actually made (since .JOIN nodes don't have * modification times, the comparison is rather unfair...).. * * Input: * cgn The child to add * pgn The parent to whose ALLSRC variable it should * be added */ static void MakeAddAllSrc(GNode *cgn, GNode *pgn) { const char *child, *allsrc; if (cgn->type & OP_MARK) return; cgn->type |= OP_MARK; if (cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE | OP_INVISIBLE)) return; if (cgn->type & OP_ARCHV) child = GNode_VarMember(cgn); else child = GNode_Path(cgn); if (cgn->type & OP_JOIN) allsrc = GNode_VarAllsrc(cgn); else allsrc = child; if (allsrc != NULL) Var_Append(pgn, ALLSRC, allsrc); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) Var_Append(pgn, OODATE, child); } else if ((pgn->mtime < cgn->mtime) || (cgn->mtime >= now && cgn->made == MADE)) { /* * It goes in the OODATE variable if the parent is * younger than the child or if the child has been * modified more recently than the start of the make. * This is to keep pmake from getting confused if * something else updates the parent after the make * starts (shouldn't happen, I know, but sometimes it * does). In such a case, if we've updated the child, * the parent is likely to have a modification time * later than that of the child and anything that * relies on the OODATE variable will be hosed. * * XXX: This will cause all made children to go in * the OODATE variable, even if they're not touched, * if RECHECK isn't defined, since cgn->mtime is set * to now in Make_Update. According to some people, * this is good... */ Var_Append(pgn, OODATE, child); } } /* * Set up the ALLSRC and OODATE variables. Sad to say, it must be * done separately, rather than while traversing the graph. This is * because Make defined OODATE to contain all sources whose modification * times were later than that of the target, *not* those sources that * were out-of-date. Since in both compatibility and native modes, * the modification time of the parent isn't found until the child * has been dealt with, we have to wait until now to fill in the * variable. As for ALLSRC, the ordering is important and not * guaranteed when in native mode, so it must be set here, too. * * If the node is a .JOIN node, its TARGET variable will be set to * match its ALLSRC variable. */ void GNode_SetLocalVars(GNode *gn) { GNodeListNode *ln; if (gn->flags.doneAllsrc) return; UnmarkChildren(gn); for (ln = gn->children.first; ln != NULL; ln = ln->next) MakeAddAllSrc(ln->datum, gn); if (!Var_Exists(gn, OODATE)) Var_Set(gn, OODATE, ""); if (!Var_Exists(gn, ALLSRC)) Var_Set(gn, ALLSRC, ""); if (gn->type & OP_JOIN) Var_Set(gn, TARGET, GNode_VarAllsrc(gn)); gn->flags.doneAllsrc = true; } static void ScheduleRandomly(GNode *gn) { GNodeListNode *ln; size_t i, n; n = 0; for (ln = toBeMade.first; ln != NULL; ln = ln->next) n++; i = n > 0 ? (size_t)random() % (n + 1) : 0; if (i == 0) { Lst_Append(&toBeMade, gn); return; } i--; for (ln = toBeMade.first; i > 0; ln = ln->next) i--; Lst_InsertBefore(&toBeMade, ln, gn); } static bool MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) { if (DEBUG(MAKE)) { debug_printf("MakeBuildChild: inspect %s%s, ", cn->name, cn->cohort_num); GNode_FprintDetails(opts.debug_file, "", cn, "\n"); } if (GNode_IsReady(cn)) return false; /* If this node is on the RHS of a .ORDER, check LHSs. */ if (IsWaitingForOrder(cn)) { /* * Can't build this (or anything else in this child list) yet */ cn->made = DEFERRED; return false; /* but keep looking */ } DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n", cn->name, cn->cohort_num); cn->made = REQUESTED; if (opts.randomizeTargets && !(cn->type & OP_WAIT)) ScheduleRandomly(cn); else if (toBeMadeNext == NULL) Lst_Append(&toBeMade, cn); else Lst_InsertBefore(&toBeMade, toBeMadeNext, cn); if (cn->unmade_cohorts != 0) { ListNode *ln; for (ln = cn->cohorts.first; ln != NULL; ln = ln->next) if (MakeBuildChild(ln->datum, toBeMadeNext)) break; } /* * If this node is a .WAIT node with unmade children * then don't add the next sibling. */ return cn->type & OP_WAIT && cn->unmade > 0; } static void MakeChildren(GNode *gn) { GNodeListNode *toBeMadeNext = toBeMade.first; GNodeListNode *ln; for (ln = gn->children.first; ln != NULL; ln = ln->next) if (MakeBuildChild(ln->datum, toBeMadeNext)) break; } /* * Start as many jobs as possible, taking them from the toBeMade queue. * * If the -q option was given, no job will be started, * but as soon as an out-of-date target is found, this function * returns true. In all other cases, this function returns false. */ static bool MakeStartJobs(void) { GNode *gn; bool have_token = false; while (!Lst_IsEmpty(&toBeMade)) { /* * Get token now to avoid cycling job-list when we only * have 1 token */ if (!have_token && !Job_TokenWithdraw()) break; have_token = true; gn = Lst_Dequeue(&toBeMade); DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num); if (gn->made != REQUESTED) { debug_printf("internal error: made = %s\n", GNodeMade_Name(gn->made)); Targ_PrintNode(gn, 2); Targ_PrintNodes(&toBeMade, 2); Targ_PrintGraph(3); abort(); } if (gn->checked_seqno == checked_seqno) { /* * We've already looked at this node since a job * finished... */ DEBUG2(MAKE, "already checked %s%s\n", gn->name, gn->cohort_num); gn->made = DEFERRED; continue; } gn->checked_seqno = checked_seqno; if (gn->unmade != 0) { /* * We can't build this yet, add all unmade children * to toBeMade, just before the current first element. */ gn->made = DEFERRED; MakeChildren(gn); /* and drop this node on the floor */ DEBUG2(MAKE, "dropped %s%s\n", gn->name, gn->cohort_num); continue; } gn->made = BEINGMADE; if (GNode_IsOODate(gn)) { DEBUG0(MAKE, "out-of-date\n"); if (opts.query) - return true; + return strcmp(gn->name, ".MAIN") != 0; GNode_SetLocalVars(gn); Job_Make(gn); have_token = false; } else { DEBUG0(MAKE, "up-to-date\n"); gn->made = UPTODATE; if (gn->type & OP_JOIN) { /* * Even for an up-to-date .JOIN node, we * need it to have its local variables so * references to it get the correct value * for .TARGET when building up the local * variables of its parent(s)... */ GNode_SetLocalVars(gn); } Make_Update(gn); } } if (have_token) Job_TokenReturn(); return false; } /* Print the status of a .ORDER node. */ static void MakePrintStatusOrderNode(GNode *ogn, GNode *gn) { if (!GNode_IsWaitingFor(ogn)) return; printf(" `%s%s' has .ORDER dependency against %s%s ", gn->name, gn->cohort_num, ogn->name, ogn->cohort_num); GNode_FprintDetails(stdout, "(", ogn, ")\n"); if (DEBUG(MAKE) && opts.debug_file != stdout) { debug_printf(" `%s%s' has .ORDER dependency against %s%s ", gn->name, gn->cohort_num, ogn->name, ogn->cohort_num); GNode_FprintDetails(opts.debug_file, "(", ogn, ")\n"); } } static void MakePrintStatusOrder(GNode *gn) { GNodeListNode *ln; for (ln = gn->order_pred.first; ln != NULL; ln = ln->next) MakePrintStatusOrderNode(ln->datum, gn); } static void MakePrintStatusList(GNodeList *, int *); /* * Print the status of a top-level node, viz. it being up-to-date already * or not created due to an error in a lower level. */ static bool MakePrintStatus(GNode *gn, int *errors) { if (gn->flags.doneCycle) { /* * We've completely processed this node before, don't do * it again. */ return false; } if (gn->unmade == 0) { gn->flags.doneCycle = true; switch (gn->made) { case UPTODATE: printf("`%s%s' is up to date.\n", gn->name, gn->cohort_num); break; case MADE: break; case UNMADE: case DEFERRED: case REQUESTED: case BEINGMADE: (*errors)++; printf("`%s%s' was not built", gn->name, gn->cohort_num); GNode_FprintDetails(stdout, " (", gn, ")!\n"); if (DEBUG(MAKE) && opts.debug_file != stdout) { debug_printf("`%s%s' was not built", gn->name, gn->cohort_num); GNode_FprintDetails(opts.debug_file, " (", gn, ")!\n"); } /* Most likely problem is actually caused by .ORDER */ MakePrintStatusOrder(gn); break; default: /* Errors - already counted */ printf("`%s%s' not remade because of errors.\n", gn->name, gn->cohort_num); if (DEBUG(MAKE) && opts.debug_file != stdout) debug_printf( "`%s%s' not remade because of errors.\n", gn->name, gn->cohort_num); break; } return false; } DEBUG3(MAKE, "MakePrintStatus: %s%s has %d unmade children\n", gn->name, gn->cohort_num, gn->unmade); /* * If printing cycles and came to one that has unmade children, * print out the cycle by recursing on its children. */ if (!gn->flags.cycle) { /* First time we've seen this node, check all children */ gn->flags.cycle = true; MakePrintStatusList(&gn->children, errors); /* Mark that this node needn't be processed again */ gn->flags.doneCycle = true; return false; } /* Only output the error once per node */ gn->flags.doneCycle = true; Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num); if ((*errors)++ > 100) /* Abandon the whole error report */ return true; /* Reporting for our children will give the rest of the loop */ MakePrintStatusList(&gn->children, errors); return false; } static void MakePrintStatusList(GNodeList *gnodes, int *errors) { GNodeListNode *ln; for (ln = gnodes->first; ln != NULL; ln = ln->next) if (MakePrintStatus(ln->datum, errors)) break; } static void ExamineLater(GNodeList *examine, GNodeList *toBeExamined) { ListNode *ln; for (ln = toBeExamined->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; if (gn->flags.remake) continue; if (gn->type & (OP_USE | OP_USEBEFORE)) continue; DEBUG2(MAKE, "ExamineLater: need to examine \"%s%s\"\n", gn->name, gn->cohort_num); Lst_Enqueue(examine, gn); } } /* * Expand .USE nodes and create a new targets list. * * Input: * targs the initial list of targets */ void Make_ExpandUse(GNodeList *targs) { GNodeList examine = LST_INIT; /* Queue of targets to examine */ Lst_AppendAll(&examine, targs); /* * Make an initial downward pass over the graph, marking nodes to * be made as we go down. * * We call Suff_FindDeps to find where a node is and to get some * children for it if it has none and also has no commands. If the * node is a leaf, we stick it on the toBeMade queue to be looked * at in a minute, otherwise we add its children to our queue and * go on about our business. */ while (!Lst_IsEmpty(&examine)) { GNode *gn = Lst_Dequeue(&examine); if (gn->flags.remake) /* We've looked at this one already */ continue; gn->flags.remake = true; DEBUG2(MAKE, "Make_ExpandUse: examine %s%s\n", gn->name, gn->cohort_num); if (gn->type & OP_DOUBLEDEP) Lst_PrependAll(&examine, &gn->cohorts); /* * Apply any .USE rules before looking for implicit * dependencies to make sure everything has commands that * should. * * Make sure that the TARGET is set, so that we can make * expansions. */ if (gn->type & OP_ARCHV) { char *eoa = strchr(gn->name, '('); char *eon = strchr(gn->name, ')'); if (eoa == NULL || eon == NULL) continue; *eoa = '\0'; *eon = '\0'; Var_Set(gn, MEMBER, eoa + 1); Var_Set(gn, ARCHIVE, gn->name); *eoa = '('; *eon = ')'; } Dir_UpdateMTime(gn, false); Var_Set(gn, TARGET, GNode_Path(gn)); UnmarkChildren(gn); HandleUseNodes(gn); if (!(gn->type & OP_MADE)) Suff_FindDeps(gn); else { PretendAllChildrenAreMade(gn); if (gn->unmade != 0) { printf( "Warning: " "%s%s still has %d unmade children\n", gn->name, gn->cohort_num, gn->unmade); } } if (gn->unmade != 0) ExamineLater(&examine, &gn->children); } Lst_Done(&examine); } /* Make the .WAIT node depend on the previous children */ static void add_wait_dependency(GNodeListNode *owln, GNode *wn) { GNodeListNode *cln; GNode *cn; for (cln = owln; (cn = cln->datum) != wn; cln = cln->next) { DEBUG3(MAKE, ".WAIT: add dependency %s%s -> %s\n", cn->name, cn->cohort_num, wn->name); /* * XXX: This pattern should be factored out, it repeats often */ Lst_Append(&wn->children, cn); wn->unmade++; Lst_Append(&cn->parents, wn); } } /* Convert .WAIT nodes into dependencies. */ static void Make_ProcessWait(GNodeList *targs) { GNode *pgn; /* 'parent' node we are examining */ GNodeListNode *owln; /* Previous .WAIT node */ GNodeList examine; /* List of targets to examine */ /* * We need all the nodes to have a common parent in order for the * .WAIT and .ORDER scheduling to work. * Perhaps this should be done earlier... */ pgn = GNode_New(".MAIN"); pgn->flags.remake = true; pgn->type = OP_PHONY | OP_DEPENDS; /* Get it displayed in the diag dumps */ Lst_Prepend(Targ_List(), pgn); { GNodeListNode *ln; for (ln = targs->first; ln != NULL; ln = ln->next) { GNode *cgn = ln->datum; Lst_Append(&pgn->children, cgn); Lst_Append(&cgn->parents, pgn); pgn->unmade++; } } /* Start building with the 'dummy' .MAIN' node */ MakeBuildChild(pgn, NULL); Lst_Init(&examine); Lst_Append(&examine, pgn); while (!Lst_IsEmpty(&examine)) { GNodeListNode *ln; pgn = Lst_Dequeue(&examine); /* We only want to process each child-list once */ if (pgn->flags.doneWait) continue; pgn->flags.doneWait = true; DEBUG1(MAKE, "Make_ProcessWait: examine %s\n", pgn->name); if (pgn->type & OP_DOUBLEDEP) Lst_PrependAll(&examine, &pgn->cohorts); owln = pgn->children.first; for (ln = pgn->children.first; ln != NULL; ln = ln->next) { GNode *cgn = ln->datum; if (cgn->type & OP_WAIT) { add_wait_dependency(owln, cgn); owln = ln; } else { Lst_Append(&examine, cgn); } } } Lst_Done(&examine); } /* * Initialize the nodes to remake and the list of nodes which are ready to * be made by doing a breadth-first traversal of the graph starting from the * nodes in the given list. Once this traversal is finished, all the 'leaves' * of the graph are in the toBeMade queue. * * Using this queue and the Job module, work back up the graph, calling on * MakeStartJobs to keep the job table as full as possible. * * Input: * targs the initial list of targets * * Results: * True if work was done, false otherwise. * * Side Effects: * The make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. */ bool Make_Run(GNodeList *targs) { int errors; /* Number of errors the Job module reports */ /* Start trying to make the current targets... */ Lst_Init(&toBeMade); Make_ExpandUse(targs); Make_ProcessWait(targs); if (DEBUG(MAKE)) { debug_printf("#***# full graph\n"); Targ_PrintGraph(1); } if (opts.query) { /* * We wouldn't do any work unless we could start some jobs * in the next loop... (we won't actually start any, of * course, this is just to see if any of the targets was out * of date) */ return MakeStartJobs(); } /* * Initialization. At the moment, no jobs are running and until some * get started, nothing will happen since the remaining upward * traversal of the graph is performed by the routines in job.c upon * the finishing of a job. So we fill the Job table as much as we can * before going into our loop. */ (void)MakeStartJobs(); /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for * output will cause us to be idle most of the time while our * children run as much as possible. Because the job table is kept * as full as possible, the only time when it will be empty is when * all the jobs which need running have been run, so that is the end * condition of this loop. Note that the Job module will exit if * there were any errors unless the keepgoing flag was given. */ while (!Lst_IsEmpty(&toBeMade) || jobTokensRunning > 0) { Job_CatchOutput(); (void)MakeStartJobs(); } errors = Job_Finish(); /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ DEBUG1(MAKE, "done: errors %d\n", errors); if (errors == 0) { MakePrintStatusList(targs, &errors); if (DEBUG(MAKE)) { debug_printf("done: errors %d\n", errors); if (errors > 0) Targ_PrintGraph(4); } } return errors > 0; } diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h index e4e374f71e0c..d62e2a3f507b 100644 --- a/contrib/bmake/make.h +++ b/contrib/bmake/make.h @@ -1,1244 +1,1251 @@ -/* $NetBSD: make.h,v 1.303 2022/06/12 13:37:32 rillig Exp $ */ +/* $NetBSD: make.h,v 1.311 2023/01/26 20:48:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /* * make.h -- * The global definitions for make */ #ifndef MAKE_MAKE_H #define MAKE_MAKE_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_STRING_H #include #else #include #endif #include #include #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if defined(__GNUC__) #define MAKE_GNUC_PREREQ(x, y) \ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ (__GNUC__ > (x))) #else #define MAKE_GNUC_PREREQ(x, y) 0 #endif #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_UNUSED __attribute__((__unused__)) #else #define MAKE_ATTR_UNUSED /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 5) #define MAKE_ATTR_DEAD __attribute__((__noreturn__)) #elif defined(__GNUC__) #define MAKE_ATTR_DEAD __volatile #else #define MAKE_ATTR_DEAD /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ #endif #if MAKE_GNUC_PREREQ(4, 0) #define MAKE_ATTR_USE __attribute__((__warn_unused_result__)) #else #define MAKE_ATTR_USE /* delete */ #endif #if __STDC_VERSION__ >= 199901L || defined(lint) #define MAKE_INLINE static inline MAKE_ATTR_UNUSED #else #define MAKE_INLINE static MAKE_ATTR_UNUSED #endif /* MAKE_STATIC marks a function that may or may not be inlined. */ #if defined(lint) /* As of 2021-07-31, NetBSD lint ignores __attribute__((unused)). */ #define MAKE_STATIC MAKE_INLINE #else #define MAKE_STATIC static MAKE_ATTR_UNUSED #endif #if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN) #include #elif defined(__bool_true_false_are_defined) /* * All files of make must be compiled with the same definition of bool. * Since one of the files includes , that means the header is * available on this platform. Recompile everything with -DUSE_C99_BOOLEAN. */ #error " is included in pre-C99 mode" #elif defined(bool) || defined(true) || defined(false) /* * In pre-C99 mode, make does not expect that bool is already defined. * You need to ensure that all translation units use the same definition for * bool. */ #error "bool/true/false is defined in pre-C99 mode" #else typedef unsigned char bool; #define true 1 #define false 0 #endif #include "lst.h" #include "make_malloc.h" #include "str.h" #include "hash.h" #include "make-conf.h" #include "buf.h" /* * some vendors don't have this --sjg */ #if defined(S_IFDIR) && !defined(S_ISDIR) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #if defined(sun) && (defined(__svr4__) || defined(__SVR4)) # 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 { /* 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 * 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. * * See the tests depsrc-*.mk and deptgt-*.mk. */ 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. */ OP_DEPENDS = 1 << 0, /* * The dependency operator '!' always executes its commands, even if * its children are up-to-date. */ OP_FORCE = 1 << 1, /* * The dependency operator '::' behaves like ':', except that it * allows multiple dependency groups to be defined. Each of these * groups is executed on its own, independently from the others. Each * individual dependency group is called a cohort. */ OP_DOUBLEDEP = 1 << 2, /* Matches the dependency operators ':', '!' and '::'. */ OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP, /* Don't care if the target doesn't exist and can't be created. */ OP_OPTIONAL = 1 << 3, /* Use associated commands for parents. */ OP_USE = 1 << 4, /* * Target is never out of date, but always execute commands anyway. * Its time doesn't matter, so it has none...sort of. */ OP_EXEC = 1 << 5, /* * Ignore non-zero exit status from shell commands when creating the * node. */ OP_IGNORE = 1 << 6, /* Don't remove the target when interrupted. */ OP_PRECIOUS = 1 << 7, /* Don't echo commands when executed. */ OP_SILENT = 1 << 8, /* * Target is a recursive make so its commands should always be * executed when it is out of date, regardless of the state of the -n * or -t flags. */ OP_MAKE = 1 << 9, /* * Target is out-of-date only if any of its children was out-of-date. */ OP_JOIN = 1 << 10, /* Assume the children of the node have been already made. */ OP_MADE = 1 << 11, /* Special .BEGIN, .END or .INTERRUPT. */ OP_SPECIAL = 1 << 12, /* Like .USE, only prepend commands. */ OP_USEBEFORE = 1 << 13, /* * The node is invisible to its parents. I.e. it doesn't show up in * the parents' local variables (.IMPSRC, .ALLSRC). */ OP_INVISIBLE = 1 << 14, /* * The node does not become the main target, even if it is the first * target in the first makefile. */ OP_NOTMAIN = 1 << 15, /* Not a file target; run always. */ OP_PHONY = 1 << 16, /* Don't search for the file in the path. */ OP_NOPATH = 1 << 17, /* * In a dependency line "target: source1 .WAIT source2", source1 is * made first, including its children. Once that is finished, * source2 is made, including its children. The .WAIT keyword may * appear more than once in a single dependency declaration. */ OP_WAIT = 1 << 18, /* .NOMETA do not create a .meta file */ OP_NOMETA = 1 << 19, /* .META we _do_ want a .meta file */ OP_META = 1 << 20, /* Do not compare commands in .meta file */ OP_NOMETA_CMP = 1 << 21, /* Possibly a submake node */ OP_SUBMAKE = 1 << 22, /* Attributes applied by PMake */ /* The node is a transformation rule, such as ".c.o". */ OP_TRANSFORM = 1 << 30, /* Target is a member of an archive */ /* XXX: How does this differ from OP_ARCHV? */ OP_MEMBER = 1 << 29, /* * The node is a library, its name has the form "-l". */ OP_LIB = 1 << 28, /* * The node is an archive member, its name has the form * "archive(member)". */ /* XXX: How does this differ from OP_MEMBER? */ OP_ARCHV = 1 << 27, /* * Target has all the commands it should. Used when parsing to catch * multiple command groups for a target. Only applies to the * dependency operators ':' and '!', but not to '::'. */ OP_HAS_COMMANDS = 1 << 26, /* * The special command "..." has been seen. All further commands from * this node will be saved on the .END node instead, to be executed * at the very end. */ OP_SAVE_CMDS = 1 << 25, /* * Already processed by Suff_FindDeps, to find dependencies from * suffix transformation rules. */ OP_DEPS_FOUND = 1 << 24, /* Node found while expanding .ALLSRC */ OP_MARK = 1 << 23 } GNodeType; typedef struct GNodeFlags { /* this target needs to be (re)made */ bool remake:1; /* children of this target were made */ bool childMade:1; /* children don't exist, and we pretend made */ bool force:1; /* Set by Make_ProcessWait() */ bool doneWait:1; /* Build requested by .ORDER processing */ bool doneOrder:1; /* Node created from .depend */ bool fromDepend:1; /* We do it once only */ bool doneAllsrc:1; /* Used by MakePrintStatus */ bool cycle:1; /* Used by MakePrintStatus */ bool doneCycle:1; } GNodeFlags; typedef struct List StringList; typedef struct ListNode StringListNode; typedef struct List GNodeList; typedef struct ListNode GNodeListNode; typedef struct SearchPath { List /* of CachedDir */ dirs; } SearchPath; /* * A graph node represents a target that can possibly be made, including its * relation to other targets and a lot of other details. */ typedef struct GNode { /* The target's name, such as "clean" or "make.c" */ char *name; /* The unexpanded name of a .USE node */ char *uname; /* * The full pathname of the file belonging to the target. * * XXX: What about .PHONY targets? These don't have an associated * path. */ char *path; /* * The type of operator used to define the sources (see the OP flags * below). * * XXX: This looks like a wild mixture of type and flags. */ GNodeType type; GNodeFlags flags; /* The state of processing on this node */ GNodeMade made; /* The number of unmade children */ int unmade; /* * The modification time; 0 means the node does not have a * corresponding file; see GNode_IsOODate. */ time_t mtime; struct GNode *youngestChild; /* * The GNodes for which this node is an implied source. May be empty. * For example, when there is an inference rule for .c.o, the node * for file.c has the node for file.o in this list. */ GNodeList implicitParents; /* * The nodes that depend on this one, or in other words, the nodes * for which this is a source. */ GNodeList parents; /* The nodes on which this one depends. */ GNodeList children; /* * .ORDER nodes we need made. The nodes that must be made (if they're * made) before this node can be made, but that do not enter into the * datedness of this node. */ GNodeList order_pred; /* * .ORDER nodes who need us. The nodes that must be made (if they're * made at all) after this node is made, but that do not depend on * this node, in the normal sense. */ GNodeList order_succ; /* * Other nodes of the same name, for targets that were defined using * the '::' dependency operator (OP_DOUBLEDEP). */ GNodeList cohorts; /* The "#n" suffix for this cohort, or "" for other nodes */ char cohort_num[8]; /* The number of unmade instances on the cohorts list */ int unmade_cohorts; /* * Pointer to the first instance of a '::' node; only set when on a * cohorts list */ struct GNode *centurion; /* Last time (sequence number) we tried to make this node */ unsigned int checked_seqno; /* * The "local" variables that are specific to this target and this * target only, such as $@, $<, $?. * * Also used for the global variable scopes SCOPE_GLOBAL, * SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with * arbitrary names. */ HashTable /* of Var pointer */ vars; /* The commands to be given to a shell to create this target. */ StringList commands; /* * 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, unlimited lifetime */ const char *fname; /* Line number where the GNode got defined, 1-based */ unsigned lineno; } GNode; /* * Keep track of whether to include when parsing the line * '.POSIX:'. */ extern enum PosixState { PS_NOT_YET, PS_MAYBE_NEXT_LINE, PS_NOW_OR_NEVER, PS_TOO_LATE } posix_state; /* Error levels for diagnostics during parsing. */ typedef enum ParseErrorLevel { /* * Exit when the current top-level makefile has been parsed * completely. */ PARSE_FATAL = 1, /* Print "warning"; may be upgraded to fatal by the -w option. */ PARSE_WARNING, /* Informational, mainly used during development of makefiles. */ PARSE_INFO } ParseErrorLevel; /* * Values returned by Cond_EvalLine and Cond_EvalCondition. */ typedef enum CondResult { CR_TRUE, /* Parse the next lines */ CR_FALSE, /* Skip the next lines */ CR_ERROR /* Unknown directive or parse error */ } CondResult; /* Names of the variables that are "local" to a specific target. */ #define TARGET "@" /* Target of dependency */ #define OODATE "?" /* All out-of-date sources */ #define ALLSRC ">" /* All sources */ #define IMPSRC "<" /* Source implied by transformation */ #define PREFIX "*" /* Common prefix */ #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ #define MEMBER "%" /* Member in "archive(member)" syntax */ /* * Global Variables */ /* True if every target is precious */ extern bool allPrecious; /* True if failed targets should be deleted */ extern bool deleteOnError; /* true while processing .depend */ extern bool doing_depend; /* .DEFAULT rule */ extern GNode *defaultNode; /* * Variables defined internally by make which should not override those set * by makefiles. */ extern GNode *SCOPE_INTERNAL; /* Variables defined in a global scope, e.g in the makefile itself. */ extern GNode *SCOPE_GLOBAL; /* Variables defined on the command line. */ extern GNode *SCOPE_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. */ extern char var_Error[]; /* The time at the start of this whole process */ extern time_t now; /* * The list of directories to search when looking for targets (set by the * special target .PATH). */ extern SearchPath dirSearchPath; /* Used for .include "...". */ extern SearchPath *parseIncPath; /* * Used for .include <...>, for the built-in sys.mk and for makefiles from * the command line arguments. */ extern SearchPath *sysIncPath; /* The default for sysIncPath. */ extern SearchPath *defSysIncPath; /* Startup directory */ extern char curdir[]; /* The basename of the program name, suffixed with [n] for sub-makes. */ extern const char *progname; extern int makelevel; /* Name of the .depend makefile */ extern char *makeDependfile; /* If we replaced environ, this will be non-NULL. */ extern char **savedEnv; extern GNode *mainNode; extern pid_t myPid; #define MAKEFLAGS ".MAKEFLAGS" #define MAKEOVERRIDES ".MAKEOVERRIDES" /* prefix when printing the target of a job */ #define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" #define MAKE_EXPORTED ".MAKE.EXPORTED" /* exported variables */ #define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all loaded makefiles */ #define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */ #define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE" -#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */ #define MAKE_MODE ".MAKE.MODE" #ifndef MAKE_LEVEL_ENV # define MAKE_LEVEL_ENV "MAKELEVEL" #endif typedef struct DebugFlags { bool DEBUG_ARCH:1; bool DEBUG_COND:1; bool DEBUG_CWD:1; bool DEBUG_DIR:1; bool DEBUG_ERROR:1; bool DEBUG_FOR:1; bool DEBUG_GRAPH1:1; bool DEBUG_GRAPH2:1; bool DEBUG_GRAPH3:1; bool DEBUG_HASH:1; bool DEBUG_JOB:1; bool DEBUG_LOUD:1; bool DEBUG_MAKE:1; bool DEBUG_META:1; bool DEBUG_PARSE:1; bool DEBUG_SCRIPT:1; bool DEBUG_SHELL:1; bool DEBUG_SUFF:1; bool DEBUG_TARG:1; bool DEBUG_VAR:1; } DebugFlags; #define CONCAT(a, b) a##b #define DEBUG(module) (opts.debug.CONCAT(DEBUG_, module)) void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); #define DEBUG_IMPL(module, args) \ do { \ if (DEBUG(module)) \ debug_printf args; \ } while (false) #define DEBUG0(module, fmt) \ DEBUG_IMPL(module, (fmt)) #define DEBUG1(module, fmt, arg1) \ DEBUG_IMPL(module, (fmt, arg1)) #define DEBUG2(module, fmt, arg1, arg2) \ DEBUG_IMPL(module, (fmt, arg1, arg2)) #define DEBUG3(module, fmt, arg1, arg2, arg3) \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3)) #define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4)) #define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5)) typedef enum PrintVarsMode { PVM_NONE, PVM_UNEXPANDED, PVM_EXPANDED } PrintVarsMode; /* Command line options */ typedef struct CmdOpts { /* -B: whether we are make compatible */ bool compatMake; /* * -d: debug control: There is one bit per module. It is up to the * module what debug information to print. */ DebugFlags debug; /* -df: debug output is written here - default stderr */ FILE *debug_file; /* * -dL: lint mode * * Runs make in strict mode, with additional checks and better error * handling. */ bool strict; /* -dV: for the -V option, print unexpanded variable values */ bool debugVflag; /* -e: check environment variables before global variables */ bool checkEnvFirst; /* -f: the makefiles to read */ StringList makefiles; /* -i: if true, ignore all errors from shell commands */ bool ignoreErrors; /* * -j: the maximum number of jobs that can run in parallel; this is * coordinated with the submakes */ int maxJobs; /* * -k: if true and an error occurs while making a node, continue * making nodes that do not depend on the erroneous node */ bool keepgoing; /* -N: execute no commands from the targets */ bool noRecursiveExecute; /* -n: execute almost no commands from the targets */ bool noExecute; /* * -q: if true, do not really make anything, just see if the targets * are out-of-date */ bool query; /* -r: raw mode, do not load the builtin rules. */ bool noBuiltins; /* -s: don't echo the shell commands before executing them */ bool silent; /* * -t: touch the targets if they are out-of-date, but don't actually * make them */ bool touch; /* -[Vv]: print expanded or unexpanded selected variables */ PrintVarsMode printVars; /* -[Vv]: the variables to print */ StringList variables; /* -W: if true, makefile parsing warnings are treated as errors */ bool parseWarnFatal; /* -w: print 'Entering' and 'Leaving' for submakes */ bool enterFlag; /* * -X: if true, do not export variables set on the command line to * the environment. */ bool varNoExportEnv; /* * The target names specified on the command line. Used to resolve * .if make(...) statements. */ StringList create; /* * Randomize the order in which the targets from toBeMade are made, * to catch undeclared dependencies. */ bool randomizeTargets; } CmdOpts; extern CmdOpts opts; /* arch.c */ void Arch_Init(void); void Arch_End(void); bool Arch_ParseArchive(char **, GNodeList *, GNode *); void Arch_Touch(GNode *); void Arch_TouchLib(GNode *); void Arch_UpdateMTime(GNode *gn); void Arch_UpdateMemberMTime(GNode *gn); void Arch_FindLib(GNode *, SearchPath *); bool Arch_LibOODate(GNode *) MAKE_ATTR_USE; bool Arch_IsLib(GNode *) MAKE_ATTR_USE; /* compat.c */ bool Compat_RunCommand(const char *, GNode *, StringListNode *); void Compat_MakeAll(GNodeList *); void Compat_Make(GNode *, GNode *); /* cond.c */ +extern unsigned int cond_depth; CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE; CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE; -void Cond_restore_depth(unsigned int); -unsigned int Cond_save_depth(void) MAKE_ATTR_USE; +void Cond_EndFile(void); /* dir.c; see also dir.h */ MAKE_INLINE const char * MAKE_ATTR_USE str_basename(const char *pathname) { const char *lastSlash = strrchr(pathname, '/'); return lastSlash != NULL ? lastSlash + 1 : pathname; } MAKE_INLINE SearchPath * MAKE_ATTR_USE SearchPath_New(void) { SearchPath *path = bmake_malloc(sizeof *path); Lst_Init(&path->dirs); return path; } void SearchPath_Free(SearchPath *); /* for.c */ struct ForLoop; int For_Eval(const char *) MAKE_ATTR_USE; bool For_Accum(const char *, int *) MAKE_ATTR_USE; void For_Run(unsigned, unsigned); bool For_NextIteration(struct ForLoop *, Buffer *); char *ForLoop_Details(struct ForLoop *); void ForLoop_Free(struct ForLoop *); +void For_Break(struct ForLoop *); /* job.c */ void JobReapChild(pid_t, int, bool); /* main.c */ void Main_ParseArgLine(const char *); char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE; void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; void DieHorribly(void) MAKE_ATTR_DEAD; void Finish(int) MAKE_ATTR_DEAD; -bool unlink_file(const char *) MAKE_ATTR_USE; +int unlink_file(const char *) MAKE_ATTR_USE; void execDie(const char *, const char *); char *getTmpdir(void) MAKE_ATTR_USE; bool ParseBoolean(const char *, bool) MAKE_ATTR_USE; const char *cached_realpath(const char *, char *); bool GetBooleanExpr(const char *, bool); /* parse.c */ void Parse_Init(void); void Parse_End(void); void PrintLocation(FILE *, bool, const GNode *); void PrintStackTrace(bool); void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE; void Parse_AddIncludeDir(const char *); void Parse_File(const char *, int); void Parse_PushInput(const char *, unsigned, unsigned, Buffer, struct ForLoop *); void Parse_MainName(GNodeList *); int Parse_NumErrors(void) MAKE_ATTR_USE; +unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE; /* suff.c */ void Suff_Init(void); void Suff_End(void); void Suff_ClearSuffixes(void); bool Suff_IsTransform(const char *) MAKE_ATTR_USE; GNode *Suff_AddTransform(const char *); void Suff_EndTransform(GNode *); void Suff_AddSuffix(const char *); SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE; void Suff_ExtendPaths(void); void Suff_AddInclude(const char *); void Suff_AddLib(const char *); void Suff_FindDeps(GNode *); SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE; void Suff_SetNull(const char *); void Suff_PrintAll(void); char *Suff_NamesStr(void) MAKE_ATTR_USE; /* targ.c */ void Targ_Init(void); void Targ_End(void); void Targ_Stats(void); GNodeList *Targ_List(void) MAKE_ATTR_USE; GNode *GNode_New(const char *) MAKE_ATTR_USE; GNode *Targ_FindNode(const char *) MAKE_ATTR_USE; GNode *Targ_GetNode(const char *) MAKE_ATTR_USE; GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE; GNode *Targ_GetEndNode(void); void Targ_FindList(GNodeList *, StringList *); void Targ_PrintCmds(GNode *); void Targ_PrintNode(GNode *, int); void Targ_PrintNodes(GNodeList *, int); const char *Targ_FmtTime(time_t) MAKE_ATTR_USE; void Targ_PrintType(GNodeType); void Targ_PrintGraph(int); void Targ_Propagate(void); const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE; /* var.c */ void Var_Init(void); void Var_End(void); typedef enum VarEvalMode { /* * Only parse the expression but don't evaluate any part of it. * * TODO: Document what Var_Parse and Var_Subst return in this mode. * As of 2021-03-15, they return unspecified, inconsistent results. */ VARE_PARSE_ONLY, /* Parse and evaluate the expression. */ VARE_WANTRES, /* * Parse and evaluate the expression. It is an error if a * subexpression evaluates to undefined. */ VARE_UNDEFERR, /* * Parse and evaluate the expression. Keep '$$' as '$$' instead of * reducing it to a single '$'. Subexpressions that evaluate to * undefined expand to an empty string. * * 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. */ VARE_EVAL_KEEP_DOLLAR, /* * Parse and evaluate the expression. 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_EVAL_KEEP_UNDEF, /* * Parse and evaluate the expression. Keep '$$' as '$$' and preserve * undefined subexpressions. */ VARE_KEEP_DOLLAR_UNDEF } VarEvalMode; typedef enum VarSetFlags { VAR_SET_NONE = 0, /* do not export */ VAR_SET_NO_EXPORT = 1 << 0, /* * Make the variable read-only. No further modification is possible, * except for another call to Var_Set with the same flag. */ VAR_SET_READONLY = 1 << 1 } VarSetFlags; /* The state of error handling returned by Var_Parse. */ typedef enum VarParseResult { /* Both parsing and evaluation succeeded. */ VPR_OK, /* Parsing or evaluating failed, with an error message. */ VPR_ERR, /* * 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: Instead of having this special return value, rather ensure * that VARE_EVAL_KEEP_UNDEF is processed properly. */ VPR_UNDEF } 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_Delete(GNode *, const char *); void Var_Undef(const char *); void Var_Set(GNode *, const char *, const char *); void Var_SetExpand(GNode *, const char *, const char *); void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags); void Var_Append(GNode *, const char *, const char *); void Var_AppendExpand(GNode *, const char *, const char *); bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE; bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE; FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE; const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE; VarParseResult Var_Parse(const char **, GNode *, VarEvalMode, FStr *); VarParseResult Var_Subst(const char *, GNode *, VarEvalMode, char **); void Var_Expand(FStr *, GNode *, VarEvalMode); void Var_Stats(void); void Var_Dump(GNode *); void Var_ReexportVars(void); void Var_Export(VarExportMode, const char *); void Var_ExportVars(const char *); void Var_UnExport(bool, const char *); +void Var_ReadOnly(const char *, bool); void Global_Set(const char *, const char *); void Global_Append(const char *, const char *); void Global_Delete(const char *); +void Global_Set_ReadOnly(const char *, const char *); /* util.c */ typedef void (*SignalProc)(int); SignalProc bmake_signal(int, SignalProc); /* make.c */ void GNode_UpdateYoungestChild(GNode *, GNode *); bool GNode_IsOODate(GNode *) MAKE_ATTR_USE; void Make_ExpandUse(GNodeList *); time_t Make_Recheck(GNode *) MAKE_ATTR_USE; void Make_HandleUse(GNode *, GNode *); void Make_Update(GNode *); void GNode_SetLocalVars(GNode *); bool Make_Run(GNodeList *); bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE; void PrintOnError(GNode *, const char *); void Main_ExportMAKEFLAGS(bool); bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE; int str2Lst_Append(StringList *, char *); void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE; +#ifndef HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif + /* See if the node was seen on the left-hand side of a dependency operator. */ MAKE_INLINE bool MAKE_ATTR_USE GNode_IsTarget(const GNode *gn) { return (gn->type & OP_OPMASK) != OP_NONE; } MAKE_INLINE const char * MAKE_ATTR_USE GNode_Path(const GNode *gn) { return gn->path != NULL ? gn->path : gn->name; } MAKE_INLINE bool MAKE_ATTR_USE GNode_IsWaitingFor(const GNode *gn) { return gn->flags.remake && gn->made <= REQUESTED; } MAKE_INLINE bool MAKE_ATTR_USE GNode_IsReady(const GNode *gn) { return gn->made > DEFERRED; } MAKE_INLINE bool MAKE_ATTR_USE GNode_IsDone(const GNode *gn) { return gn->made >= MADE; } MAKE_INLINE bool MAKE_ATTR_USE GNode_IsError(const GNode *gn) { return gn->made == ERROR || gn->made == ABORTED; } MAKE_INLINE bool MAKE_ATTR_USE GNode_IsMainCandidate(const GNode *gn) { return (gn->type & (OP_NOTMAIN | OP_USE | OP_USEBEFORE | OP_EXEC | OP_TRANSFORM)) == 0; } /* Return whether the target file should be preserved on interrupt. */ MAKE_INLINE bool MAKE_ATTR_USE GNode_IsPrecious(const GNode *gn) { /* XXX: Why are '::' targets precious? */ return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } MAKE_INLINE const char * MAKE_ATTR_USE GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } MAKE_INLINE void * MAKE_ATTR_USE UNCONST(const void *ptr) { void *ret; memcpy(&ret, &ptr, sizeof(ret)); return ret; } /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ #ifdef HAVE_LIMITS_H #include #endif #ifndef MAXPATHLEN #define MAXPATHLEN BMAKE_PATH_MAX #endif #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif #if defined(SYSV) #define KILLPG(pid, sig) kill(-(pid), (sig)) #else #define KILLPG(pid, sig) killpg((pid), (sig)) #endif MAKE_INLINE bool MAKE_ATTR_USE ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; } MAKE_INLINE bool MAKE_ATTR_USE ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; } MAKE_INLINE bool MAKE_ATTR_USE ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; } MAKE_INLINE bool MAKE_ATTR_USE ch_islower(char ch) { return islower((unsigned char)ch) != 0; } MAKE_INLINE bool MAKE_ATTR_USE ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; } MAKE_INLINE bool MAKE_ATTR_USE ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; } MAKE_INLINE char MAKE_ATTR_USE ch_tolower(char ch) { return (char)tolower((unsigned char)ch); } MAKE_INLINE char MAKE_ATTR_USE ch_toupper(char ch) { return (char)toupper((unsigned char)ch); } MAKE_INLINE void cpp_skip_whitespace(const char **pp) { while (ch_isspace(**pp)) (*pp)++; } MAKE_INLINE void cpp_skip_hspace(const char **pp) { while (**pp == ' ' || **pp == '\t') (*pp)++; } MAKE_INLINE bool cpp_skip_string(const char **pp, const char *s) { const char *p = *pp; while (*p == *s && *s != '\0') p++, s++; if (*s == '\0') *pp = p; return *s == '\0'; } MAKE_INLINE void pp_skip_whitespace(char **pp) { while (ch_isspace(**pp)) (*pp)++; } MAKE_INLINE void pp_skip_hspace(char **pp) { while (**pp == ' ' || **pp == '\t') (*pp)++; } #if defined(lint) # define MAKE_RCSID(id) extern void do_not_define_rcsid(void) #elif defined(MAKE_NATIVE) # include # ifndef __IDSTRING # define __IDSTRING(name,string) \ static const char name[] MAKE_ATTR_UNUSED = string # endif # ifndef __RCSID # define __RCSID(s) __IDSTRING(rcsid,s) # endif # ifndef __COPYRIGHT # define __COPYRIGHT(s) __IDSTRING(copyright,s) # endif # define MAKE_RCSID(id) __RCSID(id) #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) # define MAKE_RCSID(id) static volatile char \ MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id #elif defined(MAKE_ALL_IN_ONE) # define MAKE_RCSID(id) extern void do_not_define_rcsid(void) #else # define MAKE_RCSID(id) static volatile char rcsid[] = id #endif #endif diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c index f400485d25c0..f31a4666fce7 100644 --- a/contrib/bmake/meta.c +++ b/contrib/bmake/meta.c @@ -1,1729 +1,1735 @@ -/* $NetBSD: meta.c,v 1.200 2022/04/15 12:28:16 rillig Exp $ */ +/* $NetBSD: meta.c,v 1.201 2022/09/28 16:34:47 sjg Exp $ */ /* * Implement 'meta' mode. * Adapted from John Birrell's patches to FreeBSD make. * --sjg */ /* * Copyright (c) 2009-2016, Juniper Networks, Inc. * Portions Copyright (c) 2009, John Birrell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_META) #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_LIBGEN_H #include #elif !defined(HAVE_DIRNAME) char * dirname(char *); #endif #include #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) #include #endif #include "make.h" #include "dir.h" #include "job.h" #ifdef USE_FILEMON #include "filemon/filemon.h" #endif static BuildMon Mybm; /* for compat */ static StringList metaBailiwick = LST_INIT; /* our scope of control */ static char *metaBailiwickStr; /* string storage for the list */ static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */ static char *metaIgnorePathsStr; /* string storage for the list */ #ifndef MAKE_META_IGNORE_PATHS #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" #endif #ifndef MAKE_META_IGNORE_PATTERNS #define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS" #endif #ifndef MAKE_META_IGNORE_FILTER #define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER" #endif #ifndef MAKE_META_CMP_FILTER #define MAKE_META_CMP_FILTER ".MAKE.META.CMP_FILTER" #endif bool useMeta = false; static bool useFilemon = false; static bool writeMeta = false; static bool metaMissing = false; /* oodate if missing */ static bool filemonMissing = false; /* oodate if missing */ static bool metaEnv = false; /* don't save env unless asked */ static bool metaVerbose = false; static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */ static bool metaIgnorePatterns = false; /* do we need to do pattern matches */ static bool metaIgnoreFilter = false; /* do we have more complex filtering? */ static bool metaCmpFilter = false; /* do we have CMP_FILTER ? */ static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */ static bool metaSilent = false; /* if we have a .meta be SILENT */ extern bool forceJobs; extern char **environ; #define MAKE_META_PREFIX ".MAKE.META.PREFIX" #ifndef N2U # define N2U(n, u) (((n) + ((u) - 1)) / (u)) #endif #ifndef ROUNDUP # define ROUNDUP(n, u) (N2U((n), (u)) * (u)) #endif #if !defined(HAVE_STRSEP) # define strsep(s, d) stresep((s), (d), '\0') #endif #if !defined(HAVE_STRESEP) char * stresep(char **, const char *, int); #endif /* * Filemon is a kernel module which snoops certain syscalls. * * C chdir * E exec * F [v]fork * L [sym]link * M rename * R read * W write * S stat * * See meta_oodate below - we mainly care about 'E' and 'R'. * * We can still use meta mode without filemon, but * the benefits are more limited. */ #ifdef USE_FILEMON /* * Open the filemon device. */ static void meta_open_filemon(BuildMon *pbm) { int dupfd; pbm->mon_fd = -1; pbm->filemon = NULL; if (!useFilemon || pbm->mfp == NULL) return; pbm->filemon = filemon_open(); if (pbm->filemon == NULL) { useFilemon = false; warn("Could not open filemon %s", filemon_path()); return; } /* * We use a file outside of '.' * to avoid a FreeBSD kernel bug where unlink invalidates * cwd causing getcwd to do a lot more work. * We only care about the descriptor. */ if (!opts.compatMake) pbm->mon_fd = Job_TempFile("filemon.XXXXXX", NULL, 0); else pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0); if ((dupfd = dup(pbm->mon_fd)) == -1) { Punt("Could not dup filemon output: %s", strerror(errno)); } (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC); if (filemon_setfd(pbm->filemon, dupfd) == -1) { Punt("Could not set filemon file descriptor: %s", strerror(errno)); } /* we don't need these once we exec */ (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC); } /* * Read the build monitor output file and write records to the target's * metadata file. */ static int filemon_read(FILE *mfp, int fd) { char buf[BUFSIZ]; int error; /* Check if we're not writing to a meta data file.*/ if (mfp == NULL) { if (fd >= 0) close(fd); /* not interested */ return 0; } /* rewind */ if (lseek(fd, (off_t)0, SEEK_SET) < 0) { error = errno; warn("Could not rewind filemon"); fprintf(mfp, "\n"); } else { ssize_t n; error = 0; fprintf(mfp, "\n-- filemon acquired metadata --\n"); while ((n = read(fd, buf, sizeof buf)) > 0) { if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n) error = EIO; } } if (fflush(mfp) != 0) Punt("Cannot write filemon data to meta file: %s", strerror(errno)); if (close(fd) < 0) error = errno; return error; } #endif /* * when realpath() fails, * we use this, to clean up ./ and ../ */ static void eat_dots(char *buf) { char *p; while ((p = strstr(buf, "/./")) != NULL) memmove(p, p + 2, strlen(p + 2) + 1); while ((p = strstr(buf, "/../")) != NULL) { char *p2 = p + 3; if (p > buf) { do { p--; } while (p > buf && *p != '/'); } if (*p == '/') memmove(p, p2, strlen(p2) + 1); else return; /* can't happen? */ } } static char * meta_name(char *mname, size_t mnamelen, const char *dname, const char *tname, const char *cwd) { char buf[MAXPATHLEN]; char *rp, *cp; const char *tname_base; char *tp; char *dtp; size_t ldname; /* * Weed out relative paths from the target file name. * We have to be careful though since if target is a * symlink, the result will be unstable. * So we use realpath() just to get the dirname, and leave the * basename as given to us. */ if ((tname_base = strrchr(tname, '/')) != NULL) { if (cached_realpath(tname, buf) != NULL) { if ((rp = strrchr(buf, '/')) != NULL) { rp++; tname_base++; if (strcmp(tname_base, rp) != 0) strlcpy(rp, tname_base, sizeof buf - (size_t)(rp - buf)); } tname = buf; } else { /* * We likely have a directory which is about to be made. * We pretend realpath() succeeded, to have a chance * of generating the same meta file name that we will * next time through. */ if (tname[0] == '/') { strlcpy(buf, tname, sizeof buf); } else { snprintf(buf, sizeof buf, "%s/%s", cwd, tname); } eat_dots(buf); tname = buf; } } /* on some systems dirname may modify its arg */ tp = bmake_strdup(tname); dtp = dirname(tp); - if (strcmp(dname, dtp) == 0) - snprintf(mname, mnamelen, "%s.meta", tname); - else { + if (strcmp(dname, dtp) == 0) { + if (snprintf(mname, mnamelen, "%s.meta", tname) >= (int)mnamelen) + mname[mnamelen - 1] = '\0'; + } else { + int x; + ldname = strlen(dname); if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/') - snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]); + x = snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]); else - snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); - + x = snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); + if (x >= (int)mnamelen) + mname[mnamelen - 1] = '\0'; /* * Replace path separators in the file name after the * current object directory path. */ cp = mname + strlen(dname) + 1; while (*cp != '\0') { if (*cp == '/') *cp = '_'; cp++; } } free(tp); return mname; } /* * Return true if running ${.MAKE} * Bypassed if target is flagged .MAKE */ static bool is_submake(const char *cmd, GNode *gn) { static const char *p_make = NULL; static size_t p_len; char *mp = NULL; const char *cp2; bool rc = false; if (p_make == NULL) { p_make = Var_Value(gn, ".MAKE").str; p_len = strlen(p_make); } if (strchr(cmd, '$') != NULL) { (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp); /* TODO: handle errors */ cmd = mp; } cp2 = strstr(cmd, p_make); if (cp2 != NULL) { switch (cp2[p_len]) { case '\0': case ' ': case '\t': case '\n': rc = true; break; } if (cp2 > cmd && rc) { switch (cp2[-1]) { case ' ': case '\t': case '\n': break; default: rc = false; /* no match */ break; } } } free(mp); return rc; } static bool 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 *ucmd, FILE *fp, GNode *gn) { FStr xcmd = FStr_InitRefer(ucmd); Var_Expand(&xcmd, gn, VARE_WANTRES); fprintf(fp, "CMD %s\n", xcmd.str); FStr_Done(&xcmd); } static void printCMDs(GNode *gn, FILE *fp) { StringListNode *ln; for (ln = gn->commands.first; ln != NULL; ln = ln->next) printCMD(ln->datum, fp, gn); } /* * Certain node types never get a .meta file */ #define SKIP_META_TYPE(flag, str) do { \ if ((gn->type & (flag))) { \ if (verbose) \ debug_printf("Skipping meta for %s: .%s\n", gn->name, str); \ return false; \ } \ } while (false) /* * Do we need/want a .meta file ? */ static bool meta_needed(GNode *gn, const char *dname, char *objdir_realpath, bool verbose) { struct cached_stat cst; if (verbose) verbose = DEBUG(META); /* This may be a phony node which we don't want meta data for... */ /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ /* Or it may be explicitly flagged as .NOMETA */ SKIP_META_TYPE(OP_NOMETA, "NOMETA"); /* Unless it is explicitly flagged as .META */ if (!(gn->type & OP_META)) { SKIP_META_TYPE(OP_PHONY, "PHONY"); SKIP_META_TYPE(OP_SPECIAL, "SPECIAL"); SKIP_META_TYPE(OP_MAKE, "MAKE"); } /* Check if there are no commands to execute. */ 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 (any_is_submake(gn)) { DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name); return false; } } /* The object directory may not exist. Check it.. */ if (cached_stat(dname, &cst) != 0) { if (verbose) debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name); return false; } /* make sure these are canonical */ 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) { if (verbose) debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n", gn->name); return false; } return true; } static FILE * meta_create(BuildMon *pbm, GNode *gn) { FILE *fp; char buf[MAXPATHLEN]; char objdir_realpath[MAXPATHLEN]; char **ptr; FStr dname; const char *tname; char *fname; const char *cp; fp = NULL; dname = Var_Value(gn, ".OBJDIR"); tname = GNode_VarTarget(gn); /* if this succeeds objdir_realpath is realpath of dname */ if (!meta_needed(gn, dname.str, objdir_realpath, true)) goto out; dname.str = objdir_realpath; if (metaVerbose) { char *mp; /* Describe the target we are building */ (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp); /* TODO: handle errors */ if (mp[0] != '\0') fprintf(stdout, "%s\n", mp); free(mp); } /* Get the basename of the target */ cp = str_basename(tname); fflush(stdout); if (!writeMeta) /* Don't create meta data. */ goto out; fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname, dname.str, tname, objdir_realpath); #ifdef DEBUG_META_MODE DEBUG1(META, "meta_create: %s\n", fname); #endif if ((fp = fopen(fname, "w")) == NULL) Punt("Could not open meta file '%s': %s", fname, strerror(errno)); fprintf(fp, "# Meta data file %s\n", fname); printCMDs(gn, fp); fprintf(fp, "CWD %s\n", getcwd(buf, sizeof buf)); fprintf(fp, "TARGET %s\n", tname); cp = GNode_VarOodate(gn); if (cp != NULL && *cp != '\0') { fprintf(fp, "OODATE %s\n", cp); } if (metaEnv) { for (ptr = environ; *ptr != NULL; ptr++) fprintf(fp, "ENV %s\n", *ptr); } fprintf(fp, "-- command output --\n"); if (fflush(fp) != 0) Punt("Cannot write expanded command to meta file: %s", strerror(errno)); Global_Append(".MAKE.META.FILES", fname); Global_Append(".MAKE.META.CREATED", fname); gn->type |= OP_META; /* in case anyone wants to know */ if (metaSilent) { gn->type |= OP_SILENT; } out: FStr_Done(&dname); return fp; } static bool boolValue(const char *s) { switch (*s) { case '0': case 'N': case 'n': case 'F': case 'f': return false; } return true; } /* * Initialization we need before reading makefiles. */ void meta_init(void) { #ifdef USE_FILEMON /* this allows makefiles to test if we have filemon support */ Global_Set(".MAKE.PATH_FILEMON", filemon_path()); #endif } #define get_mode_bf(bf, token) \ if ((cp = strstr(make_mode, token)) != NULL) \ bf = boolValue(cp + sizeof (token) - 1) /* * Initialization we need after reading makefiles. */ void meta_mode_init(const char *make_mode) { static bool once = false; const char *cp; useMeta = true; useFilemon = true; writeMeta = true; if (make_mode != NULL) { if (strstr(make_mode, "env") != NULL) metaEnv = true; if (strstr(make_mode, "verb") != NULL) metaVerbose = true; if (strstr(make_mode, "read") != NULL) writeMeta = false; if (strstr(make_mode, "nofilemon") != NULL) useFilemon = false; if (strstr(make_mode, "ignore-cmd") != NULL) metaIgnoreCMDs = true; if (useFilemon) get_mode_bf(filemonMissing, "missing-filemon="); get_mode_bf(metaCurdirOk, "curdirok="); get_mode_bf(metaMissing, "missing-meta="); get_mode_bf(metaSilent, "silent="); } if (metaVerbose && !Var_Exists(SCOPE_GLOBAL, MAKE_META_PREFIX)) { /* * The default value for MAKE_META_PREFIX * prints the absolute path of the target. * This works be cause :H will generate '.' if there is no / * and :tA will resolve that to cwd. */ Global_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}"); } if (once) return; once = true; memset(&Mybm, 0, sizeof Mybm); /* * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} */ (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr); /* TODO: handle errors */ str2Lst_Append(&metaBailiwick, metaBailiwickStr); /* * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} */ Global_Append(MAKE_META_IGNORE_PATHS, "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}"); (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); /* TODO: handle errors */ str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr); /* * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} */ metaIgnorePatterns = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS); metaIgnoreFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER); metaCmpFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_CMP_FILTER); } /* * In each case below we allow for job==NULL */ void meta_job_start(Job *job, GNode *gn) { BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } pbm->mfp = meta_create(pbm, gn); #ifdef USE_FILEMON_ONCE /* compat mode we open the filemon dev once per command */ if (job == NULL) return; #endif #ifdef USE_FILEMON if (pbm->mfp != NULL && useFilemon) { meta_open_filemon(pbm); } else { pbm->mon_fd = -1; pbm->filemon = NULL; } #endif } /* * The child calls this before doing anything. * It does not disturb our state. */ void meta_job_child(Job *job MAKE_ATTR_UNUSED) { #ifdef USE_FILEMON BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { close(fileno(pbm->mfp)); if (useFilemon && pbm->filemon != NULL) { pid_t pid; pid = getpid(); if (filemon_setpid_child(pbm->filemon, pid) == -1) { Punt("Could not set filemon pid: %s", strerror(errno)); } } } #endif } void meta_job_parent(Job *job MAKE_ATTR_UNUSED, pid_t pid MAKE_ATTR_UNUSED) { #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (useFilemon && pbm->filemon != NULL) { filemon_setpid_parent(pbm->filemon, pid); } #endif } int meta_job_fd(Job *job MAKE_ATTR_UNUSED) { #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (useFilemon && pbm->filemon != NULL) { return filemon_readfd(pbm->filemon); } #endif return -1; } int meta_job_event(Job *job MAKE_ATTR_UNUSED) { #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (useFilemon && pbm->filemon != NULL) { return filemon_process(pbm->filemon); } #endif return 0; } void meta_job_error(Job *job, GNode *gn, bool ignerr, int status) { char cwd[MAXPATHLEN]; BuildMon *pbm; if (job != NULL) { pbm = &job->bm; if (gn == NULL) gn = job->node; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { fprintf(pbm->mfp, "\n*** Error code %d%s\n", status, ignerr ? "(ignored)" : ""); } if (gn != NULL) Global_Set(".ERROR_TARGET", GNode_Path(gn)); - getcwd(cwd, sizeof cwd); + if (getcwd(cwd, sizeof cwd) == NULL) + Punt("Cannot get cwd: %s", strerror(errno)); + Global_Set(".ERROR_CWD", cwd); if (pbm->meta_fname[0] != '\0') { Global_Set(".ERROR_META_FILE", pbm->meta_fname); } meta_job_finish(job); } void meta_job_output(Job *job, char *cp, const char *nl) { BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { if (metaVerbose) { static char *meta_prefix = NULL; static size_t meta_prefix_len; if (meta_prefix == NULL) { char *cp2; (void)Var_Subst("${" MAKE_META_PREFIX "}", SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix); /* TODO: handle errors */ 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 == NULL) return; cp++; } } fprintf(pbm->mfp, "%s%s", cp, nl); } } int meta_cmd_finish(void *pbmp) { int error = 0; BuildMon *pbm = pbmp; #ifdef USE_FILEMON int x; #endif if (pbm == NULL) pbm = &Mybm; #ifdef USE_FILEMON if (pbm->filemon != NULL) { while (filemon_process(pbm->filemon) > 0) continue; if (filemon_close(pbm->filemon) == -1) { error = errno; Punt("filemon failed: %s", strerror(errno)); } x = filemon_read(pbm->mfp, pbm->mon_fd); if (error == 0 && x != 0) error = x; pbm->mon_fd = -1; pbm->filemon = NULL; return error; } #endif fprintf(pbm->mfp, "\n"); /* ensure end with newline */ return error; } int meta_job_finish(Job *job) { BuildMon *pbm; int error = 0; int x; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { error = meta_cmd_finish(pbm); x = fclose(pbm->mfp); if (error == 0 && x != 0) error = errno; pbm->mfp = NULL; pbm->meta_fname[0] = '\0'; } return error; } void meta_finish(void) { Lst_Done(&metaBailiwick); free(metaBailiwickStr); Lst_Done(&metaIgnorePaths); free(metaIgnorePathsStr); } /* * Fetch a full line from fp - growing bufp if needed * Return length in bufp. */ static int fgetLine(char **bufp, size_t *szp, int o, FILE *fp) { char *buf = *bufp; size_t bufsz = *szp; struct stat fs; int x; if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) { check_newline: x = o + (int)strlen(&buf[o]); if (buf[x - 1] == '\n') return x; /* * We need to grow the buffer. * The meta file can give us a clue. */ if (fstat(fileno(fp), &fs) == 0) { size_t newsz; char *p; newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ); if (newsz <= bufsz) newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ); if (newsz <= bufsz) return x; /* truncated */ DEBUG2(META, "growing buffer %u -> %u\n", (unsigned)bufsz, (unsigned)newsz); p = bmake_realloc(buf, newsz); *bufp = buf = p; *szp = bufsz = newsz; /* fetch the rest */ if (fgets(&buf[x], (int)bufsz - x, fp) == NULL) return x; /* truncated! */ goto check_newline; } } return 0; } static bool prefix_match(const char *prefix, const char *path) { size_t n = strlen(prefix); return strncmp(path, prefix, n) == 0; } static bool 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 bool path_starts_with(const char *path, const char *prefix) { size_t n = strlen(prefix); if (strncmp(path, prefix, n) != 0) return false; return path[n] == '\0' || path[n] == '/'; } static bool meta_ignore(GNode *gn, const char *p) { char fname[MAXPATHLEN]; if (p == NULL) return true; if (*p == '/') { cached_realpath(p, fname); /* clean it up */ if (has_any_prefix(fname, &metaIgnorePaths)) { #ifdef DEBUG_META_MODE DEBUG1(META, "meta_oodate: ignoring path: %s\n", p); #endif return true; } } if (metaIgnorePatterns) { const char *expr; char *pm; /* * XXX: This variable is set on a target GNode but is not one of * the usual local variables. It should be deleted afterwards. * Ideally it would not be created in the first place, just like * in a .for loop. */ Var_Set(gn, ".p.", p); expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; (void)Var_Subst(expr, gn, VARE_WANTRES, &pm); /* TODO: handle errors */ if (pm[0] != '\0') { #ifdef DEBUG_META_MODE DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p); #endif free(pm); return true; } free(pm); } if (metaIgnoreFilter) { char *fm; /* skip if filter result is empty */ snprintf(fname, sizeof fname, "${%s:L:${%s:ts:}}", p, MAKE_META_IGNORE_FILTER); (void)Var_Subst(fname, gn, VARE_WANTRES, &fm); /* TODO: handle errors */ if (*fm == '\0') { #ifdef DEBUG_META_MODE DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p); #endif free(fm); return true; } free(fm); } return false; } /* * When running with 'meta' functionality, a target can be out-of-date * if any of the references in its meta data file is more recent. * We have to track the latestdir on a per-process basis. */ #define LCWD_VNAME_FMT ".meta.%d.lcwd" #define LDIR_VNAME_FMT ".meta.%d.ldir" /* * It is possible that a .meta file is corrupted, * if we detect this we want to reproduce it. * Setting oodate true will have that effect. */ #define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \ warnx("%s: %u: malformed", fname, lineno); \ oodate = true; \ continue; \ } #define DEQUOTE(p) if (*p == '\'') { \ char *ep; \ p++; \ if ((ep = strchr(p, '\'')) != NULL) \ *ep = '\0'; \ } static void append_if_new(StringList *list, const char *str) { StringListNode *ln; for (ln = list->first; ln != NULL; ln = ln->next) if (strcmp(ln->datum, str) == 0) return; Lst_Append(list, bmake_strdup(str)); } /* A "reserved" variable to store the command to be filtered */ #define META_CMD_FILTER_VAR ".MAKE.cmd_filtered" static char * meta_filter_cmd(GNode *gn, char *s) { Var_Set(gn, META_CMD_FILTER_VAR, s); Var_Subst("${" META_CMD_FILTER_VAR ":${" MAKE_META_CMP_FILTER ":ts:}}", gn, VARE_WANTRES, &s); return s; } static int meta_cmd_cmp(GNode *gn, char *a, char *b, bool filter) { int rc; rc = strcmp(a, b); if (rc == 0 || !filter) return rc; a = meta_filter_cmd(gn, a); b = meta_filter_cmd(gn, b); rc = strcmp(a, b); free(a); free(b); Var_Delete(gn, META_CMD_FILTER_VAR); return rc; } bool meta_oodate(GNode *gn, bool oodate) { static char *tmpdir = NULL; static char cwd[MAXPATHLEN]; char lcwd_vname[64]; char ldir_vname[64]; char lcwd[MAXPATHLEN]; char latestdir[MAXPATHLEN]; char fname[MAXPATHLEN]; char fname1[MAXPATHLEN]; char fname2[MAXPATHLEN]; char fname3[MAXPATHLEN]; FStr dname; const char *tname; char *p; char *link_src; char *move_target; static size_t cwdlen = 0; static size_t tmplen = 0; FILE *fp; bool needOODATE = false; StringList missingFiles; bool have_filemon = false; bool cmp_filter; if (oodate) return oodate; /* we're done */ dname = Var_Value(gn, ".OBJDIR"); tname = GNode_VarTarget(gn); /* if this succeeds fname3 is realpath of dname */ if (!meta_needed(gn, dname.str, fname3, false)) goto oodate_out; dname.str = fname3; Lst_Init(&missingFiles); /* * We need to check if the target is out-of-date. This includes * checking if the expanded command has changed. This in turn * requires that all variables are set in the same way that they * would be if the target needs to be re-built. */ GNode_SetLocalVars(gn); meta_name(fname, sizeof fname, dname.str, tname, dname.str); #ifdef DEBUG_META_MODE DEBUG1(META, "meta_oodate: %s\n", fname); #endif if ((fp = fopen(fname, "r")) != NULL) { static char *buf = NULL; static size_t bufsz; unsigned lineno = 0; int lastpid = 0; int pid; int x; StringListNode *cmdNode; struct cached_stat cst; if (buf == NULL) { bufsz = 8 * BUFSIZ; buf = bmake_malloc(bufsz); } if (cwdlen == 0) { if (getcwd(cwd, sizeof cwd) == NULL) err(1, "Could not get current working directory"); cwdlen = strlen(cwd); } strlcpy(lcwd, cwd, sizeof lcwd); strlcpy(latestdir, cwd, sizeof latestdir); if (tmpdir == NULL) { tmpdir = getTmpdir(); tmplen = strlen(tmpdir); } /* we want to track all the .meta we read */ Global_Append(".MAKE.META.FILES", fname); cmp_filter = metaCmpFilter || Var_Exists(gn, MAKE_META_CMP_FILTER); cmdNode = gn->commands.first; while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { lineno++; if (buf[x - 1] == '\n') buf[x - 1] = '\0'; else { warnx("%s: %u: line truncated at %u", fname, lineno, x); oodate = true; break; } link_src = NULL; move_target = NULL; /* Find the start of the build monitor section. */ if (!have_filemon) { if (strncmp(buf, "-- filemon", 10) == 0) { have_filemon = true; continue; } if (strncmp(buf, "# buildmon", 10) == 0) { have_filemon = true; continue; } } /* Delimit the record type. */ p = buf; #ifdef DEBUG_META_MODE DEBUG3(META, "%s: %u: %s\n", fname, lineno, buf); #endif strsep(&p, " "); if (have_filemon) { /* * We are in the 'filemon' output section. * Each record from filemon follows the general form: * * * * Where: * is a single letter, denoting the syscall. * is the process that made the syscall. * is the arguments (of interest). */ switch(buf[0]) { case '#': /* comment */ case 'V': /* version */ break; default: /* * We need to track pathnames per-process. * * Each process run by make starts off in the 'CWD' * recorded in the .meta file, if it chdirs ('C') * elsewhere we need to track that - but only for * that process. If it forks ('F'), we initialize * the child to have the same cwd as its parent. * * We also need to track the 'latestdir' of * interest. This is usually the same as cwd, but * not if a process is reading directories. * * Each time we spot a different process ('pid') * we save the current value of 'latestdir' in a * variable qualified by 'lastpid', and * re-initialize 'latestdir' to any pre-saved * value for the current 'pid' and 'CWD' if none. */ CHECK_VALID_META(p); pid = atoi(p); if (pid > 0 && pid != lastpid) { FStr ldir; if (lastpid > 0) { /* We need to remember these. */ Global_Set(lcwd_vname, lcwd); Global_Set(ldir_vname, latestdir); } 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(SCOPE_GLOBAL, ldir_vname); if (ldir.str != NULL) { strlcpy(latestdir, ldir.str, sizeof latestdir); FStr_Done(&ldir); } ldir = Var_Value(SCOPE_GLOBAL, lcwd_vname); if (ldir.str != NULL) { strlcpy(lcwd, ldir.str, sizeof lcwd); FStr_Done(&ldir); } } /* Skip past the pid. */ if (strsep(&p, " ") == NULL) continue; #ifdef DEBUG_META_MODE if (DEBUG(META)) debug_printf("%s: %u: %d: %c: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, pid, buf[0], cwd, lcwd, latestdir); #endif break; } CHECK_VALID_META(p); /* Process according to record type. */ switch (buf[0]) { case 'X': /* eXit */ Var_Delete(SCOPE_GLOBAL, lcwd_vname); Var_Delete(SCOPE_GLOBAL, ldir_vname); lastpid = 0; /* no need to save ldir_vname */ break; case 'F': /* [v]Fork */ { char cldir[64]; int child; child = atoi(p); if (child > 0) { snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child); Global_Set(cldir, lcwd); snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child); Global_Set(cldir, latestdir); #ifdef DEBUG_META_MODE if (DEBUG(META)) debug_printf( "%s: %u: %d: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, child, cwd, lcwd, latestdir); #endif } } break; case 'C': /* Chdir */ /* Update lcwd and latest directory. */ strlcpy(latestdir, p, sizeof latestdir); strlcpy(lcwd, p, sizeof lcwd); Global_Set(lcwd_vname, lcwd); Global_Set(ldir_vname, lcwd); #ifdef DEBUG_META_MODE DEBUG4(META, "%s: %u: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); #endif break; case 'M': /* renaMe */ /* * For 'M'oves we want to check * the src as for 'R'ead * and the target as for 'W'rite. */ { 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); /* FALLTHROUGH */ case 'D': /* unlink */ if (*p == '/') { /* remove any missingFiles entries that match p */ 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); } ln = next; } } if (buf[0] == 'M') { /* the target of the mv is a file 'W'ritten */ #ifdef DEBUG_META_MODE DEBUG2(META, "meta_oodate: M %s -> %s\n", p, move_target); #endif p = move_target; goto check_write; } break; case 'L': /* Link */ /* * For 'L'inks check * the src as for 'R'ead * and the target as for 'W'rite. */ link_src = p; /* now get target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); /* 'L' and 'M' put single quotes around the args */ DEQUOTE(p); DEQUOTE(link_src); #ifdef DEBUG_META_MODE DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p); #endif /* FALLTHROUGH */ case 'W': /* Write */ check_write: /* * If a file we generated within our bailiwick * but outside of .OBJDIR is missing, * we need to do it again. */ /* ignore non-absolute paths */ if (*p != '/') break; if (Lst_IsEmpty(&metaBailiwick)) break; /* ignore cwd - normal dependencies handle those */ if (strncmp(p, cwd, cwdlen) == 0) break; if (!has_any_prefix(p, &metaBailiwick)) break; /* tmpdir might be within */ if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) break; /* ignore anything containing the string "tmp" */ /* XXX: The arguments to strstr must be swapped. */ 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); } break; check_link_src: p = link_src; link_src = NULL; #ifdef DEBUG_META_MODE DEBUG1(META, "meta_oodate: L src %s\n", p); #endif /* FALLTHROUGH */ case 'R': /* Read */ case 'E': /* Exec */ /* * Check for runtime files that can't * be part of the dependencies because * they are _expected_ to change. */ if (meta_ignore(gn, p)) break; /* * The rest of the record is the file name. * Check if it's not an absolute path. */ { char *sdirs[4]; char **sdp; int sdx = 0; bool found = false; if (*p == '/') { sdirs[sdx++] = p; /* done */ } else { if (strcmp(".", p) == 0) continue; /* no point */ /* Check vs latestdir */ - snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p); - sdirs[sdx++] = fname1; + if (snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p) < (int)(sizeof fname1)) + sdirs[sdx++] = fname1; if (strcmp(latestdir, lcwd) != 0) { /* Check vs lcwd */ - snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p); - sdirs[sdx++] = fname2; + if (snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p) < (int)(sizeof fname2)) + sdirs[sdx++] = fname2; } if (strcmp(lcwd, cwd) != 0) { /* Check vs cwd */ - snprintf(fname3, sizeof fname3, "%s/%s", cwd, p); - sdirs[sdx++] = fname3; + if (snprintf(fname3, sizeof fname3, "%s/%s", cwd, p) < (int)(sizeof fname3)) + sdirs[sdx++] = fname3; } } sdirs[sdx++] = NULL; for (sdp = sdirs; *sdp != NULL && !found; sdp++) { #ifdef DEBUG_META_MODE DEBUG3(META, "%s: %u: looking for: %s\n", fname, lineno, *sdp); #endif if (cached_stat(*sdp, &cst) == 0) { found = true; p = *sdp; } } if (found) { #ifdef DEBUG_META_MODE DEBUG3(META, "%s: %u: found: %s\n", fname, lineno, p); #endif if (!S_ISDIR(cst.cst_mode) && cst.cst_mtime > gn->mtime) { DEBUG3(META, "%s: %u: file '%s' is newer than the target...\n", fname, lineno, p); oodate = true; } else if (S_ISDIR(cst.cst_mode)) { /* Update the latest directory. */ cached_realpath(p, latestdir); } } else if (errno == ENOENT && *p == '/' && strncmp(p, cwd, cwdlen) != 0) { /* * A referenced file outside of CWD is missing. * We cannot catch every eventuality here... */ append_if_new(&missingFiles, p); } } if (buf[0] == 'E') { /* previous latestdir is no longer relevant */ strlcpy(latestdir, lcwd, sizeof latestdir); } break; default: break; } if (!oodate && buf[0] == 'L' && link_src != NULL) goto check_link_src; } else if (strcmp(buf, "CMD") == 0) { /* * Compare the current command with the one in the * meta data file. */ if (cmdNode == NULL) { DEBUG2(META, "%s: %u: there were more build commands in the meta data file than there are now...\n", fname, lineno); oodate = true; } else { const char *cp; char *cmd = cmdNode->datum; bool hasOODATE = false; if (strstr(cmd, "$?") != NULL) hasOODATE = true; else if ((cp = strstr(cmd, ".OODATE")) != NULL) { /* check for $[{(].OODATE[:)}] */ if (cp > cmd + 2 && cp[-2] == '$') hasOODATE = true; } if (hasOODATE) { needOODATE = true; DEBUG2(META, "%s: %u: cannot compare command using .OODATE\n", fname, lineno); } (void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd); /* TODO: handle errors */ if ((cp = strchr(cmd, '\n')) != NULL) { int n; /* * This command contains newlines, we need to * fetch more from the .meta file before we * attempt a comparison. */ /* first put the newline back at buf[x - 1] */ buf[x - 1] = '\n'; do { /* now fetch the next line */ if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) break; x = n; lineno++; if (buf[x - 1] != '\n') { warnx("%s: %u: line truncated at %u", fname, lineno, x); break; } cp = strchr(cp + 1, '\n'); } while (cp != NULL); if (buf[x - 1] == '\n') buf[x - 1] = '\0'; } if (p != NULL && !hasOODATE && !(gn->type & OP_NOMETA_CMP) && (meta_cmd_cmp(gn, p, cmd, cmp_filter) != 0)) { DEBUG4(META, "%s: %u: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); if (!metaIgnoreCMDs) oodate = true; } free(cmd); cmdNode = cmdNode->next; } } else if (strcmp(buf, "CWD") == 0) { /* * Check if there are extra commands now * that weren't in the meta data file. */ if (!oodate && cmdNode != NULL) { DEBUG2(META, "%s: %u: there are extra build commands now that weren't in the meta data file\n", fname, lineno); oodate = true; } CHECK_VALID_META(p); if (strcmp(p, cwd) != 0) { DEBUG4(META, "%s: %u: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); oodate = true; } } } fclose(fp); if (!Lst_IsEmpty(&missingFiles)) { DEBUG2(META, "%s: missing files: %s...\n", fname, (char *)missingFiles.first->datum); oodate = true; } if (!oodate && !have_filemon && filemonMissing) { DEBUG1(META, "%s: missing filemon data\n", fname); oodate = true; } } else { if (writeMeta && (metaMissing || (gn->type & OP_META))) { const char *cp = NULL; /* if target is in .CURDIR we do not need a meta file */ 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 */ } } if (cp == NULL) { DEBUG1(META, "%s: required but missing\n", fname); oodate = true; needOODATE = true; /* assume the worst */ } } } Lst_DoneCall(&missingFiles, free); if (oodate && needOODATE) { /* * Target uses .OODATE which is empty; or we wouldn't be here. * We have decided it is oodate, so .OODATE needs to be set. * All we can sanely do is set it to .ALLSRC. */ Var_Delete(gn, OODATE); Var_Set(gn, OODATE, GNode_VarAllsrc(gn)); } oodate_out: FStr_Done(&dname); return oodate; } /* support for compat mode */ static int childPipe[2]; void meta_compat_start(void) { #ifdef USE_FILEMON_ONCE /* * We need to re-open filemon for each cmd. */ BuildMon *pbm = &Mybm; if (pbm->mfp != NULL && useFilemon) { meta_open_filemon(pbm); } else { pbm->mon_fd = -1; pbm->filemon = NULL; } #endif if (pipe(childPipe) < 0) Punt("Cannot create pipe: %s", strerror(errno)); /* Set close-on-exec flag for both */ (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC); (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC); } void meta_compat_child(void) { meta_job_child(NULL); if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0) execDie("dup2", "pipe"); } void meta_compat_parent(pid_t child) { int outfd, metafd, maxfd, nfds; char buf[BUFSIZ+1]; fd_set readfds; meta_job_parent(NULL, child); close(childPipe[1]); /* child side */ outfd = childPipe[0]; #ifdef USE_FILEMON metafd = Mybm.filemon != NULL ? filemon_readfd(Mybm.filemon) : -1; #else metafd = -1; #endif maxfd = -1; if (outfd > maxfd) maxfd = outfd; if (metafd > maxfd) maxfd = metafd; while (outfd != -1 || metafd != -1) { FD_ZERO(&readfds); if (outfd != -1) { FD_SET(outfd, &readfds); } if (metafd != -1) { FD_SET(metafd, &readfds); } nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL); if (nfds == -1) { if (errno == EINTR) continue; err(1, "select"); } 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) err(1, "read"); if (nread == 0) { close(outfd); outfd = -1; break; } fwrite(buf, 1, (size_t)nread, stdout); fflush(stdout); buf[nread] = '\0'; meta_job_output(NULL, buf, ""); } while (false); if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) { if (meta_job_event(NULL) <= 0) metafd = -1; } } } #endif /* USE_META */ diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog index b1a8313b929d..92606f6bfbe5 100644 --- a/contrib/bmake/mk/ChangeLog +++ b/contrib/bmake/mk/ChangeLog @@ -1,2014 +1,2046 @@ +2023-01-20 Simon J Gerraty + + * install-mk (MK_VERSION): 20230120 + + * sys.vars.mk: add M_On and M_Onr also cleanup to be more + consistent wrt testing MAKE_VERSION + +2023-01-12 Simon J Gerraty + + * install-mk (MK_VERSION): 20230112 + + * meta2deps.{py,sh}: assert if filemon data is truncated + we should see the '# Bye bye' record - assert if we do not. + +2022-09-09 Simon J Gerraty + + * install-mk (MK_VERSION): 20220909 + + * sys/Linux.mk set EGREP to grep -E to avoid deprecation warnings + +2022-09-06 Simon J Gerraty + + * dirdeps-options.mk: explain the need to use + ${DEP_${TARGET_SPEC_VAR}:U${TARGET_SPEC_VAR}} when refering to + ${TARGET_SPEC_VAR} + +2022-09-03 Simon J Gerraty + + * install-mk (MK_VERSION): 20220903 + + * M_cmpv handle more than 3 dots and clear leading 0's + 2022-07-20 Simon J Gerraty * install-mk (MK_VERSION): 20220720 * prog.mk: handle PROG_CXX for more than just NetBSD 2022-06-20 Simon J Gerraty * install-mk (MK_VERSION): 20220620 * yacc.mk: when we have *.y in SRCS used explicit rules and .ORDER rather than just suffix rules 2022-04-23 Simon J Gerraty * install-mk (MK_VERSION): 20220422 * gendirdeps.mk: If LOCAL_DEPENDS_GUARD is set to "no" do not capture any local depends in Makefile.depend 2022-03-25 Simon J Gerraty * install-mk (MK_VERSION): 20220323 * posix.mk: default rules for .POSIX: 2022-03-17 Simon J Gerraty * sys/*.mk: remove l from ARFLAGS 2022-03-14 Simon J Gerraty * install-mk (MK_VERSION): 20220314 * dirdeps-options.mk: allow options to be per RELDIR try DIRDEPS_OPTIONS_QUALIFIER_LIST first prefixed with ${DEP_RELDIR}. 2022-02-14 Simon J Gerraty * install-mk (MK_VERSION): 20220214 * cc-wrap.mk: fix :@ modifier 2022-02-06 Simon J Gerraty * install-mk (MK_VERSION): 20220206 * cc-wrap.mk: docuement how CCACHE etc might be set for maximum flexibility 2022-02-05 Simon J Gerraty * sys.vars.mk: use JOT_CMD (jot or seq) if available for M_JOT 2022-02-04 Simon J Gerraty * install-mk (MK_VERSION): 20220204 * host-target.mk: use .MAKE.OS if available 2022-02-02 Simon J Gerraty * install-mk (MK_VERSION): 20220202 * cc-wrap.mk: allow other entries in CC_WRAP_FILTER We add our filter on extensions last, so prior filters can apply to the whole value of .IMPSRC 2022-02-01 Simon J Gerraty * cc-wrap.mk: take advantage of target local variables to wrap compilers like CC CXX with wrappers like ccache distcc etc 2022-01-28 Simon J Gerraty * meta2deps: we do not expect any trace data for setid apps 2022-01-26 Simon J Gerraty * dirdeps.mk: ensure TARGET_SPEC and TARGET_SPEC_VARS are passed to sub-make using DIRDEPS_CACHE 2022-01-07 Simon J Gerraty * dirdeps.mk: use _cache_script to minimize the number of shells forked when generating dirdeps.cache 2022-01-02 Simon J Gerraty * install-mk (MK_VERSION): 20220101 * dirdeps.mk: initialize DEP_* and _debug_reldir earlier. If initial DIRDEPS are from command line, create the target _dirdeps_cmdline as an indication. 2022-01-01 Simon J Gerraty * init.mk (_SKIP_BUILD): when doing DIRDEPS_BUILD at top-level only some targets are allowed at level 0, for leaf makefiles only the default (all) target is restricted 2021-12-28 Simon J Gerraty * install-mk (MK_VERSION): 20211228 * meta2deps.py: filemon on Linux is not as reliable as we might like, we do not want to update DIRDEPS if filemon output is incomplete. Track pids that we 'E'xec and make sure we see an e'X'it for each one. Throw an error if we are missing any 'X' records. 2021-12-12 Simon J Gerraty * sys.mk: simplify; include meta.sys.mk if MK_META_MODE is yes. * meta.sys.mk: do not check for /dev/filemon if .MAKE.PATH_FILEMON is something else. * meta.autodep.mk: we can now reference ${.SUFFIXES} * meta2deps.py: derive a list of dirdep extensions from TARGET_SPEC to trim from dirdeps. * dirdeps.mk: flip the computation of qualified vs unqualified dirdeps - it is much simpler to check for unqualified first. 2021-12-11 Simon J Gerraty * install-mk (MK_VERSION): 20211212 * auto.dep.mk: rearrange so that the trivial implementation for recent bmake is more obvious. 2021-12-07 Simon J Gerraty * install-mk (MK_VERSION): 20211207 * Ensure guard targets are .NOTMAIN * meta.sys.mk: check for nofilemon support when we skip level 0 * auto.dep.mk: make this usable in meta mode for platforms that cannot use meta.autodep.mk * meta2deps.py: avoid confusion if MACHINE and another TARGET_SPEC_VAR have same value. 2021-11-27 Simon J Gerraty * dirdeps.mk: when building dirdeps.cache, minimize the amount of data put into env, by stripping ${SRCTOP}/ from each entry. A long sandbox name can double the amount of memory consumed and in extreme cases cause failure. While we are at it, strip ${SRCTOP}/ from a lot of the debug output. 2021-11-11 Simon J Gerraty * install-mk (MK_VERSION): 20211111 * meta.stage.mk (LN_CP_SCRIPT): if staging to NFS cp -p can fail so fallback to cp if necessary. 2021-10-30 Simon J Gerraty * man.mk (CMT2DOC): use cmt2doc.py rather than the 30 year old cmt2doc.pl 2021-10-24 Simon J Gerraty * meta.stage.mk: stage_as_and_symlink use ${STAGE_LINK_AS_$f:U$f} as the symlink (rare) 2021-10-16 Simon J Gerraty * autoconf.mk: if AUTOCONF_GENERATED_MAKEFILE is set and has not been read, throw an error after running configure telling user to restart. 2021-10-13 Simon J Gerraty * install-mk (MK_VERSION): 20211011 * Add support for SCO_SV 2021-10-01 Simon J Gerraty * install-mk (MK_VERSION): 20211001 * man.mk: use MAN_SUFFIXES and CMT2DOC_SUFFIXES for more flexibility 2021-09-13 Simon J Gerraty * options.mk (describe-options): print options and their values and optional description 2021-09-11 Simon J Gerraty * install-mk (MK_VERSION): 20210911 * options.mk (show-options): print options and their values 2021-09-08 Simon J Gerraty * install-mk (MK_VERSION): 20210909 * lib.mk: apply patch from to fix shared libs on Linux 2021-08-08 Simon J Gerraty * install-mk (MK_VERSION): 20210808 * options.mk: issue warning for WITH_*=no 2021-06-16 Simon J Gerraty * install-mk (MK_VERSION): 20210616 * dirdeps.mk: when using .MAKE.DEPENDFILE_PREFERENCE to find depend files to read, anchor MACHINE at , or end of string to avoid prefix match. 2021-05-04 Simon J Gerraty * install-mk (MK_VERSION): 20210504 * dirdeps.mk: re-implement ALL_MACHINES support to better cater for local complexities, when ONLY_TARGET_SPEC_LIST is not set. local.dirdeps.mk can set DIRDEPS_ALL_MACHINES_FILTER and/or DIRDEPS_ALL_MACHINES_FILTER_XTRAS to filter the results we get from listing all existing Makefile.depend.* 2021-04-20 Simon J Gerraty * install-mk (MK_VERSION): 20210420 * dirdeps.mk: revert previous - not always safe. 2021-03-20 Simon J Gerraty * install-mk (MK_VERSION): 20210321 * dirdeps.mk: when generating dirdeps.cache we only need to hook the initial DIRDEPS to the dirdeps target. That and any _build_xtra_dirs (like tests which should not be hooked directly to the dependency graph - to avoid cycles) 2021-01-30 Simon J Gerraty * install-mk (MK_VERSION): 20210130 * dirdeps.mk: expr 2 - 1 - 1 exits with a bad status we need to guard against this in DIRDEP_LOADAVG_REPORT. * dirdeps.mk: restore respect for TARGET_MACHINE 2021-01-06 Simon J Gerraty * 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 * sys.mk (MAKE_SHELL): use ${.SHELL:Ush} and use := when setting SHELL 2020-12-21 Simon J Gerraty * install-mk (MK_VERSION): 20201221 * dirdeps-options.mk: latest bmake allows only one arg to .undef 2020-12-11 Simon J Gerraty * 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 * 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 * 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 * install-mk (MK_VERSION): 20201106 * meta.autodep.mk: use OBJ_EXTENSIONS rather than hardcode sed args to tweak extensions for local deps. 2020-11-01 Simon J Gerraty * install-mk (MK_VERSION): 20201101 * dirdeps.mk: most leaf makefiles are not suitable for building dirdeps.cache so if RELDIR is not "." use dirdeps.mk 2020-10-28 Simon J Gerraty * install-mk (MK_VERSION): 20201028 * dirdeps.mk: if we don't have :range use equivalent of M_RANGE when building dirdeps.cache for leaf directory use -f dirdeps.mk * sys.vars.mk: add M_JOT and M_RANGE 2020-10-01 Simon J Gerraty * install-mk (MK_VERSION): 20201001 * meta2deps.{py,sh}: throw an error if we don't see filemon version 2020-09-09 Simon J Gerraty * install-mk (MK_VERSION): 20200909 * dirdeps-cache-update.mk: use cache_update_dirdep as guard target 2020-08-26 Simon J Gerraty * dirdeps.mk: ensure we cannot confuse a static cache for dynamic (even more rare that use of static cache is playing clever tricks with it) 2020-08-16 Simon J Gerraty * dirdeps-cache-update.mk: allow MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE to control when we actually update STATIC_DIRDEPS_CACHE. * stage-install.sh: create dest directory if needed before running install(1) 2020-08-10 Simon J Gerraty * dirdeps-targets.mk: include Makefile.dirdeps.options * dirdeps.mk: use _TARGETS if defined for DIRDEPS_CACHE 2020-08-09 Simon J Gerraty * dirdeps.mk: default BUILD_DIRDEPS_MAKEFILE to empty * dirdeps-cache-update.mk: building parallel cache update under the context of dirdeps-cached would be ideal, but is problematic, so it runs as a sibling. Use cache-built target to ensure we wait for it to complete if necessary. 2020-08-06 Simon J Gerraty * install-mk (MK_VERSION): 20200806 * dirdeps-options: allow TARGET_SPEC to affect option values. Use DIRDEPS_OPTIONS_QUALIFIER_LIST before using bare MK_* * dirdeps-targets.mk: check for MK_STATIC_DIRDEPS_CACHE defined before looking for STATIC_DIRDEPS_CACHE 2020-08-05 Simon J Gerraty * host-target.mk: Darwin use MACHINE for HOST_ARCH too * dirdeps-options.mk: improve debug output 2020-07-22 Simon J Gerraty * dirdeps.mk: set and export DYNAMIC_DIRDEPS_CACHE for use by dirdeps-cache-update.mk * dirdeps-targets.mk: set and export STATIC_DIRDEPS_CACHE for use by dirdeps-cache-update.mk even if we don't use it. * dirdeps-cache-update.mk: we only need worry about the background update case, with the above, the update from DIRDEPS_CACHE is simple. * meta2deps.py: R 1234 . is not interesting 2020-07-20 Simon J Gerraty * sys.mk: default MK_STATIC_DIRDEPS_CACHE from MK_DIRDEPS_CACHE * dirdeps-options.mk: do not :tu DIRDEPS_OPTIONS allows use of lower case for pseudo options. * dirdeps-cache-update.mk: magic to deal with STATIC_DIRDEPS_CACHE 2020-07-18 Simon J Gerraty * dirdeps-targets.mk: Look for Makefile.dirdeps.cache which allows us to have a static cache for expensive targets. Use -DWITHOUT_STATIC_DIRDEPS_CACHE -DWITH_DIRDEPS_CACHE to regenerate the dirdeps.cache it is a copy of. 2020-07-17 Simon J Gerraty * Get rid of BUILD_AT_LEVEL0, MK_DIRDEPS_BUILD makes more sense. 2020-07-16 Simon J Gerraty * dirdeps.mk (DIRDEP_LOADAVG_REPORT): make it easy to record load averages at intervals during build. 2020-07-15 Simon J Gerraty * install-mk (MK_VERSION): 20200715 * dirdeps.mk: tweak Checking line to make matching Finished lines for post-build analysis easier. * meta.autodep.mk: use !defined(WITHOUT_META_STATS) * progs.mk: avoid prog.mk outputting multiple Finished lines 2020-07-11 Simon J Gerraty * dirdeps.mk: further optimize dirdeps.cache generate a DIRDEPS.${.TARGET} list for other purposes and improve the layout. 2020-07-10 Simon J Gerraty * dirdeps.mk: optimize content of dirdeps.cache 2020-06-28 Simon J Gerraty * sys/*.mk: make it easier for local*sys.mk to customize by using ?= 2020-06-22 Simon J Gerraty * gendirdeps.mk (LOCAL_DEPENDS_GUARD): if we don't build at level 0 it is much safer to guard local depends with a simple check for .MAKE.LEVEL > 0 2020-06-10 Simon J Gerraty * install-mk (MK_VERSION): 20200610 * mkopt.sh: this needs posix shell so #!/bin/sh should be ok 2020-06-06 Simon J Gerraty * install-mk (MK_VERSION): 20200606 * dirdeps-targets.mk: allow for filtering of .TARGETS * meta2deps.py: fix bug in processing 'L'ink and 'M'ove entries - and we don't care about 'W'rite entries. Also ignore absolute paths that do not exist. 2020-05-25 Simon J Gerraty * install-mk (MK_VERSION): 20200525 * init.mk: expand and simplify handling of qualified vars like CPPFLAGS.${.TARGET:T} 2020-05-15 Simon J Gerraty * install-mk (MK_VERSION): 20200515 * dirdeps.mk: set _debug_* earlier and allow passing -d* flags to submake when building DIRDEPS_CACHE 2020-05-09 Simon J Gerraty * whats.mk: more easily extensible 2020-05-02 Simon J Gerraty * whats.mk: greatly simplify by adding what.c to SRCS 2020-05-01 Simon J Gerraty * whats.mk: for libs take care how we add to *OBJS * lib.mk: : works better with whats.mk 2020-04-25 Simon J Gerraty * install-mk (MK_VERSION): 20200420 * meta.stage.mk: it is not a STAGE_CONFLICT if some-target.dirdep contains the same ${RELDIR} and a prefix match for our ${TARGET_SPEC} 2020-04-16 Simon J Gerraty * install-mk (MK_VERSION): 20200416 * sys/*.mk: set MAKE_SHELL rather than SHELL so as not to interfere with user env. * sys.mk: default MAKE_SHELL to sh and SHELL to MAKE_SHELL * autodep.mk: use MAKE_SHELL. 2019-11-21 Simon J Gerraty * gendirdeps.mk: clear .SUFFIXES to avoid a lot of wasted effort, and unexport _meta_files when no longer needed as it consumes space we need for command line. 2019-11-11 Simon J Gerraty * dirdeps.mk _DIRDEP_USE: use DIRDEP_DIR and add DIRDEP_USE_PRELUDE at start - facilitates job distribution 2019-10-04 Simon J Gerraty * dirdeps-targets.mk: Use TARGET_SPEC_LAST_LIST defaults to ${${TARGET_SPEC_VARS:[-1]}_LIST} to match valid TARGET_SPEC qualified depend files. 2019-10-02 Simon J Gerraty * dirdeps-targets.mk: encapsulate logic for finding top-level targets to set initial DIRDEPS for DIRDEPS_BUILD 2019-09-27 Simon J Gerraty * install-mk (MK_VERSION): 20190911 * compiler.mk: set COMPILER_TYPE 2019-07-17 Simon J Gerraty * install-mk (MK_VERSION): 20190704 * sys/Darwin.mk: support for Objective-C and clang 2019-05-30 Simon J Gerraty * dirdeps.mk: avoid insanely long command line when generating cache 2019-05-23 Simon J Gerraty * install-mk (MK_VERSION): 20190505 * whats.mk: handle corner case SHLIB defined but not LIB 2018-09-19 Simon J Gerraty * install-mk (MK_VERSION): 20180919 * dirdeps-options.mk: .undef cannot handle var that expands to more than one var. 2018-07-08 Simon J Gerraty * meta.stage.mk: allow wildcards in STAGE_FILES.* etc. 2018-06-01 Simon J Gerraty * meta.autodep.mk: export META_FILES to avoid command line limit * gendirdeps.mk: if we have lots of .meta files put them in an @list 2018-05-28 Simon J Gerraty * dirdeps-options.mk: use local.dirdeps-options.mk not local.dirdeps-option.mk 2018-04-20 Simon J Gerraty * install-mk (MK_VERSION): 20180420 * dirdeps.mk: include local.dirdeps-build.mk when .MAKE.LEVEL > 0 ie. we are building something. 2018-04-14 Simon J Gerraty * FILES: add dirdeps-options.mk to deal with optional DIRDEPS. 2018-04-05 Simon J Gerraty * install-mk (MK_VERSION): 20180405 * ldorder.mk: describe how to use LDORDER_EXTERN_BARRIER if needed. 2018-01-18 Simon J Gerraty * install-mk (MK_VERSION): 20180118 * ldorder.mk: let make compute correct link order 2017-12-12 Simon J Gerraty * install-mk (MK_VERSION): 20171212 * gendirdeps.mk: guard against bogus entries in GENDIRDEPS_FILTER 2017-11-14 Simon J. Gerraty * install-mk (MK_VERSION): 20171111 * lib.mk: ensure META_NOECHO is set 2017-10-25 Simon J. Gerraty * Allow for host32 on rare occasions. 2017-10-18 Simon J. Gerraty * install-mk (MK_VERSION): 20171018 * whats.mk: include what_thing in what_uuid to avoid problem when building multiple apps in the same directory. 2017-08-12 Simon J. Gerraty * install-mk (MK_VERSION): 20170812 * autoconf.mk: Use CONFIGURE_DEPS so Makefile can add dependencies for config.recheck and config.gen 2017-06-30 Simon J. Gerraty * install-mk (MK_VERSION): 20170630 * meta.stage.mk: avoid triggering stage_* targets with nothing to do. 2017-05-23 Simon J. Gerraty * meta2deps.py: take special care of '..' 2017-05-15 Simon J. Gerraty * install-mk (MK_VERSION): 20170515 * dirdeps.mk (DEP_EXPORT_VARS): on rare occasions it is useful/necessary for a Makefile.depend file to export some knobs. This is complicated when we are doing DIRDEPS_CACHE, so we will handle export of any variables listed in DEP_EXPORT_VARS. 2017-05-08 Simon J. Gerraty * install-mk (MK_VERSION): 20170505 * meta2deps.py: fix botched indenation. 2017-05-05 Simon J. Gerraty * sys/*.mk: Remove setting of MAKE it is unnecessary and in many cases wrong (basname rather than full path) * scripts.mk (SCRIPTSGROUPS): make this more like files.mk and inc.mk * init.mk: define realbuild to simplify logic in {lib,prog}.mk etc 2017-05-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170501 * doc.mk: fix typo in DOC_INSTALL_OWN * inc.mk: handle INCGROUPS similar to freebsd * files.mk: add something for files too * add staging logic to lib.mk prog.mk etc. 2017-04-24 Simon J. Gerraty * install-mk (MK_VERSION): 20170424 * dirdeps.mk: set NO_DIRDEPS when bootstrapping. also target of bootstrap-this when sed is needed should be ${_want:T} 2017-04-18 Simon J. Gerraty * install-mk (MK_VERSION): 20170418 * auto.obj.mk: if using MAKEOBJDIRPREFIX check if it is a prefix match for .CURDIR - in which case .CURDIR *is* __objdir. 2017-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170401 * meta2deps.py: add is_src so we can check if obj dependency is also a src dependency. 2017-03-26 Simon J. Gerraty * install-mk (MK_VERSION): 20170326 * meta.stage.mk: do nothing if NO_STAGING is defined. 2017-03-24 Simon J. Gerraty * auto.obj.mk: handle the case of __objdir=obj or obj.${MACHINE} etc. 2017-03-18 Simon J. Gerraty * mkopt.sh: treat WITH_*=NO like no; ie. WITHOUT_* 2017-03-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170301 * dirdeps.mk (_build_all_dirs): update this outside test for empty DIRDEPS. * meta.stage.mk: allow multiple inclusion to the extent it makes sense. 2017-02-14 Simon J. Gerraty * prog.mk (install_links): depends on realinstall 2017-02-12 Simon J. Gerraty * install-mk (MK_VERSION): 20170212 * dpadd.mk: avoid applying :T:R twice to DPLIBS entries 2017-01-30 Simon J. Gerraty * install-mk (MK_VERSION): 20170130 * dirdeps.mk: use :range if we can. * sys.vars.mk: provide M_cmpv if MAKE_VERSION >= 20170130 * meta2deps.py: clean paths without using realpath() where possible. fix sort_unique. 2016-12-12 Simon J. Gerraty * install-mk (MK_VERSION): 20161212 * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir, rather than when we detect pid change. 2016-12-07 Simon J. Gerraty * install-mk (MK_VERSION): 20161207 * meta.stage.mk: add stage_as_and_symlink for staging packages. We build foo.tgz stage_as foo-${VERSION}.tgz but want to be able to use foo.tgz to reference the latest staged version - so we make foo.tgz a symlink to it. Using a target to do both operations ensures we stay in sync. 2016-11-26 Simon J. Gerraty * install-mk (MK_VERSION): 20161126 * dirdeps.mk: set DIRDEPS_CACHE before we include local.dirdeps.mk so it can add dependencies. 2016-10-10 Simon J. Gerraty * dirdeps.mk: set DEP_* before we expand .MAKE.DEPENDFILE_PREFERENCE do that they can influence the result correctly. * dirdeps.mk (${DIRDEPS_CACHE}): make sure we pass on TARGET_SPEC * dirdeps.mk: Add ONLY_TARGET_SPEC_LIST and NOT_TARGET_SPEC_LIST similar to ONLY_MACHINE_LIST and NOT_MACHINE_LIST 2016-10-05 Simon J. Gerraty * dirdeps.mk: remove dependence on jot (normal situations anyway). Before we read another Makefile.depend* set DEP_* vars from _DEP_TARGET_SPEC in case it uses any of them with := When bootstrapping, trim any ,* from extention of chosen _src Makefile.depend* to get the machine value we subst for. 2016-09-30 Simon J. Gerraty * dirdeps.mk: use TARGET_SPEC_VARS to qualify components added to DEP_SKIP_DIR and DEP_DIRDEPS_FILTER * sys.mk: extract some bits to sys.{debug,vars}.mk for easier re-use by others. 2016-09-23 Simon Gerraty * lib.mk: Use ${PICO} for extension for PIC objects. default to .pico (like NetBSD) safe on case insensitive filesystem. 2016-08-19 Simon J. Gerraty * meta.sys.mk (META_COOKIE_TOUCH): use ${.OBJDIR}/${.TARGET:T} as default 2016-08-15 Simon J. Gerraty * install-mk (MK_VERSION): 20160815 * dirdeps.mk (.MAKE.META.IGNORE_FILTER): set filter to only consider Makefile.depend* when checking if DIRDEPS_CACHE is up-to-date. 2016-08-13 Simon J. Gerraty * meta.sys.mk (.MAKE.META.IGNORE_PATHS): in meta mode we can ignore the mtime of makefiles 2016-08-02 Simon J. Gerraty * install-mk (MK_VERSION): 20160802 * lib.mk (libinstall): depends on beforinstall * prog.mk (proginstall): depends on beforinstall patch from Lauri Tirkkonen * dirdeps.mk (bootstrap): When bootstrapping; creat .MAKE.DEPENDFILE_DEFAULT and allow additional filtering via .MAKE.DEPENDFILE_BOOTSTRAP_SED * dirdeps.mk: move some comments to where they make sense. 2016-07-27 Simon J. Gerraty * dirdeps.mk (DIRDEPS_CACHE): no dirname. 2016-06-02 Simon J. Gerraty * install-mk (MK_VERSION): 20160602 * meta.autodep.mk: when passing META_FILES to gendirdeps.mk do not apply :T to META_XTRAS patch from Bryan Drewery at FreeBSD.org. 2016-05-30 Simon J. Gerraty * install-mk (MK_VERSION): 20160530 * meta.stage.mk: we assume ${CLEANFILES} gets .NOPATH make it so. 2016-05-12 Simon J. Gerraty * install-mk (MK_VERSION): 20160512 * dpadd.mk: always include local.dpadd.mk if it exists remove some things that better belong in local.dpadd.mk skip INCLUDES_* for staged libs unless SRC_* defined. * own.mk: add INCLUDEDIR 2016-04-18 Simon J. Gerraty * dirdeps.mk: when doing -f dirdeps.mk if target suppies no TARGET_MACHINE - :E will be empty or match part of path, use ${MACHINE} 2016-04-07 Simon J. Gerraty * meta.autodep.mk: issue a warning if UPDATE_DEPENDFILE=NO due to NO_FILEMON_COOKIE * dirdeps.mk: move the logic that allows for make -f dirdeps.mk some/dir.${TARGET_SPEC} inside the check for !target(_DIRDEP_USE) 2016-04-04 Simon J. Gerraty * Use <> when including local*.mk and others which may exist elsewhere so that user can better control what they get. * meta.autodep.mk (NO_FILEMON_COOKIE): create a cookie if we ever build dir with nofilemon so that UPDATE_DEPENDFILE will be forced to NO until cleaned. 2016-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20160401 * meta2deps.py: fix old print statement when debugging. * gendirdeps.mk: META2DEPS_CMD append M2D_EXCLUDES with -X patch from Bryan Drewery 2016-03-22 Simon J. Gerraty * install-mk (MK_VERSION): 20160317 (St. Pats) * warnings.mk: g++ does not like -Wimplicit * sys.mk sys/*.mk lib.mk prog.mk: use CXX_SUFFIXES to handle the pelthora of common suffixes for C++ * lib.mk: use .So for shared objects 2016-03-15 Simon J. Gerraty * install-mk (MK_VERSION): 20160315 * meta.stage.mk (LN_CP_SCRIPT): do not ln(1) if we have to chmod(1) normally only applies to scripts. * dirdeps.mk: NO_DIRDEPS_BELOW to supress DIRDEPS below RELDIR as well as outside it. 2016-03-10 Simon J. Gerraty * install-mk (MK_VERSION): 20160310 * dirdeps.mk: use targets rather than a list to track DIRDEPS that we have processed; the list gets very inefficient as number of DIRDEPS gets large. * sys.dependfile.mk: fix comment wrt MACHINE * meta.autodep.mk: ignore staged DPADDs when bootstrapping. patch from Bryan Drewery 2016-03-02 Simon J. Gerraty * meta2deps.sh: don't ignore subdirs. patch from Bryan Drewery 2016-02-26 Simon J. Gerraty * install-mk (MK_VERSION): 20160226 * gendirdeps.mk: mark _DEPENDFILE .NOMETA 2016-02-20 Simon J. Gerraty * dirdeps.mk: we shouldn't normally include .depend but if we do use .dinclude if we can. 2016-02-18 Simon J. Gerraty * install-mk (MK_VERSION): 20160218 * sys.clean-env.mk: with recent change to Var_Subst() we cannot use the '$$' trick, but .export-literal does the job we need. * auto.dep.mk: make use .dinclude if we can. 2016-02-05 Simon J. Gerraty * dirdeps.mk: Add _build_all_dirs such that local.dirdeps.mk can add fully qualified dirs to it. These will be built normally but the current DEP_RELDIR will not depend on then (to avoid cycles). This makes it easy to hook things like unit-tests into build. 2016-01-21 Simon J. Gerraty * dirdeps.mk: add bootstrap-empty 2015-12-12 Simon J. Gerraty * install-mk (MK_VERSION): 20151212 * auto.obj.mk: do not require MAKEOBJDIRPREFIX to exist. only apply :tA to __objdir when comparing to .OBJDIR 2015-11-14 Simon J. Gerraty * install-mk (MK_VERSION): 20151111 * meta.sys.mk: include sys.dependfile.mk * sys.mk (OPTIONS_DEFAULT_NO): use options.mk to set MK_AUTO_OBJ and MK_DIRDEPS_BUILD include local.sys.env.mk early include local.sys.mk later * own.mk (OPTIONS_DEFAULT_NO): AUTO_OBJ etc moved to sys.mk 2015-11-13 Simon J. Gerraty * meta.sys.mk (META_COOKIE_TOUCH): add ${META_COOKIE_TOUCH} to the end of scripts to touch cookie * meta.stage.mk: stage_libs should ignore SYMLINKS. 2015-10-23 Simon J. Gerraty * install-mk (MK_VERSION): 20151022 * sys.mk: BSD/OS does not have 'type' as a shell builtin. 2015-10-20 Simon J. Gerraty * install-mk (MK_VERSION): 20151020 * dirdeps.mk: Add logic for make -f dirdeps.mk some/dir.${TARGET_SPEC} 2015-10-14 Simon J. Gerraty * install-mk (MK_VERSION): 20151010 2015-10-02 Simon J. Gerraty * meta.stage.mk: use staging: ${STAGE_TARGETS:... to have stage_lins run last in non-jobs mode. Use .ORDER only for jobs mode. 2015-09-02 Simon J. Gerraty * rst2htm.mk: allow for per target flags etc. 2015-09-01 Simon J. Gerraty * install-mk (MK_VERSION): 20150901 * doc.mk: create dir if needed use DOC_INSTALL_OWN 2015-06-15 Simon J. Gerraty * install-mk (MK_VERSION): 20150615 * auto.obj.mk: allow use of MAKEOBJDIRPREFIX too. Follow make's normal precedence rules. * gendirdeps.mk: allow customization of the header. eg. for FreeBSD: GENDIRDEPS_HEADER= echo '\# ${FreeBSD:L:@v@$$$v$$ @:M*F*}'; * meta.autodep.mk: ignore dirdeps.cache* * meta.stage.mk: when bootstrapping options it can be handy to throw warnings rather than errors for staging conflicts. * meta.sys.mk: include local.meta.sys.mk for customization 2015-06-06 Simon J. Gerraty * install-mk (MK_VERSION): 20150606 * dirdeps.mk: don't rely on manually maintained Makefile.depend to set DEP_RELDIR and reset DIRDEPS. By setting DEP_RELDIR ourselves we can skip :tA * gendirdeps.mk: skip setting DEP_RELDIR. 2015-05-24 Simon J. Gerraty * dirdeps.mk: avoid wildcards like make(bootstrap*) 2015-05-20 Simon J. Gerraty * install-mk (MK_VERSION): 20150520 * dirdeps.mk: when we are building dirdeps cache file we *want* meta_oodate to look at all the Makefile.depend files, so set .MAKE.DEPENDFILE to something that won't match. * meta.stage.mk: for STAGE_AS_* basename of file may not be unique so first use absolute path as key. Also skip staging at level 0. 2015-04-30 Simon J. Gerraty * install-mk (MK_VERSION): 20150430 * dirdeps.mk: fix _count_dirdeps for non-cache case. 2015-04-16 Simon J. Gerraty * install-mk (MK_VERSION): 20150411 bump version * own.mk: put AUTO_OBJ in OPTIONS_DEFAULT_NO rather than YES. it is here mainly for documentation purposes, since if using auto.obj.mk it is better done via sys.mk 2015-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20150401 * meta2deps.sh: support @list * meta2deps.py: updates from Juniper o add EXCLUDES o skip bogus input files. o treat 'M' and 'L' as both an 'R' and a 'W' 2015-03-03 Simon J. Gerraty * install-mk (MK_VERSION): 20150303 * dirdeps.mk: if MK_DIRDEPS_CACHE is yes, use dirdeps-cache which is built via sub-make so we have a .meta file to tell if it is out-of-date. The dirdeps-cache contains the same dependency rules that we normaly construct on the fly. This adds a few seconds overhead when the cache is out of date, but for a large target, the savings can be significant (10-20min). 2014-11-18 Simon J. Gerraty * install-mk (MK_VERSION): 20141118 * meta.stage.mk: add stale_staged * dirdeps.mk (_DIRDEP_USE_LEVEL): allow this to be tweaked only useful under very rare conditions such as FreeBSD's make universe. * auto.obj.mk: Allow MK_AUTO_OBJ to set MKOBJDIRS=auto 2014-11-11 Simon J. Gerraty * install-mk (MK_VERSION): 20141111 * mkopt.sh: use consistent semantics for _mk_opt and _mk_opts 2014-11-09 Simon J. Gerraty * FILES: include mkopt.sh which allows handling options in shell scripts in a manner compatible with options.mk 2014-10-12 Simon J. Gerraty * meta.stage.mk: ensure only _STAGED_DIRS under objroot are used for GENDIRDEPS_FILTER to avoid surprises. 2014-10-10 Simon J. Gerraty * dirdeps.mk (NSkipHostDir): this needs SRCTOP prepended since by the time it is applied to __depdirs they have. * dirdeps.mk fix filtering of _machines since M_dep_qual_fixes expects patterns like *.${MACHINE} * cython.mk (pyprefix?): use pyprefix to find python bits since prefix might be something else (where we install our stuff) 2014-09-11 Simon J. Gerraty * install-mk (MK_VERSION): 20140911 * dirdeps.mk: add bootstrap target to simplify adding support for new MACHINE. 2014-09-01 Simon J. Gerraty * gendirdeps.mk: Add handling of GENDIRDEPS_FILTER_DIR_VARS and GENDIRDEPS_FILTER_VARS to make it easier to produce sharable Makefile.depend files. 2014-08-28 Simon J. Gerraty * install-mk (MK_VERSION): 20140828 * cython.mk: capture logic for building python extension modules with Cython. 2014-08-08 Simon J. Gerraty * meta.stage.mk (_STAGE_AS_BASENAME_USE): Add StageAs variant 2014-08-02 Simon J. Gerraty * install-mk (MK_VERSION): 20140801 * dep.mk: use explicit MKDEP_MK rather than overload MKDEP to identify the autodep.mk variant. * sys.dependfile.mk: delete .MAKE.DEPENDFILE if its initial value does not match .MAKE.DEPENDFILE_PREFIX * meta.autodep.mk: if _bootstrap_dirdeps add RELDIR to DIRDEPS 2014-05-22 Simon J. Gerraty * install-mk (MK_VERSION): 20140522 * lib.mk: use CC to link shlib for linux too patch from Brendan MacDonell 2014-05-05 Simon J. Gerraty * meta.autodep.mk: add _reldir_{finish,failed} for gathering stats if WITH_META_STATS is defined. 2014-05-02 Simon J. Gerraty * dirdeps.mk: accept -DWITHOUT_DIRDEPS (same a as -DNO_DIRDEPS) to supress dirdeps outside of .CURDIR. 2014-04-05 Simon J. Gerraty * Fix spelling errors - patch from Pedro Giffuni 2014-03-14 Simon J. Gerraty * install-mk (MK_VERSION): 20140314 * dirdeps.mk (beforedirdeps): a handy hook * dirdeps.mk (DIRDEP_MAKE): allow the actual command we run to visit leaf dirs to be intercepted (eg. for distributed build). * dirdeps.mk (__depdirs): ensure // don't sneak in * gendirdeps.mk (DIRDEPS): ensure // don't sneak in 2014-02-21 Simon J. Gerraty * rst2htm.mk (RST2PDF): add support for rst2pdf 2014-02-14 Simon J. Gerraty * install-mk (MK_VERSION): bump version * dirdeps.mk (_last_dependfile): use .INCLUDEDFROMFILE if available. 2014-02-10 Simon J. Gerraty * options.mk: avoid :U so this isn't bmake dependent 2014-02-09 Simon J. Gerraty * options.mk: cleanup and simplify semanitcs NO_* dominates all, if both WITH_* and WITHOUT_* are defined then result is DOMINATE_* which defaults to "no". Ie. WITHOUT_ normally wins. 2013-12-12 Simon J. Gerraty * install-mk (MK_VERSION): bump version * meta2deps.py: convert to print function for python3 compat. we also need to open files with mode 'r' rather than 'rb' otherwise we get bytes instead of strings. 2013-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version * dirdeps.mk: when TARGET_SPEC_VARS is more than just MACHINE apply the same filtering (M_dep_qual_fixes) when setting _machines as _build_dirs. Also fix the filtering of Makefile.depend files - for reporting what we are looking for (M_dep_qual_fixes can get confused by Makefile.depend) Add some more debug info. 2013-09-04 Simon J. Gerraty * gendirdeps.mk (_objtops): fix typo also while processing M2D_OBJROOTS to gather qualdir_list qualify $ql with loop iterator to ensure correct results. 2013-08-01 Simon J. Gerraty * install-mk (MK_VERSION): 20130801 * libs.mk: update to match progs.mk 2013-07-26 Simon J. Gerraty * install-mk (MK_VERSION): 20130726 some updates from Juniper and FreeBSD o meta2deps.py: indicate file and line number when we hit parse errors also allow @file to provide huge list of .meta files. * meta2deps.py: add try_parse() to cleanup the above. 2013-07-16 Simon J. Gerraty * install-mk (MK_VERSION): 20130716 * own.mk: add GPROG as an option * prog.mk: honor MK_GPROF==yes 2013-05-10 Simon J. Gerraty * install-mk (MK_VERSION): 20130505 * gendirdeps.mk, meta2deps.py, meta2deps.sh: handle $TARGET_SPEC for when $MACHINE isn't enough for objdir distinction. Bring meta2deps.sh closer to par with meta2deps.py. 2013-04-18 Simon J. Gerraty * meta.stage.mk: set INSTALL to STAGE_INSTALL when making 'all' also if the target 'beforeinstall' exists, make it depend on .dirdep (incase it uses STAGE_INSTALL). 2013-04-17 Simon J. Gerraty * install-mk (MK_VERSION): 20130401 ;-) * meta.stage.mk (STAGE_INSTALL_SH): add stage-install.sh as wrapper around install(1). * options.mk (OPTION_PREFIX): Allow a prefix other than MK_ 2013-03-30 Simon J. Gerraty * meta2deps.py (MetaFile.__init__): ensure self.cwd is initialized. * install-mk (MK_VERSION): bump version 2013-03-21 Simon J. Gerraty * install-mk (MK_VERSION): bump version * gendirdeps.mk: do not apply :tA to DPADD entries, since we lose any trailing /., rather apply :tA only when needed. * gendirdeps.mk: better mimic meta2deps handling of .dirdep files. * meta.stage.mk (LN_CP_SCRIPT): Add LnCp to do the ln||cp dance consistently. * dirdeps.mk: better describe the dance in sys.mk for TARGET_SPEC. 2013-03-18 Simon J. Gerraty * gendirdeps.mk: revert the dance around .MAKE.DEPENDFILE_DEFAULT it is simpler to just not update when say building for "host" (where we know we apply filters to DIRDEPS), and using a non-machine qualified dependfile. 2013-03-16 Simon J. Gerraty * dirdeps.mk: improve DIRDEPS filtering by allowing DEP_SKIP_DIR and DEP_DIRDEPS_FILTER to vary by DEP_MACHINE and DEP_TARGET_SPEC * gendirdeps.mk: ensure _objroot has trailing / if it needs it. * meta2deps.py: if machine is "host", then also trim self.host_target from any OBJROOTS. 2013-03-11 Simon J. Gerraty * gendirdeps.mk: if .MAKE.DEPENDFILE_DEFAULT is not machine qualified but _DEPENDFILE is, and .MAKE.DEPENDFILE_DEFAULT exists but _DEPENDFILE does not, compare the new _DEPENDFILE against .MAKE.DEPENDFILE_DEFAULT and discard if the same. 2013-03-08 Simon J. Gerraty * meta.stage.mk: use STAGE_TARGETS to control .ORDER and hook to all: via staging: 2013-03-07 Simon J. Gerraty * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT): use a separate variable for the default .MAKE.DEPENDFILE value so that it can be controlled independently of .MAKE.DEPENDFILE_PREFERENCE * meta.stage.mk: throw error if cp fails etc. Stage*() return early if passed no args. .ORDER stage_* 2013-03-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version * gendirdeps.mk: handle multiple M2D_OBJROOTS better. 2013-02-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20130210 * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py from Juniper. o dirdeps.mk now fully supports TARGET_SPEC consisting of more than just MACHINE. o no longer use DEP_MACHINE from Makefile.depend* so remove it. 2013-01-23 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20130123 * meta.stage.mk: add stage_links (hard links). if doing hard links, we add dest to link as well. Default the stage dir for [sym]links to STAGE_OBJTOP since these are typically specified as absolute paths. Add -m "mode" flag to StageFiles and StageAs. 2012-11-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121111 * autoconf.mk: avoid meta mode seeing changed commands for config.status * meta.autodep.mk: pass resolved MAKESYSPATH to gendirdeps in case we were found via .../mk * sys.clean-env.mk: move it from examples, we and others use it "as is". * FILES: add srctop.mk and options.mk * own.mk: convert to using options.mk which is modeled after FreeBSD's handling of MK_* but more flexible. This allows MK_* for boolean knobs to not be confused with MK* which can be commands. * examples/sys.clean-env.mk: add WITH[OUT]_ to MAKE_ENV_SAVE_PREFIX_LIST. Mention that HOME=/var/empty might be a good idea. 2012-11-08 Simon J. Gerraty * sys.dependfile.mk: if not depend file exists, $MACHINE specific ones are supported but not the default, check if any exist and follow suit. 2012-11-06 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121106 2012-11-05 Simon J. Gerraty * import latest dirdeps.mk and meta2deps.py from Juniper. * progs.mk: add MAN and CXXFLAGS to PROG_VARS also add PROGS_TARGETS and pass on PROG_CXX if it seems appropriate. 2012-11-04 Simon J. Gerraty * meta.stage.mk: update CLEANFILES remove redundant cp of .dirdep from STAGE_AS_SCRIPT. * progs.mk: Add LDADD to PROG_VARS 2012-10-12 Simon J. Gerraty * meta.stage.mk (STAGE_DIR_FILTER): track dirs we stage to in _STAGED_DIRS so that these can be turned into filters for GENDIRDEPS_FILTER. 2012-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121010 * meta.stage.mk (STAGE_DIRDEP_SCRIPT): check that an existing target.dirdep matches .dirdep 2012-08-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120808 * import latest meta2deps.py from Juniper. 2012-07-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120711 * dep.mk: add explicit dependencies on SRCS after applying SRCS_DEP_FILTER * meta.autodep.mk: add explicit dependencies on SRCS after applying SRCS_DEP_FILTER * meta.autodep.mk: ensure GENDIRDEPS_FILTER is exported if needed. 2012-06-26 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120626 * meta.sys.mk: ignore PYTHON if it does not exist compare ${.MAKE.DEPENDFILE:E} against ${MACHINE} is more reliable. * meta.stage.mk: examine .MAKE.DEPENDFILE_PREFERENCE for any entries ending in .${MACHINE} to decide if qualified _dirdep is needed. * gendirdeps.mk: only produce unqualified deps if no .MAKE.DEPENDFILE_PREFERENCE ends in .${MACHINE} * meta.subdir.mk: apply SUBDIRDEPS_FILTER 2012-04-20 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120420 * add sys.dependfile.mk so we can experiment with .MAKE.DEPENDFILE_PREFERENCE * meta.autodep.mk: _DEPENDFILE is precious! 2012-03-15 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120315 * install-new.mk: avoid being interrupted 2012-02-26 Simon J. Gerraty * man.mk: MAN might have multiple values so be careful with exists(). 2012-01-19 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120112 * fix examples/sys.clean-env.mk so that MAKEOBJDIR is handled as: MAKEOBJDIR='${.CURDIR:S,${SRCTOP},${OBJTOP},}' 2011-12-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111201 * import dirdeps.mk from Juniper sjg@ o more consistent handling of DEP_MACHINE, especially when dealing with an odd Makefile.depend, when normally using Makefile.depend.${MACHINE} 2011-11-22 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111122 * meta.autodep.mk: add some debug output, be more crisp about updating. Use ${.ALLTARGETS:M*.o} as a clue for .depend 2011-11-13 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111111 it's too cool to miss * import meta* updates from Juniper sjg@ o dirdeps.mk set DEP_MACHINE for Makefile.depend (when we are normally using Makefile.depend.${MACHINE}), handy for read-only manually maintained dependencies. o meta2deps.py add a clear 'ERROR:' token if an exception is raised. o gendirdeps.mk if ERROR: from meta2deps.py do not update anything. 2011-10-30 Simon J. Gerraty * install-new.mk separate the cmp and copy logic to its own function. 2011-10-28 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111028 * sys.mk: include auto.obj.mk if MKOBJDIRS is set to auto * subdir.mk: ensure _SUBDIRUSE is provided * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk * meta.subdir.mk: always allow for Makefile.depend 2011-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111010 o minor tweak to *dirdeps.mk from Juniper sjg@ 2011-10-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111001 o add meta2deps.py from Juniper sjg@ o tweak gendirdeps.mk to work with meta2deps.py when not cross-building * autoconf.mk: add autoconf-input as a hook for regenerating AUTOCONF_INPUTS (configure). 2011-08-24 Simon J. Gerraty * meta.autodep.mk: if we do not have OBJS, .depend isn't a useful trigger for updating Makefile.depend* 2011-08-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110808 * obj.mk: minor cleanup * auto.obj.mk: improve description of Mkdirs and honor NO_OBJ too. 2011-08-01 Simon J. Gerraty * auto.obj.mk (.OBJDIR): throw an error if we cannot use the specified dir. 2011-06-28 Simon J. Gerraty * meta.autodep.mk: if XMAKE_META_FILE is set the makefile uses a foreign make, and so dependencies can only be gathered from a clean tree build. 2011-06-24 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110622 * meta.autodep.mk: improve bootstraping 2011-06-10 Simon J. Gerraty * yacc.mk: handle the corner case of .c being removed while .h remains. 2011-06-08 Simon J. Gerraty * yacc.mk: do .y.h and .y.c separately 2011-06-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110606 * don't store SRC_DIRDEPS in Makefile.depend* by default not everyone needs it. 2011-05-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110505 first release including meta mode makefiles 2011-05-02 Simon J. Gerraty * meta.stage.mk: add STAGE_AS_SETS and stage_as for things that need to be staged with different names. 2011-05-01 Simon J. Gerraty * meta.stage.mk: add notion of STAGE_SETS so a makefile can stage to multiple dirs 2011-04-03 Simon J. Gerraty * rst2htm.mk: convert rst to s5 (slides) or plain html depending on target name. 2011-03-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110330 2011-03-29 Simon J. Gerraty * sys.mk (_DEBUG_MAKE_FLAGS): use indirection so that DEBUG_MAKE_FLAGS0 can be used to debug level 0 only and DEBUG_MAKE_FLAGS for the rest. * sys.mk: re-define M_whence in terms of M_type. M_type is useful for checking if something is a builtin. 2011-03-16 Simon J. Gerraty * meta.stage.mk: add stage_symlinks and leverage StageLinks for stage_libs 2011-03-10 Simon J. Gerraty * dirdeps.mk: correct value for _depdir_files depends on .MAKE.DEPENDFILE Add our copyright - just to make it clear we have frobbed this quite a bit. DEP_MACHINE needs to be set to MACHINE each time, if using only Makefile.depend (cf. Makefile.depend.${MACHINE}) * meta.stage.mk: meta mode version of staging * init.mk, final.mk: include local.*.mk to simplify customization 2011-03-03 Simon J. Gerraty * auto.obj.mk: just because we are doing mk destroy, we should still set .OBJDIR correctly if it exists. * install-mk (mksrc): do not exclude meta.sys.mk 2011-03-01 Simon J. Gerraty * host-target.mk: set/export _HOST_ARCH etc separately, catch junk resulting from uname -p, so we can find sys/Linux.mk correctly. 2011-02-18 Simon J. Gerraty * meta.sys.mk: throw an error if /dev/filemon is missing and we expected to be updating Makefile.depend* 2011-02-14 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110214 * meta.subdir.mk: add support for -DBOOTSTRAP_DEPENDFILES 2010-09-25 Simon J. Gerraty * meta.sys.mk: not valid for older bmake 2010-09-24 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100919 include dirdeps.mk et al from Juniper Networks, for meta mode - requires filemon(9). * sys.mk, subdir.mk: Add hooks for meta mode. we do this as meta.sys.mk, meta.autodep.mk and meta.subdir.mk to make turning it on/off simple. 2010-06-16 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100616 * fix typo in sys.mk 2010-06-12 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100612 * lib.mk: remove duplicate addition to SOBJS 2010-06-10 Simon J. Gerraty * sys.mk: Add a means of selectively turning on debug flags. Eg. DEBUG_MAKE_FLAGS=-dv DEBUG_MAKE_DIRS="*lib/sjg" will act as if we did make -dv if .CURDIR ends in lib/sjg DEBUG_MAKE_SYS_DIRS does the same thing, but we set the flags at the start of sys.mk rather than the end. This only makes sense for leaf dirs, so we check that .MAKE.LEVEL > 0 2010-06-09 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100608 * sys.mk: include sys.env.mk later so it can use M_ListToSkip et al. * examples/sys.clean-env.mk: require MAKE_VERIONS >= 20100606 also make it easier for folk to tweak 2010-06-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100606 do not install examples/* * FILES: add examples/sys.clean-env.mk * examples/sys.clean-env.mk: use .export-env to handle MAKEOBJDIR this requires bmake-20100606 or later to work. 2010-05-13 Simon J. Gerraty * sys.mk (M_tA): better simulate the result of :tA if not available. 2010-05-04 Simon J. Gerraty * sys.mk: canonicalize MAKE_VERSION old versions reported bmake- build- whereas we only care about 2010-04-25 Simon J. Gerraty * install-mk: just warn about FORCE_{BSD,SYS}_MK being ignored * lib.mk: we only build the shared lib if SHLIB_FULLVERSION is !empty 2010-04-22 Simon J. Gerraty * dpadd.mk: use LDADD_* if defined. 2010-04-21 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100420 * sys/NetBSD.mk: add MACHINE_CPU to keep netbsd makefiles happy * autoconf.mk allow AUTO_AUTOCONF 2010-04-19 Simon J. Gerraty * obj.mk: add objwarn to keep freebsd makefiles happy * auto.obj.mk: ensure Mkdirs is available. * FILES: add auto.dep.mk - a simpler version of autodep.mk * dep.mk: auto.dep.mk does not do 'make depend' so ignore it if asked to do that. fix/simplify the tests for when to run mkdep. * auto.dep.mk: add some explanation of how/what we do. * autodep.mk: skip the .OPTIONAL frobbing of .depend bmake's FROM_DEPEND flag makes it redundant. 2010-04-13 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100404 * subdir.mk: protect from multiple inclusion using _SUBDIRUSE. * obj.mk: protect from multiple inclusion even as bsd.obj.mk Also create a target _SUBDIRUSE so that we can be used without subdir.mk 2010-04-12 Simon J. Gerraty * dep.mk: use <> when .including so can override. 2010-01-11 Simon J. Gerraty * lib.mk (SHLIB_LINKS): ensure a string comparison. 2010-01-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100102 * own.mk: ensure PRINTOBJDIR works * autoconf.mk: pass on CONFIGURE_ARGS * init.mk: handle COPTS.${.IMPSRC:T} etc. * lib.mk: allow sys.mk to control SHLIB_FULLVERSION fix handling of symlinks for darwin * libnames.mk: add DSHLIBEXT for libs which only exist as shared. * man.mk: suppress chown when not root. * rst2htm.mk: allow srcs from multiple locations. * sys.mk: M_whence, stop after 1st line of output. * sys/Darwin.mk: Use .dylib for DSHLIBEXT and HOST_LIBEXT * sys/SunOS.mk: we need to export PATH 2009-12-23 Simon J. Gerraty * install-mk (MK_VERSION): bump version include rst2htm.mk 2009-12-17 Simon J. Gerraty * sys.mk,libnames.mk add .-include this allows local customization without the need to edit the distributed files. 2009-12-14 Simon J. Gerraty * dpadd.mk (__dpadd_libdirs): order -L's to avoid picking up older versions already installed. 2009-12-13 Simon J. Gerraty * stage.mk (.stage-install): generalize lib.mk's .libinstall * rules.mk rules for generic Makefile. * inc.mk install for includes. 2009-12-11 Simon J. Gerraty * sys/NetBSD.mk (MAKE_VERSION): some of our *.mk want to check this, so provide it if using native make. 2009-12-10 Simon J. Gerraty * FILES: move all the platform *.sys.mk files to sys/*.mk * Rename Generic.sys.mk to sys.mk - we always want it. 2009-11-17 Simon J. Gerraty * install-mk (MK_VERSION): bump version * host-target.mk: only export the expensive stuff * Generic.sys.mk (sys_mk): for SunOS we need to look for ${HOST_OS}.${HOST_OSMAJOR} too! 2009-11-07 Simon J. Gerraty * install-mk (MK_VERSION): bump version * lib.mk: if sys.mk doesn't give us an lorder, don't use it. based on patch from Greg Olszewski. * Generic.sys.mk: if we have nothing to work with set LORDER etc only if we can find it. 2009-09-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version * man.mk: cleanman: remove CLEANMAN if defined. 2009-09-04 Simon J. Gerraty * SunOS.5.sys.mk (CC): Use ?= like the other *sys.mk 2009-07-17 Simon J. Gerraty * install-mk (MK_VERSION): bump version include auto.obj.mk 2009-03-26 Simon J. Gerraty * prog.mk,lib.mk: ensure test of USE_DPADD_MK doesn't fail. 2008-11-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version man.mk: ensure we generate *.cat1 etc in . 2008-07-16 Simon J. Gerraty * install-mk (MK_VERSION): bump version add prlist.mk 2007-11-25 Simon J. Gerraty * Generic.sys.mk: Allow os specific sys.mk to be in a subdir of ${.PARSEDIR} 2007-11-22 Simon J. Gerraty * install-mk (MK_VERSION): bump version * general cleanup * dpadd.mk introduce DPMAGIC_LIBS_* 2007-04-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version * libs.mk, progs.mk, autodep.mk: allow for per lib/prog depend files and ensure clean is called for each lib/prog. 2007-03-27 Simon J. Gerraty * autodep.mk (.depend): delete lines that do not start with space and do not contain ':' 2007-02-16 Simon J. Gerraty * autodep.mk (.depend): gcc may wrap lines if pathnames are long so make sure the transform for .OPTIONAL copes. 2007-02-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version * own.mk: make sure RM and LN are defined. * obj.mk: fix a typo, and objlink target. 2006-12-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version * added libs.mk - analogous to progs.mk make both of them always inlcude {lib,prog}.mk 2006-12-28 Simon J. Gerraty * progs.mk: add a means of building multiple apps in one dir. 2006-11-26 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20061126 * warnings.mk: detect invalid WARNINGS_SET * warnings.mk: use ${.TARGET:T:R}.o when looking for target specific warnings. * For .cc sources, turn off warnings that g++ vomits on. 2006-11-08 Simon J. Gerraty * own.mk: if __initialized__ target doesn't exist and we are FreeBSD we got here directly from sys.mk 2006-11-06 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20061106 add scripts.mk 2006-03-18 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060318 * autodep.mk: avoid := when modifying OBJS into __dependsrcs 2006-03-02 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060302 * autodep.mk: use -MF et al to help gcc+ccache DTRT. 2006-03-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060301 * autodep.mk (.depend): if MAKE_VERSION is newer than 20050530 we can make .END depend on .depend and make .depend depend on __depsrcs that exist. * dpadd.mk: add SRC_PATHADD 2005-11-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20051104 * prog.mk: remove all the LIBC?= junk, use .-include libnames.mk instead (none by default). also if USE_DPADD_MK is set, include that. 2005-10-09 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20051001 Add UnixWare.sys.mk from Klaus Heinz. 2005-04-05 Simon J. Gerraty * install-mk: always install *.sys.mk and if need be symlink one to sys.mk 2005-03-22 Simon J. Gerraty * subdir.mk, own.mk: use .MAKE rather than MAKE 2004-02-15 Simon J. Gerraty * own.mk: don't use NetBSD's _SRC_TOP_ it can cause confusion. Also don't take just 'mk' as a srctop indicator. 2004-02-14 Simon J. Gerraty * warnings.mk: overhauled, now very powerful. 2004-02-03 Simon J. Gerraty * Generic.sys.mk: need to use ${.PARSEDIR} with exists(). 2004-02-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20040201 * extract HOST_TARGET stuff to host-target.mk so own.mk and Generic.sys.mk can share. * fix typo in autodep.mk _SUBDIRUSE not _SUBDIR. 2003-09-30 Simon J. Gerraty * install-mk (MK_VERSION): 20030930 * rename generic.sys.mk to Generic.sys.mk so that it does not get installed (unless being used as sys.mk) * set OS and ROOT_GROUP for those that we know the value. for others (eg. Generic.sys.mk) wrap the != in an .ifndef so we don't do it again for each sub-make. 2003-09-28 Simon J. Gerraty * install-mk (MK_VERSION): 20030928 Add some extra *.sys.mk from bootstrap-pkgsrc some of these likely still need work. Make everything default to root:wheel ownership, sys.mk can set ROOT_GROUP accordingly. 2003-08-07 Simon J. Gerraty * install-mk: if FORCE_BSD_MK={cp,ln} use the ones in SYS_MK_DIR not the portable ones. 2003-07-31 Simon J. Gerraty * install-mk: add ability to use cp -f when updating destination .mk files. Also now possible to play games with FORCE_SYS_MK=ln etc on *BSD machines to link /usr/share/mk/sys.mk into dest - not recommended unless you seriously want to. 2003-07-28 Simon J. Gerraty * own.mk (IMPFLAGS): add support for COPTS.${IMPSRC:T} etc for semi-compatability with NetBSD. 2003-07-23 Simon J. Gerraty * install-mk: add a version indicator 2003-07-22 Simon J. Gerraty * prog.mk: don't try and use ${LIBCRT0} if its /dev/null * install-mk: Allow FORCE_SYS_MK to come from env diff --git a/contrib/bmake/mk/compiler.mk b/contrib/bmake/mk/compiler.mk index 83c0cead8f25..ce394c3275bf 100644 --- a/contrib/bmake/mk/compiler.mk +++ b/contrib/bmake/mk/compiler.mk @@ -1,43 +1,43 @@ -# $Id: compiler.mk,v 1.10 2021/12/08 05:56:50 sjg Exp $ +# $Id: compiler.mk,v 1.11 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2019, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .NOTMAIN .if ${MACHINE} == "common" COMPILER_TYPE = none COMPILER_VERSION = 0 .endif .if empty(COMPILER_TYPE) || empty(COMPILER_VERSION) # gcc does not always say gcc _v != (${CC} --version) 2> /dev/null | \ - egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'; echo + ${EGREP:Uegrep} -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'; echo .if empty(COMPILER_TYPE) .if ${_v:Mclang} != "" COMPILER_TYPE = clang .elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != "" || ${CC:T:M*gcc*} != "" COMPILER_TYPE = gcc .endif .endif .if empty(COMPILER_VERSION) COMPILER_VERSION != echo "${_v:M[1-9].[0-9]*}:[1]" | \ awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3;}' .endif .undef _v .endif # just in case we don't recognize compiler COMPILER_TYPE ?= unknown COMPILER_VERSION ?= 0 .endif diff --git a/contrib/bmake/mk/dirdeps-options.mk b/contrib/bmake/mk/dirdeps-options.mk index b31d2033ae98..9a97615bbeb8 100644 --- a/contrib/bmake/mk/dirdeps-options.mk +++ b/contrib/bmake/mk/dirdeps-options.mk @@ -1,104 +1,111 @@ -# $Id: dirdeps-options.mk,v 1.20 2022/03/17 20:11:36 sjg Exp $ +# $Id: dirdeps-options.mk,v 1.21 2022/09/06 22:18:45 sjg Exp $ # # @(#) Copyright (c) 2018-2022, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # ## # # This makefile is used to deal with optional DIRDEPS. # # It is to be included by Makefile.depend.options in a # directory which has DIRDEPS affected by optional features. # Makefile.depend.options should set DIRDEPS_OPTIONS and # may also set specific DIRDEPS.* for those options. # # If a Makefile.depend.options file exists, it will be included by # dirdeps.mk and meta.autodep.mk # # We include local.dirdeps-options.mk which may also define DIRDEPS.* # for options. # # Thus a directory, that is affected by an option FOO would have # a Makefile.depend.options that sets # DIRDEPS_OPTIONS= FOO # It can also set either/both of # DIRDEPS.FOO.yes # DIRDEPS.FOO.no # to whatever applies for that dir, or it can rely on globals # set in local.dirdeps-options.mk # Either way, we will .undef DIRDEPS.* when done. # # In some cases the value of MK_FOO might depend on TARGET_SPEC # so we qualify MK_FOO with .${TARGET_SPEC} and each component # TARGET_SPEC_VAR (in reverse order) before using MK_FOO. # +# Because Makefile.depend.options are processed at both level 0 (when +# computing DIRDEPS to build) and higher (when updating +# Makefile.depend* after successful build), it is important that +# all references to TARGET_SPEC_VARs should use the syntax +# ${DEP_${TARGET_SPEC_VAR}:U${TARGET_SPEC_VAR}} to ensure correct +# behavior. +# # This should have been set by Makefile.depend.options # before including us DIRDEPS_OPTIONS ?= # pickup any DIRDEPS.* we need .-include .if ${.MAKE.LEVEL} == 0 # :U below avoids potential errors when we := # some options can depend on TARGET_SPEC! DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \ ${DEP_RELDIR} \ ${DEP_TARGET_SPEC:U${TARGET_SPEC}} \ ${TARGET_SPEC_VARSr:U${TARGET_SPEC_VARS}:@v@${DEP_$v:U${$v}}@} # note that we need to include $o in the variable _o$o # to ensure correct evaluation. .for o in ${DIRDEPS_OPTIONS} .undef _o$o .undef _v$o .for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST:S,^,${DEP_RELDIR}.,} \ ${DIRDEPS_OPTIONS_QUALIFIER_LIST} #.info MK_$o.$x=${MK_$o.$x:Uundefined} .if defined(MK_$o.$x) _o$o ?= MK_$o.$x _v$o ?= ${MK_$o.$x} .endif .endfor _v$o ?= ${MK_$o} .if ${_debug_reldir:U0} .info ${DEP_RELDIR:U${RELDIR}}.${DEP_TARGET_SPEC:U${TARGET_SPEC}}: o=$o ${_o$o:UMK_$o}=${_v$o:U} DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} .endif DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} .endfor DIRDEPS := ${DIRDEPS:O:u} .if ${_debug_reldir:U0} .info ${DEP_RELDIR:U${RELDIR}}: DIRDEPS=${DIRDEPS} .endif # avoid cross contamination .for o in ${DIRDEPS_OPTIONS} .undef DIRDEPS.$o.yes .undef DIRDEPS.$o.no .undef _o$o .undef _v$o .endfor .else # whether options are enabled or not, # we want to filter out the relevant DIRDEPS.* # we should only be included by meta.autodep.mk # if dependencies are to be updated .for o in ${DIRDEPS_OPTIONS} .for d in ${DIRDEPS.$o.yes} ${DIRDEPS.$o.no} .if exists(${SRCTOP}/$d) GENDIRDEPS_FILTER += N$d* .elif exists(${SRCTOP}/${d:R}) GENDIRDEPS_FILTER += N${d:R}* .endif .endfor .endfor .endif diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk index ee31e47c76c5..94b493a77fa6 100644 --- a/contrib/bmake/mk/dirdeps.mk +++ b/contrib/bmake/mk/dirdeps.mk @@ -1,934 +1,934 @@ -# $Id: dirdeps.mk,v 1.151 2022/01/28 01:13:14 sjg Exp $ +# $Id: dirdeps.mk,v 1.152 2022/08/01 23:09:19 sjg Exp $ # Copyright (c) 2010-2022, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Much of the complexity here is for supporting cross-building. # If a tree does not support that, simply using plain Makefile.depend # should provide sufficient clue. # Otherwise the recommendation is to use Makefile.depend.${MACHINE} # as expected below. # Note: this file gets multiply included. # This is what we do with DIRDEPS # DIRDEPS: # This is a list of directories - relative to SRCTOP, it is # normally only of interest to .MAKE.LEVEL 0. # In some cases the entry may be qualified with a . # or . suffix (see TARGET_SPEC_VARS below), # for example to force building something for the pseudo # machines "host" or "common" regardless of current ${MACHINE}. # # All unqualified entries end up being qualified with .${TARGET_SPEC} # and partially qualified (if TARGET_SPEC_VARS has multiple # entries) are also expanded to a full .. # The _DIRDEP_USE target uses the suffix to set TARGET_SPEC # correctly when visiting each entry. # # The fully qualified directory entries are used to construct a # dependency graph that will drive the build later. # # Also, for each fully qualified directory target, we will search # using ${.MAKE.DEPENDFILE_PREFERENCE} to find additional # dependencies. We use Makefile.depend (default value for # .MAKE.DEPENDFILE_PREFIX) to refer to these makefiles to # distinguish them from others. # # Before each Makefile.depend file is read, we set # DEP_RELDIR to be the RELDIR (path relative to SRCTOP) for # its directory, and DEP_MACHINE etc according to the . # represented by the suffix of the corresponding target. # # Since each Makefile.depend file includes dirdeps.mk, this # processing is recursive and results in .MAKE.LEVEL 0 learning the # dependencies of the tree wrt the initial directory (_DEP_RELDIR). # # NOTE: given the extent of processing that DIRDEPS undergoes it # is important that any variables in entries use :U to guard # against surprises when undefined. # # TARGET_SPEC_VARS # The default value is just MACHINE, and for most environments # this is sufficient. The _DIRDEP_USE target actually sets # both MACHINE and TARGET_SPEC to the suffix of the current # target so that in the general case TARGET_SPEC can be ignored. # # If more than MACHINE is needed then sys.mk needs to decompose # TARGET_SPEC and set the relevant variables accordingly. # It is important that MACHINE be included in and actually be # the first member of TARGET_SPEC_VARS. This allows other # variables to be considered optional, and some of the treatment # below relies on MACHINE being the first entry. # Note: TARGET_SPEC cannot contain any '.'s so the target # triple used by compiler folk won't work (directly anyway). # # For example: # # # Always list MACHINE first, # # other variables might be optional. # TARGET_SPEC_VARS = MACHINE TARGET_OS # .if ${TARGET_SPEC:Uno:M*,*} != "" # _tspec := ${TARGET_SPEC:S/,/ /g} # MACHINE := ${_tspec:[1]} # TARGET_OS := ${_tspec:[2]} # # etc. # # We need to stop that TARGET_SPEC affecting any submakes # # and deal with MACHINE=${TARGET_SPEC} in the environment. # TARGET_SPEC = # # export but do not track # .export-env TARGET_SPEC # .export ${TARGET_SPEC_VARS} # .for v in ${TARGET_SPEC_VARS:O:u} # .if empty($v) # .undef $v # .endif # .endfor # .endif # # make sure we know what TARGET_SPEC is # # as we may need it to find Makefile.depend* # TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} # # The following variables can influence the initial DIRDEPS # computation with regard to the TARGET_SPECs that will be # built. # Most should also be considered by init.mk # # ONLY_TARGET_SPEC_LIST # Defines a list of TARGET_SPECs for which the current # directory can be built. # If ALL_MACHINES is defined, we build for all the # TARGET_SPECs listed. # # ONLY_MACHINE_LIST # As for ONLY_TARGET_SPEC_LIST but only specifies # MACHINEs. # # NOT_TARGET_SPEC_LIST # A list of TARGET_SPECs for which the current # directory should not be built. # # NOT_MACHINE_LIST # A list of MACHINEs the current directory should not be # built for. # # DIRDEPS_EXPORT_VARS (DEP_EXPORT_VARS) # It is discouraged, but sometimes necessary for a # Makefile.depend file to influence the environment. # Doing this is correctly (especially if using DIRDEPS_CACHE) is # tricky so a Makefile.depend file can set DIRDEPS_EXPORT_VARS # and dirdeps.mk will do the deed: # # MK_UEFI = yes # DIRDEPS_EXPORT_VARS = MK_UEFI # # _build_xtra_dirs # local.dirdeps.mk can add targets to this variable. # They will be hooked into the build, but independent of # any other DIRDEP. # # This allows for adding TESTS to the build, such that the build # if any test fails, but without the risk of introducing # circular dependencies. now_utc ?= ${%s:L:gmtime} .if !defined(start_utc) start_utc := ${now_utc} .endif .if !target(bootstrap) && (make(bootstrap) || \ make(bootstrap-this) || \ make(bootstrap-recurse) || \ make(bootstrap-empty)) # disable most of below .MAKE.LEVEL = 1 .endif # touch this at your peril _DIRDEP_USE_LEVEL?= 0 .if ${.MAKE.LEVEL} == ${_DIRDEP_USE_LEVEL} # only the first instance is interested in all this # the first time we are included the _DIRDEP_USE target will not be defined # we can use this as a clue to do initialization and other one time things. .if !target(_DIRDEP_USE) # do some setup we only need once _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} .if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != "" # This little trick let's us do # # mk -f dirdeps.mk some/dir.${TARGET_SPEC} # all: ${.TARGETS:Nall}: all DIRDEPS := ${.TARGETS:M*[/.]*} # so that -DNO_DIRDEPS works DEP_RELDIR := ${DIRDEPS:[1]:R} # this will become DEP_MACHINE below TARGET_MACHINE := ${DIRDEPS:[1]:E:C/,.*//} .if ${TARGET_MACHINE:N*/*} == "" TARGET_MACHINE := ${MACHINE} .endif # disable DIRDEPS_CACHE as it does not like this trick MK_DIRDEPS_CACHE = no # incase anyone needs to know _dirdeps_cmdline: .endif # make sure we get the behavior we expect .MAKE.SAVE_DOLLARS = no # make sure these are empty to start with _DEP_TARGET_SPEC = # If TARGET_SPEC_VARS is other than just MACHINE # it should be set by sys.mk or similar by now. # TARGET_SPEC must not contain any '.'s. TARGET_SPEC_VARS ?= MACHINE # this is what we started with TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} # this is what we mostly use below DEP_TARGET_SPEC = ${TARGET_SPEC_VARS:S,^,DEP_,:@v@${$v:U}@:ts,} # make sure we have defaults .for v in ${TARGET_SPEC_VARS} DEP_$v ?= ${$v} .endfor .if ${TARGET_SPEC_VARS:[#]} > 1 # Ok, this gets more complex (putting it mildly). # In order to stay sane, we need to ensure that all the build_dirs # we compute below are fully qualified wrt DEP_TARGET_SPEC. # The makefiles may only partially specify (eg. MACHINE only), # so we need to construct a set of modifiers to fill in the gaps. .if ${MAKE_VERSION} >= 20170130 _tspec_x := ${TARGET_SPEC_VARS:range} .else # do it the hard way _tspec_x := ${TARGET_SPEC_VARS:[#]:@x@i=1;while [ $$i -le $x ]; do echo $$i; i=$$((i + 1)); done;@:sh} .endif # this handles unqualified entries M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC}; # there needs to be at least one item missing for these to make sense .for i in ${_tspec_x:[2..-1]} _tspec_m$i := ${TARGET_SPEC_VARS:[2..$i]:@w@[^,]+@:ts,} _tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,} M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i}; .endfor TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]} .else # A harmless? default. M_dep_qual_fixes = U .endif .if !defined(.MAKE.DEPENDFILE_PREFERENCE) # .MAKE.DEPENDFILE_PREFERENCE makes the logic below neater? # you really want this set by sys.mk or similar .MAKE.DEPENDFILE_PREFERENCE = ${_CURDIR}/${.MAKE.DEPENDFILE:T} .if ${.MAKE.DEPENDFILE:E} == "${TARGET_SPEC}" .if ${TARGET_SPEC} != ${MACHINE} .MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R}.$${MACHINE} .endif .MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R} .endif .endif _default_dependfile := ${.MAKE.DEPENDFILE_PREFERENCE:[1]:T} _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*} # for machine specific dependfiles we require ${MACHINE} to be at the end # also for the sake of sanity we require a common prefix .if !defined(.MAKE.DEPENDFILE_PREFIX) # knowing .MAKE.DEPENDFILE_PREFIX helps .if !empty(_machine_dependfiles) .MAKE.DEPENDFILE_PREFIX := ${_machine_dependfiles:[1]:T:R} .else .MAKE.DEPENDFILE_PREFIX := ${_default_dependfile:T} .endif .endif # this is how we identify non-machine specific dependfiles N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}} # this gets reset for each dirdep we check DEP_RELDIR ?= ${RELDIR} # remember the initial value of DEP_RELDIR - we test for it below. _DEP_RELDIR := ${DEP_RELDIR} # this can cause lots of output! # set to a set of glob expressions that might match RELDIR DEBUG_DIRDEPS ?= no # make sure this target exists dirdeps: beforedirdeps .WAIT beforedirdeps: .endif # !target(_DIRDEP_USE) .if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != "" _debug_reldir = 1 .else _debug_reldir = 0 .endif .if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend depend:L:M$x}@} != "" _debug_search = 1 .else _debug_search = 0 .endif # First off, we want to know what ${MACHINE} to build for. # This can be complicated if we are using a mixture of ${MACHINE} specific # and non-specific Makefile.depend* # if we were included recursively _DEP_TARGET_SPEC should be valid. .if empty(_DEP_TARGET_SPEC) DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}} _DEP_TARGET_SPEC := ${DEP_TARGET_SPEC} .if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" # record that we've read dependfile for this _dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: .endif .endif # by now _DEP_TARGET_SPEC should be set, parse it. .if ${TARGET_SPEC_VARS:[#]} > 1 # we need to parse DEP_MACHINE may or may not contain more info _tspec := ${_DEP_TARGET_SPEC:S/,/ /g} .for i in ${_tspec_x} DEP_${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]} .endfor .for v in ${TARGET_SPEC_VARS:O:u} .if empty(DEP_$v) .undef DEP_$v .endif .endfor .else DEP_MACHINE := ${_DEP_TARGET_SPEC} .endif # reset each time through _build_all_dirs = _build_xtra_dirs = # DIRDEPS_CACHE can be very handy for debugging. # Also if repeatedly building the same target, # we can avoid the overhead of re-computing the tree dependencies. MK_DIRDEPS_CACHE ?= no BUILD_DIRDEPS_CACHE ?= no BUILD_DIRDEPS ?= yes .if ${MK_DIRDEPS_CACHE} == "yes" # this is where we will cache all our work DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.TARGETS}:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.} .endif # pickup customizations # as below you can use !target(_DIRDEP_USE) to protect things # which should only be done once. .-include .if !target(_DIRDEP_USE) # things we skip for host tools SKIP_HOSTDIR ?= NSkipHostDir = ${SKIP_HOSTDIR:N*.host*:S,$,.host*,:N.host*:S,^,${SRCTOP}/,:${M_ListToSkip}} # things we always skip # SKIP_DIRDEPS allows for adding entries on command line. SKIP_DIR += .host *.WAIT ${SKIP_DIRDEPS} SKIP_DIR.host += ${SKIP_HOSTDIR} DEP_SKIP_DIR = ${SKIP_DIR} \ ${SKIP_DIR.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${SKIP_DIR.${DEP_$v}:U}@} \ ${SKIP_DIRDEPS.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${SKIP_DIRDEPS.${DEP_$v}:U}@} NSkipDir = ${DEP_SKIP_DIR:${M_ListToSkip}} .if defined(NODIRDEPS) || defined(WITHOUT_DIRDEPS) NO_DIRDEPS = .elif defined(WITHOUT_DIRDEPS_BELOW) NO_DIRDEPS_BELOW = .endif .if defined(NO_DIRDEPS) # confine ourselves to the original dir and below. DIRDEPS_FILTER += M${_DEP_RELDIR}* .elif defined(NO_DIRDEPS_BELOW) DIRDEPS_FILTER += M${_DEP_RELDIR} .endif # this is what we run below DIRDEP_MAKE ?= ${.MAKE} DIRDEP_DIR ?= ${.TARGET:R} # if you want us to report load averages during build # DIRDEP_USE_PRELUDE += ${DIRDEP_LOADAVG_REPORT}; DIRDEP_LOADAVG_CMD ?= ${UPTIME:Uuptime} | sed 's,.*\(load\),\1,' DIRDEP_LOADAVG_LAST = 0 # yes the expression here is a bit complicated, # the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}} # when we want to report. # Note: expr(1) will exit 1 if the expression evaluates to 0 # hence the || true DIRDEP_LOADAVG_REPORT = \ - test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ + test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTERVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`" # we suppress SUBDIR when visiting the leaves # we assume sys.mk will set MACHINE_ARCH # you can add extras to DIRDEP_USE_ENV # if there is no makefile in the target directory, we skip it. _DIRDEP_USE: .USE .MAKE @for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \ test -s ${.TARGET:R}/$$m || continue; \ echo "${TRACER}Checking ${.TARGET:S,^${SRCTOP}/,,} for ${.TARGET:E} ..."; \ ${DIRDEP_USE_PRELUDE} \ MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \ TARGET_SPEC=${.TARGET:E} \ MACHINE=${.TARGET:E} \ ${DIRDEP_MAKE} -C ${DIRDEP_DIR} || exit 1; \ break; \ done .ifdef ALL_MACHINES .if empty(ONLY_TARGET_SPEC_LIST) && empty(ONLY_MACHINE_LIST) # we start with everything _machine_list != echo; 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}* 2> /dev/null # some things we know we want to ignore DIRDEPS_TARGETS_SKIP_LIST += \ *~ \ *.bak \ *.inc \ *.old \ *.options \ *.orig \ *.rej \ # first trim things we know we want to skip # and provide canonical form _machine_list := ${_machine_list:${DIRDEPS_TARGETS_SKIP_LIST:${M_ListToSkip}}:T:E} # cater for local complexities # local.dirdeps.mk can set # DIRDEPS_ALL_MACHINES_FILTER and # DIRDEPS_ALL_MACHINES_FILTER_XTRAS for final tweaks .if !empty(ALL_TARGET_SPEC_LIST) .if ${_debug_reldir} .info ALL_TARGET_SPEC_LIST=${ALL_TARGET_SPEC_LIST} .endif DIRDEPS_ALL_MACHINES_FILTER += \ @x@$${ALL_TARGET_SPEC_LIST:@s@$${x:M$$s}@}@ .elif !empty(ALL_MACHINE_LIST) .if ${_debug_reldir} .info ALL_MACHINE_LIST=${ALL_MACHINE_LIST} .endif .if ${TARGET_SPEC_VARS:[#]} > 1 # the space below can result in extraneous ':' DIRDEPS_ALL_MACHINES_FILTER += \ @x@$${ALL_MACHINE_LIST:@m@$${x:M$$m,*} $${x:M$$m}@}@ .else DIRDEPS_ALL_MACHINES_FILTER += \ @x@$${ALL_MACHINE_LIST:@m@$${x:M$$m}@}@ .endif .endif # add local XTRAS - default to something benign DIRDEPS_ALL_MACHINES_FILTER += \ ${DIRDEPS_ALL_MACHINES_FILTER_XTRAS:UNbak} .if ${_debug_reldir} .info _machine_list=${_machine_list} .info DIRDEPS_ALL_MACHINES_FILTER=${DIRDEPS_ALL_MACHINES_FILTER} .endif _only_machines := ${_machine_list:${DIRDEPS_ALL_MACHINES_FILTER:ts:}:S,:, ,g} .else _only_machines := ${ONLY_TARGET_SPEC_LIST:U} ${ONLY_MACHINE_LIST:U} .endif .if empty(_only_machines) # we must be boot-strapping _only_machines := ${TARGET_MACHINE:U${ALL_TARGET_SPEC_LIST:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}}} .endif # cleanup the result _only_machines := ${_only_machines:O:u} .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: ALL_MACHINES _only_machines=${_only_machines} .endif .else # ! ALL_MACHINES # if ONLY_TARGET_SPEC_LIST or ONLY_MACHINE_LIST is set, we are limited to that. # Note that ONLY_TARGET_SPEC_LIST should be fully qualified. # if TARGET_MACHINE is set - it is really the same as ONLY_MACHINE_LIST # otherwise DEP_MACHINE is it - so DEP_MACHINE will match. _only_machines := ${ONLY_TARGET_SPEC_LIST:U:M${DEP_MACHINE},*} .if empty(_only_machines) _only_machines := ${ONLY_MACHINE_LIST:U${TARGET_MACHINE:U${DEP_MACHINE}}:M${DEP_MACHINE}} .endif .endif .if !empty(NOT_MACHINE_LIST) _only_machines := ${_only_machines:${NOT_MACHINE_LIST:${M_ListToSkip}}} .endif .if !empty(NOT_TARGET_SPEC_LIST) # we must first qualify _dm := ${DEP_MACHINE} _only_machines := ${_only_machines:M*,*} ${_only_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,} DEP_MACHINE := ${_dm} _only_machines := ${_only_machines:${NOT_TARGET_SPEC_LIST:${M_ListToSkip}}} .endif # clean up _only_machines := ${_only_machines:O:u} .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _only_machines=${_only_machines} .endif # make sure we have a starting place? DIRDEPS ?= ${RELDIR} .endif # target .if !defined(NO_DIRDEPS) && !defined(NO_DIRDEPS_BELOW) .if ${MK_DIRDEPS_CACHE} == "yes" # just ensure this exists build-dirdeps: M_oneperline = @x@\\${.newline} $$x@ .if ${BUILD_DIRDEPS_CACHE} == "no" .if !target(dirdeps-cached) # we do this via sub-make BUILD_DIRDEPS = no # ignore anything but these .MAKE.META.IGNORE_FILTER = M*/${.MAKE.DEPENDFILE_PREFIX}* dirdeps: dirdeps-cached dirdeps-cached: ${DIRDEPS_CACHE} .MAKE @echo "${TRACER}Using ${DIRDEPS_CACHE}" @MAKELEVEL=${.MAKE.LEVEL} \ TARGET_SPEC=${TARGET_SPEC} \ ${TARGET_SPEC_VARS:@v@$v=${$v}@} \ ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \ dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no # leaf makefiles rarely work for building DIRDEPS_CACHE .if ${RELDIR} != "." BUILD_DIRDEPS_MAKEFILE ?= -f dirdeps.mk .endif # these should generally do BUILD_DIRDEPS_MAKEFILE ?= BUILD_DIRDEPS_TARGETS ?= ${.TARGETS} .if ${DIRDEPS_CACHE} != ${STATIC_DIRDEPS_CACHE:Uno} && ${DIRDEPS_CACHE:M${SRCTOP}/*} == "" # export this for dirdeps-cache-update.mk DYNAMIC_DIRDEPS_CACHE := ${DIRDEPS_CACHE} .export DYNAMIC_DIRDEPS_CACHE # we need the .meta file to ensure we update if # any of the Makefile.depend* changed. # We do not want to compare the command line though. ${DIRDEPS_CACHE}: .META .NOMETA_CMP +@{ echo '# Autogenerated - do NOT edit!'; echo; \ echo 'BUILD_DIRDEPS=no'; echo; \ echo '.include '; echo; \ } > ${.TARGET}.new +@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \ DIRDEPS="${DIRDEPS}" \ TARGET_SPEC=${TARGET_SPEC} \ MAKEFLAGS= ${DIRDEP_CACHE_MAKE:U${.MAKE}} -C ${_CURDIR} \ ${BUILD_DIRDEPS_MAKEFILE} \ ${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \ .MAKE.DEPENDFILE=.none \ ${"${DEBUG_DIRDEPS:Nno}":?DEBUG_DIRDEPS='${DEBUG_DIRDEPS}':} \ ${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \ ${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \ 3>&1 1>&2 | sed 's,${SRCTOP},_{SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \ mv ${.TARGET}.new ${.TARGET} .endif .endif .elif !target(_count_dirdeps) # we want to capture the dirdeps count in the cache .END: _count_dirdeps _count_dirdeps: .NOMETA @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS}'; } >&3 .endif .elif !make(dirdeps) && !target(_count_dirdeps) beforedirdeps: _count_dirdeps _count_dirdeps: .NOMETA @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS} seconds=`expr ${now_utc} - ${start_utc}`" .endif .endif .if ${BUILD_DIRDEPS} == "yes" # the rest is done repeatedly for every Makefile.depend we read. # if we are anything but the original dir we care only about the # machine type we were included for.. .if ${DEP_RELDIR} == "." _this_dir := ${SRCTOP} .else _this_dir := ${SRCTOP}/${DEP_RELDIR} .endif # on rare occasions, there can be a need for extra help _dep_hack := ${_this_dir}/${.MAKE.DEPENDFILE_PREFIX}.inc .-include <${_dep_hack}> .-include <${_dep_hack:R}.options> .if ${DEP_RELDIR} != ${_DEP_RELDIR} || ${DEP_TARGET_SPEC} != ${TARGET_SPEC} # this should be all _machines := ${DEP_MACHINE} .else # this is the machine list we actually use below _machines := ${_only_machines} .if defined(HOSTPROG) || ${DEP_MACHINE:Nhost*} == "" # we need to build this guy's dependencies for host as well. .if ${DEP_MACHINE:Nhost*} == "" _machines += ${DEP_MACHINE} .else _machines += host .endif .endif _machines := ${_machines:O:u} .endif .if ${TARGET_SPEC_VARS:[#]} > 1 # we need to tweak _machines _dm := ${DEP_MACHINE} # apply the same filtering that we do when qualifying DIRDEPS. # M_dep_qual_fixes expects .${MACHINE}* so add (and remove) '.' # Again we expect that any already qualified machines are fully qualified. _machines := ${_machines:M*,*} ${_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,} DEP_MACHINE := ${_dm} _machines := ${_machines:O:u} .endif # reset each time through _build_dirs = .if ${DEP_RELDIR} == ${_DEP_RELDIR} # pickup other machines for this dir if necessary _build_dirs += ${_machines:@m@${_CURDIR}.$m@} .endif .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: nDIRDEPS=${DIRDEPS:[#]} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}' .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}' .endif .if !empty(DIRDEPS) # these we reset each time through as they can depend on DEP_MACHINE DEP_DIRDEPS_FILTER = \ ${DIRDEPS_FILTER.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${DIRDEPS_FILTER.${DEP_$v}:U}@} \ ${DIRDEPS_FILTER:U} .if empty(DEP_DIRDEPS_FILTER) # something harmless DEP_DIRDEPS_FILTER = U .endif # this is what we start with __depdirs := ${DIRDEPS:${NSkipDir}:${DEP_DIRDEPS_FILTER:ts:}:C,//+,/,g:O:u:@d@${SRCTOP}/$d@} # some entries may be qualified with . or . # we can tell the unqualified ones easily - because they exist __unqual_depdirs := ${__depdirs:@d@${exists($d):?$d:}@} __qual_depdirs := ${__depdirs:${__unqual_depdirs:Uno:${M_ListToSkip}}} .if ${DEP_RELDIR} == ${_DEP_RELDIR} # if it was called out - we likely need it. __hostdpadd := ${DPADD:U.:M${HOST_OBJTOP}/*:S,${HOST_OBJTOP}/,,:H:${NSkipDir}:${DIRDEPS_FILTER:ts:}:S,$,.host,:N.*:@d@${SRCTOP}/$d@} \ ${DPADD:U.:M${HOST_OBJTOP32:Uno}/*:S,${HOST_OBJTOP32:Uno}/,,:H:${NSkipDir}:${DIRDEPS_FILTER:ts:}:S,$,.host32,:N.*:@d@${SRCTOP}/$d@} __qual_depdirs += ${__hostdpadd} .endif .if ${_debug_reldir} .info DEP_DIRDEPS_FILTER=${DEP_DIRDEPS_FILTER:ts:} .info depdirs=${__depdirs:S,^${SRCTOP}/,,} .info qualified=${__qual_depdirs:S,^${SRCTOP}/,,} .info unqualified=${__unqual_depdirs:S,^${SRCTOP}/,,} .endif # _build_dirs is what we will feed to _DIRDEP_USE _build_dirs += \ ${__qual_depdirs:M*.host:${NSkipHostDir}:N.host} \ ${__qual_depdirs:N*.host} \ ${_machines:Mhost*:@m@${__unqual_depdirs:@d@$d.$m@}@:${NSkipHostDir}:N.host} \ ${_machines:Nhost*:@m@${__unqual_depdirs:@d@$d.$m@}@} # qualify everything now _build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u} .endif # empty DIRDEPS _build_all_dirs += ${_build_dirs} ${_build_xtra_dirs} _build_all_dirs := ${_build_all_dirs:O:u} # Normally if doing make -V something, # we do not want to waste time chasing DIRDEPS # but if we want to count the number of Makefile.depend* read, we do. .if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS:U}} == "" .if !empty(_build_all_dirs) .if ${BUILD_DIRDEPS_CACHE} == "yes" # we use _cache_script to minimize the number of times we fork the shell _cache_script = echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; # guard against _new_dirdeps being too big for a single command line _new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,} _cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,} .export _cache_xtra_deps _new_dirdeps .if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS) # Discouraged, but there are always exceptions. # Handle it here rather than explain how. DIRDEPS_EXPORT_VARS ?= ${DEP_EXPORT_VARS} _cache_xvars := echo; ${DIRDEPS_EXPORT_VARS:@v@echo '$v = ${$v}';@} echo '.export ${DIRDEPS_EXPORT_VARS}'; echo; _cache_script += ${_cache_xvars} .endif .else # this makes it all happen dirdeps: ${_build_all_dirs} .endif ${_build_all_dirs}: _DIRDEP_USE .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs:S,^${SRCTOP}/,,} .endif .if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS) .export ${DIRDEPS_EXPORT_VARS} ${DEP_EXPORT_VARS} DIRDEPS_EXPORT_VARS = DEP_EXPORT_VARS = .endif # this builds the dependency graph .for m in ${_machines} .if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs) _cache_deps = _cache_script += echo; echo 'DIRDEPS.${_this_dir}.$m = \'; .endif # it would be nice to do :N${.TARGET} .if !empty(__qual_depdirs) .for q in ${__qual_depdirs:${M_dep_qual_fixes:ts:}:E:O:u:N$m} .if ${_debug_reldir} || ${DEBUG_DIRDEPS:@x@${${DEP_RELDIR}.$m:L:M$x}${${DEP_RELDIR}.$q:L:M$x}@} != "" .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q:S,^${SRCTOP}/,,} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" _cache_deps += ${_build_dirs:M*.$q:S,^${SRCTOP}/,,} .else ${_this_dir}.$m: ${_build_dirs:M*.$q} .endif .endfor .endif .if ${_debug_reldir} .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" .if !empty(_build_dirs) _cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,} .if !empty(_cache_deps) .export _cache_deps _cache_script += for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done; .endif # anything in _{build,env}_xtra_dirs is hooked to dirdeps: only x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \ echo; echo 'dirdeps: ${_this_dir}.$m \'; \ for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \ echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3 .endif .else ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} .endif .endfor .endif # Now find more dependencies - and recurse. .for d in ${_build_all_dirs} .if !target(_dirdeps_checked.$d) # once only _dirdeps_checked.$d: .if ${_debug_search} .info checking ${d:S,^${SRCTOP}/,,} .endif # Note: _build_all_dirs is fully qualifed so d:R is always the directory .if exists(${d:R}) # we pass _DEP_TARGET_SPEC to tell the next step what we want _DEP_TARGET_SPEC := ${d:E} # some makefiles may still look at this _DEP_MACHINE := ${d:E:C/,.*//} # set these too in case Makefile.depend* uses them .if ${TARGET_SPEC_VARS:[#]} > 1 _dtspec := ${_DEP_TARGET_SPEC:S/,/ /g} .for i in ${_tspec_x} DEP_${TARGET_SPEC_VARS:[$i]} := ${_dtspec:[$i]} .endfor .else DEP_MACHINE := ${_DEP_MACHINE} .endif # Warning: there is an assumption here that MACHINE is always # the first entry in TARGET_SPEC_VARS. # If TARGET_SPEC and MACHINE are insufficient, you have a problem. _m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:C;${MACHINE}((,.+)?)$;${d:E:C/,.*//}\1;:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]} .if !empty(_m) # M_dep_qual_fixes isn't geared to Makefile.depend _qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}} .if ${_debug_search} .info Looking for ${_qm} .endif # set this "just in case" # we can skip :tA since we computed the path above DEP_RELDIR := ${_m:H:S,^${SRCTOP}/,,} # and reset this DIRDEPS = .if ${_debug_reldir} && ${_qm} != ${_m} .info loading ${_m} for ${d:E} .endif .include <${_m}> .else .-include .endif .endif .endif .endfor .endif # -V .endif # BUILD_DIRDEPS .elif ${.MAKE.LEVEL} > 42 .error You should have stopped recursing by now. .else # we are building something DEP_RELDIR := ${RELDIR} _DEP_RELDIR := ${RELDIR} # Since we are/should be included by .MAKE.DEPENDFILE # This is a final opportunity to add/hook global rules. .-include # skip _reldir_{finish,failed} if not included from Makefile.depend* # or not in meta mode .if !defined(WITHOUT_META_STATS) && ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" && ${.MAKE.MODE:Mmeta} != "" meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \ created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}} .if !target(_reldir_finish) .END: _reldir_finish _reldir_finish: .NOMETA @echo "${TRACER}Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" .endif .if !target(_reldir_failed) .ERROR: _reldir_failed _reldir_failed: .NOMETA @echo "${TRACER}Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" .endif .endif # pickup local dependencies .if ${MAKE_VERSION} < 20160220 .-include <.depend> .else .dinclude <.depend> .endif .endif # bootstrapping new dependencies made easy? .if !target(bootstrap-empty) .if !target(bootstrap) && (make(bootstrap) || \ make(bootstrap-this) || \ make(bootstrap-recurse) || \ make(bootstrap-empty)) # if we are bootstrapping create the default _want = ${.CURDIR}/${.MAKE.DEPENDFILE_DEFAULT:T} .if exists(${_want}) # stop here ${.TARGETS:Mboot*}: .elif !make(bootstrap-empty) # find a Makefile.depend to use as _src _src != cd ${.CURDIR} && for m in ${.MAKE.DEPENDFILE_PREFERENCE:T:S,${MACHINE},*,}; do test -s $$m || continue; echo $$m; break; done; echo .if empty(_src) .error cannot find any of ${.MAKE.DEPENDFILE_PREFERENCE:T}${.newline}Use: bootstrap-empty .endif _src?= ${.MAKE.DEPENDFILE} .MAKE.DEPENDFILE_BOOTSTRAP_SED+= -e 's/${_src:E:C/,.*//}/${MACHINE}/g' # just create Makefile.depend* for this dir bootstrap-this: .NOTMAIN @echo Bootstrapping ${RELDIR}/${_want:T} from ${_src:T}; \ echo You need to build ${RELDIR} to correctly populate it. .if ${_src:T} != ${.MAKE.DEPENDFILE_PREFIX:T} (cd ${.CURDIR} && sed ${.MAKE.DEPENDFILE_BOOTSTRAP_SED} ${_src} > ${_want:T}) .else cp ${.CURDIR}/${_src:T} ${_want} .endif # create Makefile.depend* for this dir and its dependencies bootstrap: bootstrap-recurse bootstrap-recurse: bootstrap-this _mf := ${.PARSEFILE} bootstrap-recurse: .NOTMAIN .MAKE @cd ${SRCTOP} && \ for d in `cd ${RELDIR} && ${.MAKE} -B -f ${"${.MAKEFLAGS:M-n}":?${_src}:${.MAKE.DEPENDFILE:T}} -V DIRDEPS`; do \ test -d $$d || d=$${d%.*}; \ test -d $$d || continue; \ echo "Checking $$d for bootstrap ..."; \ (cd $$d && ${.MAKE} -f ${_mf} bootstrap-recurse); \ done .endif # create an empty Makefile.depend* to get the ball rolling. bootstrap-empty: .NOTMAIN .NOMETA @echo Creating empty ${RELDIR}/${_want:T}; \ echo You need to build ${RELDIR} to correctly populate it. @{ echo DIRDEPS=; echo ".include "; } > ${_want} .endif .endif diff --git a/contrib/bmake/mk/gendirdeps.mk b/contrib/bmake/mk/gendirdeps.mk index b449bb5cc9d9..1ff2036237ed 100644 --- a/contrib/bmake/mk/gendirdeps.mk +++ b/contrib/bmake/mk/gendirdeps.mk @@ -1,388 +1,388 @@ -# $Id: gendirdeps.mk,v 1.47 2022/04/23 21:37:03 sjg Exp $ +# $Id: gendirdeps.mk,v 1.48 2022/09/09 17:44:29 sjg Exp $ # Copyright (c) 2011-2020, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # This makefile [re]generates ${.MAKE.DEPENDFILE} # .include # Assumptions: # RELDIR is the relative path from ${SRCTOP} to ${_CURDIR} # (SRCTOP is ${SB}/src) # _CURDIR is the absolute version of ${.CURDIR} # _OBJDIR is the absolute version of ${.OBJDIR} # _objroot is realpath of ${_OBJTOP} without ${MACHINE} # this may be different from _OBJROOT if $SB/obj is a # symlink to another filesystem. # _objroot must be a prefix match for _objtop .MAIN: all # keep this simple .MAKE.MODE = compat all: _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} _OBJTOP ?= ${OBJTOP} _OBJROOT ?= ${OBJROOT:U${_OBJTOP:H}} .if ${_OBJROOT:M*/} _slash=/ .else _slash= .endif _objroot ?= ${_OBJROOT:tA}${_slash} _this = ${.PARSEDIR}/${.PARSEFILE} # remember what to make _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T} # We do _not_ want to read our own output! .MAKE.DEPENDFILE = /dev/null # caller should have set this META_FILES ?= ${.MAKE.META.FILES} .if !empty(META_FILES) .if ${.MAKE.LEVEL} > 0 && !empty(GENDIRDEPS_FILTER) # so we can compare below .-include <${_DEPENDFILE}> # yes, I mean :U with no value _DIRDEPS := ${DIRDEPS:U:O:u} .endif META_FILES := ${META_FILES:T:O:u} # pickup customizations .-include # these are actually prefixes that we'll skip # they should all be absolute paths SKIP_GENDIRDEPS ?= .if !empty(SKIP_GENDIRDEPS) -_skip_gendirdeps = egrep -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' | +_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' | .else _skip_gendirdeps = .endif # Below we will turn _{VAR} into ${VAR} which keeps this simple # GENDIRDEPS_FILTER_DIR_VARS is a list of dirs to be substiuted for. # GENDIRDEPS_FILTER_VARS is more general. # In each case order matters. .if !empty(GENDIRDEPS_FILTER_DIR_VARS) GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@} .endif .if !empty(GENDIRDEPS_FILTER_VARS) GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u} .endif # this (*should* be set in meta.sys.mk) # is the script that extracts what we want. META2DEPS ?= ${.PARSEDIR}/meta2deps.sh META2DEPS := ${META2DEPS} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" && ${DEBUG_GENDIRDEPS:Uno:Mmeta2d*} != "" _time = time _sh_x = sh -x _py_d = -ddd .else _time = _sh_x = _py_d = .endif .if ${META2DEPS:E} == "py" # we can afford to do this all the time. DPDEPS ?= no META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d} .if ${DPDEPS:tl} != "no" META2DEPS_CMD += -D ${DPDEPS} .endif META2DEPS_FILTER = sed 's,^src:,${SRCTOP}/,;s,^\([^/]\),${OBJTOP}/\1,' | .elif ${META2DEPS:E} == "sh" META2DEPS_CMD = ${_time} ${_sh_x} ${META2DEPS} OBJTOP=${_OBJTOP} .else META2DEPS_CMD ?= ${META2DEPS} .endif .if ${TARGET_OBJ_SPEC:U${MACHINE}} != ${MACHINE} META2DEPS_CMD += -T ${TARGET_OBJ_SPEC} .endif META2DEPS_CMD += \ -R ${RELDIR} -H ${HOST_TARGET} \ ${M2D_OBJROOTS:O:u:@o@-O $o@} \ ${M2D_EXCLUDES:O:u:@o@-X $o@} \ M2D_OBJROOTS += ${OBJTOP} ${_OBJROOT} ${_objroot} .if defined(SB_OBJROOT) M2D_OBJROOTS += ${SB_OBJROOT} .endif .if defined(STAGE_ROOT) M2D_OBJROOTS += ${STAGE_ROOT} .endif .if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} == "" # meta2deps.py only groks objroot # so we need to give it what it expects # and tell it not to add machine qualifiers META2DEPS_ARGS += MACHINE=none .endif .if defined(SB_BACKING_SB) META2DEPS_CMD += -S ${SB_BACKING_SB}/src M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX} .endif GENDIRDEPS_SEDCMDS += \ -e 's,//*$$,,;s,\.${HOST_TARGET:Uhost}$$,.host,' \ -e 's,\.${HOST_TARGET32:Uhost32}$$,.host32,' \ -e 's,\.${MACHINE}$$,,' \ -e 's:\.${TARGET_SPEC:U${MACHINE}}$$::' # we are only interested in the dirs # specifically those we read something from. # we canonicalize them to keep things simple # if we are using a split-fs sandbox, it gets a little messier. _objtop := ${_OBJTOP:tA} # some people put *.meta in META_XTRAS to make sure we get here _meta_files := ${META_FILES:N\*.meta:O:u} # assume a big list _meta_files_arg= @meta.list .if empty(_meta_files) && ${META_FILES:M\*.meta} != "" # XXX this should be considered a bad idea, # since we cannot ignore stale .meta x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo .elif ${_meta_files:[#]} > 500 .export _meta_files x != echo; for m in $$_meta_files; do echo $$m; done > meta.list # _meta_files is consuming a lot of env space # that can impact command line length, # and we do not need it any more .undef _meta_files .unexport _meta_files .else _meta_files_arg:= ${_meta_files} .endif dir_list != cd ${_OBJDIR} && \ ${META2DEPS_CMD} MACHINE=${MACHINE} \ SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \ ${META2DEPS_ARGS} \ ${_meta_files_arg} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \ sed ${GENDIRDEPS_SEDCMDS} .if ${dir_list:M*ERROR\:*} != "" .warning ${dir_list:tW:C,.*(ERROR),\1,} .warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,} # we are not going to update anything .else dpadd_dir_list= .if !empty(DPADD) _nonlibs := ${DPADD:T:Nlib*:N*include} .if !empty(_nonlibs) ddep_list = .for f in ${_nonlibs:@x@${DPADD:M*/$x}@} .if exists($f.dirdep) ddep_list += $f.dirdep .elif exists(${f:H}.dirdep) ddep_list += ${f:H}.dirdep .else dir_list += ${f:H:tA} dpadd_dir_list += ${f:H:tA} .endif .endfor .if !empty(ddep_list) ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \ sed ${GENDIRDEPS_SEDCMDS} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: raw_dir_list='${dir_list}' .info ${RELDIR}: ddeps='${ddeps}' .endif dir_list += ${ddeps} .endif .endif .endif # DIRDEPS represent things that had to have been built first # so they should all be undir OBJTOP. # Note that ${_OBJTOP}/bsd/include/machine will get reported # to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we # will want to visit bsd/include # so we add # ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:} # to GENDIRDEPS_DIR_LIST_XTRAS _objtops = ${OBJTOP} ${_OBJTOP} ${_objtop} _objtops := ${_objtops:O:u} dirdep_list = \ ${_objtops:@o@${dir_list:M$o*/*:C,$o[^/]*/,,}@} \ ${GENDIRDEPS_DIR_LIST_XTRAS} # sort longest first M2D_OBJROOTS := ${M2D_OBJROOTS:O:u:[-1..1]} # anything we use from an object dir other than ours # needs to be qualified with its . suffix # (we used the pseudo machine "host" for the HOST_TARGET). skip_ql= ${SRCTOP}* ${_objtops:@o@$o*@} .for o in ${M2D_OBJROOTS:${skip_ql:${M_ListToSkip}}} # we need := so only skip_ql to this point applies ql.$o := ${dir_list:${skip_ql:${M_ListToSkip}}:M$o*/*/*:C,$o([^/]+)/(.*),\2.\1,:S,.${HOST_TARGET},.host,} qualdir_list += ${ql.$o} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: o=$o ${ql.$o qualdir_list:L:@v@$v=${$v}@} .endif skip_ql+= $o* .endfor dirdep_list := ${dirdep_list:O:u} qualdir_list := ${qualdir_list:N*.${MACHINE}:O:u} DIRDEPS = \ ${dirdep_list:N${RELDIR}:N${RELDIR}/*} \ ${qualdir_list:N${RELDIR}.*:N${RELDIR}/*} # We only consider things below $RELDIR/ if they have a makefile. # This is the same test that _DIRDEP_USE applies. # We have do a double test with dirdep_list as it _may_ contain # qualified dirs - if we got anything from a stage dir. # qualdir_list we know are all qualified. # It would be nice do peform this check for all of DIRDEPS, # but we cannot assume that all of the tree is present, # in fact we can only assume that RELDIR is. DIRDEPS += \ ${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \ ${qualdir_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/${d:R}/$m):?$d:}@}@} # what modifiers do we allow in GENDIRDEPS_FILTER GENDIRDEPS_FILTER_MASK += @CMNS DIRDEPS := ${DIRDEPS:${GENDIRDEPS_FILTER:UNno:M[${GENDIRDEPS_FILTER_MASK:O:u:ts}]*:ts:}:C,//+,/,g:O:u} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: M2D_OBJROOTS=${M2D_OBJROOTS} .info ${RELDIR}: M2D_EXCLUDES=${M2D_EXCLUDES} .info ${RELDIR}: dir_list='${dir_list}' .info ${RELDIR}: dpadd_dir_list='${dpadd_dir_list}' .info ${RELDIR}: dirdep_list='${dirdep_list}' .info ${RELDIR}: qualdir_list='${qualdir_list}' .info ${RELDIR}: SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS}' .info ${RELDIR}: GENDIRDEPS_FILTER='${GENDIRDEPS_FILTER}' .info ${RELDIR}: FORCE_DPADD='${DPADD}' .info ${RELDIR}: DIRDEPS='${DIRDEPS}' .endif # SRC_DIRDEPS is for checkout logic src_dirdep_list = \ ${dir_list:M${SRCTOP}/*:S,${SRCTOP}/,,} SRC_DIRDEPS = \ ${src_dirdep_list:N${RELDIR}:N${RELDIR}/*:C,(/h)/.*,,} SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u} # if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put # SRC_DIRDEPS_FILE = ${_DEPENDFILE} # in local.gendirdeps.mk .if ${SRC_DIRDEPS_FILE:Uno:tl} != "no" ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@ $d \\${.newline}@}'; echo; .if ${SRC_DIRDEPS_FILE:T} == ${_DEPENDFILE:T} _include_src_dirdeps = ${ECHO_SRC_DIRDEPS} .else all: ${SRC_DIRDEPS_FILE} .if !target(${SRC_DIRDEPS_FILE}) ${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS} @(${ECHO_SRC_DIRDEPS}) > $@ .endif .endif .endif _include_src_dirdeps ?= all: ${_DEPENDFILE} # if this is going to exist it would be there by now .if !exists(.depend) CAT_DEPEND = /dev/null .endif CAT_DEPEND ?= .depend .if !empty(_DIRDEPS) && ${DIRDEPS} != ${_DIRDEPS} # we may have changed a filter .PHONY: ${_DEPENDFILE} .endif # set this to 'no' and we will not capture any # local depends LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0 # 'cat .depend' should suffice, but if we are mixing build modes # .depend may contain things we don't want. # The sed command at the end of the stream, allows for the filters # to output _{VAR} tokens which we will turn into proper ${VAR} references. ${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):?$m:}@} ${_this} ${META2DEPS} @(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \ echo 'DIRDEPS = \'; \ echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \ ${_include_src_dirdeps} \ echo '.include '; \ [ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \ echo; \ echo '.if ${LOCAL_DEPENDS_GUARD}'; \ echo '# local dependencies - needed for -jN in clean tree'; \ [ -s ${CAT_DEPEND} ] && { grep : ${CAT_DEPEND} | grep -v '[/\\]'; }; \ echo '.endif' ) | sed 's,_\([{(]\),$$\1,g' > $@.new${.MAKE.PID} @${InstallNew}; InstallNew -s $@.new${.MAKE.PID} .endif # meta2deps failed .elif !empty(SUBDIR) DIRDEPS := ${SUBDIR:S,^,${RELDIR}/,:O:u} all: ${_DEPENDFILE} ${_DEPENDFILE}: .NOMETA ${MAKEFILE} ${_this} @(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \ echo 'DIRDEPS = \'; \ echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \ echo '.include '; \ echo ) | sed 's,_\([{(]\),$$\1,g' > $@.new @${InstallNew}; InstallNew $@.new .else # nothing to do all ${_DEPENDFILE}: .endif ${_DEPENDFILE}: .PRECIOUS # don't waste time looking for ways to make .meta files .SUFFIXES: diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk index 5fbdd49e26b5..51fb68b959a0 100644 --- a/contrib/bmake/mk/install-mk +++ b/contrib/bmake/mk/install-mk @@ -1,185 +1,191 @@ : # NAME: # install-mk - install mk files # # SYNOPSIS: # install-mk [options] [var=val] [dest] # # DESCRIPTION: # This tool installs mk files in a semi-intelligent manner into # "dest". # # Options: # # -n just say what we want to do, but don't touch anything. # # -f use -f when copying sys,mk. # # -v be verbose # # -q be quiet # # -m "mode" # Use "mode" for installed files (444). # # -o "owner" # Use "owner" for installed files. # # -g "group" # Use "group" for installed files. # # var=val # Set "var" to "val". See below. # # All our *.mk files are copied to "dest" with appropriate # ownership and permissions. # # By default if a sys.mk can be found in a standard location # (that bmake will find) then no sys.mk will be put in "dest". # # SKIP_SYS_MK: # If set, we will avoid installing our 'sys.mk' # This is probably a bad idea. # # SKIP_BSD_MK: # If set, we will skip making bsd.*.mk links to *.mk # # sys.mk: # # By default (and provided we are not installing to the system # mk dir - '/usr/share/mk') we install our own 'sys.mk' which # includes a sys specific file, or a generic one. # # # AUTHOR: # Simon J. Gerraty # RCSid: -# $Id: install-mk,v 1.220 2022/07/22 20:08:56 sjg Exp $ +# $Id: install-mk,v 1.224 2023/01/20 17:34:06 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # -MK_VERSION=20220720 +MK_VERSION=20230120 OWNER= GROUP= MODE=444 BINMODE=555 ECHO=: SKIP= cp_f=-f while : do case "$1" in *=*) eval "$1"; shift;; +f) cp_f=; shift;; -f) cp_f=-f; shift;; -m) MODE=$2; shift 2;; -o) OWNER=$2; shift 2;; -g) GROUP=$2; shift 2;; -v) ECHO=echo; shift;; -q) ECHO=:; shift;; -n) ECHO=echo SKIP=:; shift;; --) shift; break;; *) break;; esac done case $# in 0) echo "$0 [options] []" echo "eg." echo "$0 -o bin -g bin -m 444 /usr/local/share/mk" exit 1 ;; esac dest=$1 os=${2:-`uname`} osrel=${3:-`uname -r`} Do() { $ECHO "$@" $SKIP "$@" } Error() { echo "ERROR: $@" >&2 exit 1 } Warning() { echo "WARNING: $@" >&2 } [ "$FORCE_SYS_MK" ] && Warning "ignoring: FORCE_{BSD,SYS}_MK (no longer supported)" SYS_MK_DIR=${SYS_MK_DIR:-/usr/share/mk} SYS_MK=${SYS_MK:-$SYS_MK_DIR/sys.mk} realpath() { [ -d $1 ] && cd $1 && 'pwd' && return echo $1 } +# some Linux systems have deprecated egrep in favor of grep -E +case "`echo bmake | egrep 'a' 2>&1`" in +*"grep -E"*) egrep='grep -E';; +*) egrep=egrep;; +esac + if [ -s $SYS_MK -a -d $dest ]; then # if this is a BSD system we don't want to touch $SYS_MK dest=`realpath $dest` sys_mk_dir=`realpath $SYS_MK_DIR` if [ $dest = $sys_mk_dir ]; then case "$os" in *BSD*) SKIP_SYS_MK=: SKIP_BSD_MK=: ;; *) # could be fake? if [ ! -d $dest/sys -a ! -s $dest/Generic.sys.mk ]; then SKIP_SYS_MK=: # play safe SKIP_BSD_MK=: fi ;; esac fi fi [ -d $dest/sys ] || Do mkdir -p $dest/sys [ -d $dest/sys ] || Do mkdir $dest/sys || exit 1 [ -z "$SKIP" ] && dest=`realpath $dest` cd `dirname $0` mksrc=`'pwd'` if [ $mksrc = $dest ]; then SKIP_MKFILES=: else # we do not install the examples - mk_files=`grep '^[a-z].*\.mk' FILES | egrep -v '(examples/|^sys\.mk|sys/)'` - mk_scripts=`egrep '^[a-z].*\.(sh|py)' FILES | egrep -v '/'` + mk_files=`grep '^[a-z].*\.mk' FILES | $egrep -v '(examples/|^sys\.mk|sys/)'` + mk_scripts=`$egrep '^[a-z].*\.(sh|py)' FILES | $egrep -v '/'` sys_mk_files=`grep 'sys/.*\.mk' FILES` SKIP_MKFILES= [ -z "$SKIP_SYS_MK" ] && mk_files="sys.mk $mk_files" fi $SKIP_MKFILES Do cp $cp_f $mk_files $dest $SKIP_MKFILES Do cp $cp_f $sys_mk_files $dest/sys $SKIP_MKFILES Do cp $cp_f $mk_scripts $dest $SKIP cd $dest $SKIP_MKFILES Do chmod $MODE $mk_files $sys_mk_files $SKIP_MKFILES Do chmod $BINMODE $mk_scripts [ "$GROUP" ] && $SKIP_MKFILES Do chgrp $GROUP $mk_files $sys_mk_files [ "$OWNER" ] && $SKIP_MKFILES Do chown $OWNER $mk_files $sys_mk_files # if this is a BSD system the bsd.*.mk should exist and be used. if [ -z "$SKIP_BSD_MK" ]; then for f in dep doc files inc init lib links man nls obj own prog subdir do b=bsd.$f.mk [ -s $b ] || Do ln -s $f.mk $b done fi exit 0 diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk index f9ed6d305b99..cd08ac3b3520 100644 --- a/contrib/bmake/mk/meta.autodep.mk +++ b/contrib/bmake/mk/meta.autodep.mk @@ -1,329 +1,329 @@ -# $Id: meta.autodep.mk,v 1.55 2021/12/13 08:12:01 sjg Exp $ +# $Id: meta.autodep.mk,v 1.56 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # _this ?= ${.PARSEFILE} .if !target(__${_this}__) __${_this}__: .NOTMAIN .-include PICO?= .pico .if defined(SRCS) .if ${MAKE_VERSION:U0} >= 20211212 OBJ_EXTENSIONS += ${.SUFFIXES:M*o} .else # it would be nice to be able to query .SUFFIXES OBJ_EXTENSIONS += .o .po .lo ${PICO} .endif # explicit dependencies help short-circuit .SUFFIX searches SRCS_DEP_FILTER+= N*.[hly] .for s in ${SRCS:${SRCS_DEP_FILTER:O:u:ts:}} .for e in ${OBJ_EXTENSIONS:O:u} .if !target(${s:T:R}$e) ${s:T:R}$e: $s .endif .endfor .endfor .endif .if make(gendirdeps) # you are supposed to know what you are doing! UPDATE_DEPENDFILE = yes .elif !empty(.TARGETS) && !make(all) # do not update the *depend* files # unless we are building the entire directory or the default target. # NO means don't update .depend - or Makefile.depend* # no means update .depend but not Makefile.depend* UPDATE_DEPENDFILE = NO .elif ${.MAKEFLAGS:M-k} != "" # it is a bad idea to update anything UPDATE_DEPENDFILE = NO .endif _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} _OBJTOP ?= ${OBJTOP} _OBJROOT ?= ${OBJROOT:U${_OBJTOP}} _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T} .if ${.MAKE.LEVEL} > 0 # do not allow auto update if we ever built this dir without filemon NO_FILEMON_COOKIE = .nofilemon CLEANFILES += ${NO_FILEMON_COOKIE} .if ${.MAKE.MODE:Uno:Mnofilemon} != "" UPDATE_DEPENDFILE = NO all: ${NO_FILEMON_COOKIE} ${NO_FILEMON_COOKIE}: .NOMETA @echo UPDATE_DEPENDFILE=NO > ${.TARGET} .elif exists(${NO_FILEMON_COOKIE}) UPDATE_DEPENDFILE = NO .warning ${RELDIR} built with nofilemon; UPDATE_DEPENDFILE=NO .endif .endif .if ${.MAKE.LEVEL} == 0 UPDATE_DEPENDFILE = NO .endif .if !exists(${_DEPENDFILE}) _bootstrap_dirdeps = yes .endif _bootstrap_dirdeps ?= no UPDATE_DEPENDFILE ?= yes .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} update=${UPDATE_DEPENDFILE} .endif .if !empty(XMAKE_META_FILE) .if exists(${.OBJDIR}/${XMAKE_META_FILE}) # we cannot get accurate dependencies from an update build UPDATE_DEPENDFILE = NO .else META_XTRAS += ${XMAKE_META_FILE} .endif .endif .if ${_bootstrap_dirdeps} == "yes" || exists(${_DEPENDFILE}) # if it isn't supposed to be touched by us the Makefile should have # UPDATE_DEPENDFILE = no WANT_UPDATE_DEPENDFILE ?= yes .endif .if ${WANT_UPDATE_DEPENDFILE:Uno:tl} != "no" .if ${.MAKE.MODE:Uno:Mmeta*} == "" || ${.MAKE.MODE:Uno:M*read*} != "" UPDATE_DEPENDFILE = no .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} update=${UPDATE_DEPENDFILE} .endif .if ${UPDATE_DEPENDFILE:tl} == "yes" # sometimes we want .meta files generated to aid debugging/error detection # but do not want to consider them for dependencies # for example the result of running configure # just make sure this is not empty META_FILE_FILTER ?= N.meta # never consider these META_FILE_FILTER += Ndirdeps.cache* .if !empty(DPADD) # if we have any non-libs in DPADD, # they probably need to be paid attention to .if !empty(DPLIBS) FORCE_DPADD = ${DPADD:${DPLIBS:${M_ListToSkip}}:${DPADD_LAST:${M_ListToSkip}}} .else _nonlibs := ${DPADD:T:Nlib*:N*include} .if !empty(_nonlibs) FORCE_DPADD += ${_nonlibs:@x@${DPADD:M*/$x}@} .endif .endif .endif .if !make(gendirdeps) .END: gendirdeps .endif # if we don't have OBJS, then .depend isn't useful .if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "") # some makefiles and/or targets contain # circular dependencies if you dig too deep # (as meta mode is apt to do) # so we provide a means of suppressing them. # the input to the loop below is target: dependency # with just one dependency per line. # Also some targets are not really local, or use random names. # Use local.autodep.mk to provide local additions! SUPPRESS_DEPEND += \ ${SB:S,/,_,g}* \ *:y.tab.c \ *.c:*.c \ *.h:*.h .NOPATH: .depend # we use ${.MAKE.META.CREATED} to trigger an update but # we process using ${.MAKE.META.FILES} # the double $$ defers initial evaluation # if necessary, we fake .po dependencies, just so the result # in Makefile.depend* is stable # The current objdir may be referred to in various ways OBJDIR_REFS += ${.OBJDIR} ${.OBJDIR:tA} ${_OBJDIR} ${RELOBJTOP}/${RELDIR} _depend = .depend # it would be nice to be able to get .SUFFIXES as ${.SUFFIXES} # we actually only care about the .SUFFIXES of files that might be # generated by tools like yacc. .if ${MAKE_VERSION:U0} >= 20211212 DEPEND_SUFFIXES += ${.SUFFIXES:N.sh:N*[0-9aFfglopmnrSsty]} .else DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh .endif .depend: .NOMETA $${.MAKE.META.CREATED} ${_this} @echo "Updating $@: ${.OODATE:T:[1..8]}" - @egrep -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ + @${EGREP:Uegrep} -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ sed -e 's, \./, ,${OBJDIR_REFS:O:u:@d@;s, $d/, ,@};/\//d' \ -e 's,^\([^/][^/]*\).meta...[0-9]* ,\1: ,' | \ sort -u | \ while read t d; do \ case "$$d:" in $$t) continue;; esac; \ case "$$t$$d" in ${SUPPRESS_DEPEND:U.:O:u:ts|}) continue;; esac; \ echo $$t $$d; \ done > $@.${.MAKE.PID} @case "${.MAKE.META.FILES:T:M*.po.*}" in \ *.po.*) mv $@.${.MAKE.PID} $@;; \ *) { cat $@.${.MAKE.PID}; \ sed ${OBJ_EXTENSIONS:N.o:N.po:@o@-e 's,\$o:,.o:,'@} \ -e 's,\.o:,.po:,' $@.${.MAKE.PID}; } | sort -u > $@; \ rm -f $@.${.MAKE.PID};; \ esac .else # make sure this exists .depend: # do _not_ assume that .depend is in any fit state for us to use CAT_DEPEND = /dev/null .if ${.MAKE.LEVEL} > 0 .export CAT_DEPEND .endif _depend = .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} _depend=${_depend} .endif .if ${UPDATE_DEPENDFILE} == "yes" gendirdeps: ${_DEPENDFILE} .endif .if !target(${_DEPENDFILE}) .if ${_bootstrap_dirdeps} == "yes" # We are boot-strapping a new directory # Use DPADD to seed DIRDEPS .if !empty(DPADD) # anything which matches ${_OBJROOT}* but not ${_OBJTOP}* # needs to be qualified in DIRDEPS # The pseudo machine "host" is used for HOST_TARGET DIRDEPS += \ ${DPADD:M${_OBJTOP}*:H:C,${_OBJTOP}[^/]*/,,:N.:O:u} \ ${DPADD:M${_OBJROOT}*:N${_OBJTOP}*:N${STAGE_ROOT:U${_OBJTOP}}/*:H:S,${_OBJROOT},,:C,^([^/]+)/(.*),\2.\1,:S,${HOST_TARGET}$,host,:N.*:O:u} .endif .endif _gendirdeps_mutex = .if defined(NEED_GENDIRDEPS_MUTEX) # If a src dir gets built with multiple object dirs, # we need a mutex. Obviously, this is best avoided. # Note if .MAKE.DEPENDFILE is common for all ${MACHINE} # you either need to mutex, or ensure only one machine builds at a time! # lockf is an example of a suitable tool LOCKF ?= /usr/bin/lockf .if exists(${LOCKF}) GENDIRDEPS_MUTEXER ?= ${LOCKF} -k .endif .if empty(GENDIRDEPS_MUTEXER) .error NEED_GENDIRDEPS_MUTEX defined, but GENDIRDEPS_MUTEXER not set .else _gendirdeps_mutex = ${GENDIRDEPS_MUTEXER} ${GENDIRDEPS_MUTEX:U${_CURDIR}/Makefile} .endif .endif # If we have META_XTRAS we most likely did not create them # but we need to behave as if we did. # Avoid adding glob patterns to .MAKE.META.CREATED though. .MAKE.META.CREATED += ${META_XTRAS:N*\**:O:u} .if make(gendirdeps) META_FILES = *.meta .elif ${OPTIMIZE_OBJECT_META_FILES:Uno:tl} == "no" META_FILES = ${.MAKE.META.FILES:T:N.depend*:O:u} .else # if we have 1000's of .o.meta, ${PICO}.meta etc we need only look at one set # it is left as an exercise for the reader to work out what this does META_FILES = ${.MAKE.META.FILES:T:N.depend*:N*o.meta:O:u} \ ${.MAKE.META.FILES:T:M*.${.MAKE.META.FILES:M*o.meta:R:E:O:u:[1]}.meta:O:u} .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} xtras=${META_XTRAS} .endif .if ${.MAKE.LEVEL} > 0 .if ${UPDATE_DEPENDFILE} == "yes" .-include <${.CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.options> .endif .if !empty(GENDIRDEPS_FILTER) .export GENDIRDEPS_FILTER .endif # export to avoid blowing command line limit META_FILES := ${META_XTRAS:U:O:u} ${META_FILES:U:T:O:u:${META_FILE_FILTER:ts:}} .export META_FILES .endif # we might have .../ in MAKESYSPATH _makesyspath:= ${_PARSEDIR} ${_DEPENDFILE}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} $${.MAKE.META.CREATED} @echo Checking $@: ${.OODATE:T:[1..8]} @(cd . && ${GENDIRDEPS_ENV} \ SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS:O:u}' \ DPADD='${FORCE_DPADD:O:u}' ${_gendirdeps_mutex} \ MAKESYSPATH=${_makesyspath} \ ${.MAKE} -f gendirdeps.mk RELDIR=${RELDIR} _DEPENDFILE=${_DEPENDFILE}) @test -s $@ && touch $@; : .endif .endif .endif .if ${_bootstrap_dirdeps} == "yes" DIRDEPS+= ${RELDIR}.${TARGET_SPEC:U${MACHINE}} # make sure this is included at least once .include .else ${_DEPENDFILE}: .PRECIOUS .endif CLEANFILES += *.meta filemon.* *.db # these make it easy to gather some stats now_utc = ${%s:L:gmtime} start_utc := ${now_utc} meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \ created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}} .if !target(_reldir_finish) #.END: _reldir_finish .if target(gendirdeps) _reldir_finish: gendirdeps .endif _reldir_finish: .NOMETA @echo "${TIME_STAMP} Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" .endif .if !target(_reldir_failed) #.ERROR: _reldir_failed _reldir_failed: .NOMETA @echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" .endif .if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 .END: _reldir_finish .ERROR: _reldir_failed .endif .endif diff --git a/contrib/bmake/mk/meta.stage.mk b/contrib/bmake/mk/meta.stage.mk index 1da45f4f2167..e4ca45820110 100644 --- a/contrib/bmake/mk/meta.stage.mk +++ b/contrib/bmake/mk/meta.stage.mk @@ -1,370 +1,370 @@ -# $Id: meta.stage.mk,v 1.64 2021/12/08 05:56:50 sjg Exp $ +# $Id: meta.stage.mk,v 1.65 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2011-2017, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .ifndef NO_STAGING .if !target(__${.PARSEFILE}__) # the guard target is defined later .-include .if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} != "" # this is generally safer anyway _dirdep ?= ${RELDIR}.${TARGET_SPEC:U${MACHINE}} .else _dirdep ?= ${RELDIR} .endif CLEANFILES+= .dirdep # this allows us to trace dependencies back to their src dir .dirdep: .NOPATH .if !commands(.dirdep) .dirdep: @echo '${_dirdep}' > $@ .endif .if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == "" _stage_file_basename = `basename $$f` _stage_target_dirname = `dirname $$t` .else _stage_file_basename = $${f\#\#*/} _stage_target_dirname = $${t%/*} .endif _OBJROOT ?= ${OBJROOT:U${OBJTOP:H}} .if ${_OBJROOT:M*/} != "" _objroot ?= ${_OBJROOT:tA}/ .else _objroot ?= ${_OBJROOT:tA} .endif # make sure this is global _STAGED_DIRS ?= .export _STAGED_DIRS # add each dir we stage to to _STAGED_DIRS # and make sure we have absolute paths so that bmake # will match against .MAKE.META.BAILIWICK STAGE_DIR_FILTER = tA:@d@$${_STAGED_DIRS::+=$$d}$$d@ # convert _STAGED_DIRS into suitable filters GENDIRDEPS_FILTER += Nnot-empty-is-important \ ${_STAGED_DIRS:O:u:M${OBJTOP}*:S,${OBJTOP}/,N,} \ ${_STAGED_DIRS:O:u:M${_objroot}*:N${OBJTOP}*:S,${_objroot},,:C,^([^/]+)/(.*),N\2.\1,:S,${HOST_TARGET},.host,} LN_CP_SCRIPT = LnCp() { \ rm -f $$2 2> /dev/null; \ { [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \ cp -p $$1 $$2 2> /dev/null || cp $$1 $$2; } # a staging conflict should cause an error # a warning is handy when bootstapping different options. STAGE_CONFLICT?= ERROR .if ${STAGE_CONFLICT:tl} == "error" STAGE_CONFLICT_ACTION= exit 1 .else STAGE_CONFLICT_ACTION= .endif # it is an error for more than one src dir to try and stage # the same file STAGE_DIRDEP_SCRIPT = ${LN_CP_SCRIPT}; StageDirdep() { \ t=$$1; \ if [ -s $$t.dirdep ]; then \ cmp -s .dirdep $$t.dirdep && return; \ x=`cat $$t.dirdep`; \ case "${RELDIR}:${_dirdep}" in $${x%.*}:$${x}*) ;; \ *) echo "${STAGE_CONFLICT}: $$t installed by $$x not ${_dirdep}" >&2; \ ${STAGE_CONFLICT_ACTION} ;; esac; \ fi; \ LnCp .dirdep $$t.dirdep || exit 1; } # common logic for staging files # this all relies on RELDIR being set to a subdir of SRCTOP # we use ln(1) if we can, else cp(1) STAGE_FILE_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageFiles() { \ case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ for f in "$$@"; do \ case "$$f" in */*) t=$$dest/${_stage_file_basename};; *) t=$$dest/$$f;; esac; \ StageDirdep $$t; \ LnCp $$f $$t || exit 1; \ [ -z "$$mode" ] || chmod $$mode $$t; \ done; :; } STAGE_LINKS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageLinks() { \ case "$$1" in "") return;; --) shift;; -*) ldest= lnf=$$1; shift;; /*) ldest=$$1/;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ while test $$\# -ge 2; do \ l=$$ldest$$1; shift; \ t=$$dest/$$1; \ case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \ shift; \ StageDirdep $$t; \ rm -f $$t 2>/dev/null; \ ln $$lnf $$l $$t || exit 1; \ done; :; } STAGE_AS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageAs() { \ case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ while test $$\# -ge 2; do \ s=$$1; shift; \ t=$$dest/$$1; \ case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \ shift; \ StageDirdep $$t; \ LnCp $$s $$t || exit 1; \ [ -z "$$mode" ] || chmod $$mode $$t; \ done; :; } # this is simple, a list of the "staged" files depends on this, _STAGE_BASENAME_USE: .USE .dirdep ${.TARGET:T} @${STAGE_FILE_SCRIPT}; StageFiles ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T} _STAGE_AS_BASENAME_USE: .USE .dirdep ${.TARGET:T} @${STAGE_AS_SCRIPT}; StageAs ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T} ${STAGE_AS_${.TARGET:T}:U${.TARGET:T}} .endif # first time .if !empty(STAGE_INCSDIR) .if !empty(STAGE_INCS) stage_incs: ${STAGE_INCS:N*\**} .endif .if target(stage_incs) || !empty(.ALLTARGETS:Mstage_includes) STAGE_TARGETS += stage_incs STAGE_INCS ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_includes: stage_incs stage_incs: .dirdep @${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_INCSDIR:${STAGE_DIR_FILTER}} ${STAGE_INCS} @touch $@ .endif .endif .if !empty(STAGE_LIBDIR) .if !empty(STAGE_LIBS) stage_libs: ${STAGE_LIBS:N*\**} .endif .if target(stage_libs) STAGE_TARGETS += stage_libs STAGE_LIBS ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_libs: .dirdep @${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${STAGE_LIBS} .if !defined(NO_SHLIB_LINKS) .if !empty(SHLIB_LINKS) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} \ ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*} $t@} .elif !empty(SHLIB_LINK) && !empty(SHLIB_NAME) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${SHLIB_NAME} ${SHLIB_LINK} .endif .endif @touch $@ .endif .endif .if !empty(STAGE_DIR) STAGE_SETS += _default STAGE_DIR._default = ${STAGE_DIR} STAGE_LINKS_DIR._default = ${STAGE_LINKS_DIR:U${STAGE_OBJTOP}} STAGE_SYMLINKS_DIR._default = ${STAGE_SYMLINKS_DIR:U${STAGE_OBJTOP}} STAGE_FILES._default = ${STAGE_FILES} STAGE_LINKS._default = ${STAGE_LINKS} STAGE_SYMLINKS._default = ${STAGE_SYMLINKS} .endif .if !empty(STAGE_SETS) CLEANFILES += ${STAGE_SETS:@s@stage*$s@} # some makefiles need to populate multiple directories .for s in ${STAGE_SETS:O:u} .if !empty(STAGE_FILES.$s) stage_files.$s: ${STAGE_FILES.$s:N*\**} .endif .if target(stage_files.$s) || target(stage_files${s:S,^,.,:N._default}) STAGE_TARGETS += stage_files STAGE_FILES.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_files.$s) .stage_files.$s: .if $s != "_default" stage_files: stage_files.$s stage_files.$s: .dirdep .else STAGE_FILES ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_files: .dirdep .endif @${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s:O} @touch $@ .endif .endif .if !empty(STAGE_LINKS.$s) stage_links.$s: .endif .if target(stage_links.$s) || target(stage_links${s:S,^,.,:N._default}) STAGE_LINKS_DIR.$s ?= ${STAGE_OBJTOP} STAGE_TARGETS += stage_links .if !target(.stage_links.$s) .stage_links.$s: .if $s != "_default" stage_links: stage_links.$s stage_links.$s: .dirdep .else stage_links: .dirdep .endif @${STAGE_LINKS_SCRIPT}; StageLinks ${STAGE_LINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_LINKS.$s} @touch $@ .endif .endif .if !empty(STAGE_SYMLINKS.$s) stage_symlinks.$s: .endif .if target(stage_symlinks.$s) || target(stage_symlinks${s:S,^,.,:N._default}) STAGE_SYMLINKS_DIR.$s ?= ${STAGE_OBJTOP} STAGE_TARGETS += stage_symlinks .if !target(.stage_symlinks.$s) .stage_symlinks.$s: .if $s != "_default" stage_symlinks: stage_symlinks.$s stage_symlinks.$s: .dirdep .else stage_symlinks: .dirdep .endif @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_SYMLINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_SYMLINKS.$s} @touch $@ .endif .endif .endfor .endif .if !empty(STAGE_AS_SETS) CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@} # sometimes things need to be renamed as they are staged # each ${file} will be staged as ${STAGE_AS_${file:T}} # one could achieve the same with SYMLINKS # stage_as_and_symlink makes the original name (or ${STAGE_LINK_AS_${name}}) # a symlink to the new name # it is the same as using stage_as and stage_symlinks but ensures # both operations happen together .for s in ${STAGE_AS_SETS:O:u} .if !empty(STAGE_AS.$s) stage_as.$s: ${STAGE_AS.$s:N*\**} .endif .if target(stage_as.$s) STAGE_TARGETS += stage_as STAGE_AS.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_as.$s) .stage_as.$s: stage_as: stage_as.$s stage_as.$s: .dirdep @${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@} @touch $@ .endif .endif .if !empty(STAGE_AS_AND_SYMLINK.$s) stage_as_and_symlink.$s: ${STAGE_AS_AND_SYMLINK.$s:N*\**} .endif .if target(stage_as_and_symlink.$s) STAGE_TARGETS += stage_as_and_symlink STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_as_and_symlink.$s) .stage_as_and_symlink.$s: stage_as_and_symlink: stage_as_and_symlink.$s stage_as_and_symlink.$s: .dirdep @${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@} @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} ${STAGE_LINK_AS_${f}:U$f}@} @touch $@ .endif .endif .endfor .endif CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes # this lot also only makes sense the first time... .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .NOTMAIN # stage_*links usually needs to follow any others. # for non-jobs mode the order here matters staging: ${STAGE_TARGETS:N*_links} ${STAGE_TARGETS:M*_links} .if ${.MAKE.JOBS:U0} > 0 && ${STAGE_TARGETS:U:M*_links} != "" # the above isn't sufficient .for t in ${STAGE_TARGETS:N*links:O:u} .ORDER: $t stage_links .endfor .endif # generally we want staging to wait until everything else is done STAGING_WAIT ?= .WAIT .if ${.MAKE.LEVEL} > 0 all: ${STAGING_WAIT} staging .endif .if exists(${.PARSEDIR}/stage-install.sh) && !defined(STAGE_INSTALL) # this will run install(1) and then followup with .dirdep files. STAGE_INSTALL := sh ${.PARSEDIR:tA}/stage-install.sh INSTALL="${INSTALL}" OBJDIR=${.OBJDIR:tA} .endif # if ${INSTALL} gets run during 'all' assume it is for staging? .if ${.TARGETS:Nall} == "" && defined(STAGE_INSTALL) INSTALL := ${STAGE_INSTALL} .if target(beforeinstall) beforeinstall: .dirdep .endif .endif .NOPATH: ${STAGE_FILES} .if !empty(STAGE_TARGETS) # for backwards compat make sure they exist ${STAGE_TARGETS}: .NOPATH: ${CLEANFILES} MK_STALE_STAGED?= no .if ${MK_STALE_STAGED} == "yes" all: stale_staged # get a list of paths that we have just staged # get a list of paths that we have previously staged to those same dirs # anything in the 2nd list but not the first is stale - remove it. stale_staged: staging .NOMETA - @egrep '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \ + @${EGREP:Uegrep} '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \ sed "/\.dirdep/d;s,.* '*\(${STAGE_OBJTOP}/[^ '][^ ']*\).*,\1," | \ sort > ${.TARGET}.staged1 @grep -l '${_dirdep}' /dev/null ${_STAGED_DIRS:M${STAGE_OBJTOP}*:O:u:@d@$d/*.dirdep@} | \ sed 's,\.dirdep,,' | sort > ${.TARGET}.staged2 @comm -13 ${.TARGET}.staged1 ${.TARGET}.staged2 > ${.TARGET}.stale @test ! -s ${.TARGET}.stale || { \ echo "Removing stale staged files..."; \ sed 's,.*,& &.dirdep,' ${.TARGET}.stale | xargs rm -f; } .endif .endif .endif .endif diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py index bc6975182429..d7820ec71b5b 100755 --- a/contrib/bmake/mk/meta2deps.py +++ b/contrib/bmake/mk/meta2deps.py @@ -1,813 +1,824 @@ #!/usr/bin/env python from __future__ import print_function """ This script parses each "meta" file and extracts the information needed to deduce build and src dependencies. It works much the same as the original shell script, but is *much* more efficient. The parsing work is handled by the class MetaFile. We only pay attention to a subset of the information in the "meta" files. Specifically: 'CWD' to initialize our notion. 'C' to track chdir(2) on a per process basis 'R' files read are what we really care about. directories read, provide a clue to resolving subsequent relative paths. That is if we cannot find them relative to 'cwd', we check relative to the last dir read. 'W' files opened for write or read-write, for filemon V3 and earlier. 'E' files executed. 'L' files linked 'V' the filemon version, this record is used as a clue that we have reached the interesting bit. """ """ RCSid: - $Id: meta2deps.py,v 1.44 2022/01/29 02:42:01 sjg Exp $ + $Id: meta2deps.py,v 1.45 2023/01/18 01:35:24 sjg Exp $ Copyright (c) 2011-2020, Simon J. Gerraty Copyright (c) 2011-2017, Juniper Networks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import os import re import sys import stat def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): """ Return an absolute path, resolving via cwd or last_dir if needed. """ if path.endswith('/.'): path = path[0:-2] if len(path) > 0 and path[0] == '/': if os.path.exists(path): return path if debug > 2: print("skipping non-existent:", path, file=debug_out) return None if path == '.': return cwd if path.startswith('./'): return cwd + path[1:] if last_dir == cwd: last_dir = None for d in [last_dir, cwd]: if not d: continue if path == '..': dw = d.split('/') p = '/'.join(dw[:-1]) if not p: p = '/' return p p = '/'.join([d,path]) if debug > 2: print("looking for:", p, end=' ', file=debug_out) if not os.path.exists(p): if debug > 2: print("nope", file=debug_out) p = None continue if debug > 2: print("found:", p, file=debug_out) return p return None def cleanpath(path): """cleanup path without using realpath(3)""" if path.startswith('/'): r = '/' else: r = '' p = [] w = path.split('/') for d in w: if not d or d == '.': continue if d == '..': try: p.pop() continue except: break p.append(d) return r + '/'.join(p) def abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): """ Return an absolute path, resolving via cwd or last_dir if needed. this gets called a lot, so we try to avoid calling realpath. """ rpath = resolve(path, cwd, last_dir, debug, debug_out) if rpath: path = rpath elif len(path) > 0 and path[0] == '/': return None if (path.find('/') < 0 or path.find('./') > 0 or path.endswith('/..')): path = cleanpath(path) return path def sort_unique(list, cmp=None, key=None, reverse=False): if sys.version_info[0] == 2: list.sort(cmp, key, reverse) else: list.sort(reverse=reverse) nl = [] le = None for e in list: if e == le: continue le = e nl.append(e) return nl def add_trims(x): return ['/' + x + '/', '/' + x, x + '/', x] def target_spec_exts(target_spec): """return a list of dirdep extensions that could match target_spec""" if target_spec.find(',') < 0: return ['.'+target_spec] w = target_spec.split(',') n = len(w) e = [] while n > 0: e.append('.'+','.join(w[0:n])) n -= 1 return e class MetaFile: """class to parse meta files generated by bmake.""" conf = None dirdep_re = None host_target = None srctops = [] objroots = [] excludes = [] seen = {} obj_deps = [] src_deps = [] file_deps = [] def __init__(self, name, conf={}): """if name is set we will parse it now. conf can have the follwing keys: SRCTOPS list of tops of the src tree(s). CURDIR the src directory 'bmake' was run from. RELDIR the relative path from SRCTOP to CURDIR MACHINE the machine we built for. set to 'none' if we are not cross-building. More specifically if machine cannot be deduced from objdirs. TARGET_SPEC Sometimes MACHINE isn't enough. HOST_TARGET when we build for the pseudo machine 'host' the object tree uses HOST_TARGET rather than MACHINE. OBJROOTS a list of the common prefix for all obj dirs it might end in '/' or '-'. DPDEPS names an optional file to which per file dependencies will be appended. For example if 'some/path/foo.h' is read from SRCTOP then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output. This can allow 'bmake' to learn all the dirs within the tree that depend on 'foo.h' EXCLUDES A list of paths to ignore. ccache(1) can otherwise be trouble. debug desired debug level debug_out open file to send debug output to (sys.stderr) """ self.name = name self.debug = conf.get('debug', 0) self.debug_out = conf.get('debug_out', sys.stderr) self.machine = conf.get('MACHINE', '') self.machine_arch = conf.get('MACHINE_ARCH', '') self.target_spec = conf.get('TARGET_SPEC', self.machine) self.exts = target_spec_exts(self.target_spec) self.curdir = conf.get('CURDIR') self.reldir = conf.get('RELDIR') self.dpdeps = conf.get('DPDEPS') self.pids = {} self.line = 0 if not self.conf: # some of the steps below we want to do only once self.conf = conf self.host_target = conf.get('HOST_TARGET') for srctop in conf.get('SRCTOPS', []): if srctop[-1] != '/': srctop += '/' if not srctop in self.srctops: self.srctops.append(srctop) _srctop = os.path.realpath(srctop) if _srctop[-1] != '/': _srctop += '/' if not _srctop in self.srctops: self.srctops.append(_srctop) trim_list = add_trims(self.machine) if self.machine == 'host': trim_list += add_trims(self.host_target) if self.target_spec != self.machine: trim_list += add_trims(self.target_spec) for objroot in conf.get('OBJROOTS', []): for e in trim_list: if objroot.endswith(e): # this is not what we want - fix it objroot = objroot[0:-len(e)] if objroot[-1] != '/': objroot += '/' if not objroot in self.objroots: self.objroots.append(objroot) _objroot = os.path.realpath(objroot) if objroot[-1] == '/': _objroot += '/' if not _objroot in self.objroots: self.objroots.append(_objroot) # we want the longest match self.srctops.sort(reverse=True) self.objroots.sort(reverse=True) self.excludes = conf.get('EXCLUDES', []) if self.debug: print("host_target=", self.host_target, file=self.debug_out) print("srctops=", self.srctops, file=self.debug_out) print("objroots=", self.objroots, file=self.debug_out) print("excludes=", self.excludes, file=self.debug_out) print("ext_list=", self.exts, file=self.debug_out) self.dirdep_re = re.compile(r'([^/]+)/(.+)') if self.dpdeps and not self.reldir: if self.debug: print("need reldir:", end=' ', file=self.debug_out) if self.curdir: srctop = self.find_top(self.curdir, self.srctops) if srctop: self.reldir = self.curdir.replace(srctop,'') if self.debug: print(self.reldir, file=self.debug_out) if not self.reldir: self.dpdeps = None # we cannot do it? self.cwd = os.getcwd() # make sure this is initialized self.last_dir = self.cwd if name: self.try_parse() def reset(self): """reset state if we are being passed meta files from multiple directories.""" self.seen = {} self.obj_deps = [] self.src_deps = [] self.file_deps = [] def dirdeps(self, sep='\n'): """return DIRDEPS""" return sep.strip() + sep.join(self.obj_deps) def src_dirdeps(self, sep='\n'): """return SRC_DIRDEPS""" return sep.strip() + sep.join(self.src_deps) def file_depends(self, out=None): """Append DPDEPS_${file} += ${RELDIR} for each file we saw, to the output file.""" if not self.reldir: return None for f in sort_unique(self.file_deps): print('DPDEPS_%s += %s' % (f, self.reldir), file=out) # these entries provide for reverse DIRDEPS lookup for f in self.obj_deps: print('DEPDIRS_%s += %s' % (f, self.reldir), file=out) def seenit(self, dir): """rememer that we have seen dir.""" self.seen[dir] = 1 def add(self, list, data, clue=''): """add data to list if it isn't already there.""" if data not in list: list.append(data) if self.debug: print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out) def find_top(self, path, list): """the logical tree may be split across multiple trees""" for top in list: if path.startswith(top): if self.debug > 2: print("found in", top, file=self.debug_out) return top return None def find_obj(self, objroot, dir, path, input): """return path within objroot, taking care of .dirdep files""" ddep = None for ddepf in [path + '.dirdep', dir + '/.dirdep']: if not ddep and os.path.exists(ddepf): ddep = open(ddepf, 'r').readline().strip('# \n') if self.debug > 1: print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out) for e in self.exts: if ddep.endswith(e): ddep = ddep[0:-len(e)] break if not ddep: # no .dirdeps, so remember that we've seen the raw input self.seenit(input) self.seenit(dir) if self.machine == 'none': if dir.startswith(objroot): return dir.replace(objroot,'') return None m = self.dirdep_re.match(dir.replace(objroot,'')) if m: ddep = m.group(2) dmachine = m.group(1) if dmachine != self.machine: if not (self.machine == 'host' and dmachine == self.host_target): if self.debug > 2: print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out) ddep += '.' + dmachine return ddep def try_parse(self, name=None, file=None): """give file and line number causing exception""" try: self.parse(name, file) except: # give a useful clue print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr) raise def parse(self, name=None, file=None): """A meta file looks like: # Meta data file "path" CMD "command-line" CWD "cwd" TARGET "target" -- command output -- -- filemon acquired metadata -- # buildmon version 3 V 3 C "pid" "cwd" E "pid" "path" F "pid" "child" R "pid" "path" W "pid" "path" X "pid" "status" D "pid" "path" L "pid" "src" "target" M "pid" "old" "new" S "pid" "path" # Bye bye We go to some effort to avoid processing a dependency more than once. Of the above record types only C,E,F,L,R,V and W are of interest. """ version = 0 # unknown if name: self.name = name; if file: f = file cwd = self.last_dir = self.cwd else: f = open(self.name, 'r') skip = True pid_cwd = {} pid_last_dir = {} last_pid = 0 + eof_token = False self.line = 0 if self.curdir: self.seenit(self.curdir) # we ignore this - interesting = 'CEFLRVX' + interesting = '#CEFLRVX' for line in f: self.line += 1 # ignore anything we don't care about if not line[0] in interesting: continue if self.debug > 2: print("input:", line, end=' ', file=self.debug_out) w = line.split() if skip: if w[0] == 'V': skip = False version = int(w[1]) """ if version < 4: # we cannot ignore 'W' records # as they may be 'rw' interesting += 'W' """ elif w[0] == 'CWD': self.cwd = cwd = self.last_dir = w[1] self.seenit(cwd) # ignore this if self.debug: print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out) continue + if w[0] == '#': + # check the file has not been truncated + if line.find('Bye') > 0: + eof_token = True + continue + pid = int(w[1]) if pid != last_pid: if last_pid: pid_last_dir[last_pid] = self.last_dir cwd = pid_cwd.get(pid, self.cwd) self.last_dir = pid_last_dir.get(pid, self.cwd) last_pid = pid # process operations if w[0] == 'F': npid = int(w[2]) pid_cwd[npid] = cwd pid_last_dir[npid] = cwd last_pid = npid continue elif w[0] == 'C': cwd = abspath(w[2], cwd, None, self.debug, self.debug_out) if not cwd: cwd = w[2] if self.debug > 1: print("missing cwd=", cwd, file=self.debug_out) if cwd.endswith('/.'): cwd = cwd[0:-2] self.last_dir = pid_last_dir[pid] = cwd pid_cwd[pid] = cwd if self.debug > 1: print("cwd=", cwd, file=self.debug_out) continue if w[0] == 'X': try: del self.pids[pid] except KeyError: pass continue if w[2] in self.seen: if self.debug > 2: print("seen:", w[2], file=self.debug_out) continue # file operations if w[0] in 'ML': # these are special, tread src as read and # target as write self.parse_path(w[2].strip("'"), cwd, 'R', w) self.parse_path(w[3].strip("'"), cwd, 'W', w) continue elif w[0] in 'ERWS': path = w[2] if w[0] == 'E': self.pids[pid] = path elif path == '.': continue self.parse_path(path, cwd, w[0], w) - assert(version > 0) + if version == 0: + raise AssertionError('missing filemon data') + if not eof_token: + raise AssertionError('truncated filemon data') + setid_pids = [] # self.pids should be empty! for pid,path in self.pids.items(): try: # no guarantee that path is still valid if os.stat(path).st_mode & (stat.S_ISUID|stat.S_ISGID): # we do not expect anything after Exec setid_pids.append(pid) continue except: # we do not care why the above fails, # we do not want to miss the ERROR below. pass print("ERROR: missing eXit for {} pid {}".format(path, pid)) for pid in setid_pids: del self.pids[pid] assert(len(self.pids) == 0) if not file: f.close() def is_src(self, base, dir, rdir): """is base in srctop""" for dir in [dir,rdir]: if not dir: continue path = '/'.join([dir,base]) srctop = self.find_top(path, self.srctops) if srctop: if self.dpdeps: self.add(self.file_deps, path.replace(srctop,''), 'file') self.add(self.src_deps, dir.replace(srctop,''), 'src') self.seenit(dir) return True return False def parse_path(self, path, cwd, op=None, w=[]): """look at a path for the op specified""" if not op: op = w[0] # we are never interested in .dirdep files as dependencies if path.endswith('.dirdep'): return for p in self.excludes: if p and path.startswith(p): if self.debug > 2: print("exclude:", p, path, file=self.debug_out) return # we don't want to resolve the last component if it is # a symlink path = resolve(path, cwd, self.last_dir, self.debug, self.debug_out) if not path: return dir,base = os.path.split(path) if dir in self.seen: if self.debug > 2: print("seen:", dir, file=self.debug_out) return # we can have a path in an objdir which is a link # to the src dir, we may need to add dependencies for each rdir = dir dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out) if dir: rdir = os.path.realpath(dir) else: dir = rdir if rdir == dir: rdir = None # now put path back together path = '/'.join([dir,base]) if self.debug > 1: print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out) if op in 'RWS': if path in [self.last_dir, cwd, self.cwd, self.curdir]: if self.debug > 1: print("skipping:", path, file=self.debug_out) return if os.path.isdir(path): if op in 'RW': self.last_dir = path; if self.debug > 1: print("ldir=", self.last_dir, file=self.debug_out) return if op in 'ER': # finally, we get down to it if dir == self.cwd or dir == self.curdir: return if self.is_src(base, dir, rdir): self.seenit(w[2]) if not rdir: return objroot = None for dir in [dir,rdir]: if not dir: continue objroot = self.find_top(dir, self.objroots) if objroot: break if objroot: ddep = self.find_obj(objroot, dir, path, w[2]) if ddep: self.add(self.obj_deps, ddep, 'obj') if self.dpdeps and objroot.endswith('/stage/'): sp = '/'.join(path.replace(objroot,'').split('/')[1:]) self.add(self.file_deps, sp, 'file') else: # don't waste time looking again self.seenit(w[2]) self.seenit(dir) def main(argv, klass=MetaFile, xopts='', xoptf=None): """Simple driver for class MetaFile. Usage: script [options] [key=value ...] "meta" ... Options and key=value pairs contribute to the dictionary passed to MetaFile. -S "SRCTOP" add "SRCTOP" to the "SRCTOPS" list. -C "CURDIR" -O "OBJROOT" add "OBJROOT" to the "OBJROOTS" list. -m "MACHINE" -a "MACHINE_ARCH" -H "HOST_TARGET" -D "DPDEPS" -d bumps debug level """ import getopt # import Psyco if we can # it can speed things up quite a bit have_psyco = 0 try: import psyco psyco.full() have_psyco = 1 except: pass conf = { 'SRCTOPS': [], 'OBJROOTS': [], 'EXCLUDES': [], } try: machine = os.environ['MACHINE'] if machine: conf['MACHINE'] = machine machine_arch = os.environ['MACHINE_ARCH'] if machine_arch: conf['MACHINE_ARCH'] = machine_arch srctop = os.environ['SB_SRC'] if srctop: conf['SRCTOPS'].append(srctop) objroot = os.environ['SB_OBJROOT'] if objroot: conf['OBJROOTS'].append(objroot) except: pass debug = 0 output = True opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts) for o, a in opts: if o == '-a': conf['MACHINE_ARCH'] = a elif o == '-d': debug += 1 elif o == '-q': output = False elif o == '-H': conf['HOST_TARGET'] = a elif o == '-S': if a not in conf['SRCTOPS']: conf['SRCTOPS'].append(a) elif o == '-C': conf['CURDIR'] = a elif o == '-O': if a not in conf['OBJROOTS']: conf['OBJROOTS'].append(a) elif o == '-R': conf['RELDIR'] = a elif o == '-D': conf['DPDEPS'] = a elif o == '-m': conf['MACHINE'] = a elif o == '-T': conf['TARGET_SPEC'] = a elif o == '-X': if a not in conf['EXCLUDES']: conf['EXCLUDES'].append(a) elif xoptf: xoptf(o, a, conf) conf['debug'] = debug # get any var=val assignments eaten = [] for a in args: if a.find('=') > 0: k,v = a.split('=') if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']: if k == 'SRCTOP': k = 'SRCTOPS' elif k == 'OBJROOT': k = 'OBJROOTS' if v not in conf[k]: conf[k].append(v) else: conf[k] = v eaten.append(a) continue break for a in eaten: args.remove(a) debug_out = conf.get('debug_out', sys.stderr) if debug: print("config:", file=debug_out) print("psyco=", have_psyco, file=debug_out) for k,v in list(conf.items()): print("%s=%s" % (k,v), file=debug_out) m = None for a in args: if a.endswith('.meta'): if not os.path.exists(a): continue m = klass(a, conf) elif a.startswith('@'): # there can actually multiple files per line for line in open(a[1:]): for f in line.strip().split(): if not os.path.exists(f): continue m = klass(f, conf) if output and m: print(m.dirdeps()) print(m.src_dirdeps('\nsrc:')) dpdeps = conf.get('DPDEPS') if dpdeps: m.file_depends(open(dpdeps, 'w')) return m if __name__ == '__main__': try: main(sys.argv) except: # yes, this goes to stdout print("ERROR: ", sys.exc_info()[1]) raise diff --git a/contrib/bmake/mk/meta2deps.sh b/contrib/bmake/mk/meta2deps.sh index 4b7f9588987a..d590a383f82e 100755 --- a/contrib/bmake/mk/meta2deps.sh +++ b/contrib/bmake/mk/meta2deps.sh @@ -1,437 +1,455 @@ #!/bin/sh # NAME: # meta2deps.sh - extract useful info from .meta files # # SYNOPSIS: # meta2deps.sh SB="SB" "meta" ... # # DESCRIPTION: # This script looks each "meta" file and extracts the # information needed to deduce build and src dependencies. # # To do this, we extract the 'CWD' record as well as all the # syscall traces which describe 'R'ead, 'C'hdir and 'E'xec # syscalls. # # The typical meta file looks like:: #.nf # # # Meta data file "path" # CMD "command-line" # CWD "cwd" # TARGET "target" # -- command output -- # -- filemon acquired metadata -- # # buildmon version 2 # V 2 # E "pid" "path" # R "pid" "path" # C "pid" "cwd" # R "pid" "path" # X "pid" "status" #.fi # # The fact that all the syscall entry lines start with a single # character make these files quite easy to process using sed(1). # # To simplify the logic the 'CWD' line is made to look like a # normal 'C'hdir entry, and "cwd" is remembered so that it can # be prefixed to any "path" which is not absolute. # # If the "path" being read ends in '.srcrel' it is the content # of (actually the first line of) that file that we are # interested in. # # Any "path" which lies outside of the sandbox "SB" is generally # not of interest and is ignored. # # The output, is a set of absolute paths with "SB" like: #.nf # # $SB/obj-i386/bsd/include # $SB/obj-i386/bsd/lib/csu/i386 # $SB/obj-i386/bsd/lib/libc # $SB/src/bsd/include # $SB/src/bsd/sys/i386/include # $SB/src/bsd/sys/sys # $SB/src/pan-release/rtsock # $SB/src/pfe-shared/include/jnx #.fi # # Which can then be further processed by 'gendirdeps.mk' # # If we are passed 'DPDEPS='"dpdeps", then for each src file # outside of "CURDIR" we read, we output a line like: #.nf # # DPDEPS_$path += $RELDIR #.fi # # with "$path" geting turned into reldir's, so that we can end # up with a list of all the directories which depend on each src # file in another directory. This can allow for efficient yet # complete testing of changes. # RCSid: -# $Id: meta2deps.sh,v 1.18 2022/01/28 21:17:43 sjg Exp $ +# $Id: meta2deps.sh,v 1.20 2023/01/18 01:35:24 sjg Exp $ # Copyright (c) 2010-2013, Juniper Networks, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. meta2src() { cat /dev/null "$@" | sed -n '/^R .*\.[chyl]$/s,^..[0-9]* ,,p' | sort -u } meta2dirs() { cat /dev/null "$@" | sed -n '/^R .*\/.*\.[a-z0-9][^\/]*$/s,^..[0-9]* \(.*\)/[^/]*$,\1,p' | sort -u } add_list() { sep=' ' suffix= while : do case "$1" in "|") sep="$1"; shift;; -s) suffix="$2"; shift 2;; *) break;; esac done name=$1 shift eval list="\$$name" for top in "$@" do case "$sep$list$sep" in *"$sep$top$suffix$sep"*) continue;; esac list="${list:+$list$sep}$top$suffix" done eval "$name=\"$list\"" } +# some Linux systems have deprecated egrep in favor of grep -E +# but not everyone supports that +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) ;; +*) egrep() { grep -E "$@"; } +esac + _excludes_f() { egrep -v "$EXCLUDES" } error() { echo "ERROR: $@" >&2 exit 1 } meta2deps() { DPDEPS= SRCTOPS=$SRCTOP OBJROOTS= EXCLUDES= while : do case "$1" in *=*) eval export "$1"; shift;; -a) MACHINE_ARCH=$2; shift 2;; -m) MACHINE=$2; shift 2;; -C) CURDIR=$2; shift 2;; -H) HOST_TARGET=$2; shift 2;; -S) add_list SRCTOPS $2; shift 2;; -O) add_list OBJROOTS $2; shift 2;; -X) add_list EXCLUDES '|' $2; shift 2;; -R) RELDIR=$2; shift 2;; -T) TARGET_SPEC=$2; shift 2;; *) break;; esac done _th= _o= case "$MACHINE" in host) _ht=$HOST_TARGET;; esac for o in $OBJROOTS do case "$MACHINE,/$o/" in host,*$HOST_TARGET*) ;; *$MACHINE*|*${TARGET_SPEC:-$MACHINE}*) ;; *) add_list _o $o; continue;; esac for x in $_ht $TARGET_SPEC $MACHINE do case "$o" in "") continue;; */$x/) add_list _o ${o%$x/}; o=;; */$x) add_list _o ${o%$x}; o=;; *$x/) add_list _o ${o%$x/}; o=;; *$x) add_list _o ${o%$x}; o=;; esac done done OBJROOTS="$_o" case "$OBJTOP" in "") for o in $OBJROOTS do OBJTOP=$o${TARGET_SPEC:-$MACHINE} break done ;; esac src_re= obj_re= add_list '|' -s '/*' src_re $SRCTOPS add_list '|' -s '*' obj_re $OBJROOTS [ -z "$RELDIR" ] && unset DPDEPS tf=/tmp/m2d$$-$USER rm -f $tf.* trap 'rm -f $tf.*; trap 0' 0 > $tf.dirdep > $tf.qual > $tf.srcdep > $tf.srcrel > $tf.dpdeps seenit= seensrc= lpid= case "$EXCLUDES" in "") _excludes=cat;; *) _excludes=_excludes_f;; esac # handle @list files case "$@" in *@[!.]*) for f in "$@" do case "$f" in *.meta) cat $f;; @*) xargs cat < ${f#@};; *) cat $f;; esac done ;; *) cat /dev/null "$@";; esac 2> /dev/null | - sed -e 's,^CWD,C C,;/^[CREFLMVX] /!d' -e "s,',,g" | - $_excludes | ( version=no epids= xpids= + sed -e 's,^CWD,C C,;/^[#CREFLMVX] /!d' -e "s,',,g" | + $_excludes | ( version=no epids= xpids= eof_token=no while read op pid path junk do : op=$op pid=$pid path=$path # we track cwd and ldir (of interest) per pid # CWD is bmake's cwd case "$lpid,$pid" in ,C) CWD=$path cwd=$path ldir=$path if [ -z "$SB" ]; then SB=`echo $CWD | sed 's,/obj.*,,'` fi SRCTOP=${SRCTOP:-$SB/src} case "$verion" in no) ;; # ignore 0) error "no filemon data";; *) ;; esac version=0 + case "$eof_token" in + no) ;; # ignore + 0) error "truncated filemon data";; + esac + eof_token=0 continue ;; $pid,$pid) ;; - *) + [1-9]*) case "$lpid" in "") ;; *) eval ldir_$lpid=$ldir;; esac eval ldir=\${ldir_$pid:-$CWD} cwd=\${cwd_$pid:-$CWD} lpid=$pid ;; esac : op=$op path=$path case "$op,$path" in V,*) version=$pid; continue;; W,*srcrel|*.dirdep) continue;; C,*) case "$path" in /*) cwd=$path;; *) cwd=`cd $cwd/$path 2> /dev/null && /bin/pwd`;; esac # watch out for temp dirs that no longer exist test -d ${cwd:-/dev/null/no/such} || cwd=$CWD eval cwd_$pid=$cwd continue ;; F,*) # $path is new pid eval cwd_$path=$cwd ldir_$path=$ldir continue ;; + \#,bye) eof_token=1; continue;; + \#*) continue;; *) dir=${path%/*} case "$op" in E) # setid apps get no tracing so we won't see eXit case `'ls' -l $path 2> /dev/null | sed 's, .*,,'` in *s*) ;; *) epids="$epids $pid";; esac ;; X) xpids="$xpids $pid"; continue;; esac case "$path" in $src_re|$obj_re) ;; /*/stage/*) ;; /*) continue;; *) for path in $ldir/$path $cwd/$path do test -e $path && break done dir=${path%/*} ;; esac ;; esac # avoid repeating ourselves... case "$DPDEPS,$seensrc," in ,*) case ",$seenit," in *,$dir,*) continue;; esac ;; *,$path,*) continue;; esac # canonicalize if needed case "/$dir/" in */../*|*/./*) rdir=$dir dir=`cd $dir 2> /dev/null && /bin/pwd` seen="$rdir,$dir" ;; *) seen=$dir;; esac case "$dir" in ${CURDIR:-.}|"") continue;; $src_re) # avoid repeating ourselves... case "$DPDEPS,$seensrc," in ,*) case ",$seenit," in *,$dir,*) continue;; esac ;; esac ;; *) case ",$seenit," in *,$dir,*) continue;; esac ;; esac if [ -d $path ]; then case "$path" in */..) ldir=${dir%/*};; *) ldir=$path;; esac continue fi [ -f $path ] || continue case "$dir" in $CWD) continue;; # ignore $src_re) seenit="$seenit,$seen" echo $dir >> $tf.srcdep case "$DPDEPS,$reldir,$seensrc," in ,*) ;; *) seensrc="$seensrc,$path" echo "DPDEPS_$dir/${path##*/} += $RELDIR" >> $tf.dpdeps ;; esac continue ;; esac # if there is a .dirdep we cannot skip # just because we've seen the dir before. if [ -s $path.dirdep ]; then # this file contains: # '# ${RELDIR}.' echo $path.dirdep >> $tf.qual continue elif [ -s $dir.dirdep ]; then echo $dir.dirdep >> $tf.qual seenit="$seenit,$seen" continue fi seenit="$seenit,$seen" case "$dir" in $obj_re) echo $dir;; esac done > $tf.dirdep : version=$version case "$version" in 0) error "no filemon data";; esac + : eof_token=$eof_token + case "$eof_token" in + 0) error "truncated filemon data";; + esac for p in $epids do : p=$p case " $xpids " in *" $p "*) ;; *) error "missing eXit for pid $p";; esac done ) || exit 1 _nl=echo for f in $tf.dirdep $tf.qual $tf.srcdep do [ -s $f ] || continue case $f in *qual) # a list of .dirdep files # we can prefix everything with $OBJTOP to # tell gendirdeps.mk that these are # DIRDEP entries, since they are already # qualified with . as needed. # We strip .$MACHINE though xargs cat < $f | sort -u | sed "s,^# ,,;s,^,$OBJTOP/,;s,\.${TARGET_SPEC:-$MACHINE}\$,,;s,\.$MACHINE\$,," ;; *) sort -u $f;; esac _nl=: done if [ -s $tf.dpdeps ]; then case "$DPDEPS" in */*) ;; *) echo > $DPDEPS;; # the echo is needed! esac sort -u $tf.dpdeps | sed "s,${SRCTOP}/,,;s,${SB_BACKING_SB:-$SB}/src/,," >> $DPDEPS fi # ensure we produce _something_ else egrep -v gets upset $_nl } case /$0 in */meta2dep*) meta2deps "$@";; */meta2dirs*) meta2dirs "$@";; */meta2src*) meta2src "$@";; esac diff --git a/contrib/bmake/mk/sys.clean-env.mk b/contrib/bmake/mk/sys.clean-env.mk index 85e829c0a8f2..1d0119d6d37a 100644 --- a/contrib/bmake/mk/sys.clean-env.mk +++ b/contrib/bmake/mk/sys.clean-env.mk @@ -1,130 +1,130 @@ -# $Id: sys.clean-env.mk,v 1.24 2022/01/15 17:34:42 sjg Exp $ +# $Id: sys.clean-env.mk,v 1.25 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # This makefile would normally be included by sys.env.mk # The variables used by this makefile include: # # MAKE_ENV_SAVE_VAR_LIST # The actuall list of variables from the environment that will be # preserved. # MAKE_ENV_SAVE_PREFIX_LIST # A list of prefixes to match against the environment - the results # are added to MAKE_ENV_SAVE_VAR_LIST after being filtered by... # MAKE_ENV_SAVE_EXCLUDE_LIST # A list of words or patterns which is turned into a list of :N # modifiers. .if ${.MAKE.LEVEL} == 0 && ${MAKE_VERSION} >= 20100606 # We save any env var that starts with the words in MAKE_ENV_SAVE_PREFIX_LIST. # This gets expanded to an egrep expression like '^(A|B|C...)' # and added to MAKE_ENV_SAVE_VAR_LIST below. # If any of these end up being too greedy, MAKE_ENV_SAVE_EXCLUDE_LIST # can be used to filter. MAKE_ENV_SAVE_PREFIX_LIST += \ CCACHE \ CVS \ DEBUG \ DISTCC \ HOST \ MACHINE \ MAKE \ MK \ NEED_ \ SB_ \ SSH \ SVN \ USE_ \ WITH_ \ WITHOUT_ \ # This could be a list of vars or patterns to explicitly exclude. MAKE_ENV_SAVE_EXCLUDE_LIST += _ # This is the actual list that we will save # HOME is probably something worth clobbering eg. # HOME=/var/empty MAKE_ENV_SAVE_VAR_LIST += \ HOME \ LOGNAME \ OBJROOT \ OBJTOP \ PATH \ SB \ SRCTOP \ USER \ ${_env_vars:${MAKE_ENV_SAVE_EXCLUDE_LIST:${M_ListToSkip}}} -_env_vars != env | egrep '^(${MAKE_ENV_SAVE_PREFIX_LIST:ts|})' | sed 's,=.*,,'; echo +_env_vars != env | ${EGREP:Uegrep} '^(${MAKE_ENV_SAVE_PREFIX_LIST:ts|})' | sed 's,=.*,,'; echo _export_list = .for v in ${MAKE_ENV_SAVE_VAR_LIST:O:u} .if defined($v) _export_list += $v # Save current value $v := ${$v} .endif .endfor # Now, clobber the environment .unexport-env # This is a list of vars that we handle specially below _tricky_env_vars = MAKEOBJDIR OBJTOP # Export our selection - sans tricky ones .export ${_export_list:${_tricky_env_vars:${M_ListToSkip}}} # This next bit may need tweaking # if you don't happen to like the way I set it. .if defined(MAKEOBJDIR) # We are going to set this to the equivalent of the shell's # MAKEOBJDIR='${.CURDIR:S,${SRCTOP},${OBJTOP},}' _srctop := ${SRCTOP:U${SB_SRC:U${SB}/src}} _objroot := ${OBJROOT:U${SB_OBJROOT:U${SB}/${SB_OBJPREFIX}}} .if ${MAKE_VERSION} < 20160218 _objtop := ${OBJTOP:U${_objroot}${MACHINE}} # Take care of ${MACHINE} .if ${MACHINE:Nhost*} == "" || ${OBJTOP} == ${HOST_OBJTOP:Uno} OBJTOP = ${_objtop:S,${HOST_TARGET}$,\${MACHINE},} .else OBJTOP = ${_objtop:S,${MACHINE}$,\${MACHINE},} .endif # Export like this MAKEOBJDIR = $${.CURDIR:S,${_srctop},$${OBJTOP},} #.info ${MAKE_SAVE_ENV_VARS _srctop _objroot _objtop OBJTOP MAKEOBJDIR:L:@v@${.newline}$v=${$v}@} # Export these as-is, and do not track... # otherwise the environment will be ruined when we evaluate them below. .export-env ${_tricky_env_vars} # Now evaluate for ourselves .for v in ${_tricky_env_vars} $v := ${$v} .endfor .else # we cannot rely on the '$$' trick (depending on .MAKE.SAVE_DOLLARS) # but we can export a literal (unexpanded) value SRCTOP := ${_srctop} OBJROOT := ${_objroot} OBJTOP = ${OBJROOT}${MACHINE} MAKEOBJDIR = ${.CURDIR:S,${SRCTOP},${OBJTOP},} .export-literal SRCTOP OBJROOT ${_tricky_env_vars} .endif #.info ${_tricky_env_vars:@v@${.newline}$v=${$v}@} #showenv: -# @env | egrep 'OBJ|SRC' +# @env | ${EGREP:Uegrep} 'OBJ|SRC' .endif # MAKEOBJDIR .endif # level 0 diff --git a/contrib/bmake/mk/sys.mk b/contrib/bmake/mk/sys.mk index e39d5ac74548..966409870cc5 100644 --- a/contrib/bmake/mk/sys.mk +++ b/contrib/bmake/mk/sys.mk @@ -1,155 +1,158 @@ -# $Id: sys.mk,v 1.53 2021/12/13 05:50:13 sjg Exp $ +# $Id: sys.mk,v 1.54 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2003-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # Avoid putting anything platform specific in here. # _DEBUG_MAKE_FLAGS etc. .include .if !empty(_DEBUG_MAKE_FLAGS) .if ${_DEBUG_MAKE_SYS_DIRS:Uno:@x@${.CURDIR:M$x}@} != "" .MAKEFLAGS: ${_DEBUG_MAKE_FLAGS} .endif .endif # useful modifiers .include # we expect a recent bmake .if !defined(_TARGETS) # some things we do only once _TARGETS := ${.TARGETS} .-include .endif # we need HOST_TARGET etc below. .include # early customizations .-include # Popular suffixes for C++ CXX_SUFFIXES += .cc .cpp .cxx .C CXX_SUFFIXES := ${CXX_SUFFIXES:O:u} # find the OS specifics .if defined(SYS_OS_MK) .include <${SYS_OS_MK}> .else _sys_mk = .for x in ${HOST_OSTYPE} ${HOST_TARGET} ${HOST_OS} ${MACHINE} Generic .if empty(_sys_mk) .-include _sys_mk := ${.MAKE.MAKEFILES:M*/$x.mk} .if !empty(_sys_mk) _sys_mk := sys/${_sys_mk:T} .endif .endif .if empty(_sys_mk) # might be an old style .-include <$x.sys.mk> _sys_mk := ${.MAKE.MAKEFILES:M*/$x.sys.mk:T} .endif .endfor SYS_OS_MK := ${_sys_mk} .export SYS_OS_MK .endif +# some sys/ may have set this to grep -E +EGREP ?= egrep + # some options we need to know early OPTIONS_DEFAULT_NO += \ DIRDEPS_BUILD \ DIRDEPS_CACHE OPTIONS_DEFAULT_DEPENDENT += \ AUTO_OBJ/DIRDEPS_BUILD \ META_MODE/DIRDEPS_BUILD \ STAGING/DIRDEPS_BUILD \ STATIC_DIRDEPS_CACHE/DIRDEPS_CACHE \ .-include # :Uno incase options.mk not installed .if ${MK_META_MODE:Uno} == "yes" .-include .MAKE.MODE ?= meta verbose {META_MODE} .endif # make sure we have a harmless value .MAKE.MODE ?= normal # if you want objdirs make them automatic # and do it early before we compute .PATH .if ${MK_AUTO_OBJ:Uno} == "yes" || ${MKOBJDIRS:Uno} == "auto" .include .endif .if !empty(SRCTOP) .if ${.CURDIR} == ${SRCTOP} RELDIR = . .elif ${.CURDIR:M${SRCTOP}/*} RELDIR := ${.CURDIR:S,${SRCTOP}/,,} .endif .endif MACHINE_ARCH.host ?= ${_HOST_ARCH} MACHINE_ARCH.${MACHINE} ?= ${MACHINE} .if empty(MACHINE_ARCH) MACHINE_ARCH = ${MACHINE_ARCH.${MACHINE}} .endif .ifndef ROOT_GROUP ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group .export ROOT_GROUP .endif unix ?= We run ${_HOST_OSNAME}. # We need a Bourne/POSIX 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. # We repeat the mkdir -p a number of times to try and work around this. # We stop looping as soon as the dir exists. # If we get to the end of the loop, a plain mkdir will issue an error. Mkdirs= Mkdirs() { \ for d in $$*; do \ for i in 1 2 3 4 5 6; do \ mkdir -p $$d; \ test -d $$d && return 0; \ done; \ mkdir $$d || exit $$?; \ done; } # this often helps with debugging .SUFFIXES: .cpp-out .c.cpp-out: @${COMPILE.c:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$' ${CXX_SUFFIXES:%=%.cpp-out}: @${COMPILE.cc:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$' # late customizations .-include # if .CURDIR is matched by any entry in DEBUG_MAKE_DIRS we # will apply DEBUG_MAKE_FLAGS, now. .if !empty(_DEBUG_MAKE_FLAGS) .if ${_DEBUG_MAKE_DIRS:Uno:@x@${.CURDIR:M$x}@} != "" .MAKEFLAGS: ${_DEBUG_MAKE_FLAGS} .endif .endif diff --git a/contrib/bmake/mk/sys.vars.mk b/contrib/bmake/mk/sys.vars.mk index b759cc3f5cfb..f62adbcf08ba 100644 --- a/contrib/bmake/mk/sys.vars.mk +++ b/contrib/bmake/mk/sys.vars.mk @@ -1,113 +1,123 @@ -# $Id: sys.vars.mk,v 1.9 2022/02/05 19:04:53 sjg Exp $ +# $Id: sys.vars.mk,v 1.12 2023/01/20 17:34:06 sjg Exp $ # -# @(#) Copyright (c) 2003-2009, Simon J. Gerraty +# @(#) Copyright (c) 2003-2023, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # We use the following paradigm for preventing multiple inclusion. # It relies on the fact that conditionals and dependencies are resolved # at the time they are read. # -# _this ?= ${.PARSEFILE} +# _this ?= ${.PARSEDIR:tA}/${.PARSEFILE} # .if !target(__${_this}__) # __${_this}__: .NOTMAIN # -.if ${MAKE_VERSION:U0} > 20100408 -_this = ${.PARSEDIR:tA}/${.PARSEFILE} -.else -_this = ${.PARSEDIR}/${.PARSEFILE} -.endif # if this is an ancient version of bmake MAKE_VERSION ?= 0 .if ${MAKE_VERSION:M*make-*} # turn it into what we want - just the date MAKE_VERSION := ${MAKE_VERSION:[1]:C,.*-,,} .endif +.if ${MAKE_VERSION} < 20100414 +_this = ${.PARSEDIR}/${.PARSEFILE} +.else +_this = ${.PARSEDIR:tA}/${.PARSEFILE} +.endif + # some useful modifiers # A useful trick for testing multiple :M's against something # :L says to use the variable's name as its value - ie. literal # got = ${clean* destroy:${M_ListToMatch:S,V,.TARGETS,}} -M_ListToMatch = L:@m@$${V:M$$m}@ +M_ListToMatch = L:@m@$${V:U:M$$m}@ # match against our initial targets (see above) M_L_TARGETS = ${M_ListToMatch:S,V,_TARGETS,} # turn a list into a set of :N modifiers # NskipFoo = ${Foo:${M_ListToSkip}} M_ListToSkip= O:u:S,^,N,:ts: # type should be a builtin in any sh since about 1980, # but sadly there are exceptions! .if ${.MAKE.OS:Unknown:NBSD/OS} == "" _type_sh = which .endif # AUTOCONF := ${autoconf:L:${M_whence}} M_type = @x@(${_type_sh:Utype} $$x) 2> /dev/null; echo;@:sh:[0]:N* found*:[@]:C,[()],,g M_whence = ${M_type}:M/*:[1] # produce similar output to jot(1) or seq(1) # eg. ${LIST:[#]:${M_JOT}} # would be 1 2 3 4 5 if LIST has 5 words # ${9:L:${M_JOT}} # would be 1 2 3 4 5 6 7 8 9 .if ${.MAKE.LEVEL} == 0 .for x in jot seq .if empty(JOT_CMD) JOT_CMD := ${$x:L:${M_whence}} .endif .endfor .if !empty(JOT_CMD) .export JOT_CMD .endif .endif .if !empty(JOT_CMD) M_JOT = [1]:S,^,${JOT_CMD} ,:sh .else M_JOT = [1]:@x@i=1;while [ $$$$i -le $$x ]; do echo $$$$i; i=$$$$((i + 1)); done;@:sh .endif # ${LIST:${M_RANGE}} is 1 2 3 4 5 if LIST has 5 words -.if ${MAKE_VERSION} >= 20170130 -M_RANGE = range -.else +.if ${MAKE_VERSION} < 20170130 M_RANGE = [#]:${M_JOT} +.else +M_RANGE = range .endif # convert a path to a valid shell variable M_P2V = tu:C,[./-],_,g # convert path to absolute -.if ${MAKE_VERSION:U0} > 20100408 -M_tA = tA -.else +.if ${MAKE_VERSION} < 20100414 M_tA = C,.*,('cd' & \&\& 'pwd') 2> /dev/null || echo &,:sh +.else +M_tA = tA .endif -.if ${MAKE_VERSION:U0} >= 20170130 +# absoulte path to what we are reading. +_PARSEDIR = ${.PARSEDIR:${M_tA}} + +.if ${MAKE_VERSION} >= 20170130 # M_cmpv allows comparing dotted versions like 3.1.2 # ${3.1.2:L:${M_cmpv}} -> 3001002 # we use big jumps to handle 3 digits per dot: # ${123.456.789:L:${M_cmpv}} -> 123456789 -M_cmpv.units = 1 1000 1000000 -M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh +M_cmpv.units = 1 1000 1000000 1000000000 1000000000000 +M_cmpv = S,., ,g:C,^0*([0-9]),\1,:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .endif -# absoulte path to what we are reading. -_PARSEDIR = ${.PARSEDIR:${M_tA}} - # many projects use MAJOR MINOR PATCH versioning # ${OPENSSL:${M_M.M.P_VERSION}} is equivalent to # ${OPENSSL_MAJOR_VERSION}.${OPENSSL_MINOR_VERSION}.${OPENSSL_PATCH_VERSION} M_M.M.P_VERSION = L:@v@$${MAJOR MINOR PATCH:L:@t@$${$$v_$$t_VERSION:U0}@}@:ts. + +# numeric sort +.if ${MAKE_VERSION} < 20210803 +M_On = O +M_Onr = O +.else +M_On = On +M_Onr = Onr +.endif diff --git a/contrib/bmake/mk/sys/Linux.mk b/contrib/bmake/mk/sys/Linux.mk index fbba793e734c..0c95419c0abe 100644 --- a/contrib/bmake/mk/sys/Linux.mk +++ b/contrib/bmake/mk/sys/Linux.mk @@ -1,188 +1,192 @@ -# $Id: Linux.mk,v 1.15 2022/03/25 23:43:06 sjg Exp $ +# $Id: Linux.mk,v 1.16 2022/09/09 17:44:29 sjg Exp $ # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 OS ?= Linux unix ?= We run ${OS}. ROOT_GROUP ?= root # would be better to work out where it is... LIBCRT0 ?= /dev/null NEED_SOLINKS ?=yes .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= r RANLIB ?= ranlib AS ?= as AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/gcc) CC ?= gcc -pipe DBG ?= -O -g STATIC ?= -static .else CC ?= cc -pipe DBG ?= -g STATIC ?= -Bstatic .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} YACC ?= yacc YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} chmod a+x ${.TARGET} + + +# egrep is deprecated +EGREP = grep -E diff --git a/contrib/bmake/mkdeps.sh b/contrib/bmake/mkdeps.sh index dd87c4f5020e..3e4ea6711aa9 100755 --- a/contrib/bmake/mkdeps.sh +++ b/contrib/bmake/mkdeps.sh @@ -1,314 +1,321 @@ : # NAME: # mkdeps - generate dependencies # # SYNOPSIS: # mkdeps [options] file ... # # DESCRIPTION: # This script updates "makefile" with dependencies for # "file"(s). It borrows ideas from various makedepend scripts # and should be compatible with most. # # By default we use grep to extract include file names from # source files. We source an "rc" file '$Mydir/.${Myname}rc' which # can contain variable assignments such as: #.nf # # cpp_c=/usr/lib/cpp # cpp_cc=g++ -E # ... # #.fi # If the variable 'cpp_$suffix' is set, we use it as our cpp in # place of grep. The program referenced by these variables are # expected to produce output like: #.nf # # # 10 \"/usr/include/stdio.h\" 1 # #.fi # This allows us to skip most of our processing. For lex,yacc # and other source files, grep is probably just as quick and # certainly more portable. # # If the "rc" file does not exist, we create it and attempt to # find cpp or an equivalent cc invocation to assign to 'cpp_c'. # # AUTHOR: # Simon J. Gerraty # # RCSid: -# $Id: mkdeps.sh,v 1.23 2002/11/29 06:58:59 sjg Exp $ +# $Id: mkdeps.sh,v 1.24 2022/09/09 18:44:56 sjg Exp $ # # @(#) Copyright (c) 1993 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@zen.void.oz.au # Myname=`basename $0 .sh` Mydir=`dirname $0` case `echo -n .` in -n*) N=; C="\c";; *) N=-n; C=;; esac cc_include=-I/usr/include TF=/tmp/dep.$$ EF=/tmp/deperr.$$ > $EF case "$*" in *-n*) # don't use rc file rc=/dev/null norc=yes;; *) rc=$Mydir/.${Myname}rc ;; esac update= Include=include if [ x"$norc" = x -a -f $rc ]; then . $rc else # if /usr/lib/cpp or equivalent is available it is better than # grepping .c files. # See what (if anything) works on this system... echo : > $rc echo "# pre-processor for .c files" >> $rc # try a couple of sane places first for d in /usr/libexec /usr/lib /usr/bin /lib /usr/ccs/bin do cpp_c=$d/cpp [ -x $cpp_c ] && break done if [ -x $cpp_c ]; then echo cpp_c=$cpp_c >> $rc else cpp_c= # rats see if cc can be used echo "#include " > /tmp/f$$.c echo "main() { return 0; }" >> /tmp/f$$.c # try some sensible args to cc for arg in -E -P -M do ok=`${REALCC:-${CC:-cc}} $arg /tmp/f$$.c 2>/dev/null | grep '^#.*stdio.h' | tail -1` case "$ok" in "") ;; *) cpp_c="${REALCC:-${CC:-cc}} $arg" echo cpp_c="'$cpp_c'" >> $rc break;; esac done rm -f /tmp/f$$.c fi fi +# some Linux systems have deprecated egrep in favor of grep -E +# but not everyone supports that +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) ;; +*) egrep() { grep -E "$@"; } +esac + clean_up() { trap "" 2 3 trap 0 if [ -s $EF ]; then egrep -vi "included from|warning" $EF > ${EF}2 if [ -s ${EF}2 ]; then cat $EF >&2 rm -f .depend ests=1 fi fi rm -f $TF $EF* exit ${ests:-0} } # this lot does not work on HPsUX - complain to Hp. trap clean_up 0 trap exit 2 3 get_incs() { case "$cpp" in grep) # set IGNORE="<" to skip system includes egrep '^#[ ]*include' $* | egrep -v "$IGNORE" | \ sed -e 's/^.*include[^"<]*["<]//' -e 's/[">].*//g';; *) # $cpp (eg. /usr/lib/cpp or cc -E) should produce output like: # 1 "/usr/include/stdio.h" 2 # set IGNORE=/usr/include to skip system includes $cpp $cpp_opts $cc_include $* 2>> $EF | egrep '^#.*\.h"' | sed 's,^#.*"\(.*\)".*,\1,' | egrep -v "$IGNORE" | sort -u;; esac } gen_deps() { llen=$1 shift for ifile in $* do case "$cpp" in grep) # this lot is not needed if not using grep. for dir in $srcdir $dirlist /usr/include do [ -f "$dir/$ifile" ] && break done if [ ! -f "$dir/$ifile" ]; then # produce a useful error message (useful to emacs or error) iline=`grep -n ".*include.*[\"<]$ifile[\">]" $file | cut -d: -f1` echo "\"$file\", line $iline: cannot find include file \"$ifile\"" >> $EF # no point adding to dependency list as the resulting makefile # would not work anyway... continue fi ifile=$dir/$ifile # check whether we have done it yet case `grep "$ifile" $TF` in "") echo "$ifile" >> $TF;; *) continue;; # no repeats... esac ;; esac len=`expr "$ifile " : '.*'` if [ "`expr $llen + $len`" -gt ${width:-76} ]; then echo "\\" >> .depend echo $N " $C" >> .depend llen=8 fi echo $N "$ifile $C" >> .depend llen=`expr $llen + $len` case "$cpp" in grep) # this lot is not needed unless using grep. ilist=`get_incs $ifile` # recurse needed? [ "$ilist" ] && llen=`gen_deps $llen $ilist` ;; esac done echo $llen } for f in makefile Makefile do test -s $f && { MAKEFILE=$f; break; } done MAKEFILE=${MAKEFILE:-makefile} IGNORE=${IGNORE:-"^-"} # won't happen obj=o cpp_opts= # incase cpp != grep vpath= append= progDep= set -- `getopt "AanNV:s:w:o:I:D:b:f:i:p" "$@"` for key in "$@" do case $key in --) shift; break;; -A) Include=;; # cat .depend >> $MAKEFILE -a) append=yes; shift;; -n) shift;; # ignore rc -N) update=no; shift;; # don't update $MAKEFILE -I) cpp_opts="$cpp_opts$1$2 "; dirlist="$dirlist $2"; shift 2;; -o) obj=$2; shift 2;; -s) shift 2;; # can't handle it anyway... -w) width=$2; shift 2;; -f) MAKEFILE=$2; shift 2;; -b) BASEDIR=$2; shift 2;; -i) IGNORE="$2"; shift 2;; # ignore headers matching this... -D) cpp_opts="$cpp_opts$1$2 "; shift 2;; -V) VPATH="$2"; shift 2;; # where to look for files -p) progDep=yes; shift;; esac done [ "$VPATH" ] && vpath=`IFS=:; set -- $VPATH; echo $*` [ "$append" ] || > .depend for file in $* do cpp= suffix=`expr $file : '.*\.\([^.]*\)'` eval cpp=\"\${cpp_${suffix}:-grep}\" if [ ! -f $file -a "$vpath" ]; then for d in . $vpath do [ -f $d/$file ] && { file=$d/$file; break; } done fi srcdir=`dirname $file` base=`basename $file .$suffix` ilist=`get_incs $file` if [ "$ilist" ]; then > $TF if [ "$progDep" ]; then echo "$base: $file \\" >> .depend else echo "$base.$obj: $file \\" >> .depend fi echo $N " $C" >> .depend llen=8 llen=`gen_deps $llen $ilist` echo >> .depend echo >> .depend elif [ "$progDep" ]; then echo "$base: $file" >> .depend echo >> .depend fi done if [ -s .depend ]; then # ./foo.h looks ugly mv .depend $TF { test "$BASEDIR" && sed -e "s;$BASEDIR;\$(BASEDIR);g" $TF || cat $TF; } | sed 's;\([^.]\)\./;\1;g' > .depend # # Save the manually updated section of the makefile # if [ x$update != xno ]; then trap "" 2 # don't die if we got this far # if make doesn't support include, then append our deps... depended=`grep 'include.*\.depend' $MAKEFILE` test "$depended" && clean_up sed '/^# DO NOT DELETE.*depend.*$/,$d' < $MAKEFILE > $TF mv $TF $MAKEFILE cat <> $MAKEFILE # DO NOT DELETE THIS LINE -- make depend depends on it # Do not edit anything below, it was added automagically by $Myname. ! case "$Include" in "") cat .depend >> $MAKEFILE;; .include) echo '.include ".depend"' >> $MAKEFILE;; include) echo include .depend >> $MAKEFILE;; esac fi fi clean_up diff --git a/contrib/bmake/os.sh b/contrib/bmake/os.sh old mode 100755 new mode 100644 index 1fc66ecc6a63..6bf52420c90f --- a/contrib/bmake/os.sh +++ b/contrib/bmake/os.sh @@ -1,258 +1,264 @@ : # NAME: # os.sh - operating system specifics # # DESCRIPTION: # This file is included at the start of processing. Its role is # to set the variables OS, OSREL, OSMAJOR, MACHINE and MACHINE_ARCH to # reflect the current system. # # It also sets variables such as MAILER, LOCAL_FS, PS_AXC to hide # certain aspects of different UNIX flavours. # # SEE ALSO: # site.sh,funcs.sh # # AUTHOR: # Simon J. Gerraty # RCSid: -# $Id: os.sh,v 1.59 2021/11/14 04:18:00 sjg Exp $ +# $Id: os.sh,v 1.62 2023/01/17 18:30:21 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # this lets us skip sourcing it again _OS_SH=: OS=`uname` OSREL=`uname -r` OSMAJOR=`IFS=.; set $OSREL; echo $1` MACHINE=`uname -m` MACHINE_ARCH=`uname -p 2>/dev/null || echo $MACHINE` # there is at least one case of `uname -p` outputting # a bunch of usless drivel case "$MACHINE_ARCH" in unknown|*[!A-Za-z0-9_-]*) MACHINE_ARCH="$MACHINE";; esac # we need this here, and it is not always available... Which() { case "$1" in -*) t=$1; shift;; *) t=-x;; esac case "$1" in /*) test $t $1 && echo $1;; *) # some shells cannot correctly handle `IFS` # in conjunction with the for loop. _dirs=`IFS=:; echo ${2:-$PATH}` for d in $_dirs do test $t $d/$1 && { echo $d/$1; break; } done ;; esac } # tr is insanely non-portable wrt char classes, so we need to # spell out the alphabet. sed y/// would work too. toUpper() { ${TR:-tr} abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ } toLower() { ${TR:-tr} ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz } K= -case $OS in +case "$OS" in AIX) # everyone loves to be different... OSMAJOR=`uname -v` - OSREL="$OSMAJOR.`uname -r`" + OSMINOR=`uname -r` + OSREL="$OSMAJOR.$OSMINOR" LOCAL_FS=jfs PS_AXC=-e SHARE_ARCH=$OS/$OSMAJOR.X ;; Darwin) # a bit like BSD HOST_ARCH=$MACHINE ;; SunOS) CHOWN=`Which chown /usr/etc:/usr/bin` export CHOWN # Great! Solaris keeps moving arch(1) # should just bite the bullet and use uname -p arch=`Which arch /usr/bin:/usr/ucb` MAILER=/usr/ucb/Mail LOCAL_FS=4.2 case "$OSREL" in 4.0*) # uname -m just says sun which could be anything # so use arch(1). MACHINE_ARCH=`arch` MACHINE=$MACHINE_ARCH ;; 4*) MACHINE_ARCH=`arch` ;; 5*) K=-k LOCAL_FS=ufs MAILER=mailx PS_AXC=-e # can you believe that ln on Solaris defaults to # overwriting an existing file!!!!! We want one that works! test -x /usr/xpg4/bin/ln && LN=${LN:-/usr/xpg4/bin/ln} # wonderful, 5.8's tr again require's []'s # but /usr/xpg4/bin/tr causes problems if LC_COLLATE is set! # use toUpper/toLower instead. ;; esac case "$OS/$MACHINE_ARCH" in *sun386) SHARE_ARCH=$MACHINE_ARCH;; esac ;; *BSD) K=-k MAILER=/usr/bin/Mail LOCAL_FS=local : $-,$ENV case "$-,$ENV" in *i*,*) ;; *,|*ENVFILE*) ;; *) ENV=;; esac # NetBSD at least has good backward compatibility # so NetBSD/i386 is good enough case $OS in NetBSD) LOCALBASE=/usr/pkg HOST_ARCH=$MACHINE SHARE_ARCH=$OS/$HOST_ARCH ;; OpenBSD) arch=`Which arch /usr/bin:/usr/ucb:$PATH` MACHINE_ARCH=`$arch -s` ;; esac NAWK=awk export NAWK ;; HP-UX) TMP_DIRS="/tmp /usr/tmp" LOCAL_FS=hfs MAILER=mailx # don't rely on /bin/sh, its broken _shell=/bin/ksh; ENV= # also, no one would be interested in OSMAJOR=A case "$OSREL" in ?.09*) OSMAJOR=9; PS_AXC=-e;; ?.10*) OSMAJOR=10; PS_AXC=-e;; esac ;; IRIX) LOCAL_FS=efs ;; Interix) MACHINE=i386 MACHINE_ARCH=i386 ;; UnixWare|SCO_SV) OSREL=`uname -v` OSMAJOR=`IFS=.; set $OSREL; echo $1` MACHINE_ARCH=`uname -m` ;; Linux) # Not really any such thing as Linux, but # this covers red-hat and hopefully others. case $MACHINE in i?86) MACHINE_ARCH=i386;; # we don't care about i686 vs i586 esac LOCAL_FS=ext2 PS_AXC=axc [ -x /usr/bin/md5sum ] && { MD5=/usr/bin/md5sum; export MD5; } ;; QNX) case $MACHINE in x86pc) MACHINE_ARCH=i386;; esac ;; Haiku) case $MACHINE in BeBox) MACHINE_ARCH=powerpc;; BeMac) MACHINE_ARCH=powerpc;; BePC) MACHINE_ARCH=i386;; esac ;; esac LOCALBASE=${LOCALBASE:-/usr/local} HOSTNAME=${HOSTNAME:-`( hostname ) 2>/dev/null`} HOSTNAME=${HOSTNAME:-`( uname -n ) 2>/dev/null`} case "$HOSTNAME" in *.*) HOST=`IFS=.; set -- $HOSTNAME; echo $1`;; *) HOST=$HOSTNAME;; esac TMP_DIRS=${TMP_DIRS:-"/tmp /var/tmp"} MACHINE_ARCH=${MACHINE_ARCH:-$MACHINE} HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH} case "$HOST_ARCH" in x86*64|amd64) MACHINE32_ARCH=i386;; *64) MACHINE32_ARCH=`echo $MACHINE_ARCH | sed 's,64,32,'`;; *) MACHINE32_ARCH=$MACHINE_ARCH;; esac HOST_ARCH32=${HOST_ARCH32:-$MACHINE32_ARCH} # we mount server:/share/arch/$SHARE_ARCH as /usr/local SHARE_ARCH_DEFAULT=$OS/$OSMAJOR.X/$HOST_ARCH SHARE_ARCH=${SHARE_ARCH:-$SHARE_ARCH_DEFAULT} LN=${LN:-ln} TR=${TR:-tr} # Some people like have /share/$HOST_TARGET/bin etc. HOST_TARGET=`echo ${OS}${OSMAJOR}-$HOST_ARCH | tr -d / | toLower` HOST_TARGET32=`echo ${OS}${OSMAJOR}-$HOST_ARCH32 | tr -d / | toLower` export HOST_TARGET HOST_TARGET32 -case `echo -n .` in -n*) N=; C="\c";; *) N=-n; C=;; esac +case `echo -n .` in -n*) echo_n=; echo_c="\c";; *) echo_n=-n; echo_c=;; esac Echo() { case "$1" in - -n) _n=$N _c=$C; shift;; - *) _n= _c=;; + -n) shift; echo $echo_n "$@$echo_c";; + *) echo "$@";; esac - echo $_n "$@" $_c } +# for systems that deprecate egrep +case "`echo egrep | egrep 'e|g' 2>&1`" in +egrep) ;; +*) egrep() { grep -E "$@"; };; +esac + export HOSTNAME HOST export OS MACHINE MACHINE_ARCH OSREL OSMAJOR LOCAL_FS TMP_DIRS MAILER N C K PS_AXC export LN SHARE_ARCH TR export LOCALBASE case /$0 in */os.sh) for v in $* do eval vv=\$$v echo "$v='$vv'" done ;; */host_target32) echo $HOST_TARGET32;; */host_target) echo $HOST_TARGET;; esac diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c index c2ecdbe875bf..3193543f56bf 100644 --- a/contrib/bmake/parse.c +++ b/contrib/bmake/parse.c @@ -1,2974 +1,3032 @@ -/* $NetBSD: parse.c,v 1.681 2022/07/24 20:25:23 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.692 2023/01/24 00:24:02 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Parsing of makefiles. * * Parse_File is the main entry point and controls most of the other * functions in this module. * * Interface: * Parse_Init Initialize the module * * Parse_End Clean up the module * * Parse_File Parse a top-level makefile. Included files are * handled by IncludeFile instead. * * Parse_VarAssign * Try to parse the given line as a variable assignment. * Used by MainParseArgs to determine if an argument is * a target or a variable assignment. Used internally * for pretty much the same thing. * * Parse_Error Report a parse error, a warning or an informational * message. * * Parse_MainName Returns a list of the single main target to create. */ #include #include #include #include #include "make.h" #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_MMAP #include #ifndef MAP_COPY #define MAP_COPY MAP_PRIVATE #endif #ifndef MAP_FILE #define MAP_FILE 0 #endif #endif #include "dir.h" #include "job.h" #include "pathnames.h" /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: parse.c,v 1.681 2022/07/24 20:25:23 rillig Exp $"); +MAKE_RCSID("$NetBSD: parse.c,v 1.692 2023/01/24 00:24:02 sjg Exp $"); /* * A file being read. */ typedef struct IncludedFile { FStr name; /* absolute or relative to the cwd */ unsigned lineno; /* 1-based */ unsigned readLines; /* the number of physical lines that have * been read from the file */ unsigned forHeadLineno; /* 1-based */ unsigned forBodyReadLines; /* the number of physical lines that have * been read from the file above the body of * the .for loop */ - unsigned int cond_depth; /* 'if' nesting when file opened */ + unsigned int condMinDepth; /* depth of nested 'if' directives, at the + * beginning of the file */ bool depending; /* state of doing_depend on EOF */ Buffer buf; /* the file's content or the body of the .for * loop; either empty or ends with '\n' */ char *buf_ptr; /* next char to be read */ char *buf_end; /* buf_end[-1] == '\n' */ struct ForLoop *forLoop; } IncludedFile; /* Special attributes for target nodes. */ typedef enum ParseSpecial { SP_ATTRIBUTE, /* Generic attribute */ SP_BEGIN, /* .BEGIN */ SP_DEFAULT, /* .DEFAULT */ SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */ SP_END, /* .END */ SP_ERROR, /* .ERROR */ SP_IGNORE, /* .IGNORE */ SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */ SP_INTERRUPT, /* .INTERRUPT */ SP_LIBS, /* .LIBS; not mentioned in the manual page */ SP_MAIN, /* .MAIN and no user-specified targets to make */ SP_META, /* .META */ SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */ SP_NOMETA, /* .NOMETA */ SP_NOMETA_CMP, /* .NOMETA_CMP */ SP_NOPATH, /* .NOPATH */ + SP_NOREADONLY, /* .NOREADONLY */ SP_NOT, /* Not special */ SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */ SP_NULL, /* .NULL; not mentioned in the manual page */ SP_OBJDIR, /* .OBJDIR */ SP_ORDER, /* .ORDER */ SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */ SP_PATH, /* .PATH or .PATH.suffix */ SP_PHONY, /* .PHONY */ #ifdef POSIX SP_POSIX, /* .POSIX; not mentioned in the manual page */ #endif SP_PRECIOUS, /* .PRECIOUS */ + SP_READONLY, /* .READONLY */ SP_SHELL, /* .SHELL */ SP_SILENT, /* .SILENT */ SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ SP_STALE, /* .STALE */ SP_SUFFIXES, /* .SUFFIXES */ + SP_SYSPATH, /* .SYSPATH */ SP_WAIT /* .WAIT */ } ParseSpecial; typedef List SearchPathList; typedef ListNode SearchPathListNode; typedef enum VarAssignOp { VAR_NORMAL, /* = */ VAR_APPEND, /* += */ VAR_DEFAULT, /* ?= */ VAR_SUBST, /* := */ VAR_SHELL /* != or :sh= */ } VarAssignOp; typedef struct VarAssign { char *varname; /* unexpanded */ VarAssignOp op; const char *value; /* unexpanded */ } VarAssign; static bool Parse_IsVar(const char *, VarAssign *); static void Parse_Var(VarAssign *, GNode *); /* * The target to be made if no targets are specified in the command line. * This is the first target defined in any of the makefiles. */ GNode *mainNode; /* * During parsing, the targets from the left-hand side of the currently * active dependency line, or NULL if the current line does not belong to a * dependency line, for example because it is a variable assignment. * * See unit-tests/deptgt.mk, keyword "parse.c:targets". */ static GNodeList *targets; #ifdef CLEANUP /* * All shell commands for all targets, in no particular order and possibly * with duplicates. Kept in a separate list since the commands from .USE or * .USEBEFORE nodes are shared with other GNodes, thereby giving up the * easily understandable ownership over the allocated strings. */ static StringList targCmds = LST_INIT; #endif /* * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER * is seen, then set to each successive source on the line. */ static GNode *order_pred; static int parseErrors = 0; /* * The include chain of makefiles. At index 0 is the top-level makefile from * the command line, followed by the included files or .for loops, up to and * including the current file. * * See PrintStackTrace for how to interpret the data. */ static Vector /* of IncludedFile */ includes; SearchPath *parseIncPath; /* directories for "..." includes */ SearchPath *sysIncPath; /* directories for <...> includes */ SearchPath *defSysIncPath; /* default for sysIncPath */ /* * The parseKeywords table is searched using binary search when deciding * if a target or source is special. The 'spec' field is the ParseSpecial * type of the keyword (SP_NOT if the keyword isn't special as a target) while * the 'op' field is the operator to apply to the list of targets if the * keyword is used as a source ("0" if the keyword isn't special as a source) */ static const struct { const char name[17]; ParseSpecial special; /* when used as a target */ GNodeType targetAttr; /* when used as a source */ } parseKeywords[] = { { ".BEGIN", SP_BEGIN, OP_NONE }, { ".DEFAULT", SP_DEFAULT, OP_NONE }, { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, OP_NONE }, { ".END", SP_END, OP_NONE }, { ".ERROR", SP_ERROR, OP_NONE }, { ".EXEC", SP_ATTRIBUTE, OP_EXEC }, { ".IGNORE", SP_IGNORE, OP_IGNORE }, { ".INCLUDES", SP_INCLUDES, OP_NONE }, { ".INTERRUPT", SP_INTERRUPT, OP_NONE }, { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE }, { ".JOIN", SP_ATTRIBUTE, OP_JOIN }, { ".LIBS", SP_LIBS, OP_NONE }, { ".MADE", SP_ATTRIBUTE, OP_MADE }, { ".MAIN", SP_MAIN, OP_NONE }, { ".MAKE", SP_ATTRIBUTE, OP_MAKE }, { ".MAKEFLAGS", SP_MFLAGS, OP_NONE }, { ".META", SP_META, OP_META }, { ".MFLAGS", SP_MFLAGS, OP_NONE }, { ".NOMETA", SP_NOMETA, OP_NOMETA }, { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP }, { ".NOPATH", SP_NOPATH, OP_NOPATH }, + { ".NOREADONLY", SP_NOREADONLY, OP_NONE }, { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN }, { ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE }, { ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE }, { ".NULL", SP_NULL, OP_NONE }, { ".OBJDIR", SP_OBJDIR, OP_NONE }, { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL }, { ".ORDER", SP_ORDER, OP_NONE }, { ".PARALLEL", SP_PARALLEL, OP_NONE }, { ".PATH", SP_PATH, OP_NONE }, { ".PHONY", SP_PHONY, OP_PHONY }, #ifdef POSIX { ".POSIX", SP_POSIX, OP_NONE }, #endif { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS }, + { ".READONLY", SP_READONLY, OP_NONE }, { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE }, { ".SHELL", SP_SHELL, OP_NONE }, { ".SILENT", SP_SILENT, OP_SILENT }, { ".SINGLESHELL", SP_SINGLESHELL, OP_NONE }, { ".STALE", SP_STALE, OP_NONE }, { ".SUFFIXES", SP_SUFFIXES, OP_NONE }, + { ".SYSPATH", SP_SYSPATH, OP_NONE }, { ".USE", SP_ATTRIBUTE, OP_USE }, { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE }, { ".WAIT", SP_WAIT, OP_NONE }, }; enum PosixState posix_state = PS_NOT_YET; static IncludedFile * GetInclude(size_t i) { + assert(i < includes.len); return Vector_Get(&includes, i); } /* The file that is currently being read. */ static IncludedFile * CurFile(void) { return GetInclude(includes.len - 1); } +unsigned int +CurFile_CondMinDepth(void) +{ + return CurFile()->condMinDepth; +} + static Buffer LoadFile(const char *path, int fd) { ssize_t n; Buffer buf; size_t bufSize; struct stat st; bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 && st.st_size < 1024 * 1024 * 1024 ? (size_t)st.st_size : 1024; Buf_InitSize(&buf, bufSize); for (;;) { if (buf.len == buf.cap) { if (buf.cap >= 512 * 1024 * 1024) { Error("%s: file too large", path); exit(2); /* Not 1 so -q can distinguish error */ } Buf_Expand(&buf); } assert(buf.len < buf.cap); n = read(fd, buf.data + buf.len, buf.cap - buf.len); if (n < 0) { Error("%s: read error: %s", path, strerror(errno)); exit(2); /* Not 1 so -q can distinguish error */ } if (n == 0) break; buf.len += (size_t)n; } assert(buf.len <= buf.cap); if (!Buf_EndsWith(&buf, '\n')) Buf_AddByte(&buf, '\n'); return buf; /* may not be null-terminated */ } /* * Print the current chain of .include and .for directives. In Parse_Fatal * or other functions that already print the location, includingInnermost * would be redundant, but in other cases like Error or Fatal it needs to be * included. */ void PrintStackTrace(bool includingInnermost) { const IncludedFile *entries; size_t i, n; - entries = GetInclude(0); n = includes.len; if (n == 0) return; + entries = GetInclude(0); if (!includingInnermost && entries[n - 1].forLoop == NULL) n--; /* already in the diagnostic */ for (i = n; i-- > 0;) { const IncludedFile *entry = entries + i; const char *fname = entry->name.str; char dirbuf[MAXPATHLEN + 1]; if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) fname = realpath(fname, dirbuf); if (entry->forLoop != NULL) { char *details = ForLoop_Details(entry->forLoop); debug_printf("\tin .for loop from %s:%u with %s\n", fname, entry->forHeadLineno, details); free(details); } else if (i + 1 < n && entries[i + 1].forLoop != NULL) { /* entry->lineno is not a useful line number */ } else debug_printf("\tin %s:%u\n", fname, entry->lineno); } } /* Check if the current character is escaped on the current line. */ static bool IsEscaped(const char *line, const char *p) { bool escaped = false; while (p > line && *--p == '\\') escaped = !escaped; return escaped; } /* * Add the filename and lineno to the GNode so that we remember where it * was first defined. */ static void RememberLocation(GNode *gn) { IncludedFile *curFile = CurFile(); gn->fname = Str_Intern(curFile->name.str); gn->lineno = curFile->lineno; } /* * Look in the table of keywords for one matching the given string. * Return the index of the keyword, or -1 if it isn't there. */ static int FindKeyword(const char *str) { int start = 0; int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1; while (start <= end) { int curr = start + (end - start) / 2; int diff = strcmp(str, parseKeywords[curr].name); if (diff == 0) return curr; if (diff < 0) end = curr - 1; else start = curr + 1; } return -1; } void PrintLocation(FILE *f, bool useVars, const GNode *gn) { char dirbuf[MAXPATHLEN + 1]; FStr dir, base; const char *fname; unsigned lineno; if (gn != NULL) { fname = gn->fname; lineno = gn->lineno; } else if (includes.len > 0) { IncludedFile *curFile = CurFile(); fname = curFile->name.str; lineno = curFile->lineno; } else return; if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) { (void)fprintf(f, "\"%s\" line %u: ", fname, lineno); return; } dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR"); if (dir.str == NULL) dir.str = "."; if (dir.str[0] != '/') dir.str = realpath(dir.str, dirbuf); base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE"); if (base.str == NULL) base.str = str_basename(fname); (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno); FStr_Done(&base); FStr_Done(&dir); } static void MAKE_ATTR_PRINTFLIKE(5, 0) ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn, ParseErrorLevel level, const char *fmt, va_list ap) { static bool fatal_warning_error_printed = false; (void)fprintf(f, "%s: ", progname); PrintLocation(f, useVars, gn); if (level == PARSE_WARNING) (void)fprintf(f, "warning: "); (void)vfprintf(f, fmt, ap); (void)fprintf(f, "\n"); (void)fflush(f); if (level == PARSE_FATAL) parseErrors++; if (level == PARSE_WARNING && opts.parseWarnFatal) { if (!fatal_warning_error_printed) { Error("parsing warnings being treated as errors"); fatal_warning_error_printed = true; } parseErrors++; } if (DEBUG(PARSE)) PrintStackTrace(false); } static void MAKE_ATTR_PRINTFLIKE(3, 4) ParseErrorInternal(const GNode *gn, ParseErrorLevel level, const char *fmt, ...) { va_list ap; (void)fflush(stdout); va_start(ap, fmt); ParseVErrorInternal(stderr, false, gn, level, fmt, ap); va_end(ap); if (opts.debug_file != stdout && opts.debug_file != stderr) { va_start(ap, fmt); ParseVErrorInternal(opts.debug_file, false, gn, level, fmt, ap); va_end(ap); } } /* * Print a parse error message, including location information. * * If the level is PARSE_FATAL, continue parsing until the end of the * current top-level makefile, then exit (see Parse_File). * * Fmt is given without a trailing newline. */ void Parse_Error(ParseErrorLevel level, const char *fmt, ...) { va_list ap; (void)fflush(stdout); va_start(ap, fmt); ParseVErrorInternal(stderr, true, NULL, level, fmt, ap); va_end(ap); if (opts.debug_file != stdout && opts.debug_file != stderr) { va_start(ap, fmt); ParseVErrorInternal(opts.debug_file, true, NULL, level, fmt, ap); va_end(ap); } } /* * Handle an .info, .warning or .error directive. For an .error directive, * exit immediately. */ static void HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg) { char *xmsg; if (umsg[0] == '\0') { Parse_Error(PARSE_FATAL, "Missing argument for \".%s\"", levelName); return; } (void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg); /* TODO: handle errors */ Parse_Error(level, "%s", xmsg); free(xmsg); if (level == PARSE_FATAL) { PrintOnError(NULL, "\n"); exit(1); } } /* * Add the child to the parent's children, and for non-special targets, vice * versa. Special targets such as .END do not need to be informed once the * child target has been made. */ static void LinkSource(GNode *pgn, GNode *cgn, bool isSpecial) { if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts)) pgn = pgn->cohorts.last->datum; Lst_Append(&pgn->children, cgn); pgn->unmade++; /* Special targets like .END don't need any children. */ if (!isSpecial) Lst_Append(&cgn->parents, pgn); if (DEBUG(PARSE)) { debug_printf("# LinkSource: added child %s - %s\n", pgn->name, cgn->name); Targ_PrintNode(pgn, 0); Targ_PrintNode(cgn, 0); } } /* Add the node to each target from the current dependency group. */ static void LinkToTargets(GNode *gn, bool isSpecial) { GNodeListNode *ln; for (ln = targets->first; ln != NULL; ln = ln->next) LinkSource(ln->datum, gn, isSpecial); } static bool TryApplyDependencyOperator(GNode *gn, GNodeType op) { /* * If the node occurred on the left-hand side of a dependency and the * operator also defines a dependency, they must match. */ if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) && ((op & OP_OPMASK) != (gn->type & OP_OPMASK))) { Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); return false; } if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { /* * If the node was of the left-hand side of a '::' operator, * we need to create a new instance of it for the children * and commands on this dependency line since each of these * dependency groups has its own attributes and commands, * separate from the others. * * The new instance is placed on the 'cohorts' list of the * initial one (note the initial one is not on its own * cohorts list) and the new instance is linked to all * parents of the initial instance. */ GNode *cohort; /* * Propagate copied bits to the initial node. They'll be * propagated back to the rest of the cohorts later. */ - gn->type |= op & ~OP_OPMASK; + gn->type |= op & (unsigned)~OP_OPMASK; cohort = Targ_NewInternalNode(gn->name); if (doing_depend) RememberLocation(cohort); /* * Make the cohort invisible as well to avoid duplicating it * into other variables. True, parents of this target won't * tend to do anything with their local variables, but better * safe than sorry. * * (I think this is pointless now, since the relevant list * traversals will no longer see this node anyway. -mycroft) */ cohort->type = op | OP_INVISIBLE; Lst_Append(&gn->cohorts, cohort); cohort->centurion = gn; gn->unmade_cohorts++; snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", (unsigned int)gn->unmade_cohorts % 1000000); } else { /* * We don't want to nuke any previous flags (whatever they * were) so we just OR the new operator into the old. */ gn->type |= op; } return true; } static void ApplyDependencyOperator(GNodeType op) { GNodeListNode *ln; for (ln = targets->first; ln != NULL; ln = ln->next) if (!TryApplyDependencyOperator(ln->datum, op)) break; } /* * We add a .WAIT node in the dependency list. After any dynamic dependencies * (and filename globbing) have happened, it is given a dependency on each * previous child, back until the previous .WAIT node. The next child won't * be scheduled until the .WAIT node is built. * * We give each .WAIT node a unique name (mainly for diagnostics). */ static void ApplyDependencySourceWait(bool isSpecial) { static unsigned wait_number = 0; char name[6 + 10 + 1]; GNode *gn; snprintf(name, sizeof name, ".WAIT_%u", ++wait_number); gn = Targ_NewInternalNode(name); if (doing_depend) RememberLocation(gn); gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; LinkToTargets(gn, isSpecial); } static bool ApplyDependencySourceKeyword(const char *src, ParseSpecial special) { int keywd; GNodeType targetAttr; if (*src != '.' || !ch_isupper(src[1])) return false; keywd = FindKeyword(src); if (keywd == -1) return false; targetAttr = parseKeywords[keywd].targetAttr; if (targetAttr != OP_NONE) { ApplyDependencyOperator(targetAttr); return true; } if (parseKeywords[keywd].special == SP_WAIT) { ApplyDependencySourceWait(special != SP_NOT); return true; } return false; } /* * In a line like ".MAIN: source1 source2", add all sources to the list of * things to create, but only if the user didn't specify a target on the * command line and .MAIN occurs for the first time. * * See HandleDependencyTargetSpecial, branch SP_MAIN. * See unit-tests/cond-func-make-main.mk. */ static void ApplyDependencySourceMain(const char *src) { Lst_Append(&opts.create, bmake_strdup(src)); /* * Add the name to the .TARGETS variable as well, so the user can * employ that, if desired. */ Global_Append(".TARGETS", src); } /* * For the sources of a .ORDER target, create predecessor/successor links * between the previous source and the current one. */ static void ApplyDependencySourceOrder(const char *src) { GNode *gn; gn = Targ_GetNode(src); if (doing_depend) RememberLocation(gn); if (order_pred != NULL) { Lst_Append(&order_pred->order_succ, gn); Lst_Append(&gn->order_pred, order_pred); if (DEBUG(PARSE)) { debug_printf( "# .ORDER forces '%s' to be made before '%s'\n", order_pred->name, gn->name); Targ_PrintNode(order_pred, 0); Targ_PrintNode(gn, 0); } } /* * The current source now becomes the predecessor for the next one. */ order_pred = gn; } /* The source is not an attribute, so find/create a node for it. */ static void ApplyDependencySourceOther(const char *src, GNodeType targetAttr, ParseSpecial special) { GNode *gn; gn = Targ_GetNode(src); if (doing_depend) RememberLocation(gn); if (targetAttr != OP_NONE) gn->type |= targetAttr; else LinkToTargets(gn, special != SP_NOT); } /* * Given the name of a source in a dependency line, figure out if it is an * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise * decide if there is some attribute which should be applied *to* the source * because of some special target (such as .PHONY) and apply it if so. * Otherwise, make the source a child of the targets. */ static void ApplyDependencySource(GNodeType targetAttr, const char *src, ParseSpecial special) { if (ApplyDependencySourceKeyword(src, special)) return; if (special == SP_MAIN) ApplyDependencySourceMain(src); else if (special == SP_ORDER) ApplyDependencySourceOrder(src); else ApplyDependencySourceOther(src, targetAttr, special); } /* * If we have yet to decide on a main target to make, in the absence of any * user input, we want the first target on the first dependency line that is * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. */ static void MaybeUpdateMainTarget(void) { GNodeListNode *ln; if (mainNode != NULL) return; for (ln = targets->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; if (GNode_IsMainCandidate(gn)) { DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name); mainNode = gn; return; } } } static void InvalidLineType(const char *line) { if (strncmp(line, "<<<<<<", 6) == 0 || strncmp(line, ">>>>>>", 6) == 0) Parse_Error(PARSE_FATAL, "Makefile appears to contain unresolved CVS/RCS/??? merge conflicts"); else if (line[0] == '.') { const char *dirstart = line + 1; const char *dirend; cpp_skip_whitespace(&dirstart); dirend = dirstart; while (ch_isalnum(*dirend) || *dirend == '-') dirend++; Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", (int)(dirend - dirstart), dirstart); } else Parse_Error(PARSE_FATAL, "Invalid line type"); } static void ParseDependencyTargetWord(char **pp, const char *lstart) { const char *cp = *pp; while (*cp != '\0') { if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' || *cp == '(') && !IsEscaped(lstart, cp)) break; if (*cp == '$') { /* * Must be a dynamic source (would have been expanded * otherwise). * * There should be no errors in this, as they would * have been discovered in the initial Var_Subst and * we wouldn't be here. */ FStr val; (void)Var_Parse(&cp, SCOPE_CMDLINE, VARE_PARSE_ONLY, &val); FStr_Done(&val); } else cp++; } *pp += cp - *pp; } /* * Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. * * See the tests deptgt-*.mk. */ static void HandleDependencyTargetSpecial(const char *targetName, ParseSpecial *inout_special, SearchPathList **inout_paths) { switch (*inout_special) { case SP_PATH: if (*inout_paths == NULL) *inout_paths = Lst_New(); Lst_Append(*inout_paths, &dirSearchPath); break; + case SP_SYSPATH: + if (*inout_paths == NULL) + *inout_paths = Lst_New(); + Lst_Append(*inout_paths, sysIncPath); + break; case SP_MAIN: /* * Allow targets from the command line to override the * .MAIN node. */ if (!Lst_IsEmpty(&opts.create)) *inout_special = SP_NOT; break; case SP_BEGIN: case SP_END: case SP_STALE: case SP_ERROR: case SP_INTERRUPT: { GNode *gn = Targ_GetNode(targetName); if (doing_depend) RememberLocation(gn); gn->type |= OP_NOTMAIN | OP_SPECIAL; Lst_Append(targets, gn); break; } case SP_DEFAULT: { /* * Need to create a node to hang commands on, but we don't * want it in the graph, nor do we want it to be the Main * Target. We claim the node is a transformation rule to make * life easier later, when we'll use Make_HandleUse to * actually apply the .DEFAULT commands. */ GNode *gn = GNode_New(".DEFAULT"); gn->type |= OP_NOTMAIN | OP_TRANSFORM; Lst_Append(targets, gn); defaultNode = gn; break; } case SP_DELETE_ON_ERROR: deleteOnError = true; break; case SP_NOTPARALLEL: opts.maxJobs = 1; break; case SP_SINGLESHELL: opts.compatMake = true; break; case SP_ORDER: order_pred = NULL; break; default: break; } } static bool HandleDependencyTargetPath(const char *suffixName, SearchPathList **inout_paths) { SearchPath *path; path = Suff_GetPath(suffixName); if (path == NULL) { Parse_Error(PARSE_FATAL, "Suffix '%s' not defined (yet)", suffixName); return false; } if (*inout_paths == NULL) *inout_paths = Lst_New(); Lst_Append(*inout_paths, path); return true; } /* See if it's a special target and if so set inout_special to match it. */ static bool HandleDependencyTarget(const char *targetName, ParseSpecial *inout_special, GNodeType *inout_targetAttr, SearchPathList **inout_paths) { int keywd; if (!(targetName[0] == '.' && ch_isupper(targetName[1]))) return true; /* * See if the target is a special target that must have it * or its sources handled specially. */ keywd = FindKeyword(targetName); if (keywd != -1) { if (*inout_special == SP_PATH && parseKeywords[keywd].special != SP_PATH) { Parse_Error(PARSE_FATAL, "Mismatched special targets"); return false; } *inout_special = parseKeywords[keywd].special; *inout_targetAttr = parseKeywords[keywd].targetAttr; HandleDependencyTargetSpecial(targetName, inout_special, inout_paths); } else if (strncmp(targetName, ".PATH", 5) == 0) { *inout_special = SP_PATH; if (!HandleDependencyTargetPath(targetName + 5, inout_paths)) return false; } return true; } static void HandleSingleDependencyTargetMundane(const char *name) { GNode *gn = Suff_IsTransform(name) ? Suff_AddTransform(name) : Targ_GetNode(name); if (doing_depend) RememberLocation(gn); Lst_Append(targets, gn); } static void HandleDependencyTargetMundane(const char *targetName) { if (Dir_HasWildcards(targetName)) { StringList targetNames = LST_INIT; SearchPath *emptyPath = SearchPath_New(); SearchPath_Expand(emptyPath, targetName, &targetNames); SearchPath_Free(emptyPath); while (!Lst_IsEmpty(&targetNames)) { char *targName = Lst_Dequeue(&targetNames); HandleSingleDependencyTargetMundane(targName); free(targName); } } else HandleSingleDependencyTargetMundane(targetName); } static void SkipExtraTargets(char **pp, const char *lstart) { bool warning = false; const char *p = *pp; while (*p != '\0') { if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':')) break; if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t')) warning = true; p++; } - if (warning) - Parse_Error(PARSE_WARNING, "Extra target ignored"); + if (warning) { + const char *start = *pp; + cpp_skip_whitespace(&start); + Parse_Error(PARSE_WARNING, "Extra target '%.*s' ignored", + (int)(p - start), start); + } *pp += p - *pp; } static void CheckSpecialMundaneMixture(ParseSpecial special) { switch (special) { case SP_DEFAULT: case SP_STALE: case SP_BEGIN: case SP_END: case SP_ERROR: case SP_INTERRUPT: /* * These create nodes on which to hang commands, so targets * shouldn't be empty. */ case SP_NOT: /* Nothing special here -- targets may be empty. */ break; default: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. " "Mundane ones ignored"); break; } } /* * In a dependency line like 'targets: sources' or 'targets! sources', parse * the operator ':', '::' or '!' from between the targets and the sources. */ static GNodeType ParseDependencyOp(char **pp) { if (**pp == '!') return (*pp)++, OP_FORCE; if (**pp == ':' && (*pp)[1] == ':') return *pp += 2, OP_DOUBLEDEP; else if (**pp == ':') return (*pp)++, OP_DEPENDS; else return OP_NONE; } static void -ClearPaths(SearchPathList *paths) +ClearPaths(ParseSpecial special, SearchPathList *paths) { if (paths != NULL) { SearchPathListNode *ln; for (ln = paths->first; ln != NULL; ln = ln->next) SearchPath_Clear(ln->datum); } - - Dir_SetPATH(); + if (special == SP_SYSPATH) + Dir_SetSYSPATH(); + else + Dir_SetPATH(); } static char * FindInDirOfIncludingFile(const char *file) { char *fullname, *incdir, *slash, *newName; int i; fullname = NULL; incdir = bmake_strdup(CurFile()->name.str); slash = strrchr(incdir, '/'); if (slash != NULL) { *slash = '\0'; /* * Now do lexical processing of leading "../" on the * filename. */ for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { slash = strrchr(incdir + 1, '/'); if (slash == NULL || strcmp(slash, "/..") == 0) break; *slash = '\0'; } newName = str_concat3(incdir, "/", file + i); fullname = Dir_FindFile(newName, parseIncPath); if (fullname == NULL) fullname = Dir_FindFile(newName, &dirSearchPath); free(newName); } free(incdir); return fullname; } static char * FindInQuotPath(const char *file) { const char *suff; SearchPath *suffPath; char *fullname; fullname = FindInDirOfIncludingFile(file); if (fullname == NULL && (suff = strrchr(file, '.')) != NULL && (suffPath = Suff_GetPath(suff)) != NULL) fullname = Dir_FindFile(file, suffPath); if (fullname == NULL) fullname = Dir_FindFile(file, parseIncPath); if (fullname == NULL) fullname = Dir_FindFile(file, &dirSearchPath); return fullname; } /* * Handle one of the .[-ds]include directives by remembering the current file * and pushing the included file on the stack. After the included file has * finished, parsing continues with the including file; see Parse_PushInput * and ParseEOF. * * System includes are looked up in sysIncPath, any other includes are looked * up in the parsedir and then in the directories specified by the -I command * line options. */ static void IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) { Buffer buf; char *fullname; /* full pathname of file */ int fd; fullname = file[0] == '/' ? bmake_strdup(file) : NULL; if (fullname == NULL && !isSystem) fullname = FindInQuotPath(file); if (fullname == NULL) { SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath; fullname = Dir_FindFile(file, path); } if (fullname == NULL) { if (!silent) Parse_Error(PARSE_FATAL, "Could not find %s", file); return; } if ((fd = open(fullname, O_RDONLY)) == -1) { if (!silent) Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); free(fullname); return; } buf = LoadFile(fullname, fd); (void)close(fd); Parse_PushInput(fullname, 1, 0, buf, NULL); if (depinc) doing_depend = depinc; /* only turn it on */ free(fullname); } /* Handle a "dependency" line like '.SPECIAL:' without any sources. */ static void HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths) { switch (special) { case SP_SUFFIXES: Suff_ClearSuffixes(); break; case SP_PRECIOUS: allPrecious = true; break; case SP_IGNORE: opts.ignoreErrors = true; break; case SP_SILENT: opts.silent = true; break; case SP_PATH: - ClearPaths(paths); + case SP_SYSPATH: + ClearPaths(special, paths); break; #ifdef POSIX case SP_POSIX: if (posix_state == PS_NOW_OR_NEVER) { /* * With '-r', 'posix.mk' (if it exists) * can effectively substitute for 'sys.mk', * otherwise it is an extension. */ Global_Set("%POSIX", "1003.2"); IncludeFile("posix.mk", true, false, true); } break; #endif default: break; } } static void AddToPaths(const char *dir, SearchPathList *paths) { if (paths != NULL) { SearchPathListNode *ln; for (ln = paths->first; ln != NULL; ln = ln->next) (void)SearchPath_Add(ln->datum, dir); } } /* * If the target was one that doesn't take files as its sources but takes * something like suffixes, we take each space-separated word on the line as * a something and deal with it accordingly. */ static void ParseDependencySourceSpecial(ParseSpecial special, const char *word, SearchPathList *paths) { switch (special) { case SP_SUFFIXES: Suff_AddSuffix(word); break; case SP_PATH: AddToPaths(word, paths); break; case SP_INCLUDES: Suff_AddInclude(word); break; case SP_LIBS: Suff_AddLib(word); break; + case SP_NOREADONLY: + Var_ReadOnly(word, false); + break; case SP_NULL: Suff_SetNull(word); break; case SP_OBJDIR: Main_SetObjdir(false, "%s", word); break; + case SP_READONLY: + Var_ReadOnly(word, true); + break; + case SP_SYSPATH: + AddToPaths(word, paths); + break; default: break; } } static bool ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special, GNodeType *inout_targetAttr, SearchPathList **inout_paths) { char savec = *nameEnd; *nameEnd = '\0'; if (!HandleDependencyTarget(name, inout_special, inout_targetAttr, inout_paths)) return false; if (*inout_special == SP_NOT && *name != '\0') HandleDependencyTargetMundane(name); else if (*inout_special == SP_PATH && *name != '.' && *name != '\0') Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name); *nameEnd = savec; return true; } static bool -ParseDependencyTargets(char **inout_cp, +ParseDependencyTargets(char **pp, const char *lstart, ParseSpecial *inout_special, GNodeType *inout_targetAttr, SearchPathList **inout_paths) { - char *cp = *inout_cp; + char *p = *pp; for (;;) { - char *tgt = cp; + char *tgt = p; - ParseDependencyTargetWord(&cp, lstart); + ParseDependencyTargetWord(&p, lstart); /* * If the word is followed by a left parenthesis, it's the * name of one or more files inside an archive. */ - if (!IsEscaped(lstart, cp) && *cp == '(') { - cp = tgt; - if (!Arch_ParseArchive(&cp, targets, SCOPE_CMDLINE)) { + if (!IsEscaped(lstart, p) && *p == '(') { + p = tgt; + if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) { Parse_Error(PARSE_FATAL, "Error in archive specification: \"%s\"", tgt); return false; } continue; } - if (*cp == '\0') { + if (*p == '\0') { InvalidLineType(lstart); return false; } - if (!ApplyDependencyTarget(tgt, cp, inout_special, + if (!ApplyDependencyTarget(tgt, p, inout_special, inout_targetAttr, inout_paths)) return false; if (*inout_special != SP_NOT && *inout_special != SP_PATH) - SkipExtraTargets(&cp, lstart); + SkipExtraTargets(&p, lstart); else - pp_skip_whitespace(&cp); + pp_skip_whitespace(&p); - if (*cp == '\0') + if (*p == '\0') break; - if ((*cp == '!' || *cp == ':') && !IsEscaped(lstart, cp)) + if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p)) break; } - *inout_cp = cp; + *pp = p; return true; } static void ParseDependencySourcesSpecial(char *start, ParseSpecial special, SearchPathList *paths) { char savec; while (*start != '\0') { char *end = start; while (*end != '\0' && !ch_isspace(*end)) end++; savec = *end; *end = '\0'; ParseDependencySourceSpecial(special, start, paths); *end = savec; if (savec != '\0') end++; pp_skip_whitespace(&end); start = end; } } static void LinkVarToTargets(VarAssign *var) { GNodeListNode *ln; for (ln = targets->first; ln != NULL; ln = ln->next) Parse_Var(var, ln->datum); } static bool ParseDependencySourcesMundane(char *start, ParseSpecial special, GNodeType targetAttr) { while (*start != '\0') { char *end = start; VarAssign var; /* * Check for local variable assignment, * rest of the line is the value. */ if (Parse_IsVar(start, &var)) { /* * Check if this makefile has disabled * setting local variables. */ bool target_vars = GetBooleanExpr( "${.MAKE.TARGET_LOCAL_VARIABLES}", true); if (target_vars) LinkVarToTargets(&var); free(var.varname); if (target_vars) return true; } /* * The targets take real sources, so we must beware of archive * specifications (i.e. things with left parentheses in them) * and handle them accordingly. */ for (; *end != '\0' && !ch_isspace(*end); end++) { if (*end == '(' && end > start && end[-1] != '$') { /* * Only stop for a left parenthesis if it * isn't at the start of a word (that'll be * for variable changes later) and isn't * preceded by a dollar sign (a dynamic * source). */ break; } } if (*end == '(') { GNodeList sources = LST_INIT; if (!Arch_ParseArchive(&start, &sources, SCOPE_CMDLINE)) { Parse_Error(PARSE_FATAL, "Error in source archive spec \"%s\"", start); return false; } while (!Lst_IsEmpty(&sources)) { GNode *gn = Lst_Dequeue(&sources); ApplyDependencySource(targetAttr, gn->name, special); } Lst_Done(&sources); end = start; } else { if (*end != '\0') { *end = '\0'; end++; } ApplyDependencySource(targetAttr, start, special); } pp_skip_whitespace(&end); start = end; } return true; } /* * From a dependency line like 'targets: sources', parse the sources. * * See the tests depsrc-*.mk. */ static void ParseDependencySources(char *p, GNodeType targetAttr, ParseSpecial special, SearchPathList **inout_paths) { if (*p == '\0') { HandleDependencySourcesEmpty(special, *inout_paths); } else if (special == SP_MFLAGS) { Main_ParseArgLine(p); return; } else if (special == SP_SHELL) { if (!Job_ParseShell(p)) { Parse_Error(PARSE_FATAL, "improper shell specification"); return; } return; } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL || special == SP_DELETE_ON_ERROR) { return; } /* Now go for the sources. */ - if (special == SP_SUFFIXES || special == SP_PATH || - special == SP_INCLUDES || special == SP_LIBS || - special == SP_NULL || special == SP_OBJDIR) { + switch (special) { + case SP_INCLUDES: + case SP_LIBS: + case SP_NOREADONLY: + case SP_NULL: + case SP_OBJDIR: + case SP_PATH: + case SP_READONLY: + case SP_SUFFIXES: + case SP_SYSPATH: ParseDependencySourcesSpecial(p, special, *inout_paths); if (*inout_paths != NULL) { Lst_Free(*inout_paths); *inout_paths = NULL; } if (special == SP_PATH) Dir_SetPATH(); - } else { + if (special == SP_SYSPATH) + Dir_SetSYSPATH(); + break; + default: assert(*inout_paths == NULL); if (!ParseDependencySourcesMundane(p, special, targetAttr)) return; + break; } MaybeUpdateMainTarget(); } /* * Parse a dependency line consisting of targets, followed by a dependency * operator, optionally followed by sources. * * The nodes of the sources are linked as children to the nodes of the * targets. Nodes are created as necessary. * * The operator is applied to each node in the global 'targets' list, * which is where the nodes found for the targets are kept. * * The sources are parsed in much the same way as the targets, except * that they are expanded using the wildcarding scheme of the C-Shell, * and a target is created for each expanded word. Each of the resulting * nodes is then linked to each of the targets as one of its children. * * Certain targets and sources such as .PHONY or .PRECIOUS are handled * specially, see ParseSpecial. * * Transformation rules such as '.c.o' are also handled here, see * Suff_AddTransform. * * Upon return, the value of the line is unspecified. */ static void ParseDependency(char *line) { char *p; SearchPathList *paths; /* search paths to alter when parsing a list * of .PATH targets */ GNodeType targetAttr; /* from special sources */ ParseSpecial special; /* in special targets, the children are * linked as children of the parent but not * vice versa */ GNodeType op; DEBUG1(PARSE, "ParseDependency(%s)\n", line); p = line; paths = NULL; targetAttr = OP_NONE; special = SP_NOT; if (!ParseDependencyTargets(&p, line, &special, &targetAttr, &paths)) goto out; if (!Lst_IsEmpty(targets)) CheckSpecialMundaneMixture(special); op = ParseDependencyOp(&p); if (op == OP_NONE) { InvalidLineType(line); goto out; } ApplyDependencyOperator(op); pp_skip_whitespace(&p); ParseDependencySources(p, targetAttr, special, &paths); out: if (paths != NULL) Lst_Free(paths); } /* * Determine the assignment operator and adjust the end of the variable * name accordingly. */ static VarAssign AdjustVarassignOp(const char *name, const char *nameEnd, const char *op, const char *value) { VarAssignOp type; VarAssign va; if (op > name && op[-1] == '+') { op--; type = VAR_APPEND; } else if (op > name && op[-1] == '?') { op--; type = VAR_DEFAULT; } else if (op > name && op[-1] == ':') { op--; type = VAR_SUBST; } else if (op > name && op[-1] == '!') { op--; type = VAR_SHELL; } else { type = VAR_NORMAL; #ifdef SUNSHCMD while (op > name && ch_isspace(op[-1])) op--; if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) { op -= 3; type = VAR_SHELL; } #endif } va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op); va.op = type; va.value = value; return va; } /* * Parse a variable assignment, consisting of a single-word variable name, * optional whitespace, an assignment operator, optional whitespace and the * variable value. * * Note: There is a lexical ambiguity with assignment modifier characters * in variable names. This routine interprets the character before the = * as a modifier. Therefore, an assignment like * C++=/usr/bin/CC * is interpreted as "C+ +=" instead of "C++ =". * * Used for both lines in a file and command line arguments. */ static bool Parse_IsVar(const char *p, VarAssign *out_var) { const char *nameStart, *nameEnd, *firstSpace, *eq; int level = 0; cpp_skip_hspace(&p); /* Skip to variable name */ /* * During parsing, the '+' of the operator '+=' is initially parsed * as part of the variable name. It is later corrected, as is the * ':sh' modifier. Of these two (nameEnd and eq), the earlier one * determines the actual end of the variable name. */ nameStart = p; firstSpace = NULL; /* * Scan for one of the assignment operators outside a variable * expansion. */ while (*p != '\0') { char ch = *p++; if (ch == '(' || ch == '{') { level++; continue; } if (ch == ')' || ch == '}') { level--; continue; } if (level != 0) continue; if ((ch == ' ' || ch == '\t') && firstSpace == NULL) firstSpace = p - 1; while (ch == ' ' || ch == '\t') ch = *p++; if (ch == '\0') return false; #ifdef SUNSHCMD if (ch == ':' && p[0] == 's' && p[1] == 'h') { p += 2; continue; } #endif if (ch == '=') eq = p - 1; else if (*p == '=' && (ch == '+' || ch == ':' || ch == '?' || ch == '!')) eq = p; else if (firstSpace != NULL) return false; else continue; nameEnd = firstSpace != NULL ? firstSpace : eq; p = eq + 1; cpp_skip_whitespace(&p); *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p); return true; } return false; } /* * Check for syntax errors such as unclosed expressions or unknown modifiers. */ static void VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope) { if (opts.strict) { if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) { char *expandedValue; (void)Var_Subst(uvalue, scope, VARE_PARSE_ONLY, &expandedValue); /* TODO: handle errors */ free(expandedValue); } } } /* Perform a variable assignment that uses the operator ':='. */ static void VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, FStr *out_avalue) { char *evalue; /* * make sure that we set the variable the first time to nothing * so that it gets substituted. * * TODO: Add a test that demonstrates why this code is needed, * apart from making the debug log longer. * * XXX: The variable name is expanded up to 3 times. */ if (!Var_ExistsExpand(scope, name)) Var_SetExpand(scope, name, ""); (void)Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF, &evalue); /* TODO: handle errors */ Var_SetExpand(scope, name, evalue); *out_avalue = FStr_InitOwn(evalue); } /* Perform a variable assignment that uses the operator '!='. */ static void VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, FStr *out_avalue) { FStr cmd; char *output, *error; cmd = FStr_InitRefer(uvalue); Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR); output = Cmd_Exec(cmd.str, &error); Var_SetExpand(scope, name, output); *out_avalue = FStr_InitOwn(output); if (error != NULL) { Parse_Error(PARSE_WARNING, "%s", error); free(error); } FStr_Done(&cmd); } /* * Perform a variable assignment. * * The actual value of the variable is returned in *out_true_avalue. * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal * value. * * Return whether the assignment was actually performed, which is usually * the case. It is only skipped if the operator is '?=' and the variable * already exists. */ static bool VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, GNode *scope, FStr *out_true_avalue) { FStr avalue = FStr_InitRefer(uvalue); if (op == VAR_APPEND) Var_AppendExpand(scope, name, uvalue); else if (op == VAR_SUBST) VarAssign_EvalSubst(scope, name, uvalue, &avalue); else if (op == VAR_SHELL) VarAssign_EvalShell(name, uvalue, scope, &avalue); else { /* XXX: The variable name is expanded up to 2 times. */ if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name)) return false; /* Normal assignment -- just do it. */ Var_SetExpand(scope, name, uvalue); } *out_true_avalue = avalue; return true; } static void VarAssignSpecial(const char *name, const char *avalue) { if (strcmp(name, MAKEOVERRIDES) == 0) Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */ else if (strcmp(name, ".CURDIR") == 0) { /* * Someone is being (too?) clever... * Let's pretend they know what they are doing and * re-initialize the 'cur' CachedDir. */ Dir_InitCur(avalue); Dir_SetPATH(); } else if (strcmp(name, MAKE_JOB_PREFIX) == 0) Job_SetPrefix(); else if (strcmp(name, MAKE_EXPORTED) == 0) Var_ExportVars(avalue); } /* Perform the variable assignment in the given scope. */ static void Parse_Var(VarAssign *var, GNode *scope) { FStr avalue; /* actual value (maybe expanded) */ VarCheckSyntax(var->op, var->value, scope); if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) { VarAssignSpecial(var->varname, avalue.str); FStr_Done(&avalue); } } /* * See if the command possibly calls a sub-make by using the variable * expressions ${.MAKE}, ${MAKE} or the plain word "make". */ static bool MaybeSubMake(const char *cmd) { const char *start; for (start = cmd; *start != '\0'; start++) { const char *p = start; char endc; /* XXX: What if progname != "make"? */ if (strncmp(p, "make", 4) == 0) if (start == cmd || !ch_isalnum(p[-1])) if (!ch_isalnum(p[4])) return true; if (*p != '$') continue; p++; if (*p == '{') endc = '}'; else if (*p == '(') endc = ')'; else continue; p++; if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */ p++; if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc) return true; } return false; } /* * Append the command to the target node. * * The node may be marked as a submake node if the command is determined to * be that. */ static void GNode_AddCommand(GNode *gn, char *cmd) { /* Add to last (ie current) cohort for :: targets */ if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL) gn = gn->cohorts.last->datum; /* if target already supplied, ignore commands */ if (!(gn->type & OP_HAS_COMMANDS)) { Lst_Append(&gn->commands, cmd); if (MaybeSubMake(cmd)) gn->type |= OP_SUBMAKE; RememberLocation(gn); } else { #if 0 /* XXX: We cannot do this until we fix the tree */ Lst_Append(&gn->commands, cmd); Parse_Error(PARSE_WARNING, "overriding commands for target \"%s\"; " "previous commands defined at %s: %u ignored", gn->name, gn->fname, gn->lineno); #else Parse_Error(PARSE_WARNING, "duplicate script for target \"%s\" ignored", gn->name); ParseErrorInternal(gn, PARSE_WARNING, "using previous script for \"%s\" defined here", gn->name); #endif } } /* * Add a directory to the path searched for included makefiles bracketed * by double-quotes. */ void Parse_AddIncludeDir(const char *dir) { (void)SearchPath_Add(parseIncPath, dir); } /* * Parse a directive like '.include' or '.-include'. * * .include "user-makefile.mk" * .include */ static void ParseInclude(char *directive) { char endc; /* '>' or '"' */ char *p; bool silent = directive[0] != 'i'; FStr file; p = directive + (silent ? 8 : 7); pp_skip_hspace(&p); if (*p != '"' && *p != '<') { Parse_Error(PARSE_FATAL, ".include filename must be delimited by '\"' or '<'"); return; } if (*p++ == '<') endc = '>'; else endc = '"'; file = FStr_InitRefer(p); /* Skip to matching delimiter */ while (*p != '\0' && *p != endc) p++; if (*p != endc) { Parse_Error(PARSE_FATAL, "Unclosed .include filename. '%c' expected", endc); return; } *p = '\0'; Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES); IncludeFile(file.str, endc == '>', directive[0] == 'd', silent); FStr_Done(&file); } /* * Split filename into dirname + basename, then assign these to the * given variables. */ static void SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) { const char *slash, *basename; FStr dirname; slash = strrchr(filename, '/'); if (slash == NULL) { dirname = FStr_InitRefer(curdir); basename = filename; } else { dirname = FStr_InitOwn(bmake_strsedup(filename, slash)); basename = slash + 1; } Global_Set(dirvar, dirname.str); Global_Set(filevar, basename); DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n", dirvar, dirname.str, filevar, basename); FStr_Done(&dirname); } /* * Return the immediately including file. * * This is made complicated since the .for loop is implemented as a special * kind of .include; see For_Run. */ static const char * GetActuallyIncludingFile(void) { size_t i; const IncludedFile *incs = GetInclude(0); for (i = includes.len; i >= 2; i--) if (incs[i - 1].forLoop == NULL) return incs[i - 2].name.str; return NULL; } /* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */ static void SetParseFile(const char *filename) { const char *including; SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE"); including = GetActuallyIncludingFile(); if (including != NULL) { SetFilenameVars(including, ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); } else { Global_Delete(".INCLUDEDFROMDIR"); Global_Delete(".INCLUDEDFROMFILE"); } } static bool StrContainsWord(const char *str, const char *word) { size_t strLen = strlen(str); size_t wordLen = strlen(word); const char *p; if (strLen < wordLen) return false; for (p = str; p != NULL; p = strchr(p, ' ')) { if (*p == ' ') p++; if (p > str + strLen - wordLen) return false; if (memcmp(p, word, wordLen) == 0 && (p[wordLen] == '\0' || p[wordLen] == ' ')) return true; } return false; } /* * XXX: Searching through a set of words with this linear search is * inefficient for variables that contain thousands of words. * * XXX: The paths in this list don't seem to be normalized in any way. */ static bool VarContainsWord(const char *varname, const char *word) { FStr val = Var_Value(SCOPE_GLOBAL, varname); bool found = val.str != NULL && StrContainsWord(val.str, word); FStr_Done(&val); return found; } /* * Track the makefiles we read - so makefiles can set dependencies on them. * Avoid adding anything more than once. * * Time complexity: O(n) per call, in total O(n^2), where n is the number * of makefiles that have been loaded. */ static void TrackInput(const char *name) { if (!VarContainsWord(MAKE_MAKEFILES, name)) Global_Append(MAKE_MAKEFILES, name); } /* Parse from the given buffer, later return to the current file. */ void Parse_PushInput(const char *name, unsigned lineno, unsigned readLines, Buffer buf, struct ForLoop *forLoop) { IncludedFile *curFile; if (forLoop != NULL) name = CurFile()->name.str; else TrackInput(name); DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n", forLoop != NULL ? ".for loop in": "file", name, lineno); curFile = Vector_Push(&includes); curFile->name = FStr_InitOwn(bmake_strdup(name)); curFile->lineno = lineno; curFile->readLines = readLines; curFile->forHeadLineno = lineno; curFile->forBodyReadLines = readLines; curFile->buf = buf; curFile->depending = doing_depend; /* restore this on EOF */ curFile->forLoop = forLoop; if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf)) abort(); /* see For_Run */ curFile->buf_ptr = curFile->buf.data; curFile->buf_end = curFile->buf.data + curFile->buf.len; - curFile->cond_depth = Cond_save_depth(); + curFile->condMinDepth = cond_depth; SetParseFile(name); } /* Check if the directive is an include directive. */ static bool IsInclude(const char *dir, bool sysv) { if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv)) dir++; if (strncmp(dir, "include", 7) != 0) return false; /* Space is not mandatory for BSD .include */ return !sysv || ch_isspace(dir[7]); } #ifdef SYSVINCLUDE /* Check if the line is a SYSV include directive. */ static bool IsSysVInclude(const char *line) { const char *p; if (!IsInclude(line, true)) return false; /* Avoid interpreting a dependency line as an include */ for (p = line; (p = strchr(p, ':')) != NULL;) { /* end of line -> it's a dependency */ if (*++p == '\0') return false; /* '::' operator or ': ' -> it's a dependency */ if (*p == ':' || ch_isspace(*p)) return false; } return true; } /* Push to another file. The line points to the word "include". */ static void ParseTraditionalInclude(char *line) { char *cp; /* current position in file spec */ bool done = false; bool silent = line[0] != 'i'; char *file = line + (silent ? 8 : 7); char *all_files; DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file); pp_skip_whitespace(&file); (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files); /* TODO: handle errors */ for (file = all_files; !done; file = cp + 1) { /* Skip to end of line or next whitespace */ for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++) continue; if (*cp != '\0') *cp = '\0'; else done = true; IncludeFile(file, false, false, silent); } free(all_files); } #endif #ifdef GMAKEEXPORT /* Parse "export =", and actually export it. */ static void ParseGmakeExport(char *line) { char *variable = line + 6; char *value; DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable); pp_skip_whitespace(&variable); for (value = variable; *value != '\0' && *value != '='; value++) continue; if (*value != '=') { Parse_Error(PARSE_FATAL, "Variable/Value missing from \"export\""); return; } *value++ = '\0'; /* terminate variable */ /* * Expand the value before putting it in the environment. */ (void)Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES, &value); /* TODO: handle errors */ setenv(variable, value, 1); free(value); } #endif /* * Called when EOF is reached in the current file. If we were reading an * include file or a .for loop, the includes stack is popped and things set * up to go back to reading the previous file at the previous location. * * Results: * true to continue parsing, i.e. it had only reached the end of an * included file, false if the main file has been parsed completely. */ static bool ParseEOF(void) { IncludedFile *curFile = CurFile(); doing_depend = curFile->depending; if (curFile->forLoop != NULL && For_NextIteration(curFile->forLoop, &curFile->buf)) { curFile->buf_ptr = curFile->buf.data; curFile->buf_end = curFile->buf.data + curFile->buf.len; curFile->readLines = curFile->forBodyReadLines; return true; } - /* - * Ensure the makefile (or .for loop) didn't have mismatched - * conditionals. - */ - Cond_restore_depth(curFile->cond_depth); + Cond_EndFile(); FStr_Done(&curFile->name); Buf_Done(&curFile->buf); if (curFile->forLoop != NULL) ForLoop_Free(curFile->forLoop); Vector_Pop(&includes); if (includes.len == 0) { /* We've run out of input */ Global_Delete(".PARSEDIR"); Global_Delete(".PARSEFILE"); Global_Delete(".INCLUDEDFROMDIR"); Global_Delete(".INCLUDEDFROMFILE"); return false; } curFile = CurFile(); DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n", curFile->name.str, curFile->readLines + 1); SetParseFile(curFile->name.str); return true; } typedef enum ParseRawLineResult { PRLR_LINE, PRLR_EOF, PRLR_ERROR } ParseRawLineResult; /* * Parse until the end of a line, taking into account lines that end with * backslash-newline. The resulting line goes from out_line to out_line_end; * the line is not null-terminated. */ static ParseRawLineResult ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end, char **out_firstBackslash, char **out_commentLineEnd) { char *line = curFile->buf_ptr; char *buf_end = curFile->buf_end; char *p = line; char *line_end = line; char *firstBackslash = NULL; char *commentLineEnd = NULL; ParseRawLineResult res = PRLR_LINE; curFile->readLines++; for (;;) { char ch; if (p == buf_end) { res = PRLR_EOF; break; } ch = *p; if (ch == '\0' || (ch == '\\' && p[1] == '\0')) { Parse_Error(PARSE_FATAL, "Zero byte read from file"); return PRLR_ERROR; } /* Treat next character after '\' as literal. */ if (ch == '\\') { if (firstBackslash == NULL) firstBackslash = p; if (p[1] == '\n') { curFile->readLines++; if (p + 2 == buf_end) { line_end = p; *line_end = '\n'; p += 2; continue; } } p += 2; line_end = p; assert(p <= buf_end); continue; } /* * Remember the first '#' for comment stripping, unless * the previous char was '[', as in the modifier ':[#]'. */ if (ch == '#' && commentLineEnd == NULL && !(p > line && p[-1] == '[')) commentLineEnd = line_end; p++; if (ch == '\n') break; /* We are not interested in trailing whitespace. */ if (!ch_isspace(ch)) line_end = p; } curFile->buf_ptr = p; *out_line = line; *out_line_end = line_end; *out_firstBackslash = firstBackslash; *out_commentLineEnd = commentLineEnd; return res; } /* * Beginning at start, unescape '\#' to '#' and replace backslash-newline * with a single space. */ static void UnescapeBackslash(char *line, char *start) { const char *src = start; char *dst = start; char *spaceStart = line; for (;;) { char ch = *src++; if (ch != '\\') { if (ch == '\0') break; *dst++ = ch; continue; } ch = *src++; if (ch == '\0') { /* Delete '\\' at the end of the buffer. */ dst--; break; } /* Delete '\\' from before '#' on non-command lines. */ if (ch == '#' && line[0] != '\t') *dst++ = ch; else if (ch == '\n') { cpp_skip_hspace(&src); *dst++ = ' '; } else { /* Leave '\\' in the buffer for later. */ *dst++ = '\\'; *dst++ = ch; /* Keep an escaped ' ' at the line end. */ spaceStart = dst; } } /* Delete any trailing spaces - eg from empty continuations */ while (dst > spaceStart && ch_isspace(dst[-1])) dst--; *dst = '\0'; } typedef enum LineKind { /* * Return the next line that is neither empty nor a comment. * Backslash line continuations are folded into a single space. * A trailing comment, if any, is discarded. */ LK_NONEMPTY, /* * Return the next line, even if it is empty or a comment. * Preserve backslash-newline to keep the line numbers correct. * * Used in .for loops to collect the body of the loop while waiting * for the corresponding .endfor. */ LK_FOR_BODY, /* * Return the next line that starts with a dot. * Backslash line continuations are folded into a single space. * A trailing comment, if any, is discarded. * * Used in .if directives to skip over irrelevant branches while * waiting for the corresponding .endif. */ LK_DOT } LineKind; /* * Return the next "interesting" logical line from the current file. The * returned string will be freed at the end of including the file. */ static char * ReadLowLevelLine(LineKind kind) { IncludedFile *curFile = CurFile(); ParseRawLineResult res; char *line; char *line_end; char *firstBackslash; char *commentLineEnd; for (;;) { curFile->lineno = curFile->readLines + 1; res = ParseRawLine(curFile, &line, &line_end, &firstBackslash, &commentLineEnd); if (res == PRLR_ERROR) return NULL; if (line == line_end || line == commentLineEnd) { if (res == PRLR_EOF) return NULL; if (kind != LK_FOR_BODY) continue; } /* We now have a line of data */ assert(ch_isspace(*line_end)); *line_end = '\0'; if (kind == LK_FOR_BODY) return line; /* Don't join the physical lines. */ if (kind == LK_DOT && line[0] != '.') continue; break; } if (commentLineEnd != NULL && line[0] != '\t') *commentLineEnd = '\0'; if (firstBackslash != NULL) UnescapeBackslash(line, firstBackslash); return line; } static bool SkipIrrelevantBranches(void) { const char *line; while ((line = ReadLowLevelLine(LK_DOT)) != NULL) { if (Cond_EvalLine(line) == CR_TRUE) return true; /* * TODO: Check for typos in .elif directives such as .elsif * or .elseif. * * This check will probably duplicate some of the code in * ParseLine. Most of the code there cannot apply, only * ParseVarassign and ParseDependencyLine can, and to prevent * code duplication, these would need to be called with a * flag called onlyCheckSyntax. * * See directive-elif.mk for details. */ } return false; } static bool ParseForLoop(const char *line) { int rval; unsigned forHeadLineno; unsigned bodyReadLines; int forLevel; rval = For_Eval(line); if (rval == 0) return false; /* Not a .for line */ if (rval < 0) return true; /* Syntax error - error printed, ignore line */ forHeadLineno = CurFile()->lineno; bodyReadLines = CurFile()->readLines; /* Accumulate the loop body until the matching '.endfor'. */ forLevel = 1; do { line = ReadLowLevelLine(LK_FOR_BODY); if (line == NULL) { Parse_Error(PARSE_FATAL, "Unexpected end of file in .for loop"); break; } } while (For_Accum(line, &forLevel)); For_Run(forHeadLineno, bodyReadLines); return true; } /* * Read an entire line from the input file. * * Empty lines, .if and .for are completely handled by this function, * leaving only variable assignments, other directives, dependency lines * and shell commands to the caller. * * Return a line without trailing whitespace, or NULL for EOF. The returned * string will be freed at the end of including the file. */ static char * ReadHighLevelLine(void) { char *line; for (;;) { line = ReadLowLevelLine(LK_NONEMPTY); if (posix_state == PS_MAYBE_NEXT_LINE) posix_state = PS_NOW_OR_NEVER; else posix_state = PS_TOO_LATE; if (line == NULL) return NULL; if (line[0] != '.') return line; switch (Cond_EvalLine(line)) { case CR_FALSE: /* May also mean a syntax error. */ if (!SkipIrrelevantBranches()) return NULL; continue; case CR_TRUE: continue; case CR_ERROR: /* Not a conditional line */ if (ParseForLoop(line)) continue; break; } return line; } } static void FinishDependencyGroup(void) { GNodeListNode *ln; if (targets == NULL) return; for (ln = targets->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; Suff_EndTransform(gn); /* * Mark the target as already having commands if it does, to * keep from having shell commands on multiple dependency * lines. */ if (!Lst_IsEmpty(&gn->commands)) gn->type |= OP_HAS_COMMANDS; } Lst_Free(targets); targets = NULL; } /* Add the command to each target from the current dependency spec. */ static void ParseLine_ShellCommand(const char *p) { cpp_skip_whitespace(&p); if (*p == '\0') return; /* skip empty commands */ if (targets == NULL) { Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", p); return; } { char *cmd = bmake_strdup(p); GNodeListNode *ln; for (ln = targets->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; GNode_AddCommand(gn, cmd); } #ifdef CLEANUP Lst_Append(&targCmds, cmd); #endif } } +static void +HandleBreak(void) +{ + IncludedFile *curFile = CurFile(); + + if (curFile->forLoop != NULL) { + /* pretend we reached EOF */ + For_Break(curFile->forLoop); + cond_depth = CurFile_CondMinDepth(); + ParseEOF(); + } else + Parse_Error(PARSE_FATAL, "break outside of for loop"); +} + /* * See if the line starts with one of the known directives, and if so, handle * the directive. */ static bool ParseDirective(char *line) { char *cp = line + 1; const char *arg; Substring dir; pp_skip_whitespace(&cp); if (IsInclude(cp, false)) { ParseInclude(cp); return true; } dir.start = cp; while (ch_islower(*cp) || *cp == '-') cp++; dir.end = cp; if (*cp != '\0' && !ch_isspace(*cp)) return false; pp_skip_whitespace(&cp); arg = cp; - if (Substring_Equals(dir, "undef")) + if (Substring_Equals(dir, "break")) + HandleBreak(); + else if (Substring_Equals(dir, "undef")) Var_Undef(arg); else if (Substring_Equals(dir, "export")) Var_Export(VEM_PLAIN, arg); else if (Substring_Equals(dir, "export-env")) Var_Export(VEM_ENV, arg); else if (Substring_Equals(dir, "export-literal")) Var_Export(VEM_LITERAL, arg); else if (Substring_Equals(dir, "unexport")) Var_UnExport(false, arg); else if (Substring_Equals(dir, "unexport-env")) Var_UnExport(true, arg); else if (Substring_Equals(dir, "info")) HandleMessage(PARSE_INFO, "info", arg); else if (Substring_Equals(dir, "warning")) HandleMessage(PARSE_WARNING, "warning", arg); else if (Substring_Equals(dir, "error")) HandleMessage(PARSE_FATAL, "error", arg); else return false; return true; } bool Parse_VarAssign(const char *line, bool finishDependencyGroup, GNode *scope) { VarAssign var; if (!Parse_IsVar(line, &var)) return false; if (finishDependencyGroup) FinishDependencyGroup(); Parse_Var(&var, scope); free(var.varname); return true; } static char * FindSemicolon(char *p) { int level = 0; for (; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') { p++; continue; } if (*p == '$' && (p[1] == '(' || p[1] == '{')) level++; else if (level > 0 && (*p == ')' || *p == '}')) level--; else if (level == 0 && *p == ';') break; } return p; } /* * dependency -> [target...] op [source...] [';' command] * op -> ':' | '::' | '!' */ static void ParseDependencyLine(char *line) { VarEvalMode emode; char *expanded_line; const char *shellcmd = NULL; /* * For some reason - probably to make the parser impossible - * a ';' can be used to separate commands from dependencies. * Attempt to skip over ';' inside substitution patterns. */ { char *semicolon = FindSemicolon(line); if (*semicolon != '\0') { /* Terminate the dependency list at the ';' */ *semicolon = '\0'; shellcmd = semicolon + 1; } } /* * We now know it's a dependency line so it needs to have all * variables expanded before being parsed. * * XXX: Ideally the dependency line would first be split into * its left-hand side, dependency operator and right-hand side, * and then each side would be expanded on its own. This would * allow for the left-hand side to allow only defined variables * and to allow variables on the right-hand side to be undefined * as well. * * Parsing the line first would also prevent that targets * generated from variable expressions are interpreted as the * dependency operator, such as in "target${:U\:} middle: source", * in which the middle is interpreted as a source, not a target. */ /* * In lint mode, allow undefined variables to appear in dependency * lines. * * Ideally, only the right-hand side would allow undefined variables * since it is common to have optional dependencies. Having undefined * variables on the left-hand side is more unusual though. Since * both sides are expanded in a single pass, there is not much choice * what to do here. * * In normal mode, it does not matter whether undefined variables are * allowed or not since as of 2020-09-14, Var_Parse does not print * any parse errors in such a case. It simply returns the special * empty string var_Error, which cannot be detected in the result of * Var_Subst. */ emode = opts.strict ? VARE_WANTRES : VARE_UNDEFERR; (void)Var_Subst(line, SCOPE_CMDLINE, emode, &expanded_line); /* TODO: handle errors */ /* Need a fresh list for the target nodes */ if (targets != NULL) Lst_Free(targets); targets = Lst_New(); ParseDependency(expanded_line); free(expanded_line); if (shellcmd != NULL) ParseLine_ShellCommand(shellcmd); } static void ParseLine(char *line) { /* * Lines that begin with '.' can be pretty much anything: * - directives like '.include' or '.if', * - suffix rules like '.c.o:', * - dependencies for filenames that start with '.', * - variable assignments like '.tmp=value'. */ if (line[0] == '.' && ParseDirective(line)) return; if (line[0] == '\t') { ParseLine_ShellCommand(line + 1); return; } #ifdef SYSVINCLUDE if (IsSysVInclude(line)) { /* * It's an S3/S5-style "include". */ ParseTraditionalInclude(line); return; } #endif #ifdef GMAKEEXPORT if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) && strchr(line, ':') == NULL) { /* * It's a Gmake "export". */ ParseGmakeExport(line); return; } #endif if (Parse_VarAssign(line, true, SCOPE_GLOBAL)) return; FinishDependencyGroup(); ParseDependencyLine(line); } /* * Parse a top-level makefile, incorporating its content into the global * dependency graph. */ void Parse_File(const char *name, int fd) { char *line; Buffer buf; buf = LoadFile(name, fd != -1 ? fd : STDIN_FILENO); if (fd != -1) (void)close(fd); assert(targets == NULL); Parse_PushInput(name, 1, 0, buf, NULL); do { while ((line = ReadHighLevelLine()) != NULL) { DEBUG2(PARSE, "Parsing line %u: %s\n", CurFile()->lineno, line); ParseLine(line); } /* Reached EOF, but it may be just EOF of an include file. */ } while (ParseEOF()); FinishDependencyGroup(); if (parseErrors != 0) { (void)fflush(stdout); (void)fprintf(stderr, "%s: Fatal errors encountered -- cannot continue\n", progname); PrintOnError(NULL, ""); exit(1); } } /* Initialize the parsing module. */ void Parse_Init(void) { mainNode = NULL; parseIncPath = SearchPath_New(); sysIncPath = SearchPath_New(); defSysIncPath = SearchPath_New(); Vector_Init(&includes, sizeof(IncludedFile)); } /* Clean up the parsing module. */ void Parse_End(void) { #ifdef CLEANUP Lst_DoneCall(&targCmds, free); assert(targets == NULL); SearchPath_Free(defSysIncPath); SearchPath_Free(sysIncPath); SearchPath_Free(parseIncPath); assert(includes.len == 0); Vector_Done(&includes); #endif } /* * Return a list containing the single main target to create. * If no such target exists, we Punt with an obnoxious error message. */ void Parse_MainName(GNodeList *mainList) { if (mainNode == NULL) Punt("no target to make."); Lst_Append(mainList, mainNode); if (mainNode->type & OP_DOUBLEDEP) Lst_AppendAll(mainList, &mainNode->cohorts); Global_Append(".TARGETS", mainNode->name); } int Parse_NumErrors(void) { return parseErrors; } diff --git a/contrib/bmake/sigcompat.c b/contrib/bmake/sigcompat.c index 608538d77d0f..c720eeba92e5 100644 --- a/contrib/bmake/sigcompat.c +++ b/contrib/bmake/sigcompat.c @@ -1,325 +1,325 @@ /* * NAME: * sigcompat - BSD compat signals via POSIX * * SYNOPSIS: * void (*signal(int "sig", void (*"handler")(int)))(int); * int sigsetmask(int "mask"); * int sigblock(int "mask"); * int sigpause(int "mask"); * int sigvec(int "signo", struct sigvec *"sv", struct sigvec *"osv"); * * DESCRIPTION: * These implement the old BSD routines via the POSIX equivalents. * This module can be used to provide the missing routines, or if * 'FORCE_POSIX_SIGNALS' is defined, force use of these. * * Note that signal() is identical to my Signal() routine except * for checking for recursion. Within libsig, signal() just * calls Signal(). * * BUGS: * This package assumes POSIX signal handling is available and * NOT implemeneted using these routines. To be safe, we check * for recursion and abort(3) if detected. * * Sadly, on some systems, sigset_t is an array, and we cannot * test for this via #if sizeof(sigset_t) ..., so unless * 'SIGSET_T_INT' is defined, we have to assume the worst and use * memcpy(3) to handle args and return values. * * HISTORY: * These routines originate from BSD, and are derrived from the * NetBSD 1.1 implementation. They have been seriously hacked to * make them portable to other systems. * * AUTHOR: * Simon J. Gerraty */ /* * @(#)Copyright (c) 1994, Simon J. Gerraty. * * This is free software. It comes with NO WARRANTY. * Permission to use, modify and distribute this source code * is granted subject to the following conditions. * 1/ that the above copyright notice and this notice * are preserved in all copies and that due credit be given * to the author. * 2/ that any changes to this code are clearly commented * as such so that the author does not get blamed for bugs * other than his own. * * Please send copies of changes and bug-fixes to: * sjg@crufty.net */ /* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H #include #endif #if defined(sun) && !(defined(__svr4__) || defined(__SVR4)) # define NO_SIGCOMPAT #endif #if defined(__MINT__) # define NO_SIGCOMPAT #endif #if !defined(NO_SIGCOMPAT) && (defined(HAVE_SIGACTION) || defined(SA_NOCLDSTOP)) #if defined(LIBC_SCCS) && !defined(lint) /*static char *sccsid = "from: @(#)sigcompat.c 5.3 (Berkeley) 2/24/91";*/ -static char *rcsid = "$Id: sigcompat.c,v 1.23 2011/02/14 00:07:11 sjg Exp $"; +static char *rcsid = "$Id: sigcompat.c,v 1.24 2022/09/26 17:38:10 sjg Exp $"; #endif /* LIBC_SCCS and not lint */ #undef signal #include #include #include #include #include "assert.h" #ifndef ASSERT # define ASSERT assert #endif #ifdef NDEBUG # define _DBUG(x) #else # define _DBUG(x) x #endif #ifndef SA_RESTART # define SA_RESTART 2 #endif #ifndef SV_INTERRUPT # define SV_INTERRUPT SA_RESTART #endif #ifndef MASK_T # if defined(__hpux__) || defined(__hpux) # define MASK_T long # else # define MASK_T int # endif #endif /* I just hate HPsUX */ #if (defined(__HPUX_VERSION) && __HPUX_VERSION > 9) || defined(__hpux) # define PAUSE_MASK_T int #else # define PAUSE_MASK_T MASK_T #endif #ifndef SIG_HDLR # define SIG_HDLR void #endif #ifdef FORCE_POSIX_SIGNALS #if !(defined(libsig) || defined(libsjg)) /* * This little block is almost identical to Signal(), * and make this module standalone. * We don't use it in libsig by default, as some apps might use both * and expect _SignalFlags to be used by both. */ #ifndef SIGNAL_FLAGS # define SIGNAL_FLAGS 0 /* no auto-restart */ #endif int _signalFlags = SIGNAL_FLAGS; SIG_HDLR(*signal(int sig, SIG_HDLR(*handler)(int)))(int) { _DBUG(static int depth_signal = 0); struct sigaction act, oact; int n; _DBUG(++depth_signal); ASSERT(depth_signal < 2); act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = _signalFlags; n = sigaction(sig, &act, &oact); _DBUG(--depth_signal); if (n < 0) return (SIG_ERR); return (oact.sa_handler); } #else SIG_HDLR(*signal(int sig, SIG_HDLR(*handler)(int)))(int) { extern SIG_HDLR(*Signal(int, void (*)(int)))(int); _DBUG(static int depth_signal = 0); SIG_HDLR(*old) __P((int)); _DBUG(++depth_signal); ASSERT(depth_signal < 2); old = Signal(sig, handler); _DBUG(--depth_signal); return old; } #endif #endif /* * on some systems, sigset_t is an array... * it would be nicer if we could do * #if sizeof(sigset_t) > sizeof(MASK_T) */ #ifdef SIGSET_T_INT # define ss2m(ss) (MASK_T) *(ss) # define m2ss(ss, m) *ss = (sigset_t) *(m) -#else +#elif !defined(HAVE_SIGSETMASK) || defined(FORCE_POSIX_SIGNALS) static MASK_T ss2m(sigset_t *ss) { MASK_T ma[(sizeof(sigset_t) / sizeof(MASK_T)) + 1]; memcpy((char *) ma, (char *) ss, sizeof(sigset_t)); return ma[0]; } static void m2ss(sigset_t *ss, MASK_T *m) { if (sizeof(sigset_t) > sizeof(MASK_T)) memset((char *) ss, 0, sizeof(sigset_t)); memcpy((char *) ss, (char *) m, sizeof(MASK_T)); } #endif #if !defined(HAVE_SIGSETMASK) || defined(FORCE_POSIX_SIGNALS) MASK_T sigsetmask(MASK_T mask) { _DBUG(static int depth_sigsetmask = 0); sigset_t m, omask; int n; _DBUG(++depth_sigsetmask); ASSERT(depth_sigsetmask < 2); m2ss(&m, &mask); n = sigprocmask(SIG_SETMASK, (sigset_t *) & m, (sigset_t *) & omask); _DBUG(--depth_sigsetmask); if (n) return (n); return ss2m(&omask); } MASK_T sigblock(MASK_T mask) { _DBUG(static int depth_sigblock = 0); sigset_t m, omask; int n; _DBUG(++depth_sigblock); ASSERT(depth_sigblock < 2); if (mask) m2ss(&m, &mask); n = sigprocmask(SIG_BLOCK, (sigset_t *) ((mask) ? &m : 0), (sigset_t *) & omask); _DBUG(--depth_sigblock); if (n) return (n); return ss2m(&omask); } #undef sigpause /* Linux at least */ PAUSE_MASK_T sigpause(PAUSE_MASK_T mask) { _DBUG(static int depth_sigpause = 0); sigset_t m; PAUSE_MASK_T n; _DBUG(++depth_sigpause); ASSERT(depth_sigpause < 2); m2ss(&m, &mask); n = sigsuspend(&m); _DBUG(--depth_sigpause); return n; } #endif #if defined(HAVE_SIGVEC) && defined(FORCE_POSIX_SIGNALS) int sigvec(int signo, struct sigvec *sv, struct sigvec *osv) { _DBUG(static int depth_sigvec = 0); int ret; struct sigvec nsv; _DBUG(++depth_sigvec); ASSERT(depth_sigvec < 2); if (sv) { nsv = *sv; nsv.sv_flags ^= SV_INTERRUPT; /* !SA_INTERRUPT */ } ret = sigaction(signo, sv ? (struct sigaction *) & nsv : NULL, (struct sigaction *) osv); _DBUG(--depth_sigvec); if (ret == 0 && osv) osv->sv_flags ^= SV_INTERRUPT; /* !SA_INTERRUPT */ return (ret); } #endif #ifdef MAIN # ifndef sigmask # define sigmask(n) ((unsigned int)1 << (((n) - 1) & (32 - 1))) # endif int main(int argc, char *argv[]) { MASK_T old = 0; printf("expect: old=0,old=2\n"); fflush(stdout); signal(SIGQUIT, SIG_IGN); old = sigblock(sigmask(SIGINT)); printf("old=%d,", old); old = sigsetmask(sigmask(SIGALRM)); printf("old=%d\n", old); } #endif #endif diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c index d88f895630e5..5bb9938296f5 100644 --- a/contrib/bmake/str.c +++ b/contrib/bmake/str.c @@ -1,403 +1,403 @@ -/* $NetBSD: str.c,v 1.93 2022/06/11 09:24:07 rillig Exp $ */ +/* $NetBSD: str.c,v 1.94 2022/12/07 10:28:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "make.h" /* "@(#)str.c 5.8 (Berkeley) 6/1/90" */ -MAKE_RCSID("$NetBSD: str.c,v 1.93 2022/06/11 09:24:07 rillig Exp $"); +MAKE_RCSID("$NetBSD: str.c,v 1.94 2022/12/07 10:28:48 rillig Exp $"); static HashTable interned_strings; /* Return the concatenation of s1 and s2, freshly allocated. */ char * str_concat2(const char *s1, const char *s2) { size_t len1 = strlen(s1); size_t len2 = strlen(s2); char *result = bmake_malloc(len1 + len2 + 1); memcpy(result, s1, len1); memcpy(result + len1, s2, len2 + 1); return result; } /* Return the concatenation of s1, s2 and s3, freshly allocated. */ char * str_concat3(const char *s1, const char *s2, const char *s3) { size_t len1 = strlen(s1); size_t len2 = strlen(s2); size_t len3 = strlen(s3); char *result = bmake_malloc(len1 + len2 + len3 + 1); memcpy(result, s1, len1); memcpy(result + len1, s2, len2); memcpy(result + len1 + len2, s3, len3 + 1); return result; } /* * 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, * etc... are expanded. In this case, return NULL on parse errors. * * Returns the fractured words, which must be freed later using Words_Free, * unless the returned Words.words was NULL. */ SubstringWords Substring_Words(const char *str, bool expand) { size_t str_len; char *words_buf; size_t words_cap; Substring *words; size_t words_len; char inquote; char *word_start; char *word_end; const char *str_p; /* XXX: why only hspace, not whitespace? */ cpp_skip_hspace(&str); /* skip leading space chars. */ /* words_buf holds the words, separated by '\0'. */ str_len = strlen(str); words_buf = bmake_malloc(str_len + 1); words_cap = str_len / 5 > 50 ? str_len / 5 : 50; words = bmake_malloc((words_cap + 1) * sizeof(words[0])); /* * copy the string; at the same time, parse backslashes, * quotes and build the word list. */ words_len = 0; inquote = '\0'; word_start = words_buf; word_end = words_buf; for (str_p = str;; str_p++) { char ch = *str_p; switch (ch) { case '"': case '\'': if (inquote != '\0') { if (inquote == ch) inquote = '\0'; else break; } else { inquote = ch; /* Don't miss "" or '' */ if (word_start == NULL && str_p[1] == inquote) { if (!expand) { word_start = word_end; *word_end++ = ch; } else word_start = word_end + 1; str_p++; inquote = '\0'; break; } } if (!expand) { if (word_start == NULL) word_start = word_end; *word_end++ = ch; } continue; case ' ': case '\t': case '\n': if (inquote != '\0') break; if (word_start == NULL) continue; /* FALLTHROUGH */ case '\0': /* * end of a token -- make sure there's enough words * space and save off a pointer. */ if (word_start == NULL) goto done; *word_end++ = '\0'; if (words_len == words_cap) { words_cap *= 2; words = bmake_realloc(words, (words_cap + 1) * sizeof(words[0])); } words[words_len++] = Substring_Init(word_start, word_end - 1); word_start = NULL; if (ch == '\n' || ch == '\0') { if (expand && inquote != '\0') { SubstringWords res; free(words); free(words_buf); res.words = NULL; res.len = 0; res.freeIt = NULL; return res; } goto done; } continue; case '\\': if (!expand) { if (word_start == NULL) word_start = word_end; *word_end++ = '\\'; /* catch lonely '\' at end of string */ if (str_p[1] == '\0') continue; ch = *++str_p; break; } switch (ch = *++str_p) { case '\0': case '\n': /* hmmm; fix it up as best we can */ ch = '\\'; str_p--; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; } break; } if (word_start == NULL) word_start = word_end; *word_end++ = ch; } done: words[words_len] = Substring_Init(NULL, NULL); /* useful for argv */ { SubstringWords result; result.words = words; result.len = words_len; result.freeIt = words_buf; return result; } } Words Str_Words(const char *str, bool expand) { SubstringWords swords; Words words; size_t i; swords = Substring_Words(str, expand); if (swords.words == NULL) { words.words = NULL; words.len = 0; words.freeIt = NULL; return words; } words.words = bmake_malloc((swords.len + 1) * sizeof(words.words[0])); words.len = swords.len; words.freeIt = swords.freeIt; for (i = 0; i < swords.len + 1; i++) words.words[i] = UNCONST(swords.words[i].start); free(swords.words); return words; } /* * XXX: In the extreme edge case that one of the characters is from the basic * execution character set and the other isn't, the result of the comparison * differs depending on whether plain char is signed or unsigned. * * An example is the character range from \xE4 to 'a', where \xE4 may come * from U+00E4 'Latin small letter A with diaeresis'. * * If char is signed, \xE4 evaluates to -28, the first half of the condition * becomes -28 <= '0' && '0' <= 'a', which evaluates to true. * * If char is unsigned, \xE4 evaluates to 228, the second half of the * condition becomes 'a' <= '0' && '0' <= 228, which evaluates to false. */ static bool in_range(char e1, char c, char e2) { return (e1 <= c && c <= e2) || (e2 <= c && c <= e1); } /* - * Str_Match -- Test if a string matches a pattern like "*.[ch]". - * The following special characters are known *?\[] (as in fnmatch(3)). + * Test if a string matches a pattern like "*.[ch]". The pattern matching + * characters are '*', '?' and '[]', as in fnmatch(3). * * XXX: this function does not detect or report malformed patterns. * * See varmod-match.mk for examples and edge cases. */ bool Str_Match(const char *str, const char *pat) { for (; *pat != '\0'; pat++, str++) { if (*pat == '*') { /* match any substring */ pat++; while (*pat == '*') pat++; if (*pat == '\0') return true; for (; *str != '\0'; str++) if (Str_Match(str, pat)) return true; return false; } if (*str == '\0') return false; if (*pat == '?') /* match any single character */ continue; if (*pat == '[') { /* match a character from a list */ bool neg = pat[1] == '^'; pat += neg ? 2 : 1; for (;;) { if (*pat == ']' || *pat == '\0') { if (neg) break; return false; } if (*pat == *str) break; if (pat[1] == '-') { if (pat[2] == '\0') return neg; if (in_range(pat[0], *str, pat[2])) break; pat += 2; } pat++; } if (neg && *pat != ']' && *pat != '\0') return false; while (*pat != ']' && *pat != '\0') pat++; if (*pat == '\0') pat--; continue; } if (*pat == '\\') /* match the next character exactly */ pat++; if (*pat != *str) return false; } return *str == '\0'; } void Str_Intern_Init(void) { HashTable_Init(&interned_strings); } void Str_Intern_End(void) { #ifdef CLEANUP HashTable_Done(&interned_strings); #endif } /* Return a canonical instance of str, with unlimited lifetime. */ const char * Str_Intern(const char *str) { return HashTable_CreateEntry(&interned_strings, str, NULL)->key; } diff --git a/contrib/bmake/str.h b/contrib/bmake/str.h index 7a4047cb27c3..38fd2cf0da1a 100644 --- a/contrib/bmake/str.h +++ b/contrib/bmake/str.h @@ -1,349 +1,343 @@ -/* $NetBSD: str.h,v 1.15 2021/12/15 10:57:01 rillig Exp $ */ +/* $NetBSD: str.h,v 1.16 2022/12/05 23:41:24 rillig Exp $ */ /* Copyright (c) 2021 Roland Illig All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Memory-efficient string handling. */ /* A read-only string that may need to be freed after use. */ typedef struct FStr { const char *str; void *freeIt; } FStr; /* A read-only range of a character array, NOT null-terminated. */ typedef struct Substring { const char *start; const char *end; } Substring; /* * Builds a string, only allocating memory if the string is different from the * expected string. */ typedef struct LazyBuf { char *data; size_t len; size_t cap; const char *expected; } LazyBuf; /* The result of splitting a string into words. */ typedef struct Words { char **words; size_t len; void *freeIt; } Words; /* The result of splitting a string into words. */ typedef struct SubstringWords { Substring *words; size_t len; void *freeIt; } SubstringWords; MAKE_INLINE FStr FStr_Init(const char *str, void *freeIt) { FStr fstr; fstr.str = str; fstr.freeIt = freeIt; return fstr; } /* Return a string that is the sole owner of str. */ MAKE_INLINE FStr FStr_InitOwn(char *str) { return FStr_Init(str, str); } /* Return a string that refers to the shared str. */ MAKE_INLINE FStr FStr_InitRefer(const char *str) { return FStr_Init(str, NULL); } MAKE_INLINE void FStr_Done(FStr *fstr) { free(fstr->freeIt); #ifdef CLEANUP fstr->str = NULL; fstr->freeIt = NULL; #endif } MAKE_STATIC Substring Substring_Init(const char *start, const char *end) { Substring sub; sub.start = start; sub.end = end; return sub; } MAKE_INLINE Substring Substring_InitStr(const char *str) { return Substring_Init(str, str + strlen(str)); } MAKE_STATIC size_t Substring_Length(Substring sub) { return (size_t)(sub.end - sub.start); } MAKE_STATIC bool Substring_IsEmpty(Substring sub) { return sub.start == sub.end; } MAKE_INLINE bool Substring_Equals(Substring sub, const char *str) { size_t len = strlen(str); return Substring_Length(sub) == len && memcmp(sub.start, str, len) == 0; } MAKE_INLINE bool Substring_Eq(Substring sub, Substring str) { size_t len = Substring_Length(sub); return len == Substring_Length(str) && memcmp(sub.start, str.start, len) == 0; } MAKE_STATIC Substring Substring_Sub(Substring sub, size_t start, size_t end) { assert(start <= Substring_Length(sub)); assert(end <= Substring_Length(sub)); return Substring_Init(sub.start + start, sub.start + end); } MAKE_STATIC bool Substring_HasPrefix(Substring sub, Substring prefix) { return Substring_Length(sub) >= Substring_Length(prefix) && memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0; } MAKE_STATIC bool Substring_HasSuffix(Substring sub, Substring suffix) { size_t suffixLen = Substring_Length(suffix); return Substring_Length(sub) >= suffixLen && memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0; } /* Returns an independent, null-terminated copy of the substring. */ MAKE_STATIC FStr Substring_Str(Substring sub) { if (Substring_IsEmpty(sub)) return FStr_InitRefer(""); return FStr_InitOwn(bmake_strsedup(sub.start, sub.end)); } MAKE_STATIC const char * Substring_SkipFirst(Substring sub, char ch) { const char *p; for (p = sub.start; p != sub.end; p++) if (*p == ch) return p + 1; return sub.start; } MAKE_STATIC const char * Substring_LastIndex(Substring sub, char ch) { const char *p; for (p = sub.end; p != sub.start; p--) if (p[-1] == ch) return p - 1; return NULL; } MAKE_STATIC Substring Substring_Dirname(Substring pathname) { const char *p; for (p = pathname.end; p != pathname.start; p--) if (p[-1] == '/') return Substring_Init(pathname.start, p - 1); return Substring_InitStr("."); } MAKE_STATIC Substring Substring_Basename(Substring pathname) { const char *p; for (p = pathname.end; p != pathname.start; p--) if (p[-1] == '/') return Substring_Init(p, pathname.end); return pathname; } MAKE_STATIC void LazyBuf_Init(LazyBuf *buf, const char *expected) { buf->data = NULL; buf->len = 0; buf->cap = 0; buf->expected = expected; } MAKE_INLINE void LazyBuf_Done(LazyBuf *buf) { free(buf->data); } MAKE_STATIC void LazyBuf_Add(LazyBuf *buf, char ch) { if (buf->data != NULL) { if (buf->len == buf->cap) { buf->cap *= 2; buf->data = bmake_realloc(buf->data, buf->cap); } buf->data[buf->len++] = ch; } else if (ch == buf->expected[buf->len]) { buf->len++; return; } else { buf->cap = buf->len + 16; buf->data = bmake_malloc(buf->cap); memcpy(buf->data, buf->expected, buf->len); buf->data[buf->len++] = ch; } } MAKE_STATIC void LazyBuf_AddStr(LazyBuf *buf, const char *str) { const char *p; for (p = str; *p != '\0'; p++) LazyBuf_Add(buf, *p); } -MAKE_STATIC void -LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end) +MAKE_INLINE void +LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) { const char *p; - for (p = start; p != end; p++) + for (p = sub.start; p != sub.end; p++) LazyBuf_Add(buf, *p); } -MAKE_INLINE void -LazyBuf_AddSubstring(LazyBuf *buf, Substring sub) -{ - LazyBuf_AddBytesBetween(buf, sub.start, sub.end); -} - MAKE_STATIC Substring LazyBuf_Get(const LazyBuf *buf) { const char *start = buf->data != NULL ? buf->data : buf->expected; return Substring_Init(start, start + buf->len); } /* * Returns the content of the buffer as a newly allocated string. * * See LazyBuf_Get to avoid unnecessary memory allocations. */ MAKE_STATIC FStr LazyBuf_DoneGet(LazyBuf *buf) { if (buf->data != NULL) { LazyBuf_Add(buf, '\0'); return FStr_InitOwn(buf->data); } return Substring_Str(LazyBuf_Get(buf)); } Words Str_Words(const char *, bool); MAKE_INLINE void Words_Free(Words w) { free(w.words); free(w.freeIt); } SubstringWords Substring_Words(const char *, bool); MAKE_INLINE void SubstringWords_Init(SubstringWords *w) { w->words = NULL; w->len = 0; w->freeIt = NULL; } MAKE_INLINE void SubstringWords_Free(SubstringWords w) { free(w.words); free(w.freeIt); } char *str_concat2(const char *, const char *); char *str_concat3(const char *, const char *, const char *); bool Str_Match(const char *, const char *); void Str_Intern_Init(void); void Str_Intern_End(void); const char *Str_Intern(const char *); diff --git a/contrib/bmake/strlcpy.c b/contrib/bmake/strlcpy.c index b59b2f4ba5a6..111a2aa13435 100644 --- a/contrib/bmake/strlcpy.c +++ b/contrib/bmake/strlcpy.c @@ -1,63 +1,62 @@ /* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ /* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include "make.h" + #ifndef HAVE_STRLCPY #include #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; if (!dst || !src) return 0; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif diff --git a/contrib/bmake/targ.c b/contrib/bmake/targ.c index 7ce495c73b01..a3e234a959ff 100644 --- a/contrib/bmake/targ.c +++ b/contrib/bmake/targ.c @@ -1,605 +1,605 @@ -/* $NetBSD: targ.c,v 1.177 2022/04/15 12:19:28 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.179 2022/12/06 00:12:44 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Maintaining the targets and sources, which are both implemented as GNode. * * Interface: * Targ_Init Initialize the module. * * Targ_End Clean up the module. * * Targ_List Return the list of all targets so far. * * GNode_New Create a new GNode with the given name, don't add it * to allNodes. * * Targ_FindNode Find the node, or return NULL. * * Targ_GetNode Find the node, or create it. * * Targ_NewInternalNode * Create an internal node. * * Targ_FindList Given a list of names, find nodes for all * of them, creating them as necessary. * * Targ_Propagate Propagate information between related nodes. * Should be called after the makefiles are parsed * but before any action is taken. * * Debugging: * Targ_PrintGraph * Print out the entire graph, all variables and * statistics for the directory cache. */ #include #include "make.h" #include "dir.h" /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: targ.c,v 1.177 2022/04/15 12:19:28 rillig Exp $"); +MAKE_RCSID("$NetBSD: targ.c,v 1.179 2022/12/06 00:12:44 rillig Exp $"); /* * 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 = LST_INIT; static void GNode_Free(void *); #endif void Targ_Init(void) { HashTable_Init(&allTargetsByName); } void Targ_End(void) { Targ_Stats(); #ifdef CLEANUP Lst_Done(&allTargets); HashTable_Done(&allTargetsByName); Lst_DoneCall(&allNodes, GNode_Free); #endif } void Targ_Stats(void) { HashTable_DebugStats(&allTargetsByName, "targets"); } /* * Return the list of all targets, which are all nodes that appear on the * left-hand side of a dependency declaration such as "target: source". * The returned list does not contain pure sources. */ GNodeList * Targ_List(void) { return &allTargets; } /* * Create a new graph node, but don't register it anywhere. * - * Graph nodes that appear on the left-hand side of a dependency line such + * Graph nodes that occur on the left-hand side of a dependency line such * as "target: source" are called targets. XXX: In some cases (like the - * .ALLTARGETS variable), all nodes are called targets as well, even if they - * never appear on the left-hand side. This is a mistake. + * .ALLTARGETS variable), other nodes are called targets as well, even if + * they never occur on the left-hand side of a dependency line. * * Typical names for graph nodes are: - * "src.c" (an ordinary file) - * "clean" (a .PHONY target) - * ".END" (a special hook target) - * "-lm" (a library) - * "libc.a(isspace.o)" (an archive member) + * "src.c" an ordinary file + * "clean" a .PHONY target + * ".END" a special hook target + * "-lm" a library + * "libm.a(sin.o)" an archive member */ GNode * GNode_New(const char *name) { GNode *gn; gn = bmake_malloc(sizeof *gn); gn->name = bmake_strdup(name); gn->uname = NULL; gn->path = NULL; gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE; memset(&gn->flags, 0, sizeof(gn->flags)); gn->made = UNMADE; gn->unmade = 0; gn->mtime = 0; gn->youngestChild = NULL; Lst_Init(&gn->implicitParents); Lst_Init(&gn->parents); Lst_Init(&gn->children); Lst_Init(&gn->order_pred); Lst_Init(&gn->order_succ); Lst_Init(&gn->cohorts); gn->cohort_num[0] = '\0'; gn->unmade_cohorts = 0; gn->centurion = NULL; gn->checked_seqno = 0; HashTable_Init(&gn->vars); Lst_Init(&gn->commands); gn->suffix = NULL; gn->fname = NULL; gn->lineno = 0; #ifdef CLEANUP Lst_Append(&allNodes, gn); #endif return gn; } #ifdef CLEANUP static void GNode_Free(void *gnp) { GNode *gn = gnp; free(gn->name); free(gn->uname); free(gn->path); /* 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 * SCOPE_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 scopes (SCOPE_CMD, * SCOPE_GLOBAL, SCOPE_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). + * where they are not mentioned). These may be freed if their + * variable values are indeed not used anywhere else (see Trace_Init + * for the only suspicious use). */ HashTable_Done(&gn->vars); /* * Do not free the commands themselves, as they may be shared with * other nodes. */ Lst_Done(&gn->commands); /* * gn->suffix is not owned by this node. * * XXX: gn->suffix should be unreferenced here. This requires a * thorough check that the reference counting is done correctly in * all places, otherwise a suffix might be freed too early. */ free(gn); } #endif /* Get the existing global node, or return NULL. */ GNode * Targ_FindNode(const char *name) { return HashTable_FindValue(&allTargetsByName, name); } /* Get the existing global node, or create it. */ GNode * Targ_GetNode(const char *name) { bool isNew; HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew); if (!isNew) return HashEntry_Get(he); { GNode *gn = Targ_NewInternalNode(name); HashEntry_Set(he, gn); return gn; } } /* * Create a node, register it in .ALLTARGETS but don't store it in the * table of global nodes. This means it cannot be found by name. * * This is used for internal nodes, such as cohorts or .WAIT nodes. */ GNode * Targ_NewInternalNode(const char *name) { GNode *gn = GNode_New(name); Global_Append(".ALLTARGETS", name); Lst_Append(&allTargets, gn); DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name); if (doing_depend) gn->flags.fromDepend = true; return gn; } /* * Return the .END node, which contains the commands to be run when * everything else has been made. */ GNode * Targ_GetEndNode(void) { /* * Save the node locally to avoid having to search for it all * the time. */ static GNode *endNode = NULL; if (endNode == NULL) { endNode = Targ_GetNode(".END"); endNode->type = OP_SPECIAL; } return endNode; } /* Add the named nodes to the list, creating them as necessary. */ void Targ_FindList(GNodeList *gns, StringList *names) { StringListNode *ln; for (ln = names->first; ln != NULL; ln = ln->next) { const char *name = ln->datum; GNode *gn = Targ_GetNode(name); Lst_Append(gns, gn); } } static void PrintNodeNames(GNodeList *gnodes) { GNodeListNode *ln; for (ln = gnodes->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; debug_printf(" %s%s", gn->name, gn->cohort_num); } } static void PrintNodeNamesLine(const char *label, GNodeList *gnodes) { if (Lst_IsEmpty(gnodes)) return; debug_printf("# %s:", label); PrintNodeNames(gnodes); debug_printf("\n"); } void Targ_PrintCmds(GNode *gn) { StringListNode *ln; for (ln = gn->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; debug_printf("\t%s\n", cmd); } } /* * Format a modification time in some reasonable way and return it. * The formatted time is placed in a static area, so it is overwritten * with each call. */ const char * Targ_FmtTime(time_t tm) { static char buf[128]; struct tm *parts = localtime(&tm); (void)strftime(buf, sizeof buf, "%H:%M:%S %b %d, %Y", parts); return buf; } /* Print out a type field giving only those attributes the user can set. */ void Targ_PrintType(GNodeType type) { static const struct { GNodeType bit; bool internal; const char name[10]; } names[] = { { OP_MEMBER, true, "MEMBER" }, { OP_LIB, true, "LIB" }, { OP_ARCHV, true, "ARCHV" }, { OP_PHONY, true, "PHONY" }, { OP_NOTMAIN, false, "NOTMAIN" }, { OP_INVISIBLE, false, "INVISIBLE" }, { OP_MADE, true, "MADE" }, { OP_JOIN, false, "JOIN" }, { OP_MAKE, false, "MAKE" }, { OP_SILENT, false, "SILENT" }, { OP_PRECIOUS, false, "PRECIOUS" }, { OP_IGNORE, false, "IGNORE" }, { OP_EXEC, false, "EXEC" }, { OP_USE, false, "USE" }, { OP_USEBEFORE, false, "USEBEFORE" }, { OP_OPTIONAL, false, "OPTIONAL" }, }; size_t i; for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) { if (type & names[i].bit) { if (names[i].internal) DEBUG1(TARG, " .%s", names[i].name); else debug_printf(" .%s", names[i].name); } } } const char * GNodeMade_Name(GNodeMade made) { switch (made) { case UNMADE: return "unmade"; case DEFERRED: return "deferred"; case REQUESTED: return "requested"; case BEINGMADE: return "being made"; case MADE: return "made"; case UPTODATE: return "up-to-date"; case ERROR: return "error when made"; case ABORTED: return "aborted"; default: return "unknown enum_made value"; } } static const char * GNode_OpName(const GNode *gn) { switch (gn->type & OP_OPMASK) { case OP_DEPENDS: return ":"; case OP_FORCE: return "!"; case OP_DOUBLEDEP: return "::"; } return ""; } static bool GNodeFlags_IsNone(GNodeFlags flags) { return !flags.remake && !flags.childMade && !flags.force && !flags.doneWait && !flags.doneOrder && !flags.fromDepend && !flags.doneAllsrc && !flags.cycle && !flags.doneCycle; } /* Print the contents of a node. */ void Targ_PrintNode(GNode *gn, int pass) { debug_printf("# %s%s", gn->name, gn->cohort_num); GNode_FprintDetails(opts.debug_file, ", ", gn, "\n"); if (GNodeFlags_IsNone(gn->flags)) return; if (!GNode_IsTarget(gn)) return; debug_printf("#\n"); if (gn == mainNode) debug_printf("# *** MAIN TARGET ***\n"); if (pass >= 2) { if (gn->unmade > 0) debug_printf("# %d unmade children\n", gn->unmade); else debug_printf("# No unmade children\n"); if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) { if (gn->mtime != 0) { debug_printf("# last modified %s: %s\n", Targ_FmtTime(gn->mtime), GNodeMade_Name(gn->made)); } else if (gn->made != UNMADE) { debug_printf("# nonexistent (maybe): %s\n", GNodeMade_Name(gn->made)); } else debug_printf("# unmade\n"); } PrintNodeNamesLine("implicit parents", &gn->implicitParents); } else { 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); debug_printf("%-16s%s", gn->name, GNode_OpName(gn)); Targ_PrintType(gn->type); PrintNodeNames(&gn->children); debug_printf("\n"); Targ_PrintCmds(gn); debug_printf("\n\n"); 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); } static void PrintOnlySources(void) { GNodeListNode *ln; for (ln = allTargets.first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; if (GNode_IsTarget(gn)) continue; debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn)); Targ_PrintType(gn->type); debug_printf("\n"); } } /* * Input: * pass 1 => before processing * 2 => after processing * 3 => after processing, an error occurred */ void Targ_PrintGraph(int pass) { debug_printf("#*** Input graph:\n"); Targ_PrintNodes(&allTargets, pass); debug_printf("\n"); debug_printf("\n"); debug_printf("#\n"); debug_printf("# Files that are only sources:\n"); PrintOnlySources(); debug_printf("#*** Global Variables:\n"); Var_Dump(SCOPE_GLOBAL); debug_printf("#*** Command-line Variables:\n"); Var_Dump(SCOPE_CMDLINE); debug_printf("\n"); Dir_PrintDirectories(); debug_printf("\n"); Suff_PrintAll(); } /* * 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. */ void Targ_Propagate(void) { GNodeListNode *ln, *cln; 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) { GNode *cohort = cln->datum; - cohort->type |= type & ~OP_OPMASK; + cohort->type |= type & (unsigned)~OP_OPMASK; } } } diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile index 10d8f32c61c7..f4d4425e204c 100644 --- a/contrib/bmake/unit-tests/Makefile +++ b/contrib/bmake/unit-tests/Makefile @@ -1,825 +1,846 @@ -# $Id: Makefile,v 1.182 2022/07/26 19:39:32 sjg Exp $ +# $Id: Makefile,v 1.191 2023/01/24 06:09:49 sjg Exp $ # -# $NetBSD: Makefile,v 1.318 2022/06/10 21:28:50 rillig Exp $ +# $NetBSD: Makefile,v 1.331 2023/01/24 00:24:02 sjg Exp $ # # Unit tests for make(1) # # The main targets are: # # all: # run all the tests # test: # run 'all', and compare to expected results # accept: # move generated output to expected results # # Settable variables # # TEST_MAKE # The make program to be tested. # # # Adding a test case # # Each feature should get its own set of tests in its own suitably # named makefile (*.mk), with its own set of expected results (*.exp), # and it should be added to the TESTS list. # -# A few *.mk files are helper files for other tests (such as include-sub.mk) -# and are thus not added to TESTS. Such files must be ignored in -# src/tests/usr.bin/make/t_make.sh. -# .MAIN: all # 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+= cmd-errors TESTS+= cmd-errors-jobs TESTS+= cmd-errors-lint TESTS+= cmd-interrupt TESTS+= cmdline TESTS+= cmdline-redirect-stdin TESTS+= cmdline-undefined TESTS+= comment TESTS+= compat-error TESTS+= cond-cmp-numeric TESTS+= cond-cmp-numeric-eq TESTS+= cond-cmp-numeric-ge TESTS+= cond-cmp-numeric-gt TESTS+= cond-cmp-numeric-le TESTS+= cond-cmp-numeric-lt TESTS+= cond-cmp-numeric-ne TESTS+= cond-cmp-string TESTS+= cond-cmp-unary TESTS+= cond-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 TESTS+= cond-op-and TESTS+= cond-op-and-lint TESTS+= cond-op-not TESTS+= cond-op-or TESTS+= cond-op-or-lint TESTS+= cond-op-parentheses TESTS+= cond-short TESTS+= cond-token-number TESTS+= cond-token-plain TESTS+= cond-token-string TESTS+= cond-token-var TESTS+= cond-undef-lint TESTS+= counter TESTS+= counter-append TESTS+= dep TESTS+= dep-colon TESTS+= dep-colon-bug-cross-file TESTS+= dep-double-colon TESTS+= dep-double-colon-indep TESTS+= dep-duplicate TESTS+= dep-exclam TESTS+= dep-none TESTS+= dep-op-missing TESTS+= dep-percent TESTS+= dep-var TESTS+= dep-wildcards TESTS+= depsrc TESTS+= depsrc-end TESTS+= depsrc-exec TESTS+= depsrc-ignore TESTS+= depsrc-made TESTS+= depsrc-make TESTS+= depsrc-meta TESTS+= depsrc-nometa TESTS+= depsrc-nometa_cmp TESTS+= depsrc-nopath TESTS+= depsrc-notmain TESTS+= depsrc-optional TESTS+= depsrc-phony TESTS+= depsrc-precious TESTS+= depsrc-recursive TESTS+= depsrc-silent TESTS+= depsrc-use TESTS+= depsrc-usebefore 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 TESTS+= deptgt-interrupt TESTS+= deptgt-main TESTS+= deptgt-makeflags TESTS+= deptgt-no_parallel TESTS+= deptgt-nopath TESTS+= deptgt-notparallel TESTS+= deptgt-objdir TESTS+= deptgt-order TESTS+= deptgt-path TESTS+= deptgt-path-suffix TESTS+= deptgt-phony TESTS+= deptgt-posix TESTS+= deptgt-precious TESTS+= deptgt-shell TESTS+= deptgt-silent TESTS+= deptgt-silent-jobs TESTS+= deptgt-stale TESTS+= deptgt-suffixes TESTS+= dir TESTS+= dir-expand-path TESTS+= directive TESTS+= directive-dinclude TESTS+= directive-elif TESTS+= directive-elifdef 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-break TESTS+= directive-for-empty TESTS+= directive-for-errors TESTS+= directive-for-escape TESTS+= directive-for-generating-endif TESTS+= directive-for-if TESTS+= directive-for-lines TESTS+= directive-for-null TESTS+= directive-hyphen-include TESTS+= directive-if TESTS+= directive-if-nested TESTS+= directive-ifdef TESTS+= directive-ifmake TESTS+= directive-ifndef 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 TESTS+= directive-unexport-env TESTS+= directive-warning TESTS+= dollar TESTS+= doterror TESTS+= dotwait TESTS+= error TESTS+= # escape # broken by reverting POSIX changes TESTS+= export TESTS+= export-all TESTS+= export-env TESTS+= export-variants TESTS+= forloop TESTS+= forsubst TESTS+= gnode-submake TESTS+= hanoi-include TESTS+= impsrc TESTS+= include-main TESTS+= job-flags #TESTS+= job-output-long-lines TESTS+= job-output-null TESTS+= jobs-empty-commands TESTS+= jobs-empty-commands-error 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 .if ${.MAKE.UID} > 0 TESTS+= objdir-writable .endif TESTS+= opt TESTS+= opt-backwards TESTS+= opt-chdir TESTS+= opt-debug TESTS+= opt-debug-all TESTS+= opt-debug-archive TESTS+= opt-debug-curdir TESTS+= opt-debug-cond TESTS+= opt-debug-dir TESTS+= opt-debug-errors TESTS+= opt-debug-errors-jobs TESTS+= opt-debug-file TESTS+= opt-debug-for TESTS+= opt-debug-graph1 TESTS+= opt-debug-graph2 TESTS+= opt-debug-graph3 TESTS+= opt-debug-hash #TESTS+= opt-debug-jobs TESTS+= opt-debug-lint TESTS+= opt-debug-loud TESTS+= opt-debug-meta TESTS+= opt-debug-making TESTS+= opt-debug-no-rm TESTS+= opt-debug-parse TESTS+= opt-debug-suff TESTS+= opt-debug-targets TESTS+= opt-debug-varraw TESTS+= opt-debug-var TESTS+= opt-debug-x-trace TESTS+= opt-define TESTS+= opt-env TESTS+= opt-file 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-indirect 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-no-action-touch TESTS+= opt-query TESTS+= opt-raw TESTS+= opt-silent TESTS+= opt-touch TESTS+= opt-touch-jobs TESTS+= opt-tracefile TESTS+= opt-var-expanded TESTS+= opt-var-literal TESTS+= opt-version TESTS+= opt-warnings-as-errors TESTS+= opt-where-am-i TESTS+= opt-x-reduce-exported TESTS+= order TESTS+= parse TESTS+= parse-var TESTS+= phony-end TESTS+= posix TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= recursive TESTS+= sh TESTS+= sh-dots TESTS+= sh-errctl TESTS+= sh-flags TESTS+= sh-jobs TESTS+= sh-jobs-error TESTS+= sh-leading-at TESTS+= sh-leading-hyphen TESTS+= sh-leading-plus TESTS+= sh-meta-chars TESTS+= sh-multi-line TESTS+= sh-single-line TESTS+= shell-csh TESTS+= shell-custom .if exists(/bin/ksh) TESTS+= shell-ksh .endif 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 TESTS+= suff-use TESTS+= sunshcmd TESTS+= ternary TESTS+= unexport TESTS+= unexport-env TESTS+= use-inference +TESTS+= var-readonly TESTS+= var-scope TESTS+= var-scope-cmdline TESTS+= var-scope-env TESTS+= var-scope-global TESTS+= var-scope-local TESTS+= var-scope-local-legacy TESTS+= var-eval-short TESTS+= var-op TESTS+= var-op-append TESTS+= var-op-assign TESTS+= var-op-default TESTS+= var-op-expand TESTS+= var-op-shell TESTS+= var-op-sunsh TESTS+= var-recursive TESTS+= varcmd TESTS+= vardebug TESTS+= varfind TESTS+= varmisc TESTS+= varmod TESTS+= varmod-assign TESTS+= varmod-assign-shell TESTS+= varmod-defined TESTS+= varmod-edge TESTS+= varmod-exclam-shell TESTS+= varmod-extension 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 TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape TESTS+= varmod-no-match TESTS+= varmod-order TESTS+= varmod-order-numeric TESTS+= varmod-order-reverse TESTS+= varmod-order-shuffle TESTS+= varmod-order-string TESTS+= varmod-path TESTS+= varmod-quote TESTS+= varmod-quote-dollar TESTS+= varmod-range TESTS+= varmod-remember TESTS+= varmod-root TESTS+= varmod-select-words TESTS+= varmod-shell TESTS+= varmod-subst TESTS+= varmod-subst-regex TESTS+= varmod-sun-shell TESTS+= varmod-sysv TESTS+= varmod-tail TESTS+= varmod-to-abs TESTS+= varmod-to-lower TESTS+= varmod-to-many-words TESTS+= varmod-to-one-word TESTS+= varmod-to-separator TESTS+= varmod-to-upper TESTS+= varmod-undefined TESTS+= varmod-unique TESTS+= varname TESTS+= varname-dollar TESTS+= varname-dot-alltargets TESTS+= varname-dot-curdir TESTS+= varname-dot-includes TESTS+= varname-dot-includedfromdir TESTS+= varname-dot-includedfromfile TESTS+= varname-dot-libs TESTS+= varname-dot-make-dependfile TESTS+= varname-dot-make-expand_variables TESTS+= varname-dot-make-exported TESTS+= varname-dot-make-jobs TESTS+= varname-dot-make-jobs-prefix TESTS+= varname-dot-make-level TESTS+= varname-dot-make-makefile_preference TESTS+= varname-dot-make-makefiles TESTS+= varname-dot-make-meta-bailiwick TESTS+= varname-dot-make-meta-created TESTS+= varname-dot-make-meta-files TESTS+= varname-dot-make-meta-ignore_filter TESTS+= varname-dot-make-meta-ignore_paths TESTS+= varname-dot-make-meta-ignore_patterns TESTS+= varname-dot-make-meta-prefix TESTS+= varname-dot-make-mode 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 TESTS+= varname-dot-parsedir TESTS+= varname-dot-parsefile TESTS+= varname-dot-path TESTS+= varname-dot-shell TESTS+= varname-dot-suffixes TESTS+= varname-dot-targets TESTS+= varname-empty TESTS+= varname-make TESTS+= varname-make_print_var_on_error TESTS+= varname-make_print_var_on_error-jobs TESTS+= varname-makefile TESTS+= varname-makeflags TESTS+= varname-pwd TESTS+= varname-vpath TESTS+= varparse-dynamic TESTS+= varparse-errors TESTS+= varparse-mod TESTS+= varparse-undef-partial # some shells have quirks _shell := ${.SHELL:tA:T} .if ${_shell} == "dash" # dash fails -x output BROKEN_TESTS+= opt-debug-x-trace .elif ${_shell} == "ksh" BROKEN_TESTS+= sh-flags .endif .if ${.MAKE.OS:NDarwin} == "" BROKEN_TESTS+= shell-ksh .endif .if ${.MAKE.OS} == "SCO_SV" BROKEN_TESTS+= \ opt-debug-graph[23] \ varmod-localtime \ varmod-to-separator \ .if ${_shell} == "bash" BROKEN_TESTS+= job-output-null .else BROKEN_TESTS+= \ cmd-interrupt \ job-flags \ .endif .endif # Some tests just do not work on some platforms or environments # so allow for some filtering. .if !empty(BROKEN_TESTS) .warning Skipping broken tests: ${BROKEN_TESTS:O:u} TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}} .endif # Ideas for more tests: # char-0020-space.mk # char-005C-backslash.mk # escape-cond-str.mk # escape-cond-func-arg.mk # escape-varmod.mk # escape-varmod-define.mk # escape-varmod-match.mk # escape-varname.mk # escape-varassign-varname.mk # escape-varassign-varname-cmdline.mk # escape-varassign-value.mk # escape-varassign-value-cmdline.mk # escape-dependency-source.mk # escape-dependency-target.mk # escape-for-varname.mk # escape-for-item.mk # posix-*.mk (see posix.mk and posix1.mk) # Additional environment variables for some of the tests. # The base environment is -i PATH="$PATH". ENV.depsrc-optional+= TZ=UTC ENV.deptgt-phony+= MAKESYSPATH=. ENV.directive-undef= ENV_VAR=env-value -ENV.envfirst= FROM_ENV=value-from-env +ENV.opt-env= FROM_ENV=value-from-env ENV.opt-m-include-dir= ${MAKEOBJDIR:DMAKEOBJDIR=${MAKEOBJDIR}} ENV.varmisc= FROM_ENV=env ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_AFTER=env ENV.varmod-localtime+= TZ=${UTC_1:UEurope/Berlin} ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2 # Override make flags for some of the tests; default is -k. # If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of # settings FLAGS.test=-dv here, since that is closer to the test code. FLAGS.cond-func-make= via-cmdline FLAGS.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.deptgt-phony= ${STD_SED_CMDS.dd} SED_CMDS.dir= ${STD_SED_CMDS.dd} 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' \ ${:D Plain output lines are ok as well. } \ ${:D They may come in multiples of 1024 or as 10000. } \ -e '/^aa*$$/d' \ -e '/^bb*$$/d' \ ${:D The following lines should rather not occur since the job } \ ${:D marker should always be at the beginning of the line. } \ -e '/^aa*--- job-b ---$$/d' \ -e '/^bb*--- job-a ---$$/d' SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \ -e '/name/s,file,File,' \ -e 's,no such,No such,' \ -e 's,Filename,File name,' SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1} SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2} SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3} SED_CMDS.opt-debug-hash= -e 's,\(numEntries\)=[1-9][0-9],\1=,' SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(),' SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid ,' SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process ,' SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: ,' SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: ,' # The "-q" may be there or not, see jobs.c, variable shells. SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: \) -q,\1,' SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex} SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output} SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output} SED_CMDS.opt-where-am-i= -e '/usr.obj/d' # For Compat_RunCommand, useShell == false. SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,,' # For Compat_RunCommand, useShell == true. SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,,' SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1,' SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj} SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output} SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space} +SED_CMDS.sh-leading-hyphen= ${STD_SED_CMDS.shell} 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+= ${STD_SED_CMDS.shell} SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,' SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space} SED_CMDS.vardebug+= -e 's,${.SHELL},,' SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex} +SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp} SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: ",' SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: ",' SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g' SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g' SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g' -SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@} +SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL .SYSPATH:L:@v@-e '/\\$v/d'@} # Some tests need an additional round of postprocessing. POSTPROC.depsrc-wait= sed -e '/^---/d' -e 's,^\(: Making 3[abc]\)[123]$$,\1,' POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' POSTPROC.varname-dot-make-mode= sed 's,^\(: Making [abc]\)[123]$$,\1,' # Some tests reuse other tests, which makes them unnecessarily fragile. export-all.rawout: export.mk unexport.rawout: export.mk unexport-env.rawout: export.mk # End of the configuration section. # Some standard sed commands, to be used in the SED_CMDS above. # In tests that use the debugging option -dd, ignore debugging output that is # only logged in -DCLEANUP mode. STD_SED_CMDS.dd= -e '/^OpenDirs_Done:/d' STD_SED_CMDS.dd+= -e '/^CachedDir /d' # Omit details such as process IDs from the output of the -dg1 option. STD_SED_CMDS.dg1= -e '/\#.* \.$$/d' STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d' -STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d' +STD_SED_CMDS.dg1+= -e '/^\#.*\/mk/d' STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, ,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1
,' +STD_SED_CMDS.dg1+= -e '/\.SYSPATH/d' STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1} STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 ,' STD_SED_CMDS.dg3= ${STD_SED_CMDS.dg2} # 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(),' \ -e 's,^([0-9][0-9]*) \(withdrew token\),() \1,' \ -e 's, \(pid\) [0-9][0-9]*, \1 ,' \ -e 's,^\( Command:\) .*,\1 ,' # 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,,' # Normalize the output for error messages from the shell. # # $shell -c '...' # NetBSD sh ...: not found # NetBSD ksh ksh: ...: not found # bash 5.0.18 bash: ...: command not found # bash 5.1.0 bash: line 1: ...: command not found # dash dash: 1: ...: not found # # $shell -c '< /nonexistent' # NetBSD sh sh: cannot open /nonexistent: no such file # NetBSD ksh ksh: cannot open /nonexistent: No such file or directory # bash 5.0.18 bash: /nonexistent: No such file or directory # bash 5.1.0 bash: line 1: /nonexistent: No such file or directory # dash dash: 1: cannot open /nonexistent: No such file # # echo '< /nonexistent' | $shell # NetBSD sh sh: cannot open /nonexistent: no such file # NetBSD ksh ksh: [1]: cannot open /nonexistent: No such file or directory # bash 5.0.18 bash: line 1: /nonexistent: No such file or directory # bash 5.1.0 bash: line 1: /nonexistent: No such file or directory # dash dash: 1: cannot open /nonexistent: No such file # +STD_SED_CMDS.shell+= -e 's,^${.SHELL},${.SHELL:T},' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,' +STD_SED_CMDS.shell+= -e 's,: command not found,: not found,' STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,' # The actual error messages for a failed regcomp or regexec differ between the # implementations. STD_SED_CMDS.regex= \ -e 's,\(Regex compilation error:\).*,\1 (details omitted),' +# Normalize timestamps from ':gmtime' or ':localtime' to ''. +# See STD_SED_CMDS.dg2 for timestamps from the debug log. +STD_SED_CMDS.timestamp= \ + -e 's,[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [12][0-9][0-9][0-9],,' + # End of the configuration helpers section. .-include "Makefile.inc" .-include "Makefile.config" UNIT_TESTS:= ${.PARSEDIR} .PATH: ${UNIT_TESTS} .if ${USE_ABSOLUTE_TESTNAMES:Uno} == yes OUTFILES= ${TESTS:@test@${.CURDIR:tA}/${test}.out@} .else OUTFILES= ${TESTS:=.out} .endif all: ${OUTFILES} CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp CLEANFILES+= obj*.[och] lib*.a # posix1.mk CLEANFILES+= issue* .[ab]* # suffixes.mk CLEANDIRS= dir dummy # posix1.mk clean: rm -f ${CLEANFILES} rm -rf ${CLEANDIRS} TEST_MAKE?= ${.MAKE} TOOL_SED?= sed TOOL_TR?= tr TOOL_DIFF?= diff DIFF_FLAGS?= -u .if defined(.PARSEDIR) # ensure consistent results from sort(1) LC_ALL= C LANG= C .export LANG LC_ALL .endif .if ${.MAKE.MODE:Unormal:Mmeta} != "" # we don't need the noise _MKMSG_TEST= : .endif # for many tests we need a TMPDIR that will not collide # with other users. .if ${.OBJDIR} != ${.CURDIR} # easy TMPDIR:= ${.OBJDIR}/tmp .elif defined(TMPDIR) TMPDIR:= ${TMPDIR}/uid${.MAKE.UID} .else TMPDIR:= /tmp/uid${.MAKE.UID} .endif # make sure it exists .if !exist(${TMPDIR}) _!= mkdir -p ${TMPDIR} .endif -MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100 +# Some Linux systems such as Fedora have deprecated egrep in favor of grep -E. +.if ${.MAKE.OS:NLinux} == "" +EGREP= grep -E +.endif +# Keep the classical definition for all other systems. Just as the bmake code +# is kept compatible with C90, the tests are kept compatible with systems that +# are several decades old and don't follow modern POSIX standards. +EGREP?= egrep + +MAKE_TEST_ENV= EGREP="${EGREP}" +MAKE_TEST_ENV+= MALLOC_OPTIONS="JA" # for jemalloc 100 MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510 MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" LIMIT_RESOURCES?= ulimit -v 300000 .endif LIMIT_RESOURCES?= : # Each test is run in a sub-make, to keep the tests from interfering with # each other, and because they use different environment variables and # command line options. .SUFFIXES: .mk .rawout .out .mk.rawout: @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} @set -eu; \ ${LIMIT_RESOURCES}; \ cd ${.OBJDIR}; \ env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \ ${TEST_MAKE} \ -r -C ${.CURDIR} -f ${.IMPSRC} \ ${FLAGS.${.PREFIX:T}:U-k} \ > ${.TARGET}.tmp 2>&1 \ && status=$$? || status=$$?; \ echo $$status > ${.TARGET:R}.status @mv ${.TARGET}.tmp ${.TARGET} # Postprocess the test output to make the output platform-independent. # # always pretend .MAKE was called 'make' _SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' _SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' # Allow the test files to be placed anywhere. _SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = ,' _SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = ,' _SED_CMDS+= -e 's,${TMPDIR},,g' # canonicalize ${.OBJDIR} and ${.CURDIR} .if ${.OBJDIR} != ${.CURDIR} # yes this is inaccurate but none of the tests expect anywhere # which we get depending on how MAKEOBJDIR is set. _SED_CMDS+= -e 's,${.OBJDIR},,g' .endif _SED_CMDS+= -e 's,${.CURDIR},,g' _SED_CMDS+= -e 's,/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' +_SED_CMDS+= -e '/MAKE_VERSION/d' +_SED_CMDS+= -e '/EGREP=/d' + # on AT&T derived systems: false exits 255 not 1 .if ${.MAKE.OS:N*BSD} != "" _SED_CMDS+= -e 's,\(Error code\) 255,\1 1,' .endif .if ${.SHELL:T} == "ksh" _SED_CMDS+= -e '/^set [+-]v/d' .endif .rawout.out: @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \ < ${.IMPSRC} > ${.TARGET}.tmp1 @${POSTPROC.${.PREFIX:T}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 @rm ${.TARGET}.tmp1 @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 ; \ if [ -n "$${failed}" ]; then \ echo "Failed tests: $${failed}" ; false ; \ else \ echo "All tests passed" ; \ fi accept: @for test in ${TESTS}; do \ cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ || { echo "Replacing $${test}.exp" ; \ cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ done .if exists(${TEST_MAKE}) ${TESTS:=.rawout}: ${TEST_MAKE} # in meta mode, we *know* if a target script is impacted # by a makefile change. .if ${.MAKE.MODE:Unormal:Mmeta} == "" ${TESTS:=.rawout}: ${.PARSEDIR}/Makefile .endif .endif .-include diff --git a/contrib/bmake/unit-tests/Makefile.config.in b/contrib/bmake/unit-tests/Makefile.config.in index 3139a0d4d0b5..30049eaa7c26 100644 --- a/contrib/bmake/unit-tests/Makefile.config.in +++ b/contrib/bmake/unit-tests/Makefile.config.in @@ -1,6 +1,7 @@ -# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $ +# $Id: Makefile.config.in,v 1.4 2022/09/09 18:44:56 sjg Exp $ srcdir= @srcdir@ +EGREP= @egrep@ TOOL_DIFF?= @diff@ DIFF_FLAGS?= @diff_u@ UTC_1= @UTC_1@ diff --git a/contrib/bmake/unit-tests/cmd-errors-jobs.exp b/contrib/bmake/unit-tests/cmd-errors-jobs.exp index 9ed0557975b3..c6baacfe0ed7 100644 --- a/contrib/bmake/unit-tests/cmd-errors-jobs.exp +++ b/contrib/bmake/unit-tests/cmd-errors-jobs.exp @@ -1,9 +1,9 @@ -: undefined eol +: undefined--eol make: Unclosed variable "UNCLOSED" -: unclosed-variable +: unclosed-variable- make: Unclosed variable expression (expecting '}') for "UNCLOSED" -: unclosed-modifier +: unclosed-modifier- make: Unknown modifier "Z" -: unknown-modifier eol -: end eol +: unknown-modifier--eol +: end-eol exit status 0 diff --git a/contrib/bmake/unit-tests/cmd-errors-jobs.mk b/contrib/bmake/unit-tests/cmd-errors-jobs.mk index 8462a2e3497e..b3dc982de35f 100644 --- a/contrib/bmake/unit-tests/cmd-errors-jobs.mk +++ b/contrib/bmake/unit-tests/cmd-errors-jobs.mk @@ -1,32 +1,32 @@ -# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $ +# $NetBSD: cmd-errors-jobs.mk,v 1.2 2022/09/25 12:51:37 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 + : $@-${UNDEFINED}-eol # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unclosed-variable: - : $@ ${UNCLOSED + : $@-${UNCLOSED # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unclosed-modifier: - : $@ ${UNCLOSED: + : $@-${UNCLOSED: # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unknown-modifier: - : $@ ${UNKNOWN:Z} eol + : $@-${UNKNOWN:Z}-eol end: - : $@ eol + : $@-eol # XXX: As of 2020-11-02, despite the parse errors, the exit status is 0. diff --git a/contrib/bmake/unit-tests/cmd-errors.exp b/contrib/bmake/unit-tests/cmd-errors.exp index 9ed0557975b3..c6baacfe0ed7 100644 --- a/contrib/bmake/unit-tests/cmd-errors.exp +++ b/contrib/bmake/unit-tests/cmd-errors.exp @@ -1,9 +1,9 @@ -: undefined eol +: undefined--eol make: Unclosed variable "UNCLOSED" -: unclosed-variable +: unclosed-variable- make: Unclosed variable expression (expecting '}') for "UNCLOSED" -: unclosed-modifier +: unclosed-modifier- make: Unknown modifier "Z" -: unknown-modifier eol -: end eol +: unknown-modifier--eol +: end-eol exit status 0 diff --git a/contrib/bmake/unit-tests/cmd-errors.mk b/contrib/bmake/unit-tests/cmd-errors.mk index 356fe1a3e4a2..6d3880684bcf 100644 --- a/contrib/bmake/unit-tests/cmd-errors.mk +++ b/contrib/bmake/unit-tests/cmd-errors.mk @@ -1,30 +1,30 @@ -# $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $ +# $NetBSD: cmd-errors.mk,v 1.5 2022/09/25 12:51:37 rillig Exp $ # # Demonstrate how errors in variable expansions affect whether the commands # are actually executed in compat mode. all: undefined unclosed-variable unclosed-modifier unknown-modifier end # Undefined variables are not an error. They expand to empty strings. undefined: - : $@ ${UNDEFINED} eol + : $@-${UNDEFINED}-eol # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unclosed-variable: - : $@ ${UNCLOSED + : $@-${UNCLOSED # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unclosed-modifier: - : $@ ${UNCLOSED: + : $@-${UNCLOSED: # XXX: As of 2020-11-01, this command is executed even though it contains # parse errors. unknown-modifier: - : $@ ${UNKNOWN:Z} eol + : $@-${UNKNOWN:Z}-eol end: - : $@ eol + : $@-eol # XXX: As of 2020-11-02, despite the parse errors, the exit status is 0. diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp index d913a44ff889..c03ecd1b311e 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp @@ -1,15 +1,15 @@ CondParser_Eval: !(${:UINF} > 1e100) -make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or != +make: "cond-cmp-numeric.mk" line 11: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric CondParser_Eval: ${:UNaN} > NaN -make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or != +make: "cond-cmp-numeric.mk" line 16: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric CondParser_Eval: !(${:UNaN} == NaN) Comparing "NaN" == "NaN" CondParser_Eval: 123 ! 123 make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) CondParser_Eval: ${:U 123} < 124 Comparing 123.000000 < 124.000000 CondParser_Eval: ${:U123 } < 124 -make: "cond-cmp-numeric.mk" line 50: String comparison operator must be either == or != +make: "cond-cmp-numeric.mk" line 50: Comparison with '<' requires both operands '123 ' and '124' to be numeric make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk index b34e5bfc0a06..5386e4a97297 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk @@ -1,57 +1,57 @@ -# $NetBSD: cond-cmp-numeric.mk,v 1.5 2021/07/29 06:31:18 rillig Exp $ +# $NetBSD: cond-cmp-numeric.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $ # # Tests for numeric comparisons in .if conditions. .MAKEFLAGS: -dc # The ${:U...} on the left-hand side is necessary for the parser. # Even if strtod(3) parses "INF" as +Infinity, make does not accept this # since it is not really a number; see TryParseNumber. .if !(${:UINF} > 1e100) . error .endif # Neither is NaN a number; see TryParseNumber. .if ${:UNaN} > NaN . error .endif # Since NaN is not parsed as a number, both operands are interpreted # as strings and are therefore equal. If they were parsed as numbers, # they would compare unequal, since NaN is unequal to any and everything, # including itself. .if !(${:UNaN} == NaN) . error .endif # The parsing code in CondParser_Comparison only performs a light check on # whether the operator is valid, leaving the rest of the work to the # evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this # parse error is properly reported. # # XXX: The warning message does not mention the actual operator. .if 123 ! 123 . error .else . error .endif # Leading spaces are allowed for numbers. # See EvalCompare and TryParseNumber. .if ${:U 123} < 124 .else . error .endif # Trailing spaces are NOT allowed for numbers. # See EvalCompare and TryParseNumber. -# expect+1: String comparison operator must be either == or != +# expect+1: Comparison with '<' requires both operands '123 ' and '124' to be numeric .if ${:U123 } < 124 . error .else . error .endif all: @:; diff --git a/contrib/bmake/unit-tests/cond-cmp-string.exp b/contrib/bmake/unit-tests/cond-cmp-string.exp index a10341ed2121..c9c7a0777383 100644 --- a/contrib/bmake/unit-tests/cond-cmp-string.exp +++ b/contrib/bmake/unit-tests/cond-cmp-string.exp @@ -1,11 +1,11 @@ make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing") make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value")) make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value")) -make: "cond-cmp-string.mk" line 113: String comparison operator must be either == or != -make: "cond-cmp-string.mk" line 120: String comparison operator must be either == or != -make: "cond-cmp-string.mk" line 127: String comparison operator must be either == or != -make: "cond-cmp-string.mk" line 134: String comparison operator must be either == or != +make: "cond-cmp-string.mk" line 113: Comparison with '<' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 120: Comparison with '<=' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 127: Comparison with '>' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 134: Comparison with '>=' requires both operands 'string' and 'string' to be numeric make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-cmp-unary.mk b/contrib/bmake/unit-tests/cond-cmp-unary.mk index 168de0f30e3f..90cd04e5092b 100755 --- a/contrib/bmake/unit-tests/cond-cmp-unary.mk +++ b/contrib/bmake/unit-tests/cond-cmp-unary.mk @@ -1,58 +1,74 @@ -# $NetBSD: cond-cmp-unary.mk,v 1.2 2020/11/11 07:30:11 rillig Exp $ +# $NetBSD: cond-cmp-unary.mk,v 1.3 2022/09/08 05:43:20 rillig Exp $ # # Tests for unary comparisons in .if conditions, that is, comparisons with # a single operand. If the operand is a number, it is compared to zero, # if it is a string, it is tested for emptiness. -# The number 0 evaluates to false. -.if 0 +# The number 0 in all its various representations evaluates to false. +.if 0 || 0.0 || 0e0 || 0.0e0 || 0.0e10 . error .endif # Any other number evaluates to true. .if !12345 . error .endif # The empty string evaluates to false. .if "" . error .endif # Any other string evaluates to true. .if !"0" . error .endif # The empty string may come from a variable expression. # # XXX: As of 2020-11-11, this empty string is interpreted "as a number" in # EvalNotEmpty, which is plain wrong. The bug is in TryParseNumber. .if ${:U} . error .endif # A variable expression that is not surrounded by quotes is interpreted # as a number if possible, otherwise as a string. .if ${:U0} . error .endif # A non-zero number from a variable expression evaluates to true. .if !${:U12345} . error .endif # A string of whitespace should evaluate to false. # # XXX: As of 2020-11-11, the implementation in EvalNotEmpty does not skip # whitespace before testing for the end. This was probably an oversight in # a commit from 1992-04-15 saying "A variable is empty when it just contains # spaces". .if ${:U } . info This is only reached because of a bug in EvalNotEmpty. .else . error .endif +# The condition '${VAR:M*}' is almost equivalent to '${VAR:M*} != ""'. The +# only case where they differ is for a single word whose numeric value is zero. +.if ${:U0:M*} +. error +.endif +.if ${:U0:M*} == "" +. error +.endif +# Multiple words cannot be parsed as a single number, thus evaluating to true. +.if !${:U0 0:M*} +. error +.endif +.if ${:U0 0:M*} == "" +. error +.endif + all: # nothing diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.exp b/contrib/bmake/unit-tests/cond-op-parentheses.exp index 63f7b19570b5..c2530f62de64 100644 --- a/contrib/bmake/unit-tests/cond-op-parentheses.exp +++ b/contrib/bmake/unit-tests/cond-op-parentheses.exp @@ -1,7 +1,7 @@ -make: "cond-op-parentheses.mk" line 19: String comparison operator must be either == or != -make: "cond-op-parentheses.mk" line 22: Malformed conditional ((3) > 2) -make: "cond-op-parentheses.mk" line 40: Malformed conditional (() -make: "cond-op-parentheses.mk" line 53: Malformed conditional ()) +make: "cond-op-parentheses.mk" line 22: Comparison with '>' requires both operands '3' and '(2' to be numeric +make: "cond-op-parentheses.mk" line 25: Malformed conditional ((3) > 2) +make: "cond-op-parentheses.mk" line 43: Malformed conditional (() +make: "cond-op-parentheses.mk" line 56: Malformed conditional ()) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.mk b/contrib/bmake/unit-tests/cond-op-parentheses.mk index b790f8bec330..8f8c3ee1e791 100644 --- a/contrib/bmake/unit-tests/cond-op-parentheses.mk +++ b/contrib/bmake/unit-tests/cond-op-parentheses.mk @@ -1,60 +1,63 @@ -# $NetBSD: cond-op-parentheses.mk,v 1.5 2022/01/22 21:50:41 rillig Exp $ +# $NetBSD: cond-op-parentheses.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $ # # Tests for parentheses in .if conditions, which group expressions to override # the precedence of the operators '!', '&&' and '||'. Parentheses cannot be # used to form arithmetic expressions such as '(3+4)' though. # Contrary to the C family of programming languages, the outermost condition # does not have to be enclosed in parentheses. .if defined(VAR) . error .elif !1 . error .endif # Parentheses cannot enclose numbers as there is no need for it. Make does # not implement any arithmetic functions in its condition parser. If # absolutely necessary, use expr(1). -# expect+1: String comparison operator must be either == or != +# +# XXX: It's inconsistent that the right operand has unbalanced parentheses. +# +# expect+1: Comparison with '>' requires both operands '3' and '(2' to be numeric .if 3 > (2) .endif # expect+1: Malformed conditional ((3) > 2) .if (3) > 2 .endif # Test for deeply nested conditions. .if (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \ (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \ 1 \ )))))))))))))))))))))))))))))))))))))))))))))))))))))))) \ )))))))))))))))))))))))))))))))))))))))))))))))))))))))) # Parentheses can be nested at least to depth 112. There is nothing special # about this number though, much higher numbers work as well, at least on # NetBSD. The actual limit depends on the allowed call stack depth for C code # of the platform. Anyway, 112 should be enough for all practical purposes. .else . error .endif # An unbalanced opening parenthesis is a parse error. .if ( . error .else . error .endif # An unbalanced closing parenthesis is a parse error. # # Before cond.c 1.237 from 2021-01-19, CondParser_Term returned TOK_RPAREN # even though the documentation of that function promised to only ever return # TOK_TRUE, TOK_FALSE or TOK_ERROR. In cond.c 1.241, the return type of that # function was changed to a properly restricted enum type, to prevent this bug # from occurring again. .if ) . error .else . error .endif all: @:; diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp index 90da7644bd9e..a508bf62a5ea 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.exp +++ b/contrib/bmake/unit-tests/cond-token-plain.exp @@ -1,62 +1,62 @@ CondParser_Eval: ${:Uvalue} != value Comparing "value" != "value" CondParser_Eval: ${:U} != " Comparing "" != "" CondParser_Eval: ${:U#hash} != "#hash" Comparing "#hash" != "#hash" CondParser_Eval: ${:U\\} != "\\ Comparing "\" != "\" CondParser_Eval: ${:U#hash} != #hash Comparing "#hash" != "#hash" CondParser_Eval: 0 # This is treated as a comment, but why? -CondParser_Eval: ${0 # comment :?yes:no} != no -CondParser_Eval: 0 # comment +CondParser_Eval: ${0 # comment:?yes:no} != no +CondParser_Eval: 0 # comment Comparing "no" != "no" -CondParser_Eval: ${1 # comment :?yes:no} != yes -CondParser_Eval: 1 # comment +CondParser_Eval: ${1 # comment:?yes:no} != yes +CondParser_Eval: 1 # comment Comparing "yes" != "yes" CondParser_Eval: ${UNDEF:Uundefined}!=undefined Comparing "undefined" != "undefined" CondParser_Eval: ${UNDEF:U12345}>12345 Comparing 12345.000000 > 12345.000000 CondParser_Eval: ${UNDEF:U12345}<12345 Comparing 12345.000000 < 12345.000000 CondParser_Eval: (${UNDEF:U0})||0 CondParser_Eval: ${:Uvar}&&name != "var&&name" Comparing "var&&name" != "var&&name" CondParser_Eval: ${:Uvar}||name != "var||name" Comparing "var||name" != "var||name" CondParser_Eval: bare make: "cond-token-plain.mk" line 105: A bare word is treated like defined(...), and the variable 'bare' is not defined. CondParser_Eval: VAR make: "cond-token-plain.mk" line 111: A bare word is treated like defined(...). CondParser_Eval: V${:UA}R make: "cond-token-plain.mk" line 118: ok CondParser_Eval: V${UNDEF}AR make: "cond-token-plain.mk" line 126: Undefined variables in bare words expand to an empty string. CondParser_Eval: 0${:Ux00} make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions. CondParser_Eval: 0${:Ux01} make: "cond-token-plain.mk" line 138: Numbers can be composed from literals and variable expressions. CondParser_Eval: "" == make: "cond-token-plain.mk" line 144: Missing right-hand side of operator '==' CondParser_Eval: == "" make: "cond-token-plain.mk" line 152: Malformed conditional (== "") CondParser_Eval: \\ make: "cond-token-plain.mk" line 167: The variable '\\' is not defined. CondParser_Eval: \\ make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined. CondParser_Eval: "unquoted\"quoted" != unquoted"quoted Comparing "unquoted"quoted" != "unquoted"quoted" CondParser_Eval: $$$$$$$$ != "" CondParser_Eval: left == right make: "cond-token-plain.mk" line 195: Malformed conditional (left == right) CondParser_Eval: ${0:?:} || left == right CondParser_Eval: 0 make: "cond-token-plain.mk" line 201: Malformed conditional (${0:?:} || left == right) CondParser_Eval: left == right || ${0:?:} make: "cond-token-plain.mk" line 206: Malformed conditional (left == right || ${0:?:}) make: "cond-token-plain.mk" line 225: Malformed conditional (VAR.${IF_COUNT::+=1} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk index 1e9f30be9153..5fb4a72b74a5 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.mk +++ b/contrib/bmake/unit-tests/cond-token-plain.mk @@ -1,256 +1,256 @@ -# $NetBSD: cond-token-plain.mk,v 1.15 2021/12/30 02:14:55 rillig Exp $ +# $NetBSD: cond-token-plain.mk,v 1.16 2022/09/25 12:51:37 rillig Exp $ # # Tests for plain tokens (that is, string literals without quotes) # in .if conditions. These are also called bare words. .MAKEFLAGS: -dc # The word 'value' after the '!=' is a bare word. .if ${:Uvalue} != value . error .endif # Using a '#' in a string literal in a condition leads to a malformed # condition since comment parsing is done in an early phase and removes the # '#' and everything after it long before the condition parser gets to see it. # # XXX: The error message is missing for this malformed condition. # The right-hand side of the comparison is just a '"', before unescaping. .if ${:U} != "#hash" . error .endif # To get a '#' into a condition, it has to be escaped using a backslash. # This prevents the comment parser from removing it, and in turn, it becomes # visible to CondParser_String. .if ${:U\#hash} != "\#hash" . error .endif # Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles # the '#' specially, even though at this point, there should be no need for # comment handling anymore. The comments are supposed to be stripped off # in a very early parsing phase. # # See https://gnats.netbsd.org/19596 for example makefiles demonstrating the # original problems. At that time, the parser didn't recognize the comment in # the line '.else # comment3'. This workaround is not needed anymore since # comments are stripped in an earlier phase. See "case '#'" in # CondParser_Token. # # XXX: Missing error message for the malformed condition. The right-hand # side before unescaping is double-quotes, backslash, backslash. .if ${:U\\} != "\\#hash" . error .endif # The right-hand side of a comparison is not parsed as a token, therefore # the code from CondParser_Token does not apply to it. # TODO: Explain the consequences. # TODO: Does this mean that more syntactic variants are allowed here? .if ${:U\#hash} != \#hash . error .endif # XXX: What is the purpose of treating an escaped '#' in the following # condition as a comment? And why only at the beginning of a token, # just as in the shell? .if 0 \# This is treated as a comment, but why? . error .endif # Ah, ok, this can be used to add an end-of-condition comment. But does # anybody really use this? This is neither documented nor obvious since # the '#' is escaped. It's much clearer to write a comment in the line # above the condition. -.if ${0 \# comment :?yes:no} != no +.if ${0 \# comment:?yes:no} != no . error .endif -.if ${1 \# comment :?yes:no} != yes +.if ${1 \# comment:?yes:no} != yes . error .endif # Usually there is whitespace around the comparison operator, but this is # not required. .if ${UNDEF:Uundefined}!=undefined . error .endif .if ${UNDEF:U12345}>12345 . error .endif .if ${UNDEF:U12345}<12345 . error .endif .if (${UNDEF:U0})||0 . error .endif # Only the comparison operator terminates the comparison operand, and it's # a coincidence that the '!' is both used in the '!=' comparison operator # as well as for negating a comparison result. # # The boolean operators '&' and '|' don't terminate a comparison operand. .if ${:Uvar}&&name != "var&&name" . error .endif .if ${:Uvar}||name != "var||name" . error .endif # A bare word may appear alone in a condition, without any comparison # operator. It is implicitly converted into defined(bare). .if bare . error .else . info A bare word is treated like defined(...), and the variable $\ 'bare' is not defined. .endif VAR= defined .if VAR . info A bare word is treated like defined(...). .else . error .endif # Bare words may be intermixed with variable expressions. .if V${:UA}R . info ok .else . error .endif # In bare words, even undefined variables are allowed. Without the bare # words, undefined variables are not allowed. That feels inconsistent. .if V${UNDEF}AR . info Undefined variables in bare words expand to an empty string. .else . error .endif .if 0${:Ux00} . error .else . info Numbers can be composed from literals and variable expressions. .endif .if 0${:Ux01} . info Numbers can be composed from literals and variable expressions. .else . error .endif # If the right-hand side is missing, it's a parse error. .if "" == . error .else . error .endif # If the left-hand side is missing, it's a parse error as well, but without # a specific error message. .if == "" . error .else . error .endif # The '\\' is not a line continuation. Neither is it an unquoted string # literal. Instead, it is parsed as a bare word (ParseWord), # and in that context, the backslash is just an ordinary character. The # function argument thus stays '\\' (2 backslashes). This string is passed # to FuncDefined, and since there is no variable named '\\', the condition # evaluates to false. .if \\ . error .else . info The variable '\\' is not defined. .endif ${:U\\\\}= backslash .if \\ . info Now the variable '\\' is defined. .else . error .endif # Anything that doesn't start with a double quote is considered a "bare word". # Strangely, a bare word may contain double quotes inside. Nobody should ever # depend on this since it may well be unintended. See CondParser_String. .if "unquoted\"quoted" != unquoted"quoted . error .endif # FIXME: In CondParser_String, Var_Parse returns var_Error without a # corresponding error message. .if $$$$$$$$ != "" . error .else . error .endif # In a condition in an .if directive, the left-hand side must not be an # unquoted string literal. # expect+1: Malformed conditional (left == right) .if left == right .endif # Before cond.c 1.276 from 2021-09-21, a variable expression containing the # modifier ':?:' allowed unquoted string literals for the rest of the # condition. This was an unintended implementation mistake. # expect+1: Malformed conditional (${0:?:} || left == right) .if ${0:?:} || left == right .endif # This affected only the comparisons after the expression, so the following # was still a syntax error. # expect+1: Malformed conditional (left == right || ${0:?:}) .if left == right || ${0:?:} .endif # See cond-token-string.mk for similar tests where the condition is enclosed # in "quotes". .MAKEFLAGS: -d0 # As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf # looks suspicious of evaluating the expression twice: first for parsing a # bare word and second for parsing the left-hand side of a comparison. # # In '.if' directives, the left-hand side of a comparison must not be a bare # word though, and this keeps CondParser_Leaf from evaluating the expression # for the second time. The right-hand side of a comparison may be a bare # word, but that side has no risk of being parsed more than once. # # expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "") .if VAR.${IF_COUNT::+=1} != "" . error .else . error .endif .if ${IF_COUNT} != "1" . error .endif # A different situation is when CondParser.leftUnquotedOK is true. This # situation arises in expressions of the form ${cond:?yes:no}. As of # 2021-12-30, the condition in such an expression is evaluated before parsing # the condition, see varmod-ifelse.mk. To pass a variable expression to the # condition parser, it needs to be escaped. This rarely happens in practice, # in most cases the conditions are simple enough that it doesn't matter # whether the condition is first evaluated and then parsed, or vice versa. # A half-baked attempt at hiding this implementation detail is # CondParser.leftUnquotedOK, but that is a rather leaky abstraction. #.MAKEFLAGS: -dcv COND= VAR.$${MOD_COUNT::+=1} .if ${${COND} == "VAR.":?yes:no} != "yes" . error .endif # The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was # evaluated twice. In practice, expressions that occur in conditions do not # have side effects, making this problem rather academic, but it is there. .if ${MOD_COUNT} != "1 1" . error .endif #.MAKEFLAGS: -d0 diff --git a/contrib/bmake/unit-tests/deptgt.exp b/contrib/bmake/unit-tests/deptgt.exp index 0a27f562293d..73d4a7f0c33f 100644 --- a/contrib/bmake/unit-tests/deptgt.exp +++ b/contrib/bmake/unit-tests/deptgt.exp @@ -1,17 +1,17 @@ -make: "deptgt.mk" line 10: warning: Extra target ignored +make: "deptgt.mk" line 10: warning: Extra target '.PHONY' ignored make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL" Parsing line 34: ${:U}: empty-source ParseDependency(: empty-source) Parsing line 35: : command for empty targets list Parsing line 36: : empty-source ParseDependency(: empty-source) Parsing line 37: : command for empty targets list Parsing line 38: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) make: "deptgt.mk" line 46: Unknown modifier "Z" -make: "deptgt.mk" line 49: warning: Extra target ignored +make: "deptgt.mk" line 49: warning: Extra target 'ordinary' ignored make: "deptgt.mk" line 52: warning: Extra target (ordinary) ignored make: "deptgt.mk" line 55: warning: Special and mundane targets don't mix. Mundane ones ignored make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/deptgt.mk b/contrib/bmake/unit-tests/deptgt.mk index 044644dcbd66..7d43220e888a 100644 --- a/contrib/bmake/unit-tests/deptgt.mk +++ b/contrib/bmake/unit-tests/deptgt.mk @@ -1,58 +1,58 @@ -# $NetBSD: deptgt.mk,v 1.12 2021/12/13 23:38:54 rillig Exp $ +# $NetBSD: deptgt.mk,v 1.13 2023/01/03 00:00:45 rillig Exp $ # # Tests for special targets like .BEGIN or .SUFFIXES in dependency # declarations. # TODO: Implementation # Just in case anyone tries to compile several special targets in a single # dependency line: That doesn't work, and make immediately rejects it. .SUFFIXES .PHONY: .c.o # The following lines demonstrate how 'targets' is set and reset during # parsing of dependencies. To see it in action, set breakpoints in: # # ParseDependency at the beginning # FinishDependencyGroup at "targets = NULL" # Parse_File at "Lst_Free(targets)" # Parse_File at "targets = Lst_New()" # ParseLine_ShellCommand at "targets == NULL" # # Keywords: # parse.c:targets target1 target2: sources # targets := [target1, target2] : command1 # targets == [target1, target2] : command2 # targets == [target1, target2] VAR=value # targets := NULL : command3 # parse error, since targets == NULL # In a dependency declaration, the list of targets can be empty. # It doesn't matter whether the empty string is generated by a variable # expression or whether it is just omitted. .MAKEFLAGS: -dp ${:U}: empty-source : command for empty targets list : 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}: -# expect+1: warning: Extra target ignored +# expect+1: warning: Extra target 'ordinary' ignored .END ordinary: # expect+1: warning: Extra target (ordinary) ignored .PATH ordinary: # expect+1: Special and mundane targets don't mix. Mundane ones ignored ordinary .PATH: all: @:; diff --git a/contrib/bmake/unit-tests/dir.mk b/contrib/bmake/unit-tests/dir.mk index 36fe2baf978c..d3c75c8cb11d 100644 --- a/contrib/bmake/unit-tests/dir.mk +++ b/contrib/bmake/unit-tests/dir.mk @@ -1,94 +1,96 @@ -# $NetBSD: dir.mk,v 1.9 2021/01/23 10:48:49 rillig Exp $ +# $NetBSD: dir.mk,v 1.10 2023/01/24 00:24:02 sjg Exp $ # # Tests for dir.c. -.MAKEFLAGS: -m / # hide /usr/share/mk from the debug log +# hide /usr/share/mk from the debug log +.SYSPATH: +.SYSPATH: / # Dependency lines may use braces for expansion. # See DirExpandCurly for the implementation. all: {one,two,three} # XXX: The above dependency line is parsed as a single node named # "{one,two,three}". There are no individual targets "one", "two", "three" # yet. The node exists but is not a target since it never appeared # on the left-hand side of a dependency operator. However, it is listed # in .ALLTARGETS (which lists all nodes, not only targets). .if target(one) . error .endif .if target({one,two,three}) . error .endif .if ${.ALLTARGETS:M{one,two,three}} != "{one,two,three}" . error .endif one: : 1 two: : 2 three: : 3 # The braces may start in the middle of a word. all: f{our,ive} four: : 4 five: : 5 six: : 6 # Nested braces work as expected since 2020-07-31 19:06 UTC. # They had been broken at least since 2003-01-01, probably even longer. all: {{thi,fou}r,fif}teen thirteen: : 13 fourteen: : 14 fifteen: : 15 # There may be multiple brace groups side by side. all: {pre-,}{patch,configure} pre-patch patch pre-configure configure: : $@ # Empty pieces are allowed in the braces. all: {fetch,extract}{,-post} fetch fetch-post extract extract-post: : $@ # The expansions may have duplicates. # When the source of the dependency line is expanded later, each of the # expanded words will be the same. all: dup-{1,1,1,1,1,1,1} dup-1: : $@ # Other than in Bash, the braces are also expanded if there is no comma. all: {{{{{{{{{{single-word}}}}}}}}}} single-word: : $@ # Demonstrate debug logging for filename expansion, especially curly braces. .MAKEFLAGS: -dd # The below line does not call SearchPath_Expand yet. # It is expanded only when necessary, that is, when the 'debug' target is # indeed made. debug: {{thi,fou}r,fif}twen # Therefore, keep the debug logging active. .PHONY: one two three four five six .PHONY: thirteen fourteen fifteen .PHONY: single-word .PHONY: pre-patch patch pre-configure configure .PHONY: fetch fetch-post extract extract-post .PHONY: dup-1 single-word .PHONY: all diff --git a/contrib/bmake/unit-tests/directive-for-break.exp b/contrib/bmake/unit-tests/directive-for-break.exp new file mode 100644 index 000000000000..b036ebfeb661 --- /dev/null +++ b/contrib/bmake/unit-tests/directive-for-break.exp @@ -0,0 +1,4 @@ +make: "directive-for-break.mk" line 45: break outside of for loop +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/directive-for-break.mk b/contrib/bmake/unit-tests/directive-for-break.mk new file mode 100644 index 000000000000..a86acfa8bde0 --- /dev/null +++ b/contrib/bmake/unit-tests/directive-for-break.mk @@ -0,0 +1,60 @@ +# $NetBSD: directive-for-break.mk,v 1.3 2022/09/24 10:52:05 rillig Exp $ +# +# Tests for .break in .for loops, which immediately terminates processing of +# the surrounding .for loop. + + +# .break terminates the loop early. +# This is usually done within a conditional. +.for i in 1 2 3 4 5 6 7 8 +. if $i == 3 +I= $i +. break +I= unreached +. endif +.endfor +.if $I != "3" +. error +.endif + + +# The .break only breaks out of the immediately surrounding .for loop, any +# other .for loops are continued normally. +.for outer in o1 o2 o3 +. for inner in i1 i2 i3 +. if ${outer} == o2 && ${inner} == i2 +. break +. endif +COMBINED+= ${outer}-${inner} +. endfor +.endfor +# Only o2-i2 and o2-i3 are missing. +.if ${COMBINED} != "o1-i1 o1-i2 o1-i3 o2-i1 o3-i1 o3-i2 o3-i3" +. error +.endif + + +# A .break outside the context of a .for loop is an error. +.if $I == 0 +# No parse error, even though the .break occurs outside a .for loop, since +# lines from inactive branches are only parsed as far as necessary to see +# whether they belong to an .if/.elif/.else/.endif chain. +. break +.else +# expect+1: break outside of for loop +. break +.endif + + +# Since cond.c 1.335 from 2022-09-02 and before cond.c 1.338 from 2022-09-23, +# the following paragraph generated the wrong error message '4294967294 open +# conditionals'. +.if 1 +. if 2 +. for var in value +. if 3 +. break +. endif +. endfor +. endif +.endif diff --git a/contrib/bmake/unit-tests/directive-for.mk b/contrib/bmake/unit-tests/directive-for.mk index 572c4d6a5c92..95171c68031f 100755 --- a/contrib/bmake/unit-tests/directive-for.mk +++ b/contrib/bmake/unit-tests/directive-for.mk @@ -1,230 +1,246 @@ -# $NetBSD: directive-for.mk,v 1.13 2022/01/15 12:35:18 rillig Exp $ +# $NetBSD: directive-for.mk,v 1.15 2022/10/01 09:23:04 rillig Exp $ # # Tests for the .for directive. # # TODO: Describe naming conventions for the loop variables. # .for f in values # .for file in values # .for _FILE_ in values # .for .FILE. in values # .for _f_ in values # Using the .for loop, lists of values can be produced. # In simple cases, the :@var@${var}@ variable modifier can be used to # achieve the same effects. # .undef NUMBERS .for num in 1 2 3 NUMBERS+= ${num} .endfor .if ${NUMBERS} != "1 2 3" . error .endif # The .for loop also works for multiple iteration variables. # This is something that the variable modifier :@ cannot do. .for name value in VARNAME value NAME2 value2 ${name}= ${value} .endfor .if ${VARNAME} != "value" || ${NAME2} != "value2" . error .endif # The .for loop splits the items at whitespace, taking quotes into account, # just like the :M or :S variable modifiers. # # Until 2012-06-03, it had split the items exactly at whitespace, without # taking the quotes into account. This had resulted in 10 words. # .undef WORDS .for var in one t\ w\ o "three three" 'four four' `five six` WORDS+= counted .endfor .if ${WORDS:[#]} != 6 . error .endif # In the body of the .for loop, the iteration variables can be accessed # like normal variables, even though they are not really variables. # # Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so # on, before the loop body is evaluated. # # A notable effect of this implementation technique is that the .for # iteration variables and the normal global variables live in separate # namespaces and do not influence each other. # var= value before var2= value before .for var var2 in 1 2 3 4 .endfor .if ${var} != "value before" . warning After the .for loop, var must still have its original value. .endif .if ${var2} != "value before" . warning After the .for loop, var2 must still have its original value. .endif # Everything from the paragraph above also applies if the loop body is # empty, even if there is no actual iteration since the loop items are # also empty. # var= value before var2= value before .for var var2 in ${:U} .endfor .if ${var} != "value before" . warning After the .for loop, var must still have its original value. .endif .if ${var2} != "value before" . warning After the .for loop, var2 must still have its original value. .endif # Until 2008-12-21, the values of the iteration variables were simply # inserted as plain text and then parsed as usual, which made it possible # to achieve all kinds of strange effects. # # Before that date, the .for loop expanded to: # EXPANSION+= value # Since that date, the .for loop expands to: # EXPANSION${:U+}= value # EXPANSION= before EXPANSION+ = before .for plus in + EXPANSION${plus}= value .endfor .if ${EXPANSION} != "before" . error This must be a make from before 2009. .endif .if ${EXPANSION+} != "value" . error This must be a make from before 2009. .endif # When the outer .for loop is expanded, it sees the expression ${i} and # expands it. The inner loop then has nothing more to expand. .for i in outer . for i in inner . info ${i} . endfor .endfor # From https://gnats.netbsd.org/29985. # # Until 2008-12-21, the .for loop was expanded by replacing the variable # value literally in the body. This could lead to situations where the # characters from the variable value were interpreted as markup rather than # plain text. # # Until 2012-06-03, the .for loop had split the words at whitespace, without # taking quotes into account. This made it possible to have variable values # like "a:\ a:\file.txt" that ended in a single backslash. Since then, the # variable values have been replaced with expressions of the form ${:U...}, # which are not interpreted as code anymore. # # As of 2020-09-22, a comment in for.c says that it may be possible to # produce an "unwanted substitution", but there is no demonstration code yet. # # The above changes prevent a backslash at the end of a word from being # interpreted as part of the code. Because of this, the trailingBackslash # hack in Var_Subst is no longer needed and as of 2020-09-22, has been # removed. .for path in a:\ a:\file.txt d:\\ d:\\file.txt . info ${path} .endfor # Ensure that braces and parentheses are properly escaped by the .for loop. # Each line must print the same word 3 times. # See ForLoop_SubstBody. .for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{ . info $v ${v} $(v) .endfor # As of 2020-10-25, the variable names may contain arbitrary characters, # except for whitespace. This allows for creative side effects. Hopefully # nobody is misusing this "feature". var= outer .for var:Q in value "quoted" . info ${var} ${var:Q} ${var:Q:Q} .endfor # XXX: A parse error or evaluation error in the items of the .for loop # should skip the whole loop. As of 2020-12-27, the loop is expanded twice. .for var in word1 ${:Uword2:Z} word3 . info XXX: Not reached ${var} .endfor # An empty list of variables to the left of the 'in' is a parse error. .for in value # expect+0: no iteration variables in for # XXX: The loop body is evaluated once, even with the parse error above. . error # expect+0: Missing argument for ".error" .endfor # expect+0: for-less endfor # An empty list of iteration values to the right of the 'in' is accepted. # Unlike in the shell, it is not a parse error. .for var in . error .endfor # If the iteration values become empty after expanding the expressions, the # body of the loop is not evaluated. It is not a parse error. .for var in ${:U} . error .endfor # The loop body can be empty. .for var in 1 2 3 .endfor # A mismatched .if inside a .for loop is detected each time when the loop body # is processed. .for var in value . if 0 .endfor # expect+0: 1 open conditional # If there are no iteration values, the loop body is not processed, and the # check for mismatched conditionals is not performed. .for var in ${:U} . if 0 .endfor # When a .for without the corresponding .endfor occurs in an inactive branch # of an .if, the .for directive is just skipped, it does not even need a # corresponding .endfor. In other words, the behavior of the parser depends # on the actual values of the conditions in the .if clauses. .if 0 . for var in value # does not need a corresponding .endfor .endif .endfor # expect+0: for-less endfor .endif # expect+0: if-less endif # When a .for without the corresponding .endfor occurs in an active branch of # an .if, the parser just counts the number of .for and .endfor directives, # without looking at any other directives. .if 1 . for var in value . endif # expect+0: if-less endif . endfor # no 'for-less endfor' .endif # no 'if-less endif' # When make parses a .for loop, it assumes that there is no line break between # the '.' and the 'for' or 'endfor', as there is no practical reason to break # the line at this point. When make scans the outer .for loop, it does not # recognize the inner directives as such. When make scans the inner .for # loop, it recognizes the '.\n for' but does not recognize the '.\n endfor', # as LK_FOR_BODY preserves the backslash-newline sequences. .MAKEFLAGS: -df .for outer in o .\ for inner in i .\ endfor .endfor .MAKEFLAGS: -d0 + + +# When there is a variable definition 'scope=cmdline' from the command line +# (which has higher precedence than global variables) and a .for loop iterates +# over a variable of the same name, the expression '${scope}' expands to the +# value from the .for loop. This is because when the body of the .for loop is +# expanded, the expression '${scope}' is textually replaced with ${:Uloop}', +# without resolving any other variable names (ForLoop_SubstBody). Later, when +# the body of the .for loop is actually interpreted, the body text doesn't +# contain the word 'scope' anymore. +.MAKEFLAGS: scope=cmdline +.for scope in loop +. if ${scope} != "loop" +. error +. endif +.endfor diff --git a/contrib/bmake/unit-tests/export.mk b/contrib/bmake/unit-tests/export.mk index bab08ee3ea23..38670eaaaf48 100644 --- a/contrib/bmake/unit-tests/export.mk +++ b/contrib/bmake/unit-tests/export.mk @@ -1,46 +1,46 @@ -# $NetBSD: export.mk,v 1.11 2021/12/05 14:57:36 rillig Exp $ +# $NetBSD: export.mk,v 1.12 2022/09/09 18:36:15 sjg Exp $ UT_TEST= export UT_FOO= foo${BAR} UT_FU= fubar UT_ZOO= hoopie UT_NO= all # believe it or not, we expect this one to come out with $UT_FU unexpanded. UT_DOLLAR= This is $$UT_FU .export UT_FU UT_FOO .export UT_DOLLAR .if !defined(.MAKE.PID) . error .MAKE.PID must be defined .endif @= at %= percent *= asterisk ${:U!}= exclamation # A direct != would try to run "exclamation" # as a shell command and assign its output # to the empty variable. &= ampersand # This is ignored because it is internal. .export .MAKE.PID # These are ignored because they are local to the target. .export @ .export % .export * .export ! # This is exported (see the .rawout file) but not displayed since the dash # shell filters it out. To reach consistent output for each shell, the # ampersand is filtered out already by FILTER_CMD. .export & # This is ignored because it is undefined. .export UNDEFINED BAR= bar is ${UT_FU} .MAKE.EXPORTED+= UT_ZOO UT_TEST -FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)=' +FILTER_CMD?= ${EGREP} -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)=' all: @env | ${FILTER_CMD} | sort diff --git a/contrib/bmake/unit-tests/hanoi-include.mk b/contrib/bmake/unit-tests/hanoi-include.mk index ce443c506b9b..5e7d5c476dfc 100644 --- a/contrib/bmake/unit-tests/hanoi-include.mk +++ b/contrib/bmake/unit-tests/hanoi-include.mk @@ -1,42 +1,48 @@ -# $NetBSD: hanoi-include.mk,v 1.3 2022/05/08 07:27:50 rillig Exp $ +# $NetBSD: hanoi-include.mk,v 1.4 2023/01/19 22:48:42 rillig Exp $ # # Implements the Towers of Hanoi puzzle, demonstrating a bunch of more or less # useful programming techniques: # # * default assignment using the ?= assignment operator # * including the same file recursively (rather unusual) # * extracting the current value of a variable using the .for loop # * using shell commands for calculations since make is a text processor # * using the :: dependency operator for adding commands to a target # * on-the-fly variable assignment expressions using the ::= modifier # # usage: # env N=3 make -r -f hanoi-include.mk # # endless loop, since command line variables cannot be overridden: # make -r -f hanoi-include.mk N=3 N?= 5 # Move this number of disks ... FROM?= A # ... from this stack ... VIA?= B # ... via this stack ... TO?= C # ... to this stack. -.if $N == 1 +# Since make has no built-in arithmetic functions, convert N to a list of +# words and use the built-in word counting instead. +.if ${N:[#]} == 1 +N:= count ${:U:${:Urange=$N}} # 'count' + one word for every disk +.endif + +.if ${N:[#]} == 2 . for from to in ${FROM} ${TO} all:: @echo "Move the upper disk from stack ${from} to stack ${to}." . endfor .else -_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}} +_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}} . include "${.PARSEDIR}/${.PARSEFILE}" -_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}} +_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}} . for from to in ${FROM} ${TO} all:: @echo "Move the upper disk from stack ${from} to stack ${to}." . endfor -_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}} +_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}} . include "${.PARSEDIR}/${.PARSEFILE}" -_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}} +_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}} .endif diff --git a/contrib/bmake/unit-tests/include-main.exp b/contrib/bmake/unit-tests/include-main.exp index e677826373c1..f58870486f87 100644 --- a/contrib/bmake/unit-tests/include-main.exp +++ b/contrib/bmake/unit-tests/include-main.exp @@ -1,17 +1,17 @@ make: "include-main.mk" line 14: main-before-ok make: "include-main.mk" line 21: main-before-for-ok -make: "include-sub.mk" line 4: sub-before-ok -make: "include-sub.mk" line 14: sub-before-for-ok +make: "include-sub.inc" line 4: sub-before-ok +make: "include-sub.inc" line 14: sub-before-for-ok Parsing line 5: . info subsub-ok -make: "include-subsub.mk" line 5: subsub-ok - in .for loop from include-sub.mk:31 with i = include - in .for loop from include-sub.mk:30 with i = nested - in .for loop from include-sub.mk:29 with i = deeply +make: "include-subsub.inc" line 5: subsub-ok + in .for loop from include-sub.inc:31 with i = include + in .for loop from include-sub.inc:30 with i = nested + in .for loop from include-sub.inc:29 with i = deeply in include-main.mk:27 Parsing line 6: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) -make: "include-sub.mk" line 38: sub-after-ok -make: "include-sub.mk" line 45: sub-after-for-ok +make: "include-sub.inc" line 38: sub-after-ok +make: "include-sub.inc" line 45: sub-after-for-ok make: "include-main.mk" line 30: main-after-ok make: "include-main.mk" line 37: main-after-for-ok exit status 0 diff --git a/contrib/bmake/unit-tests/include-main.mk b/contrib/bmake/unit-tests/include-main.mk index 9a4c6630506b..50be0587348b 100644 --- a/contrib/bmake/unit-tests/include-main.mk +++ b/contrib/bmake/unit-tests/include-main.mk @@ -1,43 +1,43 @@ -# $NetBSD: include-main.mk,v 1.7 2022/01/08 23:41:43 rillig Exp $ +# $NetBSD: include-main.mk,v 1.8 2023/01/19 23:26:14 rillig Exp $ # # Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave # as described in the manual page. # # The manual page says that it is the "filename of the file this Makefile # was included from", while before 2020-09-05 it was the "filename in which # the latest .include happened". See parse.c, function SetParseFile. # # Since 2020-09-05, the .INCLUDEDFROMDIR and .INCLUDEDFROMFILE variables # properly handle nested includes and even .for loops. .if !defined(.INCLUDEDFROMFILE) . info main-before-ok .else . warning main-before-fail(${.INCLUDEDFROMFILE}) .endif .for i in once . if !defined(.INCLUDEDFROMFILE) . info main-before-for-ok . else . warning main-before-for-fail(${.INCLUDEDFROMFILE}) . endif .endfor -.include "include-sub.mk" +.include "include-sub.inc" .if !defined(.INCLUDEDFROMFILE) . info main-after-ok .else . warning main-after-fail(${.INCLUDEDFROMFILE}) .endif .for i in once . if !defined(.INCLUDEDFROMFILE) . info main-after-for-ok . else . warning main-after-for-fail(${.INCLUDEDFROMFILE}) . endif .endfor all: # nothing diff --git a/contrib/bmake/unit-tests/include-sub.mk b/contrib/bmake/unit-tests/include-sub.inc similarity index 91% rename from contrib/bmake/unit-tests/include-sub.mk rename to contrib/bmake/unit-tests/include-sub.inc index 57d2aafe9d1d..f26f14c9d84f 100644 --- a/contrib/bmake/unit-tests/include-sub.mk +++ b/contrib/bmake/unit-tests/include-sub.inc @@ -1,49 +1,49 @@ -# $NetBSD: include-sub.mk,v 1.9 2022/01/08 23:41:43 rillig Exp $ +# $NetBSD: include-sub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $ .if ${.INCLUDEDFROMFILE} == "include-main.mk" . info sub-before-ok .else . warning sub-before-fail(${.INCLUDEDFROMFILE}) .endif # As of 2020-09-05, the .for loop is implemented as "including a file" # with a custom buffer. Therefore this loop has side effects on these # variables. .for i in once . if ${.INCLUDEDFROMFILE} == "include-main.mk" . info sub-before-for-ok . else . warning sub-before-for-fail(${.INCLUDEDFROMFILE}) . endif .endfor # To see the variable 'includes' in action: # # Breakpoints: # Parse_PushInput at "Vector_Push(&includes)" # HandleMessage at entry # Watches: # ((const IncludedFile *[10])(*includes.items)) # *CurFile() .for i in deeply . for i in nested . for i in include -.include "include-subsub.mk" +.include "include-subsub.inc" . endfor . endfor .endfor .if ${.INCLUDEDFROMFILE} == "include-main.mk" . info sub-after-ok .else . warning sub-after-fail(${.INCLUDEDFROMFILE}) .endif .for i in once . if ${.INCLUDEDFROMFILE} == "include-main.mk" . info sub-after-for-ok . else . warning sub-after-for-fail(${.INCLUDEDFROMFILE}) . endif .endfor diff --git a/contrib/bmake/unit-tests/include-subsub.inc b/contrib/bmake/unit-tests/include-subsub.inc new file mode 100644 index 000000000000..79a6a3770090 --- /dev/null +++ b/contrib/bmake/unit-tests/include-subsub.inc @@ -0,0 +1,9 @@ +# $NetBSD: include-subsub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $ + +.if ${.INCLUDEDFROMFILE} == "include-sub.inc" +.MAKEFLAGS: -dp +. info subsub-ok +.MAKEFLAGS: -d0 +.else +. warning subsub-fail(${.INCLUDEDFROMFILE}) +.endif diff --git a/contrib/bmake/unit-tests/include-subsub.mk b/contrib/bmake/unit-tests/include-subsub.mk deleted file mode 100644 index 476d75f79556..000000000000 --- a/contrib/bmake/unit-tests/include-subsub.mk +++ /dev/null @@ -1,9 +0,0 @@ -# $NetBSD: include-subsub.mk,v 1.4 2021/01/26 23:44:56 rillig Exp $ - -.if ${.INCLUDEDFROMFILE} == "include-sub.mk" -.MAKEFLAGS: -dp -. info subsub-ok -.MAKEFLAGS: -d0 -.else -. warning subsub-fail(${.INCLUDEDFROMFILE}) -.endif diff --git a/contrib/bmake/unit-tests/job-output-null.exp b/contrib/bmake/unit-tests/job-output-null.exp index 631d4862af44..628ec54a1a6b 100644 --- a/contrib/bmake/unit-tests/job-output-null.exp +++ b/contrib/bmake/unit-tests/job-output-null.exp @@ -1,4 +1,6 @@ -1 -2a +1 trailing +2a trailing +2b trailing +2c trailing 3a without newline, 3b without newline. exit status 0 diff --git a/contrib/bmake/unit-tests/job-output-null.mk b/contrib/bmake/unit-tests/job-output-null.mk index 1efd9c667980..04786dba4096 100644 --- a/contrib/bmake/unit-tests/job-output-null.mk +++ b/contrib/bmake/unit-tests/job-output-null.mk @@ -1,45 +1,55 @@ -# $NetBSD: job-output-null.mk,v 1.3 2021/09/12 10:26:49 rillig Exp $ +# $NetBSD: job-output-null.mk,v 1.4 2022/09/03 08:03:27 rillig Exp $ # # Test how null bytes in the output of a command are handled. Make processes # them using null-terminated strings, which may cut off some of the output. # -# As of 2021-04-15, make handles null bytes from the child process -# inconsistently. It's an edge case though since typically the child -# processes output text. +# Before job.c 1.454 from 2022-09-03, make handled null bytes in the output +# from the child process inconsistently. It's an edge case though since +# typically the child processes output text. # Note: The printf commands used in this test must only use a single format # string, without parameters. This is because it is implementation-dependent # how many times the command 'printf "fmt%s" "" "" ""' calls write(2). # # NetBSD /bin/sh 1 x write("fmtfmtfmt") # Dash 1 x write("fmtfmtfmt") # NetBSD /bin/ksh 3 x write("fmt") (via /bin/printf) # Bash 5 3 x write("fmt") # -# In the latter case the output may arrive in parts, which in this test makes -# a crucial difference since the outcome of the test depends on whether there -# is a '\n' in each of the blocks from the output. +# In the latter case the output may arrive in 1 to 3 parts, depending on the +# exact timing, which in this test makes a crucial difference since before +# job.c 1.454 from 2022-09-03, the outcome of the test depended on whether +# there was a '\n' in each of the blocks from the output. Depending on the +# exact timing, the output of that test varied, its possible values were '2a', +# '2a 2b', '2a 2c', '2a 2b 2c'. .MAKEFLAGS: -j1 # force jobs mode all: .PHONY - # The null byte from the command output is kept as-is. - # See CollectOutput, which looks like it intended to replace these - # null bytes with simple spaces. + # The null byte from the command output is replaced with a single + # space by CollectOutput. @printf '1\0trailing\n' + # expect: 1 trailing # Give the parent process a chance to see the above output, but not # yet the output from the next printf command. @sleep 1 - # All null bytes from the command output are kept as-is. + # Each null byte from the command output is replaced with a single + # space. @printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n' + # expect: 2a trailing + # expect: 2b trailing + # expect: 2c trailing @sleep 1 - # The null bytes are replaced with spaces since they are not followed - # by a newline. + # Each null byte from the command output is replaced with a single + # space. Because there is no trailing newline in the output, these + # null bytes were replaced with spaces even before job.c 1.454 from + # 2022-09-03, unlike in the cases above. # # The three null bytes in a row test whether this output is # compressed to a single space like in DebugFailedTarget. It isn't. @printf '3a\0without\0\0\0newline, 3b\0without\0\0\0newline.' + # expect: 3a without newline, 3b without newline. diff --git a/contrib/bmake/unit-tests/make-exported.mk b/contrib/bmake/unit-tests/make-exported.mk index 58cb15183b8d..363ea2733a47 100755 --- a/contrib/bmake/unit-tests/make-exported.mk +++ b/contrib/bmake/unit-tests/make-exported.mk @@ -1,25 +1,25 @@ -# $NetBSD: make-exported.mk,v 1.6 2020/10/05 19:27:48 rillig Exp $ +# $NetBSD: make-exported.mk,v 1.7 2022/09/09 18:36:15 sjg Exp $ # # As of 2020-08-09, the code in Var_Export is shared between the .export # directive and the .MAKE.EXPORTED variable. This leads to non-obvious # behavior for certain variable assignments. -env= make-exported-value-env -literal= make-exported-value-literal UT_VAR= ${UNEXPANDED} # Before 2020-10-03, the following line took the code path of .export-env, # which was surprising behavior. Since 2020-10-03 this line tries to # export the variable named "-env", but that is rejected because the # variable name starts with a hyphen. .MAKE.EXPORTED= -env # Before 2020-10-03, if the value of .MAKE.EXPORTED started with "-literal", # make behaved like a mixture of .export-literal and a regular .export. # # Since 2020-10-03, the "variable" named "-literal" is not exported anymore, # it is just ignored since its name starts with '-'. .MAKE.EXPORTED= -literal UT_VAR all: - @env | sort | egrep '^UT_|make-exported-value' || true + @env | sort | ${EGREP} '^UT_|make-exported-value' || true diff --git a/contrib/bmake/unit-tests/opt-env.exp b/contrib/bmake/unit-tests/opt-env.exp index b2e9ea85bafd..39a9383953dd 100644 --- a/contrib/bmake/unit-tests/opt-env.exp +++ b/contrib/bmake/unit-tests/opt-env.exp @@ -1,5 +1 @@ -make: "opt-env.mk" line 13: Malformed conditional (${FROM_ENV} != value-from-env) -make: "opt-env.mk" line 20: value-from-mk - -make: stopped in unit-tests -exit status 1 +exit status 0 diff --git a/contrib/bmake/unit-tests/opt-query.exp b/contrib/bmake/unit-tests/opt-query.exp index 38025dcf4d3a..0ba62780d844 100644 --- a/contrib/bmake/unit-tests/opt-query.exp +++ b/contrib/bmake/unit-tests/opt-query.exp @@ -1,2 +1,24 @@ +Making commands: command during parsing -exit status 1 +commands: query status 1 + +Making opt-query-file.out-of-date in compat mode: +opt-query-file.out-of-date in compat mode: query status 1 + +Making opt-query-file.up-to-date in compat mode: +`opt-query-file.up-to-date' is up to date. +opt-query-file.up-to-date in compat mode: query status 0 + +Making phony in compat mode: +phony in compat mode: query status 1 + +Making opt-query-file.out-of-date in jobs mode: +opt-query-file.out-of-date in jobs mode: query status 1 + +Making opt-query-file.up-to-date in jobs mode: +opt-query-file.up-to-date in jobs mode: query status 0 + +Making phony in jobs mode: +phony in jobs mode: query status 1 + +exit status 0 diff --git a/contrib/bmake/unit-tests/opt-query.mk b/contrib/bmake/unit-tests/opt-query.mk index 0a7d5219a8fe..3554d69afad6 100644 --- a/contrib/bmake/unit-tests/opt-query.mk +++ b/contrib/bmake/unit-tests/opt-query.mk @@ -1,26 +1,85 @@ -# $NetBSD: opt-query.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ +# $NetBSD: opt-query.mk,v 1.7 2022/08/18 05:37:05 rillig Exp $ # # Tests for the -q command line option. # # The -q option only looks at the dependencies between the targets. # None of the commands in the targets are run, not even those that are # prefixed with '+'. -.MAKEFLAGS: -q +# This test consists of several parts: +# +# main Delegates to the actual tests. +# +# commands Ensures that none of the targets is made. +# +# variants Ensures that the up-to-date status is correctly +# reported in both compat and jobs mode, and for several +# kinds of make targets. +PART?= main + +.if ${PART} == "main" + +all: .PHONY variants cleanup + +_!= touch -f opt-query-file.up-to-date + +variants: .PHONY + +. for target in commands + @echo 'Making ${target}': + @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=commands \ + && echo "${target}: query status $$?" \ + || echo "${target}: query status $$?" + @echo +. endfor + +. for mode in compat jobs +. for target in opt-query-file.out-of-date opt-query-file.up-to-date phony + @echo 'Making ${target} in ${mode} mode': + @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=variants \ + && echo "${target} in ${mode} mode: query status $$?" \ + || echo "${target} in ${mode} mode: query status $$?" + @echo +. endfor +. endfor + +# Between 1994 and before 2022-08-17, the exit status for '-q' was always 1, +# the cause for that exit code varied over time though. +# +# expect: opt-query-file.out-of-date in compat mode: query status 1 +# expect: opt-query-file.up-to-date in compat mode: query status 0 +# expect: phony in compat mode: query status 1 +# expect: opt-query-file.out-of-date in jobs mode: query status 1 +# expect: opt-query-file.up-to-date in jobs mode: query status 0 +# expect: phony in jobs mode: query status 1 + +cleanup: .PHONY + @rm -f opt-query-file.up-to-date + +.elif ${PART} == "commands" # This command cannot be prevented from being run since it is used at parse # time, and any later variable assignments may depend on its result. != echo 'command during parsing' 1>&2; echo # None of these commands are run. .BEGIN: @echo '$@: hidden command' @+echo '$@: run always' # None of these commands are run. -all: +commands: @echo '$@: hidden command' @+echo '$@: run always' - -# The exit status 1 is because the "all" target has to be made, that is, +# The exit status 1 is because the "commands" target has to be made, that is, # it is not up-to-date. + +.elif ${PART} == "variants" + +opt-query-file.out-of-date: ${MAKEFILE} +opt-query-file.up-to-date: ${MAKEFILE} +phony: .PHONY + +.else +. error Invalid part '${PART}' +.endif diff --git a/contrib/bmake/unit-tests/parse-var.exp b/contrib/bmake/unit-tests/parse-var.exp index 39a9383953dd..bae925e8c869 100644 --- a/contrib/bmake/unit-tests/parse-var.exp +++ b/contrib/bmake/unit-tests/parse-var.exp @@ -1 +1,5 @@ -exit status 0 +make: Unfinished modifier for "BRACE_GROUP" (',' missing) +make: "parse-var.mk" line 130: Malformed conditional (0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},,}) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/parse-var.mk b/contrib/bmake/unit-tests/parse-var.mk index bd6c59f0e5cb..cca6931d14a1 100644 --- a/contrib/bmake/unit-tests/parse-var.mk +++ b/contrib/bmake/unit-tests/parse-var.mk @@ -1,13 +1,135 @@ -# $NetBSD: parse-var.mk,v 1.1 2020/10/04 06:53:15 rillig Exp $ +# $NetBSD: parse-var.mk,v 1.6 2022/09/25 21:26:23 rillig Exp $ +# +# Tests for parsing variable expressions. +# +# TODO: Add systematic tests for all of the below combinations. +# +# Written form: +# short form +# long form with braces endc == '}' +# long form with parentheses endc == ')' +# indirect modifiers endc == '\0' +# +# Based on: +# undefined variable +# global variable +# command-line variable +# environment variable +# target-local variable +# legacy variable '@F' +# +# VarEvalMode: +# parse +# eval +# eval-undeferr +# eval-keep-dollar +# eval-keep-undef +# eval-keep-dollar-undef +# +# Global mode: +# without -dL +# with -dL +# +# Modifiers: +# no +# yes, stay undefined +# convert to defined +# indirect modifiers, involving changes to VarEvalMode +# +# Error conditions: +# for the short form, EOF after the '$' +# for the short form, each character +# for the long forms, EOF right after '${' +# for the long forms, EOF after the variable name +# for the long forms, EOF after the ':' +# for the long forms, EOF after parsing a modifier +# for the long forms, ':}' +# for each modifier: syntactic error +# for each modifier: evaluation error +# +# Context: +# in a condition, only operand, unquoted +# in a condition, only operand, quoted +# in a condition, left-hand side, unquoted +# in a condition, left-hand side, quoted +# in a condition, right-hand side, unquoted +# in a condition, right-hand side, quoted +# left-hand side of a variable assignment +# right-hand side of a ':=' variable assignment +# right-hand side of a '!=' variable assignment +# shell command in a target +# .info directive +# dependency line +# items in a .for loop +# everywhere else Var_Parse is called +# +# Further influences: +# multi-level evaluations like 'other=${OTHER}' with OTHER='$$ ${THIRD}' +# +# Effects: +# How much does the parsing position advance (pp)? +# What's the value of the expression (out_val)? +# What's the status after parsing the expression (VarParseResult)? +# What error messages are printed (Parse_Error)? +# What no-effect error messages are printed (Error)? +# What error messages should be printed but aren't? +# What other side effects are there? .MAKEFLAGS: -dL -# In variable assignments, there may be spaces on the left-hand side of the -# assignment, but only if they occur inside variable expressions. +# In variable assignments, there may be spaces in the middle of the left-hand +# side of the assignment, but only if they occur inside variable expressions. +# Leading spaces (but not tabs) are possible but unusual. +# Trailing spaces are common in some coding styles, others omit them. VAR.${:U param }= value .if ${VAR.${:U param }} != "value" . error .endif -all: - @:; +# XXX: The following paragraph already uses past tense, in the hope that the +# parsing behavior can be cleaned up soon. + +# Since var.c 1.323 from 2020-07-26 18:11 and except for var.c 1.1028 from +# 2022-08-08, the exact way of parsing an expression depended on whether the +# expression was actually evaluated or merely parsed. +# +# If it was evaluated, nested expressions were parsed correctly, parsing each +# modifier according to its exact definition (see varmod.mk). +# +# If the expression was merely parsed but not evaluated (for example, because +# its value would not influence the outcome of the condition, or during the +# first pass of the ':@var@body@' modifier), and the expression contained a +# modifier, and that modifier contained a nested expression, the nested +# expression was not parsed correctly. Instead, make only counted the opening +# and closing delimiters, which failed for nested modifiers with unbalanced +# braces. +# +# This naive brace counting was implemented in ParseModifierPartDollar. As of +# var.c 1.1029, there are still several other places that merely count braces +# instead of properly parsing subexpressions. + +#.MAKEFLAGS: -dcpv +# Keep these braces outside the conditions below, to keep them simple to +# understand. If the BRACE_PAIR had been replaced with ':U{}', the '}' would +# have to be escaped, but not the '{'. This asymmetry would have made the +# example even more complicated to understand. +BRACE_PAIR= {} +# In this test word, the '{{}' in the middle will be replaced. +BRACE_GROUP= {{{{}}}} + +# The inner ':S' modifier turns the word '{}' into '{{}'. +# The outer ':S' modifier then replaces '{{}' with ''. +# In the first case, the outer expression is relevant and is parsed correctly. +.if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},,} +.endif +# In the second case, the outer expression was irrelevant. In this case, in +# the parts of the outer ':S' modifier, make only counted the braces, and since +# the inner expression '${BRACE_PAIR:...}' contains more '{' than '}', parsing +# failed with the error message 'Unfinished modifier for "BRACE_GROUP"'. Fixed +# in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 from 2022-08-23. +.if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},,} +.endif +#.MAKEFLAGS: -d0 + + +all: .PHONY diff --git a/contrib/bmake/unit-tests/sh-leading-at.exp b/contrib/bmake/unit-tests/sh-leading-at.exp index 8347fda085f7..8197a0d2b71a 100644 --- a/contrib/bmake/unit-tests/sh-leading-at.exp +++ b/contrib/bmake/unit-tests/sh-leading-at.exp @@ -1,6 +1,7 @@ ok space after @ echo 'echoed' echoed 3 +whitespace in leading part exit status 0 diff --git a/contrib/bmake/unit-tests/sh-leading-at.mk b/contrib/bmake/unit-tests/sh-leading-at.mk index 9f98005ec088..cff3d4da1263 100644 --- a/contrib/bmake/unit-tests/sh-leading-at.mk +++ b/contrib/bmake/unit-tests/sh-leading-at.mk @@ -1,18 +1,22 @@ -# $NetBSD: sh-leading-at.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: sh-leading-at.mk,v 1.6 2023/01/19 19:55:27 rillig Exp $ # # Tests for shell commands preceded by an '@', to suppress printing # the command to stdout. # # See also: # .SILENT # depsrc-silent.mk # opt-silent.mk all: @ @echo 'ok' @ echo 'space after @' echo 'echoed' # The leading '@' can be repeated. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@echo '3' + + # Since 2023-01-17, the leading '@', '+' and '-' may contain + # whitespace, for compatibility with GNU make. + @ @ @ echo 'whitespace in leading part' diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.exp b/contrib/bmake/unit-tests/sh-leading-hyphen.exp index 39a9383953dd..d049757777cd 100644 --- a/contrib/bmake/unit-tests/sh-leading-hyphen.exp +++ b/contrib/bmake/unit-tests/sh-leading-hyphen.exp @@ -1 +1,11 @@ +true +false +*** Error code 1 (ignored) +unknown-command 'needed for needshell in compat.c' +unknown-command: not found +*** Error code 127 (ignored) +unknown-long-option 'needed for needshell in compat.c' +unknown-long-option: not found +whitespace in leading part +*** Error code 127 (ignored) exit status 0 diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.mk b/contrib/bmake/unit-tests/sh-leading-hyphen.mk index d760abb9afdd..08b50a2ddc42 100644 --- a/contrib/bmake/unit-tests/sh-leading-hyphen.mk +++ b/contrib/bmake/unit-tests/sh-leading-hyphen.mk @@ -1,14 +1,29 @@ -# $NetBSD: sh-leading-hyphen.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: sh-leading-hyphen.mk,v 1.4 2023/01/19 19:55:27 rillig Exp $ # # Tests for shell commands preceded by a '-', to ignore the exit status of # the command line. # # See also: # .IGNORE # depsrc-ignore.mk # opt-ignore.mk # TODO: Implementation all: - @:; + -true + -false + + # An undefined variable expands to an empty string, without warning. + # This is used in practice for prefixing tool names or for DESTDIR. + # The '-' before 'unknown' is interpreted by make as '.IGNORE' flag. + ${UNDEF}-unknown-command 'needed for needshell in compat.c' + + # Expanding undefined variables may lead to strange error messages + # when the shell interprets single-character options as commands + # instead. + ${UNDEF} --unknown-long-option 'needed for needshell in compat.c' + + # Since 2023-01-17, the leading '@', '+' and '-' may contain + # whitespace, for compatibility with GNU make. + - - - @echo 'whitespace in leading part' diff --git a/contrib/bmake/unit-tests/sh-leading-plus.exp b/contrib/bmake/unit-tests/sh-leading-plus.exp index eb586d29f1c2..8cc26deaacb5 100644 --- a/contrib/bmake/unit-tests/sh-leading-plus.exp +++ b/contrib/bmake/unit-tests/sh-leading-plus.exp @@ -1,4 +1,6 @@ echo 'this command is not run' echo 'this command is run' this command is run +echo 'whitespace in leading part' +whitespace in leading part exit status 0 diff --git a/contrib/bmake/unit-tests/sh-leading-plus.mk b/contrib/bmake/unit-tests/sh-leading-plus.mk index ff57b4a38a7d..83e7e7a15e24 100644 --- a/contrib/bmake/unit-tests/sh-leading-plus.mk +++ b/contrib/bmake/unit-tests/sh-leading-plus.mk @@ -1,10 +1,14 @@ -# $NetBSD: sh-leading-plus.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ +# $NetBSD: sh-leading-plus.mk,v 1.5 2023/01/19 19:55:27 rillig Exp $ # # Tests for shell commands preceded by a '+', to run them even if # the command line option -n is given. .MAKEFLAGS: -n all: @echo 'this command is not run' @+echo 'this command is run' + + # Since 2023-01-17, the leading '@', '+' and '-' may contain + # whitespace, for compatibility with GNU make. + + + + @echo 'whitespace in leading part' diff --git a/contrib/bmake/unit-tests/var-op-expand.exp b/contrib/bmake/unit-tests/var-op-expand.exp index a4ba53942cf7..4916336c0400 100644 --- a/contrib/bmake/unit-tests/var-op-expand.exp +++ b/contrib/bmake/unit-tests/var-op-expand.exp @@ -1,7 +1,7 @@ -make: "var-op-expand.mk" line 265: Unknown modifier "s,value,replaced," -make: "var-op-expand.mk" line 268: warning: XXX Neither branch should be taken. -make: "var-op-expand.mk" line 273: Unknown modifier "s,value,replaced," -make: "var-op-expand.mk" line 274: warning: XXX Neither branch should be taken. +make: "var-op-expand.mk" line 274: Unknown modifier "s,value,replaced," +make: "var-op-expand.mk" line 277: warning: XXX Neither branch should be taken. +make: "var-op-expand.mk" line 282: Unknown modifier "s,value,replaced," +make: "var-op-expand.mk" line 283: warning: XXX Neither branch should be taken. make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/var-op-expand.mk b/contrib/bmake/unit-tests/var-op-expand.mk index 1d905aeb3757..f89aa6fa4b56 100644 --- a/contrib/bmake/unit-tests/var-op-expand.mk +++ b/contrib/bmake/unit-tests/var-op-expand.mk @@ -1,281 +1,290 @@ -# $NetBSD: var-op-expand.mk,v 1.16 2021/12/28 10:47:00 rillig Exp $ +# $NetBSD: var-op-expand.mk,v 1.17 2022/09/08 20:23:45 rillig Exp $ # # Tests for the := variable assignment operator, which expands its # right-hand side. # # See also: # varname-dot-make-save_dollars.mk # Force the test results to be independent of the default value of this # setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake # distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS:= yes # If the right-hand side does not contain a dollar sign, the ':=' assignment # operator has the same effect as the '=' assignment operator. VAR:= value .if ${VAR} != "value" . error .endif # When a ':=' assignment is performed, its right-hand side is evaluated and # expanded as far as possible. Contrary to other situations, '$$' and # variable expressions based on undefined variables are preserved though. # # Whether a variable expression is undefined or not is determined at the end # of evaluating the expression. The consequence is that ${:Ufallback} expands # to "fallback"; initially this expression is undefined since it is based on # the variable named "", which is guaranteed to be never defined, but at the # end of evaluating the expression ${:Ufallback}, the modifier ':U' has turned # the expression into a defined expression. # literal dollar signs VAR:= $$ $$$$ $$$$$$$$ .if ${VAR} != "\$ \$\$ \$\$\$\$" . error .endif -# reference to a variable containing a literal dollar sign +# reference to a variable containing literal dollar signs REF= $$ $$$$ $$$$$$$$ VAR:= ${REF} REF= too late .if ${VAR} != "\$ \$\$ \$\$\$\$" . error .endif # reference to an undefined variable .undef UNDEF VAR:= <${UNDEF}> +.if ${VAR} != "<>" +. error +.endif UNDEF= after .if ${VAR} != "" . error .endif # reference to a variable whose name is computed from another variable REF2= referred to REF= REF2 VAR:= ${${REF}} REF= too late .if ${VAR} != "referred to" . error .endif # expression with an indirect modifier referring to an undefined variable .undef UNDEF VAR:= ${:${UNDEF}} +.if ${VAR} != "" +. error +.endif UNDEF= Uwas undefined .if ${VAR} != "was undefined" . error .endif # expression with an indirect modifier referring to another variable that # in turn refers to an undefined variable # # XXX: Even though this is a ':=' assignment, the '${UNDEF}' in the part of # the variable modifier is not preserved. To preserve it, ParseModifierPart # would have to call VarSubstExpr somehow since this is the only piece of # code that takes care of this global variable. .undef UNDEF REF= U${UNDEF} #.MAKEFLAGS: -dv VAR:= ${:${REF}} #.MAKEFLAGS: -d0 REF= too late UNDEF= Uwas undefined .if ${VAR} != "" . error .endif # In variable assignments using the ':=' operator, undefined variables are # preserved, no matter how indirectly they are referenced. .undef REF3 REF2= <${REF3}> REF= ${REF2} VAR:= ${REF} +.if ${VAR} != "<>" +. error +.endif REF3= too late .if ${VAR} != "" . error .endif # In variable assignments using the ':=' operator, '$$' are preserved, no # matter how indirectly they are referenced. REF2= REF2:$$ $$$$ REF= REF:$$ $$$$ ${REF2} VAR:= VAR:$$ $$$$ ${REF} .if ${VAR} != "VAR:\$ \$\$ REF:\$ \$\$ REF2:\$ \$\$" . error .endif # In variable assignments using the ':=' operator, '$$' are preserved in the # expressions of the top level, but not in expressions that are nested. VAR:= top:$$ ${:Unest1\:\$\$} ${:Unest2${:U\:\$\$}} .if ${VAR} != "top:\$ nest1:\$ nest2:\$" . error .endif # In variable assignments using the ':=' operator, there may be expressions # containing variable modifiers, and these modifiers may refer to other # variables. These referred-to variables are expanded at the time of # assignment. The undefined variables are kept as-is and are later expanded # when evaluating the condition. # # Contrary to the assignment operator '=', the assignment operator ':=' # consumes the '$' from modifier parts. REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ .undef REF.undef VAR:= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} REF.word= word.after REF.undef= undef.after .if ${VAR} != "1:2:\$ 4:\$\$ undef.after, direct: 1:\$ 2:\$\$ 4:\$\$\$\$ undef.after" . error .endif # Just for comparison, the previous example using the assignment operator '=' # instead of ':='. The right-hand side of the assignment is not evaluated at # the time of assignment but only later, when ${VAR} appears in the condition. # # At that point, both REF.word and REF.undef are defined. REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ .undef REF.undef VAR= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} REF.word= word.after REF.undef= undef.after .if ${VAR} != "word.after undef.after, direct: word.after undef.after" . error .endif # Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27, # if the variable name in a ':=' assignment referred to an undefined variable, # there were actually 2 assignments to different variables: # # Global["VAR_SUBST_${UNDEF}"] = "" # Global["VAR_SUBST_"] = "" # # The variable name with the empty value actually included a dollar sign. # Variable names with dollars are not used in practice. # # It might be a good idea to forbid undefined variables on the left-hand side # of a variable assignment. .undef UNDEF VAR_ASSIGN_${UNDEF}= assigned by '=' VAR_SUBST_${UNDEF}:= assigned by ':=' .if ${VAR_ASSIGN_} != "assigned by '='" . error .endif .if defined(${:UVAR_SUBST_\${UNDEF\}}) . error .endif .if ${VAR_SUBST_} != "assigned by ':='" . error .endif # The following test case demonstrates that the variable 'LATER' is preserved # in the ':=' assignment since the variable 'LATER' is not yet defined. # After the assignment to 'LATER', evaluating the variable 'INDIRECT' # evaluates 'LATER' as well. # .undef LATER INDIRECT:= ${LATER:S,value,replaced,} .if ${INDIRECT} != "" . error .endif LATER= late-value .if ${INDIRECT} != "late-replaced" . error .endif # Same as the test case above, except for the additional modifier ':tl' when # evaluating the variable 'INDIRECT'. Nothing surprising here. .undef LATER .undef later INDIRECT:= ${LATER:S,value,replaced,} .if ${INDIRECT:tl} != "" . error .endif LATER= uppercase-value later= lowercase-value .if ${INDIRECT:tl} != "uppercase-replaced" . error .endif # Similar to the two test cases above, the situation gets a bit more involved # here, due to the double indirection. The variable 'indirect' is supposed to # be the lowercase version of the variable 'INDIRECT'. # # The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as # well, it wouldn't make a difference in this case. The crucial detail is the # assignment operator ':=' for the variable 'indirect'. During this # assignment, the variable modifier ':S,value,replaced,' is converted to # lowercase, which turns 'S' into 's', thus producing an unknown modifier. # In this case, make issues a warning, but in cases where the modifier # includes a '=', the modifier would be interpreted as a SysV-style # substitution like '.c=.o', and make would not issue a warning, leading to # silent unexpected behavior. # # As of 2021-11-20, the actual behavior is unexpected. Fixing it is not # trivial. When the assignment to 'indirect' takes place, the expressions # from the nested expression could be preserved, like this: # # Start with: # # indirect:= ${INDIRECT:tl} # # Since INDIRECT is defined, expand it, remembering that the modifier # ':tl' must still be applied to the final result. # # indirect:= ${LATER:S,value,replaced,} \ # OK \ # ${LATER:value=sysv} # # The variable 'LATER' is not defined. An idea may be to append the # remaining modifier ':tl' to each expression that is starting with an # undefined variable, resulting in: # # indirect:= ${LATER:S,value,replaced,:tl} \ # OK \ # ${LATER:value=sysv:tl} # # This would work for the first expression. The second expression ends # with the SysV modifier ':from=to', and when this modifier is parsed, # it consumes all characters until the end of the expression, which in # this case would replace the suffix 'value' with the literal 'sysv:tl', # ignoring that the ':tl' was intended to be an additional modifier. # # Due to all of this, this surprising behavior is not easy to fix. # .undef LATER .undef later INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv} indirect:= ${INDIRECT:tl} # expect+1: Unknown modifier "s,value,replaced," .if ${indirect} != " ok " . error .else . warning XXX Neither branch should be taken. .endif LATER= uppercase-value later= lowercase-value # expect+1: Unknown modifier "s,value,replaced," .if ${indirect} != "uppercase-replaced ok uppercase-sysv" . warning XXX Neither branch should be taken. .else . error .endif all: @:; diff --git a/contrib/bmake/unit-tests/parse-var.exp b/contrib/bmake/unit-tests/var-readonly.exp similarity index 100% copy from contrib/bmake/unit-tests/parse-var.exp copy to contrib/bmake/unit-tests/var-readonly.exp diff --git a/contrib/bmake/unit-tests/var-readonly.mk b/contrib/bmake/unit-tests/var-readonly.mk new file mode 100644 index 000000000000..43799e4adb5c --- /dev/null +++ b/contrib/bmake/unit-tests/var-readonly.mk @@ -0,0 +1,20 @@ +# $NetBSD: var-readonly.mk,v 1.1 2023/01/24 00:20:00 sjg Exp $ + +# the answer +N = 42 +.READONLY: N +# this should be ignored +N = 666 +.if ${N} != 42 +.error N ($N) should be 42 +.endif + +.NOREADONLY: N +# now we can change it +N = 69 +.if ${N} == 42 +.error N should not be 42 +.endif + +all: + diff --git a/contrib/bmake/unit-tests/var-scope-local-legacy.exp b/contrib/bmake/unit-tests/var-scope-local-legacy.exp index 39a9383953dd..fb7e0863b2e6 100644 --- a/contrib/bmake/unit-tests/var-scope-local-legacy.exp +++ b/contrib/bmake/unit-tests/var-scope-local-legacy.exp @@ -1 +1,6 @@ +: LEN4=undef_ +: XY=undef_ +: AF=undef_ +: %D=undef_ %F=undef_ +: @D=._ @F=all_ exit status 0 diff --git a/contrib/bmake/unit-tests/var-scope-local-legacy.mk b/contrib/bmake/unit-tests/var-scope-local-legacy.mk index e519d63e7c51..9b70e3f8f143 100644 --- a/contrib/bmake/unit-tests/var-scope-local-legacy.mk +++ b/contrib/bmake/unit-tests/var-scope-local-legacy.mk @@ -1,8 +1,17 @@ -# $NetBSD: var-scope-local-legacy.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $ +# $NetBSD: var-scope-local-legacy.mk,v 1.2 2022/09/27 19:18:45 rillig Exp $ # # Tests for legacy target-local variables, such as ${} != "" . error .endif # Special characters that would be interpreted differently can be escaped. # These are '}' (the closing character of the expression), ':', '$' and '\'. # Any other backslash sequences are preserved. # # The escaping rules for string literals in conditions are completely # different though. There, any character may be escaped using a backslash. # .if ${DEF:D \} \: \$ \\ \) \n } != " } : \$ \\ \\) \\n " . error .endif # Like in several other places in variable expressions, when # ApplyModifier_Defined calls Var_Parse, double dollars lead to a parse # error that is silently ignored. This makes all dollar signs disappear, # except for the last, which is a well-formed variable expression. # .if ${DEF:D$$$$$${DEF}} != "defined" . error .endif # Any other text is written without any further escaping. In contrast # to the :M modifier, parentheses and braces do not need to be nested. # Instead, the :D modifier is implemented sanely by parsing nested # expressions as such, without trying any shortcuts. See ApplyModifier_Match # for an inferior variant. # .if ${DEF:D!&((((} != "!&((((" . error .endif # The :D modifier is often used in combination with the :U modifier. # It does not matter in which order the :D and :U modifiers appear. .if ${UNDEF:Dyes:Uno} != no . error .endif .if ${UNDEF:Uno:Dyes} != no . error .endif .if ${DEF:Dyes:Uno} != yes . error .endif .if ${DEF:Uno:Dyes} != yes . error .endif # Since the variable with the empty name is never defined, the :D modifier # can be used to add comments in the middle of an expression. That # expression always evaluates to an empty string. .if ${:D This is a comment. } != "" . error .endif # TODO: Add more tests for parsing the plain text part, to cover each branch # of ApplyModifier_Defined. # The :D and :U modifiers behave differently from the :@var@ modifier in # that they preserve dollars in a ':=' assignment. This is because # ApplyModifier_Defined passes the emode unmodified to Var_Parse, unlike # ApplyModifier_Loop, which uses ParseModifierPart, which in turn removes # the keepDollar flag from emode. # # XXX: This inconsistency is documented nowhere. .MAKEFLAGS: -dv 8_DOLLARS= $$$$$$$$ VAR:= ${8_DOLLARS} VAR:= ${VAR:D${8_DOLLARS}} VAR:= ${VAR:@var@${8_DOLLARS}@} .MAKEFLAGS: -d0 -all: - @:; + +# Before var.c 1.1030 from 2022-08-24, the following expression caused an +# out-of-bounds read when parsing the indirect ':D' modifier. +M_U_backslash:= ${:UU\\} +.if ${:${M_U_backslash}} != "\\" +. error +.endif + + +all: .PHONY diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp index 7134c71b8d39..80361ebf6d61 100644 --- a/contrib/bmake/unit-tests/varmod-ifelse.exp +++ b/contrib/bmake/unit-tests/varmod-ifelse.exp @@ -1,32 +1,32 @@ make: Bad conditional expression 'variable expression == "literal"' in 'variable expression == "literal"?bad:bad' make: "varmod-ifelse.mk" line 27: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad}) make: Bad conditional expression ' == ""' in ' == ""?bad-assign:bad-assign' make: Bad conditional expression ' == ""' in ' == ""?bad-cond:bad-cond' make: "varmod-ifelse.mk" line 44: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond}) make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no' make: "varmod-ifelse.mk" line 66: Malformed conditional (${1 == == 2:?yes:no} != "") CondParser_Eval: "${1 == == 2:?yes:no}" != "" CondParser_Eval: 1 == == 2 Comparing 1.000000 == 0.000000 make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no' Comparing "" != "" make: "varmod-ifelse.mk" line 92: warning: Oops, the parse error should have been propagated. -CondParser_Eval: ${ ${:U\$}{VAR} == value :?ok:bad} != "ok" -CondParser_Eval: ${VAR} == value +CondParser_Eval: ${ ${:U\$}{VAR} == value:?ok:bad} != "ok" +CondParser_Eval: ${VAR} == value Comparing "value" == "value" Comparing "ok" != "ok" make: "varmod-ifelse.mk" line 153: no. -make: "varmod-ifelse.mk" line 154: String comparison operator must be either == or != +make: "varmod-ifelse.mk" line 154: Comparison with '>=' requires both operands 'no' and '10' to be numeric make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no' make: "varmod-ifelse.mk" line 154: . make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no' make: "varmod-ifelse.mk" line 159: . make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no' make: "varmod-ifelse.mk" line 160: . make: "varmod-ifelse.mk" line 167: true make: "varmod-ifelse.mk" line 169: false make: Bad conditional expression ' ' in ' ?true:false' make: "varmod-ifelse.mk" line 171: make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk index a823ae784935..2d1c54943ca1 100644 --- a/contrib/bmake/unit-tests/varmod-ifelse.mk +++ b/contrib/bmake/unit-tests/varmod-ifelse.mk @@ -1,184 +1,184 @@ -# $NetBSD: varmod-ifelse.mk,v 1.19 2022/05/08 06:51:27 rillig Exp $ +# $NetBSD: varmod-ifelse.mk,v 1.20 2022/09/25 12:51:37 rillig Exp $ # # Tests for the ${cond:?then:else} variable modifier, which evaluates either # the then-expression or the else-expression, depending on the condition. # # The modifier was added on 1998-04-01. # # Until 2015-10-11, the modifier always evaluated both the "then" and the # "else" expressions. # TODO: Implementation # The variable name of the expression is expanded and then taken as the # condition. In the below example it becomes: # # variable expression == "literal" # # This confuses the parser, which expects an operator instead of the bare # word "expression". If the name were expanded lazily, everything would be # fine since the condition would be: # # ${:Uvariable expression} == "literal" # # Evaluating the variable name lazily would require additional code in # Var_Parse and ParseVarname, it would be more useful and predictable # though. .if ${${:Uvariable expression} == "literal":?bad:bad} . error .else . error .endif # In a variable assignment, undefined variables are not an error. # Because of the early expansion, the whole condition evaluates to # ' == ""' though, which cannot be parsed because the left-hand side looks # empty. COND:= ${${UNDEF} == "":?bad-assign:bad-assign} # In a condition, undefined variables generate a "Malformed conditional" # error. That error message is wrong though. In lint mode, the correct # "Undefined variable" error message is generated. # The difference to the ':=' variable assignment is the additional # "Malformed conditional" error message. .if ${${UNDEF} == "":?bad-cond:bad-cond} . error .else . error .endif # When the :? is parsed, it is greedy. The else branch spans all the # text, up until the closing character '}', even if the text looks like # another modifier. .if ${1:?then:else:Q} != "then" . error .endif .if ${0:?then:else:Q} != "else:Q" . error .endif # This line generates 2 error messages. The first comes from evaluating the # malformed conditional "1 == == 2", which is reported as "Bad conditional # expression" by ApplyModifier_IfElse. The variable expression containing that # conditional therefore returns a parse error from Var_Parse, and this parse # error propagates to CondEvalExpression, where the "Malformed conditional" # comes from. .if ${1 == == 2:?yes:no} != "" . error .else . error .endif # If the "Bad conditional expression" appears in a quoted string literal, the # error message "Malformed conditional" is not printed, leaving only the "Bad # conditional expression". # # XXX: The left-hand side is enclosed in quotes. This results in Var_Parse # being called without VARE_UNDEFERR. When ApplyModifier_IfElse # returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the # value of the variable expression is still undefined. CondParser_String is # then supposed to do proper error handling, but since varUndefined is local # to var.c, it cannot distinguish this return value from an ordinary empty # string. The left-hand side of the comparison is therefore just an empty # string, which is obviously equal to the empty string on the right-hand side. # # XXX: The debug log for -dc shows a comparison between 1.0 and 0.0. The # condition should be detected as being malformed before any comparison is # done since there is no well-formed comparison in the condition at all. .MAKEFLAGS: -dc .if "${1 == == 2:?yes:no}" != "" . error .else . warning Oops, the parse error should have been propagated. .endif .MAKEFLAGS: -d0 # As of 2020-12-10, the variable "name" is first expanded, and the result of # this expansion is then taken as the condition. To force the variable # expression in the condition to be evaluated at exactly the right point, # the '$' of the intended '${VAR}' escapes from the parser in form of the # expression ${:U\$}. Because of this escaping, the variable "name" and thus # the condition ends up as "${VAR} == value", just as intended. # # This hack does not work for variables from .for loops since these are # expanded at parse time to their corresponding ${:Uvalue} expressions. # Making the '$' of the '${VAR}' expression indirect hides this expression # from the parser of the .for loop body. See ForLoop_SubstVarLong. .MAKEFLAGS: -dc VAR= value -.if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok" +.if ${ ${:U\$}{VAR} == value:?ok:bad} != "ok" . error .endif .MAKEFLAGS: -d0 # On 2021-04-19, when building external/bsd/tmux with HAVE_LLVM=yes and # HAVE_GCC=no, the following conditional generated this error message: # # make: Bad conditional expression 'string == "literal" && no >= 10' # in 'string == "literal" && no >= 10?yes:no' # # Despite the error message (which was not clearly marked with "error:"), # the build continued, for historical reasons, see main_Exit. # # The tricky detail here is that the condition that looks so obvious in the # form written in the makefile becomes tricky when it is actually evaluated. # This is because the condition is written in the place of the variable name # of the expression, and in an expression, the variable name is always # expanded first, before even looking at the modifiers. This happens for the # modifier ':?' as well, so when CondEvalExpression gets to see the # expression, it already looks like this: # # string == "literal" && no >= 10 # # When parsing such an expression, the parser used to be strict. It first # evaluated the left-hand side of the operator '&&' and then started parsing # the right-hand side 'no >= 10'. The word 'no' is obviously a string # literal, not enclosed in quotes, which is OK, even on the left-hand side of # the comparison operator, but only because this is a condition in the # modifier ':?'. In an ordinary directive '.if', this would be a parse error. # For strings, only the comparison operators '==' and '!=' are defined, # therefore parsing stopped at the '>', producing the 'Bad conditional # expression'. # # Ideally, the conditional expression would not be expanded before parsing # it. This would allow to write the conditions exactly as seen below. That # change has a high chance of breaking _some_ existing code and would need # to be thoroughly tested. # # Since cond.c 1.262 from 2021-04-20, make reports a more specific error # message in situations like these, pointing directly to the specific problem # instead of just saying that the whole condition is bad. STRING= string NUMBER= no # not really a number .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. # The following situation occasionally occurs with MKINET6 or similar # variables. NUMBER= # empty, not really a number either .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}. .info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}. # CondParser_LeafToken handles [0-9-+] specially, treating them as a number. PLUS= + ASTERISK= * EMPTY= # empty # "true" since "+" is not the empty string. .info ${${PLUS} :?true:false} # "false" since the variable named "*" is not defined. .info ${${ASTERISK} :?true:false} # syntax error since the condition is completely blank. .info ${${EMPTY} :?true:false} # Since the condition of the '?:' modifier is expanded before being parsed and # evaluated, it is common practice to enclose expressions in quotes, to avoid # producing syntactically invalid conditions such as ' == value'. This only # works if the expanded values neither contain quotes nor backslashes. For # strings containing quotes or backslashes, the '?:' modifier should not be # used. PRIMES= 2 3 5 7 11 .if ${1 2 3 4 5:L:@n@$n:${ ("${PRIMES:M$n}" != "") :?prime:not_prime}@} != \ "1:not_prime 2:prime 3:prime 4:not_prime 5:prime" . error .endif diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp index bbe0037673b3..9b432c806885 100644 --- a/contrib/bmake/unit-tests/varmod-loop.exp +++ b/contrib/bmake/unit-tests/varmod-loop.exp @@ -1,16 +1,22 @@ Parsing line 78: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$ CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$" Comparing "$$$$ $$$$ $$$$" != "$$$$ $$$$ $$$$" Parsing line 83: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS} CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$" Comparing "$$ $$$$ $$$$" != "$$ $$$$ $$$$" Parsing line 108: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) :varname-overwriting-target: :x1y x2y x3y: :: mod-loop-dollar:1: mod-loop-dollar:${word}$: mod-loop-dollar:$3$: mod-loop-dollar:$${word}$$: mod-loop-dollar:$$5$$: mod-loop-dollar:$$${word}$$$: +: t=$(( ${t:-0} + 1 )) +: dollar=end +: backslash=\ end +: dollar=$ at=@ backslash=\ end +: dollar=$$ at=@@ backslash=\\ end +: dollar=$$ at=@@ backslash=\\ end exit status 0 diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk index 82046ff95d79..d3cc0228efd9 100644 --- a/contrib/bmake/unit-tests/varmod-loop.mk +++ b/contrib/bmake/unit-tests/varmod-loop.mk @@ -1,189 +1,234 @@ -# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.21 2022/08/23 21:13:46 rillig Exp $ # # Tests for the :@var@...${var}...@ variable modifier. # Force the test results to be independent of the default value of this # setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake # distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes all: varname-overwriting-target all: mod-loop-dollar varname-overwriting-target: # Even "@" works as a variable name since the variable is installed # in the "current" scope, which in this case is the one from the # target. Because of this, after the loop has finished, '$@' is # undefined. This is something that make doesn't expect, this may # even trigger an assertion failure somewhere. @echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@: # Demonstrate that it is possible to generate dollar signs using the # :@ modifier. # # These are edge cases that could have resulted in a parse error as well # since the $@ at the end could have been interpreted as a variable, which # would mean a missing closing @ delimiter. mod-loop-dollar: @echo $@:${:U1:@word@${word}$@:Q}: @echo $@:${:U2:@word@$${word}$$@:Q}: @echo $@:${:U3:@word@$$${word}$$$@:Q}: @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: # It may happen that there are nested :@ modifiers that use the same name for # for the loop variable. These modifiers influence each other. # # As of 2020-10-18, the :@ modifier is implemented by actually setting a # variable in the scope of the expression and deleting it again after the # loop. This is different from the .for loops, which substitute the variable # expression with ${:Uvalue}, leading to different unwanted side effects. # # To make the behavior more predictable, the :@ modifier should restore the # loop variable to the value it had before the loop. This would result in # the string "1a b c1 2a b c2 3a b c3", making the two loops independent. .if ${:U1 2 3:@i@$i${:Ua b c:@i@$i@}${i:Uu}@} != "1a b cu 2a b cu 3a b cu" . error .endif # During the loop, the variable is actually defined and nonempty. # If the loop were implemented in the same way as the .for loop, the variable # would be neither defined nor nonempty since all expressions of the form # ${var} would have been replaced with ${:Uword} before evaluating them. .if defined(var) . error .endif .if ${:Uword:@var@${defined(var):?def:undef} ${empty(var):?empty:nonempty}@} \ != "def nonempty" . error .endif .if defined(var) . error .endif # Assignment using the ':=' operator, combined with the :@var@ modifier # 8_DOLLARS= $$$$$$$$ # This string literal is written with 8 dollars, and this is saved as the # variable value. But as soon as this value is evaluated, it goes through # Var_Subst, which replaces each '$$' with a single '$'. This could be # prevented by VARE_EVAL_KEEP_DOLLAR, but that flag is usually removed # before expanding subexpressions. See ApplyModifier_Loop and # ParseModifierPart for examples. # .MAKEFLAGS: -dcp USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$ .if ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$" . error .endif # SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS} # The ':=' assignment operator evaluates the variable value using the mode # VARE_KEEP_DOLLAR_UNDEF, which means that some dollar signs are preserved, # but not all. The dollar signs in the top-level expression and in the # indirect ${8_DOLLARS} are preserved. # # The variable modifier :@var@ does not preserve the dollar signs though, no # matter in which context it is evaluated. What happens in detail is: # First, the modifier part "${8_DOLLARS}" is parsed without expanding it. # Next, each word of the value is expanded on its own, and at this moment # in ApplyModifier_Loop, the flag keepDollar is not passed down to # ModifyWords, resulting in "$$$$" for the first word of USE_8_DOLLARS. # # The remaining words of USE_8_DOLLARS are not affected by any variable # modifier and are thus expanded with the flag keepDollar in action. # The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value # "$$$$ $$$$$$$$ $$$$$$$$". # # The variable expression in the condition then expands this raw stored value # once, resulting in "$$ $$$$ $$$$". The effects from VARE_KEEP_DOLLAR no # longer take place since they had only been active during the evaluation of # the variable assignment. .if ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$" . error .endif .MAKEFLAGS: -d0 # After looping over the words of the expression, the loop variable gets # undefined. The modifier ':@' uses an ordinary global variable for this, # which is different from the '.for' loop, which replaces ${var} with # ${:Uvalue} in the body of the loop. This choice of implementation detail # can be used for a nasty side effect. The expression ${:U:@VAR@@} evaluates # to an empty string, plus it undefines the variable 'VAR'. This is the only # possibility to undefine a global variable during evaluation. GLOBAL= before-global RESULT:= ${:U${GLOBAL} ${:U:@GLOBAL@@} ${GLOBAL:Uundefined}} .if ${RESULT} != "before-global undefined" . error .endif # The above side effect of undefining a variable from a certain scope can be # further combined with the otherwise undocumented implementation detail that # the argument of an '.if' directive is evaluated in cmdline scope. Putting # these together makes it possible to undefine variables from the cmdline # scope, something that is not possible in a straight-forward way. .MAKEFLAGS: CMDLINE=cmdline .if ${:U${CMDLINE}${:U:@CMDLINE@@}} != "cmdline" . error .endif # Now the cmdline variable got undefined. .if ${CMDLINE} != "cmdline" . error .endif # At this point, it still looks as if the cmdline variable were defined, # since the value of CMDLINE is still "cmdline". That impression is only # superficial though, the cmdline variable is actually deleted. To # demonstrate this, it is now possible to override its value using a global # variable, something that was not possible before: CMDLINE= global .if ${CMDLINE} != "global" . error .endif # Now undefine that global variable again, to get back to the original value. .undef CMDLINE .if ${CMDLINE} != "cmdline" . error .endif # What actually happened is that when CMDLINE was set by the '.MAKEFLAGS' # target in the cmdline scope, that same variable was exported to the # environment, see Var_SetWithFlags. .unexport CMDLINE .if ${CMDLINE} != "cmdline" . error .endif # The above '.unexport' has no effect since UnexportVar requires a global # variable of the same name to be defined, otherwise nothing is unexported. CMDLINE= global .unexport CMDLINE .undef CMDLINE .if ${CMDLINE} != "cmdline" . error .endif # This still didn't work since there must not only be a global variable, the # variable must be marked as exported as well, which it wasn't before. CMDLINE= global .export CMDLINE .unexport CMDLINE .undef CMDLINE .if ${CMDLINE:Uundefined} != "undefined" . error .endif # Finally the variable 'CMDLINE' from the cmdline scope is gone, and all its # traces from the environment are gone as well. To do that, a global variable # had to be defined and exported, something that is far from obvious. To # recap, here is the essence of the above story: .MAKEFLAGS: CMDLINE=cmdline # have a cmdline + environment variable .if ${:U:@CMDLINE@@}} # undefine cmdline, keep environment .endif CMDLINE= global # needed for deleting the environment .export CMDLINE # needed for deleting the environment .unexport CMDLINE # delete the environment .undef CMDLINE # delete the global helper variable .if ${CMDLINE:Uundefined} != "undefined" . error # 'CMDLINE' is gone now from all scopes .endif + +# In the loop body text of the ':@' modifier, a literal '$' is written as '$$', +# not '\$'. In the following example, each '$$' turns into a single '$', +# except for '$i', which is replaced with the then-current value '1' of the +# iteration variable. +# +# XXX: was broken in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 +# from 2022-08-23; see parse-var.mk, keyword 'BRACE_GROUP'. +all: varmod-loop-literal-dollar +varmod-loop-literal-dollar: .PHONY + : ${:U1:@i@ t=$$(( $${t:-0} + $i ))@} + + +# When parsing the loop body, each '\$', '\@' and '\\' is unescaped to '$', +# '@' and '\'; all other backslashes are retained. +# +# In practice, the '$' is not escaped as '\$', as there is a second round of +# unescaping '$$' to '$' later when the loop body is expanded after setting the +# iteration variable. +# +# After the iteration variable has been set, the loop body is expanded with +# this unescaping, regardless of whether .MAKE.SAVE_DOLLARS is set or not: +# $$ a literal '$' +# $x, ${var}, $(var) a nested expression +# any other character itself +all: escape-modifier +escape-modifier: .PHONY + # In the first round, '\$ ' is unescaped to '$ ', and since the + # variable named ' ' is not defined, the expression '$ ' expands to an + # empty string. + # expect: : dollar=end + : ${:U1:@i@ dollar=\$ end@} + + # Like in other modifiers, '\ ' is preserved, since ' ' is not one of + # the characters that _must_ be escaped. + # expect: : backslash=\ end + : ${:U1:@i@ backslash=\ end@} + + # expect: : dollar=$ at=@ backslash=\ end + : ${:U1:@i@ dollar=\$\$ at=\@ backslash=\\ end@} + # expect: : dollar=$$ at=@@ backslash=\\ end + : ${:U1:@i@ dollar=\$\$\$\$ at=\@\@ backslash=\\\\ end@} + # expect: : dollar=$$ at=@@ backslash=\\ end + : ${:U1:@i@ dollar=$$$$ at=\@\@ backslash=\\\\ end@} + all: .PHONY diff --git a/contrib/bmake/unit-tests/varmod-match.mk b/contrib/bmake/unit-tests/varmod-match.mk index 8435c2a614e0..87928267d794 100644 --- a/contrib/bmake/unit-tests/varmod-match.mk +++ b/contrib/bmake/unit-tests/varmod-match.mk @@ -1,282 +1,292 @@ -# $NetBSD: varmod-match.mk,v 1.11 2022/06/11 09:15:49 rillig Exp $ +# $NetBSD: varmod-match.mk,v 1.12 2022/08/24 21:03:57 rillig Exp $ # # Tests for the :M variable modifier, which filters words that match the # given pattern. # # See ApplyModifier_Match and ModifyWord_Match for the implementation. .MAKEFLAGS: -dc NUMBERS= One Two Three Four five six seven # Only keep words that start with an uppercase letter. .if ${NUMBERS:M[A-Z]*} != "One Two Three Four" . error .endif # Only keep words that start with a character other than an uppercase letter. .if ${NUMBERS:M[^A-Z]*} != "five six seven" . error .endif # Only keep words that don't start with s and at the same time end with # either of [ex]. # # This test case ensures that the negation from the first character class # does not propagate to the second character class. .if ${NUMBERS:M[^s]*[ex]} != "One Three five" . error .endif # Before 2020-06-13, this expression called Str_Match 601,080,390 times. # Since 2020-06-13, this expression calls Str_Match 1 time. .if ${:U****************:M****************b} .endif # As of 2022-06-11, this expression calls Str_Match 5,242,223 times. # Adding another '*?' to the pattern calls Str_Match 41,261,143 times. .if ${:U..................................................b:M*?*?*?*?*?a} .endif # To match a dollar sign in a word, double it. # # This is different from the :S and :C variable modifiers, where a '$' # has to be escaped as '\$'. .if ${:Ua \$ sign:M*$$*} != "\$" . error .endif # In the :M modifier, '\$' does not escape a dollar. Instead it is # interpreted as a backslash followed by whatever expression the # '$' starts. # # This differs from the :S, :C and several other variable modifiers. ${:U*}= asterisk .if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk" . error .endif # TODO: ${VAR:M(((}}}} # TODO: ${VAR:M{{{)))} # TODO: ${VAR:M${UNBALANCED}} # TODO: ${VAR:M${:U(((\}\}\}}} .MAKEFLAGS: -d0 # Special characters: # * matches 0 or more arbitrary characters # ? matches a single arbitrary character # \ starts an escape sequence, only outside ranges # [ starts a set for matching a single character # ] ends a set for matching a single character # - in a set, forms a range of characters # ^ as the first character in a set, negates the set # ( during parsing of the pattern, starts a nesting level # ) during parsing of the pattern, ends a nesting level # { during parsing of the pattern, starts a nesting level # } during parsing of the pattern, ends a nesting level # : during parsing of the pattern, finishes the pattern # $ during parsing of the pattern, starts a nested expression # # in a line except a shell command, starts a comment # # Pattern parts: # * matches 0 or more arbitrary characters # ? matches exactly 1 arbitrary character # \x matches exactly the character 'x' # [...] matches exactly 1 character from the set # [^...] matches exactly 1 character outside the set # [a-z] matches exactly 1 character from the range 'a' to 'z' # # [] matches never .if ${ ab a[]b a[b a b :L:M[]} != "" . error .endif # a[]b matches never .if ${ ab a[]b a[b a b [ ] :L:Ma[]b} != "" . error .endif # [^] matches exactly 1 arbitrary character .if ${ ab a[]b a[b a b [ ] :L:M[^]} != "a b [ ]" . error .endif # a[^]b matches 'a', then exactly 1 arbitrary character, then 'b' .if ${ ab a[]b a[b a b :L:Ma[^]b} != "a[b" . error .endif # [Nn0] matches exactly 1 character from the set 'N', 'n', '0' .if ${ a b N n 0 Nn0 [ ] :L:M[Nn0]} != "N n 0" . error .endif # [a-c] matches exactly 1 character from the range 'a' to 'c' .if ${ A B C a b c d [a-c] [a] :L:M[a-c]} != "a b c" . error .endif # [c-a] matches the same as [a-c] .if ${ A B C a b c d [a-c] [a] :L:M[c-a]} != "a b c" . error .endif # [^a-c67] # matches a single character, except for 'a', 'b', 'c', '6' or # '7' .if ${ A B C a b c d 5 6 7 8 [a-c] [a] :L:M[^a-c67]} != "A B C d 5 8" . error .endif # [\] matches a single backslash WORDS= a\b a[\]b ab .if ${WORDS:Ma[\]b} != "a\\b" . error .endif # : terminates the pattern .if ${ A * :L:M:} != "" . error .endif # \: matches a colon .if ${ ${:U\: \:\:} :L:M\:} != ":" . error .endif # ${:U\:} matches a colon .if ${ ${:U\:} ${:U\:\:} :L:M${:U\:}} != ":" . error .endif # [:] matches never since the ':' starts the next modifier # expect+2: Unknown modifier "]" # expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":") .if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":" . error .else . error .endif # [\] matches exactly a backslash; no escaping takes place in # character ranges # Without the 'a' in the below words, the backslash would end a word and thus # influence how the string is split into words. WORDS= 1\a 2\\a .if ${WORDS:M?[\]a} != "1\\a" . error .endif # [[-]] May look like it would match a single '[', '\' or ']', but # the inner ']' has two roles: it is the upper bound of the # character range as well as the closing character of the # character list. The outer ']' is just a regular character. WORDS= [ ] [] \] ]] .if ${WORDS:M[[-]]} != "[] \\] ]]" . error .endif # [b[-]a] # Same as for '[[-]]': the character list stops at the first # ']', and the 'a]' is treated as a literal string. WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba] .if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]" . error .endif # [-] Matches a single '-' since the '-' only becomes part of a # character range if it is preceded and followed by another # character. WORDS= - -] .if ${WORDS:M[-]} != "-" . error .endif # [ Incomplete empty character list, never matches. WORDS= a a[ .if ${WORDS:Ma[} != "" . error .endif # [^ Incomplete negated empty character list, matches any single # character. WORDS= a a[ aX .if ${WORDS:Ma[^} != "a[ aX" . error .endif # [-x1-3 Incomplete character list, matches those elements that can be # parsed without lookahead. WORDS= - + x xx 0 1 2 3 4 [x1-3 .if ${WORDS:M[-x1-3} != "- x 1 2 3" . error .endif # [^-x1-3 # Incomplete negated character list, matches any character # except those elements that can be parsed without lookahead. WORDS= - + x xx 0 1 2 3 4 [x1-3 .if ${WORDS:M[^-x1-3} != "+ 0 4" . error .endif # [\ Incomplete character list containing a single '\'. # # A word can only end with a backslash if the preceding # character is a backslash as well; in all other cases the final # backslash would escape the following space, making the space # part of the word. Only the very last word of a string can be # '\', as there is no following space that could be escaped. WORDS= \\ \a ${:Ux\\} .if ${WORDS:M?[\]} != "\\\\ x\\" . error .endif # [x- Incomplete character list containing an incomplete character # range, matches only the 'x'. WORDS= [x- x x- y .if ${WORDS:M[x-} != "x" . error .endif # [^x- Incomplete negated character list containing an incomplete # character range; matches each word that does not have an 'x' # at the position of the character list. # # XXX: Even matches strings that are longer than a single # character. WORDS= [x- x x- y yyyyy .if ${WORDS:M[^x-} != "[x- y yyyyy" . error .endif # The modifier ':tW' prevents splitting at whitespace. Even leading and # trailing whitespace is preserved. .if ${ plain string :L:tW:M*} != " plain string " . error .endif # Without the modifier ':tW', the string is split into words. All whitespace # around and between the words is normalized to a single space. .if ${ plain string :L:M*} != "plain string" . error .endif # The pattern can come from a variable expression. For single-letter # variables, either the short form or the long form can be used, just as # everywhere else. PRIMES= 2 3 5 7 11 n= 2 .if ${PRIMES:M$n} != "2" . error .endif .if ${PRIMES:M${n}} != "2" . error .endif .if ${PRIMES:M${:U2}} != "2" . error .endif + + +# Before var.c 1.1031 from 2022-08-24, the following expressions caused an +# out-of-bounds read beyond the indirect ':M' modifiers. +.if ${:U:${:UM\\}} # The ':M' pattern need not be unescaped, the +. error # resulting pattern is '\', it never matches +.endif # anything. +.if ${:U:${:UM\\\:\\}} # The ':M' pattern must be unescaped, the +. error # resulting pattern is ':\', it never matches +.endif # anything. diff --git a/contrib/bmake/unit-tests/varmod-order-numeric.mk b/contrib/bmake/unit-tests/varmod-order-numeric.mk index 542894c53942..62212bd265ad 100644 --- a/contrib/bmake/unit-tests/varmod-order-numeric.mk +++ b/contrib/bmake/unit-tests/varmod-order-numeric.mk @@ -1,53 +1,59 @@ -# $NetBSD: varmod-order-numeric.mk,v 1.7 2022/02/09 21:09:24 rillig Exp $ +# $NetBSD: varmod-order-numeric.mk,v 1.8 2022/09/27 19:18:45 rillig Exp $ # # Tests for the variable modifiers ':On', which returns the words, sorted in # ascending numeric order, and for ':Orn' and ':Onr', which additionally # reverse the order. # # The variable modifiers ':On', ':Onr' and ':Orn' were added in var.c 1.939 # from 2021-07-30. # This list contains only 32-bit numbers since the make code needs to conform # to C90, which does not necessarily provide integer types larger than 32 bit. # Make uses 'long long' for C99 or later, and 'long' for older C versions. # # To get 53-bit integers even in C90, it would be possible to switch to # 'double' instead, but that would allow floating-point numbers as well, which # is out of scope for this variable modifier. NUMBERS= 3 5 7 1 42 -42 5K -3m 1M 1k -2G .if ${NUMBERS:On} != "-2G -3m -42 1 3 5 7 42 1k 5K 1M" . error ${NUMBERS:On} .endif .if ${NUMBERS:Orn} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" . error ${NUMBERS:Orn} .endif # Both ':Onr' and ':Orn' have the same effect. .if ${NUMBERS:Onr} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" . error ${NUMBERS:Onr} .endif # Duplicate numbers are preserved in the output. In this case the # equal-valued numbers are spelled the same, so they are indistinguishable in # the output. DUPLICATES= 3 1 2 2 1 1 # subsequence of https://oeis.org/A034002 .if ${DUPLICATES:On} != "1 1 1 2 2 3" . error ${DUPLICATES:On} .endif # If there are several numbers that have the same integer value, they are # returned in unspecified order. SAME_VALUE:= ${:U 79 80 0x0050 81 :On} .if ${SAME_VALUE} != "79 80 0x0050 81" && ${SAME_VALUE} != "79 0x0050 80 81" . error ${SAME_VALUE} .endif # Hexadecimal and octal numbers can be sorted as well. MIXED_BASE= 0 010 0x7 9 .if ${MIXED_BASE:On} != "0 0x7 010 9" . error ${MIXED_BASE:On} .endif +# The measurement units for suffixes are k, M, G, but not T. +# The string '3T' evaluates to 3, the string 'x' evaluates as '0'. +.if ${4 3T 2M x:L:On} != "x 3T 4 2M" +. error +.endif + all: diff --git a/contrib/bmake/unit-tests/varmod-to-lower.mk b/contrib/bmake/unit-tests/varmod-to-lower.mk index 19d3406054b7..44116fd3eee2 100644 --- a/contrib/bmake/unit-tests/varmod-to-lower.mk +++ b/contrib/bmake/unit-tests/varmod-to-lower.mk @@ -1,21 +1,26 @@ -# $NetBSD: varmod-to-lower.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: varmod-to-lower.mk,v 1.6 2022/11/29 23:54:55 rillig Exp $ # -# Tests for the :tl variable modifier, which returns the words in the -# variable value, converted to lowercase. +# Tests for the :tl variable modifier, which converts the expression value +# to lowercase. # # TODO: What about non-ASCII characters? ISO-8859-1, UTF-8? .if ${:UUPPER:tl} != "upper" . error .endif .if ${:Ulower:tl} != "lower" . error .endif .if ${:UMixeD case.:tl} != "mixed case." . error .endif -all: - @:; +# The ':tl' modifier works on the whole string, without splitting it into +# words. +.if ${:Umultiple spaces:tl} != "multiple spaces" +. error +.endif + +all: .PHONY diff --git a/contrib/bmake/unit-tests/varmod-undefined.mk b/contrib/bmake/unit-tests/varmod-undefined.mk index e06fc73244ab..9fd41c7fdb60 100644 --- a/contrib/bmake/unit-tests/varmod-undefined.mk +++ b/contrib/bmake/unit-tests/varmod-undefined.mk @@ -1,68 +1,68 @@ -# $NetBSD: varmod-undefined.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: varmod-undefined.mk,v 1.8 2022/08/06 21:26:05 rillig Exp $ # # Tests for the :U variable modifier, which returns the given string # if the variable is undefined. # # See also: # directive-for.mk # varmod-defined.mk # The pattern ${:Uword} is heavily used when expanding .for loops. # # This is how an expanded .for loop looks like. # .for word in one # . if ${word} != one .if ${:Uone} != one # . error ${word} . error ${:Uone} # . endif .endif # .endfor # The variable expressions in the text of the :U modifier may be arbitrarily # nested. .if ${:U${:Unested}${${${:Udeeply}}}} != nested . error .endif # The nested variable expressions may contain braces, and these braces don't # need to match pairwise. In the following example, the :S modifier uses '{' # as delimiter, which confuses both editors and humans because the opening -# and # closing braces don't match anymore. It's syntactically valid though. +# and closing braces don't match anymore. It's syntactically valid though. # For more similar examples, see varmod-subst.mk, mod-subst-delimiter. .if ${:U${:Uvalue:S{a{X{}} != vXlue . error .endif # The escaping rules for the :U modifier (left-hand side) and condition # string literals (right-hand side) are completely different. # # In the :U modifier, the backslash only escapes very few characters, all # other backslashes are retained. # # In condition string literals, the backslash always escapes the following # character, no matter whether it would be necessary or not. # # In both contexts, \n is an escaped letter n, not a newline; that's what # the .newline variable is for. # # Whitespace at the edges is preserved, on both sides of the comparison. # .if ${:U \: \} \$ \\ \a \b \n } != " : } \$ \\ \\a \\b \\n " . error .endif # Even after the :U modifier has been applied, the expression still remembers # that it originated from an undefined variable, and the :U modifier can # be used to overwrite the value of the expression. # .if ${UNDEF:Uvalue:S,a,X,} != "vXlue" . error .elif ${UNDEF:Uvalue:S,a,X,:Uwas undefined} != "was undefined" . error .endif all: @:; diff --git a/contrib/bmake/unit-tests/varmod.exp b/contrib/bmake/unit-tests/varmod.exp index e36c4ded9b47..3e588dc4d83f 100644 --- a/contrib/bmake/unit-tests/varmod.exp +++ b/contrib/bmake/unit-tests/varmod.exp @@ -1,8 +1,8 @@ -make: "varmod.mk" line 42: To escape a dollar, use \$, not $$, at "$$:L} != """ -make: "varmod.mk" line 42: Invalid variable name ':', at "$:L} != """ -make: "varmod.mk" line 47: Dollar followed by nothing -make: "varmod.mk" line 56: Missing delimiter ':' after modifier "P" -make: "varmod.mk" line 57: Missing argument for ".error" +make: "varmod.mk" line 96: To escape a dollar, use \$, not $$, at "$$:L} != """ +make: "varmod.mk" line 96: Invalid variable name ':', at "$:L} != """ +make: "varmod.mk" line 101: Dollar followed by nothing +make: "varmod.mk" line 110: Missing delimiter ':' after modifier "P" +make: "varmod.mk" line 111: Missing argument for ".error" make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod.mk b/contrib/bmake/unit-tests/varmod.mk index 21ddf9103251..17bc1e6e3ae3 100644 --- a/contrib/bmake/unit-tests/varmod.mk +++ b/contrib/bmake/unit-tests/varmod.mk @@ -1,60 +1,114 @@ -# $NetBSD: varmod.mk,v 1.5 2020/12/19 22:33:11 rillig Exp $ +# $NetBSD: varmod.mk,v 1.7 2022/08/24 21:38:06 rillig Exp $ # # Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback. +# +# See also: +# varparse-errors.mk + +# As of 2022-08-06, the possible behaviors during parsing are: +# +# * `strict`: the parsing style used by most modifiers: +# * either uses `ParseModifierPart` or parses the modifier literal +# * other modifiers may follow, separated by a ':' +# +# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means +# that no further modifiers are parsed in that expression. +# +# * `no-colon`: after parsing this modifier, the following modifier +# does not need to be separated by a colon. +# Omitting this colon is bad style. +# +# * `individual`: parsing this modifier does not follow the common +# pattern of calling `ParseModifierPart`. +# +# The SysV column says whether a parse error in the modifier falls back +# trying the `:from=to` System V modifier. +# +# | **Operator** | **Behavior** | **Remarks** | **SysV** | +# |--------------|--------------|--------------------|----------| +# | `!` | no-colon | | no | +# | `:=` | greedy | | yes | +# | `?:` | greedy | | no | +# | `@` | no-colon | | no | +# | `C` | no-colon | | no | +# | `D` | individual | custom parser | N/A | +# | `E` | strict | | yes | +# | `H` | strict | | yes | +# | `L` | no-colon | | N/A | +# | `M` | individual | custom parser | N/A | +# | `N` | individual | custom parser | N/A | +# | `O` | strict | only literal value | no | +# | `P` | no-colon | | N/A | +# | `Q` | strict | | yes | +# | `R` | strict | | yes | +# | `S` | no-colon | | N/A | +# | `T` | strict | | N/A | +# | `U` | individual | custom parser | N/A | +# | `[` | strict | | no | +# | `_` | individual | strcspn | yes | +# | `gmtime` | strict | only literal value | yes | +# | `hash` | strict | | N/A | +# | `localtime` | strict | only literal value | yes | +# | `q` | strict | | yes | +# | `range` | strict | | N/A | +# | `sh` | strict | | N/A | +# | `t` | strict | | no | +# | `u` | strict | | yes | +# | `from=to` | greedy | SysV, fallback | N/A | DOLLAR1= $$ DOLLAR2= ${:U\$} # To get a single '$' sign in the value of a variable expression, it has to # be written as '$$' in a literal variable value. # # See Var_Parse, where it calls Var_Subst. .if ${DOLLAR1} != "\$" . error .endif # Another way to get a single '$' sign is to use the :U modifier. In the # argument of that modifier, a '$' is escaped using the backslash instead. # # See Var_Parse, where it calls Var_Subst. .if ${DOLLAR2} != "\$" . error .endif # It is also possible to use the :U modifier directly in the expression. # # See Var_Parse, where it calls Var_Subst. .if ${:U\$} != "\$" . error .endif # XXX: As of 2020-09-13, it is not possible to use '$$' in a variable name # to mean a single '$'. This contradicts the manual page, which says that # '$' can be escaped as '$$'. .if ${$$:L} != "" . error .endif # In lint mode, make prints helpful error messages. # For compatibility, make does not print these error messages in normal mode. # Should it? .MAKEFLAGS: -dL .if ${$$:L} != "" . error .endif # A '$' followed by nothing is an error as well. .if ${:Uword:@word@${word}$@} != "word" . error .endif # The variable modifier :P does not fall back to the SysV modifier. # Therefore the modifier :P=RE generates a parse error. # XXX: The .error should not be reached since the variable expression is # malformed, and this error should be propagated up to Cond_EvalLine. VAR= STOP .if ${VAR:P=RE} != "STORE" . error .endif all: # nothing diff --git a/contrib/bmake/unit-tests/varname-dot-newline.exp b/contrib/bmake/unit-tests/varname-dot-newline.exp index 13943539bf3b..684fb5799752 100644 --- a/contrib/bmake/unit-tests/varname-dot-newline.exp +++ b/contrib/bmake/unit-tests/varname-dot-newline.exp @@ -1,4 +1,5 @@ -make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that. first second +backslash newline: <\ +> exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-newline.mk b/contrib/bmake/unit-tests/varname-dot-newline.mk index 0565d244f298..1940dc2a990d 100644 --- a/contrib/bmake/unit-tests/varname-dot-newline.mk +++ b/contrib/bmake/unit-tests/varname-dot-newline.mk @@ -1,23 +1,31 @@ -# $NetBSD: varname-dot-newline.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: varname-dot-newline.mk,v 1.6 2023/01/26 20:48:18 sjg Exp $ # -# Tests for the special .newline variable. +# Tests for the special .newline variable, which contains a single newline +# character (U+000A). + + +# https://austingroupbugs.net/view.php?id=1549 proposes: +# > After all macro expansion is complete, when an escaped is +# > found in a command line in a makefile, the command line that is executed +# > shall contain the , the , and the next line, except +# > that the first character of the next line shall not be included if it is +# > a . # -# Contrary to the special variable named "" that is used in expressions like -# ${:Usome-value}, the variable ".newline" is not protected against -# modification. Nobody exploits that though. +# The above quote assumes that each resulting character has a "next +# line", but that's not how the .newline variable works. +BACKSLASH_NEWLINE:= \${.newline} + + +# Check that .newline is read-only NEWLINE:= ${.newline} .newline= overwritten -.if ${.newline} == ${NEWLINE} -. info The .newline variable cannot be overwritten. Good. -.else -. info The .newline variable can be overwritten. Just don't do that. +.if ${.newline} != ${NEWLINE} +. error The .newline variable can be overwritten. It should be read-only. .endif -# Restore the original value. -.newline= ${NEWLINE} - all: @echo 'first${.newline}second' + @echo 'backslash newline: <${BACKSLASH_NEWLINE}>' diff --git a/contrib/bmake/unit-tests/varparse-errors.exp b/contrib/bmake/unit-tests/varparse-errors.exp index e47127447cda..2c3568e468ca 100644 --- a/contrib/bmake/unit-tests/varparse-errors.exp +++ b/contrib/bmake/unit-tests/varparse-errors.exp @@ -1,11 +1,25 @@ make: "varparse-errors.mk" line 38: Unknown modifier "Z" make: "varparse-errors.mk" line 46: Unknown modifier "Z" make: Bad modifier ":OX" for variable "" make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX" make: Bad modifier ":OX" for variable "" make: Bad modifier ":OX" for variable "" make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX" make: Bad modifier ":OX" for variable "" +make: Unclosed variable expression, expecting '}' for modifier "Q" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "sh" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "tA" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "tsX" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "ts" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "ts\040" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "u" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "H" of variable "" with value "." +make: Unclosed variable expression, expecting '}' for modifier "[1]" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "hash" of variable "" with value "b2af338b" +make: Unclosed variable expression, expecting '}' for modifier "range" of variable "" with value "1" +make: Unclosed variable expression, expecting '}' for modifier "_" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "gmtime" of variable "" with value "" +make: Unclosed variable expression, expecting '}' for modifier "localtime" of variable "" with value "" make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varparse-errors.mk b/contrib/bmake/unit-tests/varparse-errors.mk index 51a403fa898f..9174d264db0f 100644 --- a/contrib/bmake/unit-tests/varparse-errors.mk +++ b/contrib/bmake/unit-tests/varparse-errors.mk @@ -1,71 +1,88 @@ -# $NetBSD: varparse-errors.mk,v 1.5 2022/01/24 22:59:49 rillig Exp $ +# $NetBSD: varparse-errors.mk,v 1.7 2022/08/24 22:09:41 rillig Exp $ # Tests for parsing and evaluating all kinds of variable expressions. # # This is the basis for redesigning the error handling in Var_Parse and # Var_Subst, collecting typical and not so typical use cases. # # See also: # VarParseResult # Var_Parse # Var_Subst PLAIN= plain value LITERAL_DOLLAR= To get a dollar, double $$ it. INDIRECT= An ${:Uindirect} value. REF_UNDEF= A reference to an ${UNDEF}undefined variable. ERR_UNCLOSED= An ${UNCLOSED variable expression. ERR_BAD_MOD= An ${:Uindirect:Z} expression with an unknown modifier. ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}. # In a conditional, a variable expression that is not enclosed in quotes is # expanded using the mode VARE_UNDEFERR. # The variable itself must be defined. # It may refer to undefined variables though. .if ${REF_UNDEF} != "A reference to an undefined variable." . error .endif # As of 2020-12-01, errors in the variable name are silently ignored. # Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result # in an error message and a non-zero exit status. VAR.${:U:Z}= unknown modifier in the variable name .if ${VAR.} != "unknown modifier in the variable name" . error .endif # As of 2020-12-01, errors in the variable name are silently ignored. # Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result # in an error message and a non-zero exit status. VAR.${:U:Z}post= unknown modifier with text in the variable name .if ${VAR.post} != "unknown modifier with text in the variable name" . error .endif # Demonstrate an edge case in which the 'static' for 'errorReported' in # Var_Subst actually makes a difference, preventing "a plethora of messages". # Given that this is an edge case and the error message is wrong and thus # misleading anyway, that piece of code is probably not necessary. The wrong # condition was added in var.c 1.185 from 2014-05-19. # # To trigger this difference, the variable assignment must use the assignment # operator ':=' to make VarEvalMode_ShouldKeepUndef return true. There must # be 2 expressions that create a parse error, which in this case is ':OX'. # These expressions must be nested in some way. The below expressions are # minimal, that is, removing any part of it destroys the effect. # # Without the 'static', there would be one more message like this: # Undefined variable "${:U:OX" # #.MAKEFLAGS: -dv IND= ${:OX} _:= ${:U:OX:U${IND}} ${:U:OX:U${IND}} #.MAKEFLAGS: -d0 -all: + +# Before var.c 1.032 from 2022-08-24, make complained about 'Unknown modifier' +# or 'Bad modifier' when in fact the modifier was entirely correct, it was +# just not delimited by either ':' or '}' but instead by '\0'. +UNCLOSED:= ${:U:Q +UNCLOSED:= ${:U:sh +UNCLOSED:= ${:U:tA +UNCLOSED:= ${:U:tsX +UNCLOSED:= ${:U:ts +UNCLOSED:= ${:U:ts\040 +UNCLOSED:= ${:U:u +UNCLOSED:= ${:U:H +UNCLOSED:= ${:U:[1] +UNCLOSED:= ${:U:hash +UNCLOSED:= ${:U:range +UNCLOSED:= ${:U:_ +UNCLOSED:= ${:U:gmtime +UNCLOSED:= ${:U:localtime diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c index b5a063ed20a2..9657f4b7fa18 100644 --- a/contrib/bmake/var.c +++ b/contrib/bmake/var.c @@ -1,4800 +1,4838 @@ -/* $NetBSD: var.c,v 1.1025 2022/06/14 19:57:56 rillig Exp $ */ +/* $NetBSD: var.c,v 1.1039 2023/01/26 20:48:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Handling of variables and the expressions formed from them. * * Variables are set using lines of the form VAR=value. Both the variable * name and the value can contain references to other variables, by using * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}. * * Interface: * Var_Init Initialize this module. * * Var_End Clean up the module. * * Var_Set * Var_SetExpand * Set the value of the variable, creating it if * necessary. * * Var_Append * Var_AppendExpand * Append more characters to the variable, creating it if * necessary. A space is placed between the old value and * the new one. * * Var_Exists * Var_ExistsExpand * See if a variable exists. * * Var_Value Return the unexpanded value of a variable, or NULL if * the variable is undefined. * * Var_Subst Substitute all variable expressions in a string. * * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. * * Var_Delete * Delete a variable. * * Var_ReexportVars * Export some or even all variables to the environment * of this process and its child processes. * * Var_Export Export the variable to the environment of this process * and its child processes. * * Var_UnExport Don't export the variable anymore. * * Debugging: * Var_Stats Print out hashing statistics if in -dh mode. * * Var_Dump Print out all variables defined in the given scope. * * XXX: There's a lot of almost duplicate code in these functions that only * differs in subtle details that are not mentioned in the manual page. */ #include #include #ifndef NO_REGEX #include #endif #include "make.h" #include #ifdef HAVE_INTTYPES_H #include #elif defined(HAVE_STDINT_H) #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #include "dir.h" #include "job.h" #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.1025 2022/06/14 19:57:56 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.1039 2023/01/26 20:48:17 sjg Exp $"); /* * Variables are defined using one of the VAR=value assignments. Their * value can be queried by expressions such as $V, ${VAR}, or with modifiers * such as ${VAR:S,from,to,g:Q}. * * There are 3 kinds of variables: scope variables, environment variables, * undefined variables. * * Scope variables are stored in a GNode.scope. The only way to undefine * a scope variable is using the .undef directive. In particular, it must * not be possible to undefine a variable during the evaluation of an * expression, or Var.name might point nowhere. (There is another, * unintended way to undefine a scope variable, see varmod-loop-delete.mk.) * * Environment variables are short-lived. They are returned by VarFind, and * after using them, they must be freed using VarFreeShortLived. * * Undefined variables occur during evaluation of variable expressions such * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. */ typedef struct Var { /* * The name of the variable, once set, doesn't change anymore. * For scope variables, it aliases the corresponding HashEntry name. * For environment and undefined variables, it is allocated. */ FStr name; /* The unexpanded value of the variable. */ Buffer val; /* The variable came from the command line. */ bool fromCmd:1; /* * The variable is short-lived. * These variables are not registered in any GNode, therefore they * must be freed after use. */ bool shortLived:1; /* * The variable comes from the environment. * Appending to its value moves the variable to the global scope. */ bool fromEnvironment:1; /* * The variable value cannot be changed anymore, and the variable * cannot be deleted. Any attempts to do so are silently ignored, * they are logged with -dv though. + * Use .[NO]READONLY: to adjust. * * See VAR_SET_READONLY. */ bool readOnly:1; /* - * The variable's value is currently being used by Var_Parse or - * Var_Subst. This marker is used to avoid endless recursion. - */ + * The variable is currently being accessed by Var_Parse or Var_Subst. + * This temporary marker is used to avoid endless recursion. + */ bool inUse:1; /* * The variable is exported to the environment, to be used by child * processes. */ bool exported:1; /* * At the point where this variable was exported, it contained an * unresolved reference to another variable. Before any child * process is started, it needs to be exported again, in the hope * that the referenced variable can then be resolved. */ bool reexport:1; } Var; /* * Exporting variables is expensive and may leak memory, so skip it if we * can. * * To avoid this, it might be worth encapsulating the environment variables * in a separate data structure called EnvVars. */ typedef enum VarExportedMode { VAR_EXPORTED_NONE, VAR_EXPORTED_SOME, VAR_EXPORTED_ALL } VarExportedMode; typedef enum UnexportWhat { /* Unexport the variables given by name. */ UNEXPORT_NAMED, /* * Unexport all globals previously exported, but keep the environment * inherited from the parent. */ UNEXPORT_ALL, /* * Unexport all globals previously exported and clear the environment * inherited from the parent. */ UNEXPORT_ENV } UnexportWhat; /* Flags for pattern matching in the :S and :C modifiers */ typedef struct PatternFlags { bool subGlobal:1; /* 'g': replace as often as possible */ bool subOnce:1; /* '1': replace only once */ bool anchorStart:1; /* '^': match only at start of word */ bool anchorEnd:1; /* '$': match only at end of word */ } PatternFlags; /* SepBuf builds a string from words interleaved with separators. */ typedef struct SepBuf { Buffer buf; bool needSep; /* Usually ' ', but see the ':ts' modifier. */ char sep; } SepBuf; /* * This lets us tell if we have replaced the original environ * (which we cannot free). */ char **savedEnv = NULL; /* * Special return value for Var_Parse, indicating a parse error. It may be * caused by an undefined variable, a syntax error in a modifier or * something entirely different. */ char var_Error[] = ""; /* * Special return value for Var_Parse, indicating an undefined variable in * a case where VARE_UNDEFERR is not set. This undefined variable is * typically a dynamic variable such as ${.TARGET}, whose expansion needs to * be deferred until it is defined in an actual target. * * See VARE_EVAL_KEEP_UNDEF. */ static char varUndefined[] = ""; /* * Traditionally this make consumed $$ during := like any other expansion. * Other make's do not, and this make follows straight since 2016-01-09. * * This knob allows controlling the behavior: * false to consume $$ during := assignment. * true to preserve $$ during := assignment. */ #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" static bool save_dollars = false; /* * A scope collects variable names and their values. * * The main scope is SCOPE_GLOBAL, which contains the variables that are set * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and * contains some internal make variables. These internal variables can thus * be overridden, they can also be restored by undefining the overriding * variable. * * SCOPE_CMDLINE contains variables from the command line arguments. These * override variables from SCOPE_GLOBAL. * * There is no scope for environment variables, these are generated on-the-fly * whenever they are referenced. If there were such a scope, each change to * environment variables would have to be reflected in that scope, which may * be simpler or more complex than the current implementation. * * Each target has its own scope, containing the 7 target-local variables * .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in * this scope. */ GNode *SCOPE_CMDLINE; GNode *SCOPE_GLOBAL; GNode *SCOPE_INTERNAL; static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; static const char VarEvalMode_Name[][32] = { "parse-only", "eval", "eval-defined", "eval-keep-dollar", "eval-keep-undefined", "eval-keep-dollar-and-undefined", }; static Var * VarNew(FStr name, const char *value, bool shortLived, bool fromEnvironment, bool readOnly) { size_t value_len = strlen(value); Var *var = bmake_malloc(sizeof *var); var->name = name; Buf_InitSize(&var->val, value_len + 1); Buf_AddBytes(&var->val, value, value_len); var->fromCmd = false; var->shortLived = shortLived; var->fromEnvironment = fromEnvironment; var->readOnly = readOnly; var->inUse = false; var->exported = false; var->reexport = false; return var; } static Substring CanonicalVarname(Substring name) { if (!(Substring_Length(name) > 0 && name.start[0] == '.')) return name; if (Substring_Equals(name, ".ALLSRC")) return Substring_InitStr(ALLSRC); if (Substring_Equals(name, ".ARCHIVE")) return Substring_InitStr(ARCHIVE); if (Substring_Equals(name, ".IMPSRC")) return Substring_InitStr(IMPSRC); if (Substring_Equals(name, ".MEMBER")) return Substring_InitStr(MEMBER); if (Substring_Equals(name, ".OODATE")) return Substring_InitStr(OODATE); if (Substring_Equals(name, ".PREFIX")) return Substring_InitStr(PREFIX); if (Substring_Equals(name, ".TARGET")) return Substring_InitStr(TARGET); if (Substring_Equals(name, ".SHELL") && shellPath == NULL) Shell_Init(); /* GNU make has an additional alias $^ == ${.ALLSRC}. */ return name; } static Var * GNode_FindVar(GNode *scope, Substring varname, unsigned int hash) { return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash); } /* * Find the variable in the scope, and maybe in other scopes as well. * * Input: * name name to find, is not expanded any further * scope scope in which to look first * elsewhere true to look in other scopes as well * * Results: * The found variable, or NULL if the variable does not exist. * If the variable is short-lived (such as environment variables), it * must be freed using VarFreeShortLived after use. */ static Var * VarFindSubstring(Substring name, GNode *scope, bool elsewhere) { Var *var; unsigned int nameHash; /* Replace '.TARGET' with '@', likewise for other local variables. */ name = CanonicalVarname(name); nameHash = Hash_Substring(name); var = GNode_FindVar(scope, name, nameHash); if (!elsewhere) return var; if (var == NULL && scope != SCOPE_CMDLINE) var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); if (var == NULL && scope != SCOPE_INTERNAL) { /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); } } if (var == NULL) { FStr envName; const char *envValue; envName = Substring_Str(name); envValue = getenv(envName.str); if (envValue != NULL) return VarNew(envName, envValue, true, true, false); FStr_Done(&envName); if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); if (var == NULL && scope != SCOPE_INTERNAL) var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); return var; } return NULL; } return var; } static Var * VarFind(const char *name, GNode *scope, bool elsewhere) { return VarFindSubstring(Substring_InitStr(name), scope, elsewhere); } /* If the variable is short-lived, free it, including its value. */ static void VarFreeShortLived(Var *v) { if (!v->shortLived) return; FStr_Done(&v->name); Buf_Done(&v->val); free(v); } static const char * ValueDescription(const char *value) { if (value[0] == '\0') return "# (empty)"; if (ch_isspace(value[strlen(value) - 1])) return "# (ends with space)"; return ""; } /* Add a new variable of the given name and value to the given scope. */ static Var * VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags) { HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value, false, false, (flags & VAR_SET_READONLY) != 0); HashEntry_Set(he, v); DEBUG4(VAR, "%s: %s = %s%s\n", scope->name, name, value, ValueDescription(value)); return v; } /* * Remove a variable from a scope, freeing all related memory as well. * The variable name is kept as-is, it is not expanded. */ void Var_Delete(GNode *scope, const char *varname) { HashEntry *he = HashTable_FindEntry(&scope->vars, varname); Var *v; if (he == NULL) { DEBUG2(VAR, "%s: delete %s (not found)\n", scope->name, varname); return; } DEBUG2(VAR, "%s: delete %s\n", scope->name, varname); v = he->value; if (v->inUse) { Parse_Error(PARSE_FATAL, "Cannot delete variable \"%s\" while it is used", v->name.str); return; } if (v->exported) unsetenv(v->name.str); if (strcmp(v->name.str, MAKE_EXPORTED) == 0) var_exportedVars = VAR_EXPORTED_NONE; assert(v->name.freeIt == NULL); HashTable_DeleteEntry(&scope->vars, he); Buf_Done(&v->val); free(v); } /* * Undefine one or more variables from the global scope. * The argument is expanded exactly once and then split into words. */ void Var_Undef(const char *arg) { VarParseResult vpr; char *expanded; Words varnames; size_t i; if (arg[0] == '\0') { Parse_Error(PARSE_FATAL, "The .undef directive requires an argument"); return; } vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded); if (vpr != VPR_OK) { Parse_Error(PARSE_FATAL, "Error in variable names to be undefined"); return; } varnames = Str_Words(expanded, false); if (varnames.len == 1 && varnames.words[0][0] == '\0') varnames.len = 0; for (i = 0; i < varnames.len; i++) { const char *varname = varnames.words[i]; Global_Delete(varname); } Words_Free(varnames); free(expanded); } static bool MayExport(const char *name) { if (name[0] == '.') return false; /* skip internals */ if (name[0] == '-') return false; /* skip misnamed variables */ if (name[1] == '\0') { /* * A single char. * If it is one of the variables that should only appear in * local scope, skip it, else we can get Var_Subst * into a loop. */ switch (name[0]) { case '@': case '%': case '*': case '!': return false; } } return true; } static bool ExportVarEnv(Var *v) { const char *name = v->name.str; char *val = v->val.data; char *expr; if (v->exported && !v->reexport) return false; /* nothing to do */ if (strchr(val, '$') == NULL) { if (!v->exported) setenv(name, val, 1); return true; } if (v->inUse) { /* * We recursed while exporting in a child. * This isn't going to end well, just skip it. */ return false; } /* XXX: name is injected without escaping it */ expr = str_concat3("${", name, "}"); (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val); /* TODO: handle errors */ setenv(name, val, 1); free(val); free(expr); return true; } static bool ExportVarPlain(Var *v) { if (strchr(v->val.data, '$') == NULL) { setenv(v->name.str, v->val.data, 1); v->exported = true; v->reexport = false; return true; } /* * Flag the variable as something we need to re-export. * No point actually exporting it now though, * the child process can do it at the last minute. * Avoid calling setenv more often than necessary since it can leak. */ v->exported = true; v->reexport = true; return true; } static bool ExportVarLiteral(Var *v) { if (v->exported && !v->reexport) return false; if (!v->exported) setenv(v->name.str, v->val.data, 1); return true; } /* * Mark a single variable to be exported later for subprocesses. * * Internal variables (those starting with '.') are not exported. */ static bool ExportVar(const char *name, VarExportMode mode) { Var *v; if (!MayExport(name)) return false; v = VarFind(name, SCOPE_GLOBAL, false); if (v == NULL) return false; if (mode == VEM_ENV) return ExportVarEnv(v); else if (mode == VEM_PLAIN) return ExportVarPlain(v); else return ExportVarLiteral(v); } /* * Actually export the variables that have been marked as needing to be * re-exported. */ void Var_ReexportVars(void) { char *xvarnames; /* * Several make implementations support this sort of mechanism for * tracking recursion - but each uses a different name. * We allow the makefiles to update MAKELEVEL and ensure * children see a correctly incremented value. */ char tmp[21]; snprintf(tmp, sizeof tmp, "%d", makelevel + 1); setenv(MAKE_LEVEL_ENV, tmp, 1); if (var_exportedVars == VAR_EXPORTED_NONE) return; if (var_exportedVars == VAR_EXPORTED_ALL) { HashIter hi; /* Ouch! Exporting all variables at once is crazy. */ HashIter_Init(&hi, &SCOPE_GLOBAL->vars); while (HashIter_Next(&hi) != NULL) { Var *var = hi.entry->value; ExportVar(var->name.str, VEM_ENV); } return; } (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); /* TODO: handle errors */ if (xvarnames[0] != '\0') { Words varnames = Str_Words(xvarnames, false); size_t i; for (i = 0; i < varnames.len; i++) ExportVar(varnames.words[i], VEM_ENV); Words_Free(varnames); } free(xvarnames); } static void ExportVars(const char *varnames, bool isExport, VarExportMode mode) /* TODO: try to combine the parameters 'isExport' and 'mode'. */ { Words words = Str_Words(varnames, false); size_t i; if (words.len == 1 && words.words[0][0] == '\0') words.len = 0; for (i = 0; i < words.len; i++) { const char *varname = words.words[i]; if (!ExportVar(varname, mode)) continue; if (var_exportedVars == VAR_EXPORTED_NONE) var_exportedVars = VAR_EXPORTED_SOME; if (isExport && mode == VEM_PLAIN) Global_Append(MAKE_EXPORTED, varname); } Words_Free(words); } static void ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode) { char *xvarnames; (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); /* TODO: handle errors */ ExportVars(xvarnames, isExport, mode); free(xvarnames); } /* Export the named variables, or all variables. */ void Var_Export(VarExportMode mode, const char *varnames) { if (mode == VEM_PLAIN && varnames[0] == '\0') { var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ return; } ExportVarsExpand(varnames, true, mode); } void Var_ExportVars(const char *varnames) { ExportVarsExpand(varnames, false, VEM_PLAIN); } extern char **environ; static void ClearEnv(void) { const char *cp; char **newenv; cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ if (environ == savedEnv) { /* we have been here before! */ newenv = bmake_realloc(environ, 2 * sizeof(char *)); } else { if (savedEnv != NULL) { free(savedEnv); savedEnv = NULL; } newenv = bmake_malloc(2 * sizeof(char *)); } /* Note: we cannot safely free() the original environ. */ environ = savedEnv = newenv; newenv[0] = NULL; newenv[1] = NULL; if (cp != NULL && *cp != '\0') setenv(MAKE_LEVEL_ENV, cp, 1); } static void GetVarnamesToUnexport(bool isEnv, const char *arg, FStr *out_varnames, UnexportWhat *out_what) { UnexportWhat what; FStr varnames = FStr_InitRefer(""); if (isEnv) { if (arg[0] != '\0') { Parse_Error(PARSE_FATAL, "The directive .unexport-env does not take " "arguments"); /* continue anyway */ } what = UNEXPORT_ENV; } else { what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL; if (what == UNEXPORT_NAMED) varnames = FStr_InitRefer(arg); } if (what != UNEXPORT_NAMED) { char *expanded; /* Using .MAKE.EXPORTED */ (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, &expanded); /* TODO: handle errors */ varnames = FStr_InitOwn(expanded); } *out_varnames = varnames; *out_what = what; } static void UnexportVar(Substring varname, UnexportWhat what) { Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false); if (v == NULL) { DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n", (int)Substring_Length(varname), varname.start); return; } DEBUG2(VAR, "Unexporting \"%.*s\"\n", (int)Substring_Length(varname), varname.start); if (what != UNEXPORT_ENV && v->exported && !v->reexport) unsetenv(v->name.str); v->exported = false; v->reexport = false; if (what == UNEXPORT_NAMED) { /* Remove the variable names from .MAKE.EXPORTED. */ /* XXX: v->name is injected without escaping it */ char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name.str, "}"); char *cp; (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp); /* TODO: handle errors */ Global_Set(MAKE_EXPORTED, cp); free(cp); free(expr); } } static void UnexportVars(FStr *varnames, UnexportWhat what) { size_t i; SubstringWords words; if (what == UNEXPORT_ENV) ClearEnv(); words = Substring_Words(varnames->str, false); for (i = 0; i < words.len; i++) UnexportVar(words.words[i], what); SubstringWords_Free(words); if (what != UNEXPORT_NAMED) Global_Delete(MAKE_EXPORTED); } /* * This is called when .unexport[-env] is seen. * * str must have the form "unexport[-env] varname...". */ void Var_UnExport(bool isEnv, const char *arg) { UnexportWhat what; FStr varnames; GetVarnamesToUnexport(isEnv, arg, &varnames, &what); UnexportVars(&varnames, what); FStr_Done(&varnames); } /* * When there is a variable of the same name in the command line scope, the * global variable would not be visible anywhere. Therefore there is no * point in setting it at all. * * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags. */ static bool ExistsInCmdline(const char *name, const char *val) { Var *v; v = VarFind(name, SCOPE_CMDLINE, false); if (v == NULL) return false; if (v->fromCmd) { DEBUG3(VAR, "%s: %s = %s ignored!\n", SCOPE_GLOBAL->name, name, val); return true; } VarFreeShortLived(v); return false; } /* Set the variable to the value; the name is not expanded. */ void Var_SetWithFlags(GNode *scope, const char *name, const char *val, VarSetFlags flags) { Var *v; assert(val != NULL); if (name[0] == '\0') { DEBUG0(VAR, "SetVar: variable name is empty - ignored\n"); return; } if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val)) return; /* * Only look for a variable in the given scope since anything set * here will override anything in a lower scope, so there's not much * point in searching them all. */ v = VarFind(name, scope, false); if (v == NULL) { if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { /* * This var would normally prevent the same name being * added to SCOPE_GLOBAL, so delete it from there if * needed. Otherwise -V name may show the wrong value. * * See ExistsInCmdline. */ Var_Delete(SCOPE_GLOBAL, name); } if (strcmp(name, ".SUFFIXES") == 0) { /* special: treat as readOnly */ DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n", scope->name, name, val); return; } v = VarAdd(name, val, scope, flags); } else { if (v->readOnly && !(flags & VAR_SET_READONLY)) { DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n", scope->name, name, val); return; } Buf_Clear(&v->val); Buf_AddStr(&v->val, val); DEBUG4(VAR, "%s: %s = %s%s\n", scope->name, name, val, ValueDescription(val)); if (v->exported) ExportVar(name, VEM_PLAIN); } /* * Any variables given on the command line are automatically exported * to the environment (as per POSIX standard), except for internals. */ if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && name[0] != '.') { v->fromCmd = true; /* * If requested, don't export these in the environment * individually. We still put them in MAKEOVERRIDES so * that the command-line settings continue to override * Makefile settings. */ if (!opts.varNoExportEnv) setenv(name, val, 1); /* XXX: What about .MAKE.EXPORTED? */ /* * XXX: Why not just mark the variable for needing export, as * in ExportVarPlain? */ Global_Append(MAKEOVERRIDES, name); } if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) save_dollars = ParseBoolean(val, save_dollars); if (v != NULL) VarFreeShortLived(v); } void Var_Set(GNode *scope, const char *name, const char *val) { Var_SetWithFlags(scope, name, val, VAR_SET_NONE); } /* * Set the variable name to the value val in the given scope. * * If the variable doesn't yet exist, it is created. * Otherwise the new value overwrites and replaces the old value. * * Input: * scope scope in which to set it * name name of the variable to set, is expanded once * val value to give to the variable */ void Var_SetExpand(GNode *scope, const char *name, const char *val) { const char *unexpanded_name = name; FStr varname = FStr_InitRefer(name); assert(val != NULL); Var_Expand(&varname, scope, VARE_WANTRES); if (varname.str[0] == '\0') { DEBUG2(VAR, "Var_SetExpand: variable name \"%s\" expands " "to empty string, with value \"%s\" - ignored\n", unexpanded_name, val); } else Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE); FStr_Done(&varname); } void Global_Set(const char *name, const char *value) { Var_Set(SCOPE_GLOBAL, name, value); } void Global_Delete(const char *name) { Var_Delete(SCOPE_GLOBAL, name); } +void +Global_Set_ReadOnly(const char *name, const char *value) +{ + Var_SetWithFlags(SCOPE_GLOBAL, name, value, VAR_SET_READONLY); +} + /* * Append the value to the named variable. * * If the variable doesn't exist, it is created. Otherwise a single space * and the given value are appended. */ void Var_Append(GNode *scope, const char *name, const char *val) { Var *v; v = VarFind(name, scope, scope == SCOPE_GLOBAL); if (v == NULL) { Var_SetWithFlags(scope, name, val, VAR_SET_NONE); } else if (v->readOnly) { DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", name); } else if (scope == SCOPE_CMDLINE || !v->fromCmd) { Buf_AddByte(&v->val, ' '); Buf_AddStr(&v->val, val); DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data); if (v->fromEnvironment) { /* See VarAdd. */ HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); HashEntry_Set(he, v); FStr_Done(&v->name); v->name = FStr_InitRefer(/* aliased to */ he->key); v->shortLived = false; v->fromEnvironment = false; } } } /* * The variable of the given name has the given value appended to it in the * given scope. * * If the variable doesn't exist, it is created. Otherwise the strings are * concatenated, with a space in between. * * Input: * scope scope in which this should occur * name name of the variable to modify, is expanded once * val string to append to it * * Notes: * Only if the variable is being sought in the global scope is the * environment searched. * XXX: Knows its calling circumstances in that if called with scope * an actual target, it will only search that scope since only * a local variable could be being appended to. This is actually * a big win and must be tolerated. */ void Var_AppendExpand(GNode *scope, const char *name, const char *val) { FStr xname = FStr_InitRefer(name); assert(val != NULL); Var_Expand(&xname, scope, VARE_WANTRES); if (xname.str != name && xname.str[0] == '\0') DEBUG2(VAR, "Var_AppendExpand: variable name \"%s\" expands " "to empty string, with value \"%s\" - ignored\n", name, val); else Var_Append(scope, xname.str, val); FStr_Done(&xname); } void Global_Append(const char *name, const char *value) { Var_Append(SCOPE_GLOBAL, name, value); } bool Var_Exists(GNode *scope, const char *name) { Var *v = VarFind(name, scope, true); if (v == NULL) return false; VarFreeShortLived(v); return true; } /* * See if the given variable exists, in the given scope or in other * fallback scopes. * * Input: * scope scope in which to start search * name name of the variable to find, is expanded once */ bool Var_ExistsExpand(GNode *scope, const char *name) { FStr varname = FStr_InitRefer(name); bool exists; Var_Expand(&varname, scope, VARE_WANTRES); exists = Var_Exists(scope, varname.str); FStr_Done(&varname); return exists; } /* * Return the unexpanded value of the given variable in the given scope, * or the usual scopes. * * Input: * scope scope in which to search for it * name name to find, is not expanded any further * * Results: * The value if the variable exists, NULL if it doesn't. * The value is valid until the next modification to any variable. */ FStr Var_Value(GNode *scope, const char *name) { Var *v = VarFind(name, scope, true); char *value; if (v == NULL) return FStr_InitRefer(NULL); if (!v->shortLived) return FStr_InitRefer(v->val.data); value = v->val.data; v->val.data = NULL; VarFreeShortLived(v); return FStr_InitOwn(value); } +/* + * set readOnly attribute of specified var if it exists + */ +void +Var_ReadOnly(const char *name, bool bf) +{ + Var *v; + + v = VarFind(name, SCOPE_GLOBAL, false); + if (v == NULL) { + DEBUG1(VAR, "Var_ReadOnly: %s not found\n", name); + return; + } + v->readOnly = bf; + DEBUG2(VAR, "Var_ReadOnly: %s %s\n", name, bf ? "true" : "false"); +} + /* * Return the unexpanded variable value from this node, without trying to look * up the variable in any other scope. */ const char * GNode_ValueDirect(GNode *gn, const char *name) { Var *v = VarFind(name, gn, false); return v != NULL ? v->val.data : NULL; } static VarEvalMode VarEvalMode_WithoutKeepDollar(VarEvalMode emode) { if (emode == VARE_KEEP_DOLLAR_UNDEF) return VARE_EVAL_KEEP_UNDEF; if (emode == VARE_EVAL_KEEP_DOLLAR) return VARE_WANTRES; return emode; } static VarEvalMode VarEvalMode_UndefOk(VarEvalMode emode) { return emode == VARE_UNDEFERR ? VARE_WANTRES : emode; } static bool VarEvalMode_ShouldEval(VarEvalMode emode) { return emode != VARE_PARSE_ONLY; } static bool VarEvalMode_ShouldKeepUndef(VarEvalMode emode) { return emode == VARE_EVAL_KEEP_UNDEF || emode == VARE_KEEP_DOLLAR_UNDEF; } static bool VarEvalMode_ShouldKeepDollar(VarEvalMode emode) { return emode == VARE_EVAL_KEEP_DOLLAR || emode == VARE_KEEP_DOLLAR_UNDEF; } static void SepBuf_Init(SepBuf *buf, char sep) { Buf_InitSize(&buf->buf, 32); buf->needSep = false; buf->sep = sep; } static void SepBuf_Sep(SepBuf *buf) { buf->needSep = true; } static void SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) { if (mem_size == 0) return; if (buf->needSep && buf->sep != '\0') { Buf_AddByte(&buf->buf, buf->sep); buf->needSep = false; } Buf_AddBytes(&buf->buf, mem, mem_size); } static void SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) { SepBuf_AddBytes(buf, start, (size_t)(end - start)); } static void SepBuf_AddStr(SepBuf *buf, const char *str) { SepBuf_AddBytes(buf, str, strlen(str)); } static void SepBuf_AddSubstring(SepBuf *buf, Substring sub) { SepBuf_AddBytesBetween(buf, sub.start, sub.end); } static char * SepBuf_DoneData(SepBuf *buf) { return Buf_DoneData(&buf->buf); } /* * This callback for ModifyWords gets a single word from a variable expression * and typically adds a modification of this word to the buffer. It may also * do nothing or add several words. * * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the * callback is called 3 times, once for "a", "b" and "c". * * Some ModifyWord functions assume that they are always passed a * null-terminated substring, which is currently guaranteed but may change in * the future. */ typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data); /* * Callback for ModifyWords to implement the :H modifier. * Add the dirname of the given word to the buffer. */ /*ARGSUSED*/ static void ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { SepBuf_AddSubstring(buf, Substring_Dirname(word)); } /* * Callback for ModifyWords to implement the :T modifier. * Add the basename of the given word to the buffer. */ /*ARGSUSED*/ static void ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { SepBuf_AddSubstring(buf, Substring_Basename(word)); } /* * Callback for ModifyWords to implement the :E modifier. * Add the filename suffix of the given word to the buffer, if it exists. */ /*ARGSUSED*/ static void ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *lastDot = Substring_LastIndex(word, '.'); if (lastDot != NULL) SepBuf_AddBytesBetween(buf, lastDot + 1, word.end); } /* * Callback for ModifyWords to implement the :R modifier. * Add the filename without extension of the given word to the buffer. */ /*ARGSUSED*/ static void ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *lastDot, *end; lastDot = Substring_LastIndex(word, '.'); end = lastDot != NULL ? lastDot : word.end; SepBuf_AddBytesBetween(buf, word.start, end); } /* * Callback for ModifyWords to implement the :M modifier. * Place the word in the buffer if it matches the given pattern. */ static void ModifyWord_Match(Substring word, SepBuf *buf, void *data) { const char *pattern = data; assert(word.end[0] == '\0'); /* assume null-terminated word */ if (Str_Match(word.start, pattern)) SepBuf_AddSubstring(buf, word); } /* * Callback for ModifyWords to implement the :N modifier. * Place the word in the buffer if it doesn't match the given pattern. */ static void ModifyWord_NoMatch(Substring word, SepBuf *buf, void *data) { const char *pattern = data; assert(word.end[0] == '\0'); /* assume null-terminated word */ if (!Str_Match(word.start, pattern)) SepBuf_AddSubstring(buf, word); } #ifdef SYSVVARSUB struct ModifyWord_SysVSubstArgs { GNode *scope; Substring lhsPrefix; bool lhsPercent; Substring lhsSuffix; const char *rhs; }; /* Callback for ModifyWords to implement the :%.from=%.to modifier. */ static void ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data) { const struct ModifyWord_SysVSubstArgs *args = data; FStr rhs; const char *percent; if (Substring_IsEmpty(word)) return; if (!Substring_HasPrefix(word, args->lhsPrefix) || !Substring_HasSuffix(word, args->lhsSuffix)) { SepBuf_AddSubstring(buf, word); return; } rhs = FStr_InitRefer(args->rhs); Var_Expand(&rhs, args->scope, VARE_WANTRES); percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL; if (percent != NULL) SepBuf_AddBytesBetween(buf, rhs.str, percent); if (percent != NULL || !args->lhsPercent) SepBuf_AddBytesBetween(buf, word.start + Substring_Length(args->lhsPrefix), word.end - Substring_Length(args->lhsSuffix)); SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str); FStr_Done(&rhs); } #endif struct ModifyWord_SubstArgs { Substring lhs; Substring rhs; PatternFlags pflags; bool matched; }; static const char * Substring_Find(Substring haystack, Substring needle) { size_t len, needleLen, i; len = Substring_Length(haystack); needleLen = Substring_Length(needle); for (i = 0; i + needleLen <= len; i++) if (memcmp(haystack.start + i, needle.start, needleLen) == 0) return haystack.start + i; return NULL; } /* * Callback for ModifyWords to implement the :S,from,to, modifier. * Perform a string substitution on the given word. */ static void ModifyWord_Subst(Substring word, SepBuf *buf, void *data) { struct ModifyWord_SubstArgs *args = data; size_t wordLen, lhsLen; const char *wordEnd, *match; wordLen = Substring_Length(word); wordEnd = word.end; if (args->pflags.subOnce && args->matched) goto nosub; lhsLen = Substring_Length(args->lhs); if (args->pflags.anchorStart) { if (wordLen < lhsLen || memcmp(word.start, args->lhs.start, lhsLen) != 0) goto nosub; if (args->pflags.anchorEnd && wordLen != lhsLen) goto nosub; /* :S,^prefix,replacement, or :S,^whole$,replacement, */ SepBuf_AddSubstring(buf, args->rhs); SepBuf_AddBytesBetween(buf, word.start + lhsLen, wordEnd); args->matched = true; return; } if (args->pflags.anchorEnd) { if (wordLen < lhsLen) goto nosub; if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0) goto nosub; /* :S,suffix$,replacement, */ SepBuf_AddBytesBetween(buf, word.start, wordEnd - lhsLen); SepBuf_AddSubstring(buf, args->rhs); args->matched = true; return; } if (Substring_IsEmpty(args->lhs)) goto nosub; /* unanchored case, may match more than once */ while ((match = Substring_Find(word, args->lhs)) != NULL) { SepBuf_AddBytesBetween(buf, word.start, match); SepBuf_AddSubstring(buf, args->rhs); args->matched = true; word.start = match + lhsLen; if (Substring_IsEmpty(word) || !args->pflags.subGlobal) break; } nosub: SepBuf_AddSubstring(buf, word); } #ifndef NO_REGEX /* Print the error caused by a regcomp or regexec call. */ static void VarREError(int reerr, const regex_t *pat, const char *str) { size_t errlen = regerror(reerr, pat, NULL, 0); char *errbuf = bmake_malloc(errlen); regerror(reerr, pat, errbuf, errlen); Error("%s: %s", str, errbuf); free(errbuf); } /* In the modifier ':C', replace a backreference from \0 to \9. */ static void RegexReplaceBackref(char ref, SepBuf *buf, const char *wp, const regmatch_t *m, size_t nsub) { - unsigned int n = ref - '0'; + unsigned int n = (unsigned)ref - '0'; if (n >= nsub) Error("No subexpression \\%u", n); else if (m[n].rm_so == -1) { if (opts.strict) Error("No match for subexpression \\%u", n); } else { SepBuf_AddBytesBetween(buf, wp + (size_t)m[n].rm_so, wp + (size_t)m[n].rm_eo); } } /* * The regular expression matches the word; now add the replacement to the * buffer, taking back-references from 'wp'. */ static void RegexReplace(Substring replace, SepBuf *buf, const char *wp, const regmatch_t *m, size_t nsub) { const char *rp; for (rp = replace.start; rp != replace.end; rp++) { if (*rp == '\\' && rp + 1 != replace.end && (rp[1] == '&' || rp[1] == '\\')) SepBuf_AddBytes(buf, ++rp, 1); else if (*rp == '\\' && rp + 1 != replace.end && ch_isdigit(rp[1])) RegexReplaceBackref(*++rp, buf, wp, m, nsub); else if (*rp == '&') { SepBuf_AddBytesBetween(buf, wp + (size_t)m[0].rm_so, wp + (size_t)m[0].rm_eo); } else SepBuf_AddBytes(buf, rp, 1); } } struct ModifyWord_SubstRegexArgs { regex_t re; size_t nsub; Substring replace; PatternFlags pflags; bool matched; }; /* * Callback for ModifyWords to implement the :C/from/to/ modifier. * Perform a regex substitution on the given word. */ static void ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data) { struct ModifyWord_SubstRegexArgs *args = data; int xrv; const char *wp; int flags = 0; regmatch_t m[10]; assert(word.end[0] == '\0'); /* assume null-terminated word */ wp = word.start; if (args->pflags.subOnce && args->matched) goto no_match; again: xrv = regexec(&args->re, wp, args->nsub, m, flags); if (xrv == 0) goto ok; if (xrv != REG_NOMATCH) VarREError(xrv, &args->re, "Unexpected regex error"); no_match: SepBuf_AddBytesBetween(buf, wp, word.end); return; ok: args->matched = true; SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); RegexReplace(args->replace, buf, wp, m, args->nsub); wp += (size_t)m[0].rm_eo; if (args->pflags.subGlobal) { flags |= REG_NOTBOL; if (m[0].rm_so == 0 && m[0].rm_eo == 0) { SepBuf_AddBytes(buf, wp, 1); wp++; } if (*wp != '\0') goto again; } if (*wp != '\0') SepBuf_AddStr(buf, wp); } #endif struct ModifyWord_LoopArgs { GNode *scope; const char *var; /* name of the temporary variable */ const char *body; /* string to expand */ VarEvalMode emode; }; /* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ static void ModifyWord_Loop(Substring word, SepBuf *buf, void *data) { const struct ModifyWord_LoopArgs *args; char *s; if (Substring_IsEmpty(word)) return; args = data; assert(word.end[0] == '\0'); /* assume null-terminated word */ Var_SetWithFlags(args->scope, args->var, word.start, VAR_SET_NO_EXPORT); (void)Var_Subst(args->body, args->scope, args->emode, &s); /* TODO: handle errors */ assert(word.end[0] == '\0'); /* assume null-terminated word */ DEBUG4(VAR, "ModifyWord_Loop: " "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n", word.start, args->var, args->body, s); if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n')) buf->needSep = false; SepBuf_AddStr(buf, s); free(s); } /* * The :[first..last] modifier selects words from the expression. * It can also reverse the words. */ static char * VarSelectWords(const char *str, int first, int last, char sep, bool oneBigWord) { SubstringWords words; int len, start, end, step; int i; SepBuf buf; SepBuf_Init(&buf, sep); if (oneBigWord) { /* fake what Substring_Words() would do */ words.len = 1; words.words = bmake_malloc(sizeof(words.words[0])); words.freeIt = NULL; words.words[0] = Substring_InitStr(str); /* no need to copy */ } else { words = Substring_Words(str, false); } /* * Now sanitize the given range. If first or last are negative, * convert them to the positive equivalents (-1 gets converted to len, * -2 gets converted to (len - 1), etc.). */ len = (int)words.len; if (first < 0) first += len + 1; if (last < 0) last += len + 1; /* We avoid scanning more of the list than we need to. */ if (first > last) { start = (first > len ? len : first) - 1; end = last < 1 ? 0 : last - 1; step = -1; } else { start = first < 1 ? 0 : first - 1; end = last > len ? len : last; step = 1; } for (i = start; (step < 0) == (i >= end); i += step) { SepBuf_AddSubstring(&buf, words.words[i]); SepBuf_Sep(&buf); } SubstringWords_Free(words); return SepBuf_DoneData(&buf); } /* * Callback for ModifyWords to implement the :tA modifier. * Replace each word with the result of realpath() if successful. */ /*ARGSUSED*/ static void ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) { struct stat st; char rbuf[MAXPATHLEN]; const char *rp; assert(word.end[0] == '\0'); /* assume null-terminated word */ rp = cached_realpath(word.start, rbuf); if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) SepBuf_AddStr(buf, rp); else SepBuf_AddSubstring(buf, word); } static char * SubstringWords_JoinFree(SubstringWords words) { Buffer buf; size_t i; Buf_Init(&buf); for (i = 0; i < words.len; i++) { if (i != 0) { /* * XXX: Use ch->sep instead of ' ', for consistency. */ Buf_AddByte(&buf, ' '); } Buf_AddBytesBetween(&buf, words.words[i].start, words.words[i].end); } SubstringWords_Free(words); return Buf_DoneData(&buf); } /* * Quote shell meta-characters and space characters in the string. * If quoteDollar is set, also quote and double any '$' characters. */ static void VarQuote(const char *str, bool quoteDollar, LazyBuf *buf) { const char *p; LazyBuf_Init(buf, str); for (p = str; *p != '\0'; p++) { if (*p == '\n') { const char *newline = Shell_GetNewline(); if (newline == NULL) newline = "\\\n"; LazyBuf_AddStr(buf, newline); continue; } if (ch_isspace(*p) || ch_is_shell_meta(*p)) LazyBuf_Add(buf, '\\'); LazyBuf_Add(buf, *p); if (quoteDollar && *p == '$') LazyBuf_AddStr(buf, "\\$"); } } /* * Compute the 32-bit hash of the given string, using the MurmurHash3 * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */ static char * VarHash(const char *str) { static const char hexdigits[16] = "0123456789abcdef"; const unsigned char *ustr = (const unsigned char *)str; uint32_t h = 0x971e137bU; uint32_t c1 = 0x95543787U; uint32_t c2 = 0x2ad7eb25U; size_t len2 = strlen(str); char *buf; size_t i; size_t len; for (len = len2; len != 0;) { uint32_t k = 0; switch (len) { default: k = ((uint32_t)ustr[3] << 24) | ((uint32_t)ustr[2] << 16) | ((uint32_t)ustr[1] << 8) | (uint32_t)ustr[0]; len -= 4; ustr += 4; break; case 3: k |= (uint32_t)ustr[2] << 16; /* FALLTHROUGH */ case 2: k |= (uint32_t)ustr[1] << 8; /* FALLTHROUGH */ case 1: k |= (uint32_t)ustr[0]; len = 0; } c1 = c1 * 5 + 0x7b7d159cU; c2 = c2 * 5 + 0x6bce6396U; k *= c1; k = (k << 11) ^ (k >> 21); k *= c2; h = (h << 13) ^ (h >> 19); h = h * 5 + 0x52dce729U; h ^= k; } h ^= (uint32_t)len2; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; buf = bmake_malloc(9); for (i = 0; i < 8; i++) { buf[i] = hexdigits[h & 0x0f]; h >>= 4; } buf[8] = '\0'; return buf; } static char * VarStrftime(const char *fmt, time_t t, bool gmt) { char buf[BUFSIZ]; if (t == 0) time(&t); if (*fmt == '\0') fmt = "%c"; strftime(buf, sizeof buf, fmt, gmt ? gmtime(&t) : localtime(&t)); buf[sizeof buf - 1] = '\0'; return bmake_strdup(buf); } /* * The ApplyModifier functions take an expression that is being evaluated. * Their task is to apply a single modifier to the expression. This involves * parsing the modifier, evaluating it and finally updating the value of the * expression. * * Parsing the modifier * * If parsing succeeds, the parsing position *pp is updated to point to the * first character following the modifier, which typically is either ':' or * ch->endc. The modifier doesn't have to check for this delimiter character, * this is done by ApplyModifiers. * * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not * need to be followed by a ':' or endc; this was an unintended mistake. * * If parsing fails because of a missing delimiter (as in the :S, :C or :@ * modifiers), return AMR_CLEANUP. * * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to * try the SysV modifier ${VAR:from=to} as fallback. This should only be * done as long as there have been no side effects from evaluating nested * variables, to avoid evaluating them more than once. In this case, the * parsing position may or may not be updated. (XXX: Why not? The original * parsing position is well-known in ApplyModifiers.) * * If parsing fails and the SysV modifier ${VAR:from=to} should not be used * as a fallback, either issue an error message using Error or Parse_Error * and then return AMR_CLEANUP, or return AMR_BAD for the default error * message. Both of these return values will stop processing the variable * expression. (XXX: As of 2020-08-23, evaluation of the whole string * continues nevertheless after skipping a few bytes, which essentially is * undefined behavior. Not in the sense of C, but still the resulting string * is garbage.) * * Evaluating the modifier * * After parsing, the modifier is evaluated. The side effects from evaluating * nested variable expressions in the modifier text often already happen * during parsing though. For most modifiers this doesn't matter since their * only noticeable effect is that they update the value of the expression. * Some modifiers such as ':sh' or '::=' have noticeable side effects though. * * Evaluating the modifier usually takes the current value of the variable * expression from ch->expr->value, or the variable name from ch->var->name * and stores the result back in expr->value via Expr_SetValueOwn or * Expr_SetValueRefer. * * If evaluating fails (as of 2020-08-23), an error message is printed using * Error. This function has no side-effects, it really just prints the error * message. Processing the expression continues as if everything were ok. * XXX: This should be fixed by adding proper error handling to Var_Subst, * Var_Parse, ApplyModifiers and ModifyWords. * * Housekeeping * * Some modifiers such as :D and :U turn undefined expressions into defined * expressions (see Expr_Define). * * Some modifiers need to free some memory. */ typedef enum ExprDefined { /* The variable expression is based on a regular, defined variable. */ DEF_REGULAR, /* The variable expression is based on an undefined variable. */ DEF_UNDEF, /* * The variable expression started as an undefined expression, but one * of the modifiers (such as ':D' or ':U') has turned the expression * from undefined to defined. */ DEF_DEFINED } ExprDefined; static const char ExprDefined_Name[][10] = { "regular", "undefined", "defined" }; #if __STDC_VERSION__ >= 199901L #define const_member const #else #define const_member /* no const possible */ #endif /* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */ typedef struct Expr { const char *name; FStr value; VarEvalMode const_member emode; GNode *const_member scope; ExprDefined defined; } Expr; /* * The status of applying a chain of modifiers to an expression. * * The modifiers of an expression are broken into chains of modifiers, * starting a new nested chain whenever an indirect modifier starts. There * are at most 2 nesting levels: the outer one for the direct modifiers, and * the inner one for the indirect modifiers. * * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of * modifiers: * * Chain 1 starts with the single modifier ':M*'. * Chain 2 starts with all modifiers from ${IND1}. * Chain 2 ends at the ':' between ${IND1} and ${IND2}. * Chain 3 starts with all modifiers from ${IND2}. * Chain 3 ends at the ':' after ${IND2}. * Chain 1 continues with the 2 modifiers ':O' and ':u'. * Chain 1 ends at the final '}' of the expression. * * After such a chain ends, its properties no longer have any effect. * * It may or may not have been intended that 'defined' has scope Expr while * 'sep' and 'oneBigWord' have smaller scope. * * See varmod-indirect.mk. */ typedef struct ModChain { Expr *expr; /* '\0' or '{' or '(' */ char const_member startc; /* '\0' or '}' or ')' */ char const_member endc; /* Word separator in expansions (see the :ts modifier). */ char sep; /* * True if some modifiers that otherwise split the variable value * into words, like :S and :C, treat the variable value as a single * big word, possibly containing spaces. */ bool oneBigWord; } ModChain; static void Expr_Define(Expr *expr) { if (expr->defined == DEF_UNDEF) expr->defined = DEF_DEFINED; } static const char * Expr_Str(const Expr *expr) { return expr->value.str; } static SubstringWords Expr_Words(const Expr *expr) { return Substring_Words(Expr_Str(expr), false); } static void Expr_SetValue(Expr *expr, FStr value) { FStr_Done(&expr->value); expr->value = value; } static void Expr_SetValueOwn(Expr *expr, char *value) { Expr_SetValue(expr, FStr_InitOwn(value)); } static void Expr_SetValueRefer(Expr *expr, const char *value) { Expr_SetValue(expr, FStr_InitRefer(value)); } static bool Expr_ShouldEval(const Expr *expr) { return VarEvalMode_ShouldEval(expr->emode); } static bool ModChain_ShouldEval(const ModChain *ch) { return Expr_ShouldEval(ch->expr); } typedef enum ApplyModifierResult { /* Continue parsing */ AMR_OK, /* Not a match, try other modifiers as well. */ AMR_UNKNOWN, /* Error out with "Bad modifier" message. */ AMR_BAD, /* Error out without the standard error message. */ AMR_CLEANUP } ApplyModifierResult; /* * Allow backslashes to escape the delimiter, $, and \, but don't touch other * backslashes. */ static bool IsEscapedModifierPart(const char *p, char delim, struct ModifyWord_SubstArgs *subst) { if (p[0] != '\\') return false; if (p[1] == delim || p[1] == '\\' || p[1] == '$') return true; return p[1] == '&' && subst != NULL; } /* * In a part of a modifier, parse a subexpression and evaluate it. */ static void ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch, VarEvalMode emode) { const char *p = *pp; FStr nested_val; (void)Var_Parse(&p, ch->expr->scope, VarEvalMode_WithoutKeepDollar(emode), &nested_val); /* TODO: handle errors */ LazyBuf_AddStr(part, nested_val.str); FStr_Done(&nested_val); *pp = p; } /* * In a part of a modifier, parse a subexpression but don't evaluate it. * * XXX: This whole block is very similar to Var_Parse with VARE_PARSE_ONLY. * There may be subtle edge cases though that are not yet covered in the unit * tests and that are parsed differently, depending on whether they are * evaluated or not. * * This subtle difference is not documented in the manual page, neither is * the difference between parsing ':D' and ':M' documented. No code should * ever depend on these details, but who knows. + * + * TODO: Before trying to replace this code with Var_Parse, there need to be + * more unit tests in varmod-loop.mk. The modifier ':@' uses Var_Subst + * internally, in which a '$' is escaped as '$$', not as '\$' like in other + * modifiers. When parsing the body text '$${var}', skipping over the first + * '$' would treat '${var}' as a make expression, not as a shell variable. */ static void ParseModifierPartDollar(const char **pp, LazyBuf *part) { const char *p = *pp; const char *start = *pp; if (p[1] == '(' || p[1] == '{') { char startc = p[1]; int endc = startc == '(' ? ')' : '}'; int depth = 1; for (p += 2; *p != '\0' && depth > 0; p++) { if (p[-1] != '\\') { if (*p == startc) depth++; if (*p == endc) depth--; } } - LazyBuf_AddBytesBetween(part, start, p); + LazyBuf_AddSubstring(part, Substring_Init(start, p)); *pp = p; } else { LazyBuf_Add(part, *start); *pp = p + 1; } } /* See ParseModifierPart for the documentation. */ static VarParseResult ParseModifierPartSubst( const char **pp, char delim, VarEvalMode emode, ModChain *ch, LazyBuf *part, /* * For the first part of the modifier ':S', set anchorEnd if the last * character of the pattern is a $. */ PatternFlags *out_pflags, /* * For the second part of the :S modifier, allow ampersands to be escaped * and replace unescaped ampersands with subst->lhs. */ struct ModifyWord_SubstArgs *subst ) { const char *p; p = *pp; LazyBuf_Init(part, p); while (*p != '\0' && *p != delim) { if (IsEscapedModifierPart(p, delim, subst)) { LazyBuf_Add(part, p[1]); p += 2; } else if (*p != '$') { /* Unescaped, simple text */ if (subst != NULL && *p == '&') LazyBuf_AddSubstring(part, subst->lhs); else LazyBuf_Add(part, *p); p++; } else if (p[1] == delim) { /* Unescaped '$' at end */ if (out_pflags != NULL) out_pflags->anchorEnd = true; else LazyBuf_Add(part, *p); p++; } else if (VarEvalMode_ShouldEval(emode)) ParseModifierPartExpr(&p, part, ch, emode); else ParseModifierPartDollar(&p, part); } if (*p != delim) { *pp = p; Error("Unfinished modifier for \"%s\" ('%c' missing)", ch->expr->name, delim); LazyBuf_Done(part); return VPR_ERR; } *pp = p + 1; { Substring sub = LazyBuf_Get(part); DEBUG2(VAR, "Modifier part: \"%.*s\"\n", (int)Substring_Length(sub), sub.start); } return VPR_OK; } /* * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ or * the "var" or "replacement ${var}" in :@var@replacement ${var}@, up to and * including the next unescaped delimiter. The delimiter, as well as the * backslash or the dollar, can be escaped with a backslash. * * Return VPR_OK if parsing succeeded, together with the parsed (and possibly * expanded) part. In that case, pp points right after the delimiter. The * delimiter is not included in the part though. */ static VarParseResult ParseModifierPart( /* The parsing position, updated upon return */ const char **pp, /* Parsing stops at this delimiter */ char delim, /* Mode for evaluating nested variables. */ VarEvalMode emode, ModChain *ch, LazyBuf *part ) { return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL); } MAKE_INLINE bool IsDelimiter(char c, const ModChain *ch) { - return c == ':' || c == ch->endc; + return c == ':' || c == ch->endc || c == '\0'; } /* Test whether mod starts with modname, followed by a delimiter. */ MAKE_INLINE bool ModMatch(const char *mod, const char *modname, const ModChain *ch) { size_t n = strlen(modname); return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], ch); } /* Test whether mod starts with modname, followed by a delimiter or '='. */ MAKE_INLINE bool ModMatchEq(const char *mod, const char *modname, const ModChain *ch) { size_t n = strlen(modname); return strncmp(mod, modname, n) == 0 && (IsDelimiter(mod[n], ch) || mod[n] == '='); } static bool TryParseIntBase0(const char **pp, int *out_num) { char *end; long n; errno = 0; n = strtol(*pp, &end, 0); if (end == *pp) return false; if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) return false; if (n < INT_MIN || n > INT_MAX) return false; *pp = end; *out_num = (int)n; return true; } static bool TryParseSize(const char **pp, size_t *out_num) { char *end; unsigned long n; if (!ch_isdigit(**pp)) return false; errno = 0; n = strtoul(*pp, &end, 10); if (n == ULONG_MAX && errno == ERANGE) return false; if (n > SIZE_MAX) return false; *pp = end; *out_num = (size_t)n; return true; } static bool TryParseChar(const char **pp, int base, char *out_ch) { char *end; unsigned long n; if (!ch_isalnum(**pp)) return false; errno = 0; n = strtoul(*pp, &end, base); if (n == ULONG_MAX && errno == ERANGE) return false; if (n > UCHAR_MAX) return false; *pp = end; *out_ch = (char)n; return true; } /* * Modify each word of the expression using the given function and place the * result back in the expression. */ static void ModifyWords(ModChain *ch, ModifyWordProc modifyWord, void *modifyWord_args, bool oneBigWord) { Expr *expr = ch->expr; const char *val = Expr_Str(expr); SepBuf result; SubstringWords words; size_t i; Substring word; if (oneBigWord) { SepBuf_Init(&result, ch->sep); /* XXX: performance: Substring_InitStr calls strlen */ word = Substring_InitStr(val); modifyWord(word, &result, modifyWord_args); goto done; } words = Substring_Words(val, false); DEBUG3(VAR, "ModifyWords: split \"%s\" into %u %s\n", val, (unsigned)words.len, words.len != 1 ? "words" : "word"); SepBuf_Init(&result, ch->sep); for (i = 0; i < words.len; i++) { modifyWord(words.words[i], &result, modifyWord_args); if (result.buf.len > 0) SepBuf_Sep(&result); } SubstringWords_Free(words); done: Expr_SetValueOwn(expr, SepBuf_DoneData(&result)); } /* :@var@...${var}...@ */ static ApplyModifierResult ApplyModifier_Loop(const char **pp, ModChain *ch) { Expr *expr = ch->expr; struct ModifyWord_LoopArgs args; char prev_sep; VarParseResult res; LazyBuf tvarBuf, strBuf; FStr tvar, str; args.scope = expr->scope; (*pp)++; /* Skip the first '@' */ res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf); if (res != VPR_OK) return AMR_CLEANUP; tvar = LazyBuf_DoneGet(&tvarBuf); args.var = tvar.str; if (strchr(args.var, '$') != NULL) { Parse_Error(PARSE_FATAL, "In the :@ modifier of \"%s\", the variable name \"%s\" " "must not contain a dollar", expr->name, args.var); return AMR_CLEANUP; } res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &strBuf); if (res != VPR_OK) return AMR_CLEANUP; str = LazyBuf_DoneGet(&strBuf); args.body = str.str; if (!Expr_ShouldEval(expr)) goto done; args.emode = VarEvalMode_WithoutKeepDollar(expr->emode); prev_sep = ch->sep; ch->sep = ' '; /* XXX: should be ch->sep for consistency */ ModifyWords(ch, ModifyWord_Loop, &args, ch->oneBigWord); ch->sep = prev_sep; /* XXX: Consider restoring the previous value instead of deleting. */ Var_Delete(expr->scope, args.var); done: FStr_Done(&tvar); FStr_Done(&str); return AMR_OK; } -/* :Ddefined or :Uundefined */ -static ApplyModifierResult -ApplyModifier_Defined(const char **pp, ModChain *ch) +static void +ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval, + LazyBuf *buf) { - Expr *expr = ch->expr; - LazyBuf buf; const char *p; - VarEvalMode emode = VARE_PARSE_ONLY; - if (Expr_ShouldEval(expr)) - if ((**pp == 'D') == (expr->defined == DEF_REGULAR)) - emode = expr->emode; - p = *pp + 1; - LazyBuf_Init(&buf, p); - while (!IsDelimiter(*p, ch) && *p != '\0') { + LazyBuf_Init(buf, p); + while (!IsDelimiter(*p, ch)) { /* * XXX: This code is similar to the one in Var_Parse. See if * the code can be merged. See also ApplyModifier_Match and * ParseModifierPart. */ /* Escaped delimiter or other special character */ /* See Buf_AddEscaped in for.c. */ if (*p == '\\') { char c = p[1]; - if (IsDelimiter(c, ch) || c == '$' || c == '\\') { - LazyBuf_Add(&buf, c); + if ((IsDelimiter(c, ch) && c != '\0') || + c == '$' || c == '\\') { + if (shouldEval) + LazyBuf_Add(buf, c); p += 2; continue; } } /* Nested variable expression */ if (*p == '$') { - FStr nested_val; + FStr val; - (void)Var_Parse(&p, expr->scope, emode, &nested_val); + (void)Var_Parse(&p, ch->expr->scope, + shouldEval ? ch->expr->emode : VARE_PARSE_ONLY, + &val); /* TODO: handle errors */ - if (Expr_ShouldEval(expr)) - LazyBuf_AddStr(&buf, nested_val.str); - FStr_Done(&nested_val); + if (shouldEval) + LazyBuf_AddStr(buf, val.str); + FStr_Done(&val); continue; } /* Ordinary text */ - LazyBuf_Add(&buf, *p); + if (shouldEval) + LazyBuf_Add(buf, *p); p++; } *pp = p; +} - Expr_Define(expr); +/* :Ddefined or :Uundefined */ +static ApplyModifierResult +ApplyModifier_Defined(const char **pp, ModChain *ch) +{ + Expr *expr = ch->expr; + LazyBuf buf; + bool shouldEval = + Expr_ShouldEval(expr) && + (**pp == 'D') == (expr->defined == DEF_REGULAR); + + ParseModifier_Defined(pp, ch, shouldEval, &buf); - if (VarEvalMode_ShouldEval(emode)) + Expr_Define(expr); + if (shouldEval) Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf))); - else - LazyBuf_Done(&buf); return AMR_OK; } /* :L */ static ApplyModifierResult ApplyModifier_Literal(const char **pp, ModChain *ch) { Expr *expr = ch->expr; (*pp)++; if (Expr_ShouldEval(expr)) { Expr_Define(expr); Expr_SetValueOwn(expr, bmake_strdup(expr->name)); } return AMR_OK; } static bool TryParseTime(const char **pp, time_t *out_time) { char *end; unsigned long n; if (!ch_isdigit(**pp)) return false; errno = 0; n = strtoul(*pp, &end, 10); if (n == ULONG_MAX && errno == ERANGE) return false; *pp = end; *out_time = (time_t)n; /* ignore possible truncation for now */ return true; } /* :gmtime and :localtime */ static ApplyModifierResult ApplyModifier_Time(const char **pp, ModChain *ch) { Expr *expr; time_t t; const char *args; const char *mod = *pp; bool gmt = mod[0] == 'g'; if (!ModMatchEq(mod, gmt ? "gmtime" : "localtime", ch)) return AMR_UNKNOWN; args = mod + (gmt ? 6 : 9); if (args[0] == '=') { const char *p = args + 1; if (!TryParseTime(&p, &t)) { Parse_Error(PARSE_FATAL, "Invalid time value at \"%s\"", p); return AMR_CLEANUP; } *pp = p; } else { t = 0; *pp = args; } expr = ch->expr; if (Expr_ShouldEval(expr)) Expr_SetValueOwn(expr, VarStrftime(Expr_Str(expr), t, gmt)); return AMR_OK; } /* :hash */ static ApplyModifierResult ApplyModifier_Hash(const char **pp, ModChain *ch) { if (!ModMatch(*pp, "hash", ch)) return AMR_UNKNOWN; *pp += 4; if (ModChain_ShouldEval(ch)) Expr_SetValueOwn(ch->expr, VarHash(Expr_Str(ch->expr))); return AMR_OK; } /* :P */ static ApplyModifierResult ApplyModifier_Path(const char **pp, ModChain *ch) { Expr *expr = ch->expr; GNode *gn; char *path; (*pp)++; if (!Expr_ShouldEval(expr)) return AMR_OK; Expr_Define(expr); gn = Targ_FindNode(expr->name); if (gn == NULL || gn->type & OP_NOPATH) { path = NULL; } else if (gn->path != NULL) { path = bmake_strdup(gn->path); } else { SearchPath *searchPath = Suff_FindPath(gn); path = Dir_FindFile(expr->name, searchPath); } if (path == NULL) path = bmake_strdup(expr->name); Expr_SetValueOwn(expr, path); return AMR_OK; } /* :!cmd! */ static ApplyModifierResult ApplyModifier_ShellCommand(const char **pp, ModChain *ch) { Expr *expr = ch->expr; VarParseResult res; LazyBuf cmdBuf; FStr cmd; (*pp)++; res = ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf); if (res != VPR_OK) return AMR_CLEANUP; cmd = LazyBuf_DoneGet(&cmdBuf); if (Expr_ShouldEval(expr)) { char *output, *error; output = Cmd_Exec(cmd.str, &error); Expr_SetValueOwn(expr, output); if (error != NULL) { /* XXX: why still return AMR_OK? */ Error("%s", error); free(error); } } else Expr_SetValueRefer(expr, ""); FStr_Done(&cmd); Expr_Define(expr); return AMR_OK; } /* * The :range modifier generates an integer sequence as long as the words. * The :range=7 modifier generates an integer sequence from 1 to 7. */ static ApplyModifierResult ApplyModifier_Range(const char **pp, ModChain *ch) { size_t n; Buffer buf; size_t i; const char *mod = *pp; if (!ModMatchEq(mod, "range", ch)) return AMR_UNKNOWN; if (mod[5] == '=') { const char *p = mod + 6; if (!TryParseSize(&p, &n)) { Parse_Error(PARSE_FATAL, "Invalid number \"%s\" for ':range' modifier", mod + 6); return AMR_CLEANUP; } *pp = p; } else { n = 0; *pp = mod + 5; } if (!ModChain_ShouldEval(ch)) return AMR_OK; if (n == 0) { SubstringWords words = Expr_Words(ch->expr); n = words.len; SubstringWords_Free(words); } Buf_Init(&buf); for (i = 0; i < n; i++) { if (i != 0) { /* * XXX: Use ch->sep instead of ' ', for consistency. */ Buf_AddByte(&buf, ' '); } Buf_AddInt(&buf, 1 + (int)i); } Expr_SetValueOwn(ch->expr, Buf_DoneData(&buf)); return AMR_OK; } /* Parse a ':M' or ':N' modifier. */ static char * ParseModifier_Match(const char **pp, const ModChain *ch) { const char *mod = *pp; Expr *expr = ch->expr; bool copy = false; /* pattern should be, or has been, copied */ bool needSubst = false; const char *endpat; char *pattern; /* * In the loop below, ignore ':' unless we are at (or back to) the * original brace level. * XXX: This will likely not work right if $() and ${} are intermixed. */ /* * XXX: This code is similar to the one in Var_Parse. * See if the code can be merged. * See also ApplyModifier_Defined. */ int nest = 0; const char *p; for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) { - if (*p == '\\' && + if (*p == '\\' && p[1] != '\0' && (IsDelimiter(p[1], ch) || p[1] == ch->startc)) { if (!needSubst) copy = true; p++; continue; } if (*p == '$') needSubst = true; if (*p == '(' || *p == '{') nest++; if (*p == ')' || *p == '}') { nest--; if (nest < 0) break; } } *pp = p; endpat = p; if (copy) { char *dst; const char *src; /* Compress the \:'s out of the pattern. */ pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1); dst = pattern; src = mod + 1; for (; src < endpat; src++, dst++) { if (src[0] == '\\' && src + 1 < endpat && /* XXX: ch->startc is missing here; see above */ IsDelimiter(src[1], ch)) src++; *dst = *src; } *dst = '\0'; } else { pattern = bmake_strsedup(mod + 1, endpat); } if (needSubst) { char *old_pattern = pattern; /* * XXX: Contrary to ParseModifierPart, a dollar in a ':M' or * ':N' modifier must be escaped as '$$', not as '\$'. */ (void)Var_Subst(pattern, expr->scope, expr->emode, &pattern); /* TODO: handle errors */ free(old_pattern); } DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern); return pattern; } /* :Mpattern or :Npattern */ static ApplyModifierResult ApplyModifier_Match(const char **pp, ModChain *ch) { char mod = **pp; char *pattern; pattern = ParseModifier_Match(pp, ch); if (ModChain_ShouldEval(ch)) { ModifyWordProc modifyWord = mod == 'M' ? ModifyWord_Match : ModifyWord_NoMatch; ModifyWords(ch, modifyWord, pattern, ch->oneBigWord); } free(pattern); return AMR_OK; } static void ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord) { for (;; (*pp)++) { if (**pp == 'g') pflags->subGlobal = true; else if (**pp == '1') pflags->subOnce = true; else if (**pp == 'W') *oneBigWord = true; else break; } } MAKE_INLINE PatternFlags PatternFlags_None(void) { PatternFlags pflags = { false, false, false, false }; return pflags; } /* :S,from,to, */ static ApplyModifierResult ApplyModifier_Subst(const char **pp, ModChain *ch) { struct ModifyWord_SubstArgs args; bool oneBigWord; VarParseResult res; LazyBuf lhsBuf, rhsBuf; char delim = (*pp)[1]; if (delim == '\0') { Error("Missing delimiter for modifier ':S'"); (*pp)++; return AMR_CLEANUP; } *pp += 2; args.pflags = PatternFlags_None(); args.matched = false; if (**pp == '^') { args.pflags.anchorStart = true; (*pp)++; } res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf, &args.pflags, NULL); if (res != VPR_OK) return AMR_CLEANUP; args.lhs = LazyBuf_Get(&lhsBuf); res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf, NULL, &args); if (res != VPR_OK) { LazyBuf_Done(&lhsBuf); return AMR_CLEANUP; } args.rhs = LazyBuf_Get(&rhsBuf); oneBigWord = ch->oneBigWord; ParsePatternFlags(pp, &args.pflags, &oneBigWord); ModifyWords(ch, ModifyWord_Subst, &args, oneBigWord); LazyBuf_Done(&lhsBuf); LazyBuf_Done(&rhsBuf); return AMR_OK; } #ifndef NO_REGEX /* :C,from,to, */ static ApplyModifierResult ApplyModifier_Regex(const char **pp, ModChain *ch) { struct ModifyWord_SubstRegexArgs args; bool oneBigWord; int error; VarParseResult res; LazyBuf reBuf, replaceBuf; FStr re; char delim = (*pp)[1]; if (delim == '\0') { Error("Missing delimiter for :C modifier"); (*pp)++; return AMR_CLEANUP; } *pp += 2; res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf); if (res != VPR_OK) return AMR_CLEANUP; re = LazyBuf_DoneGet(&reBuf); res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf); if (res != VPR_OK) { FStr_Done(&re); return AMR_CLEANUP; } args.replace = LazyBuf_Get(&replaceBuf); args.pflags = PatternFlags_None(); args.matched = false; oneBigWord = ch->oneBigWord; ParsePatternFlags(pp, &args.pflags, &oneBigWord); - if (!ModChain_ShouldEval(ch)) { - LazyBuf_Done(&replaceBuf); - FStr_Done(&re); - return AMR_OK; - } + if (!ModChain_ShouldEval(ch)) + goto done; error = regcomp(&args.re, re.str, REG_EXTENDED); if (error != 0) { VarREError(error, &args.re, "Regex compilation error"); LazyBuf_Done(&replaceBuf); FStr_Done(&re); return AMR_CLEANUP; } args.nsub = args.re.re_nsub + 1; if (args.nsub > 10) args.nsub = 10; ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord); regfree(&args.re); +done: LazyBuf_Done(&replaceBuf); FStr_Done(&re); return AMR_OK; } #endif /* :Q, :q */ static ApplyModifierResult ApplyModifier_Quote(const char **pp, ModChain *ch) { LazyBuf buf; bool quoteDollar; quoteDollar = **pp == 'q'; if (!IsDelimiter((*pp)[1], ch)) return AMR_UNKNOWN; (*pp)++; if (!ModChain_ShouldEval(ch)) return AMR_OK; VarQuote(Expr_Str(ch->expr), quoteDollar, &buf); if (buf.data != NULL) Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf)); else LazyBuf_Done(&buf); return AMR_OK; } /*ARGSUSED*/ static void ModifyWord_Copy(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) { SepBuf_AddSubstring(buf, word); } /* :ts */ static ApplyModifierResult ApplyModifier_ToSep(const char **pp, ModChain *ch) { const char *sep = *pp + 2; /* * Even in parse-only mode, proceed as normal since there is * neither any observable side effect nor a performance penalty. * Checking for wantRes for every single piece of code in here * would make the code in this function too hard to read. */ /* ":ts" or ":ts:" */ if (sep[0] != ch->endc && IsDelimiter(sep[1], ch)) { *pp = sep + 1; ch->sep = sep[0]; goto ok; } /* ":ts" or ":ts:" */ if (IsDelimiter(sep[0], ch)) { *pp = sep; ch->sep = '\0'; /* no separator */ goto ok; } /* ":ts". */ if (sep[0] != '\\') { (*pp)++; /* just for backwards compatibility */ return AMR_BAD; } /* ":ts\n" */ if (sep[1] == 'n') { *pp = sep + 2; ch->sep = '\n'; goto ok; } /* ":ts\t" */ if (sep[1] == 't') { *pp = sep + 2; ch->sep = '\t'; goto ok; } /* ":ts\x40" or ":ts\100" */ { const char *p = sep + 1; int base = 8; /* assume octal */ if (sep[1] == 'x') { base = 16; p++; } else if (!ch_isdigit(sep[1])) { (*pp)++; /* just for backwards compatibility */ return AMR_BAD; /* ":ts". */ } if (!TryParseChar(&p, base, &ch->sep)) { Parse_Error(PARSE_FATAL, "Invalid character number at \"%s\"", p); return AMR_CLEANUP; } if (!IsDelimiter(*p, ch)) { (*pp)++; /* just for backwards compatibility */ return AMR_BAD; } *pp = p; } ok: ModifyWords(ch, ModifyWord_Copy, NULL, ch->oneBigWord); return AMR_OK; } static char * str_toupper(const char *str) { char *res; size_t i, len; len = strlen(str); res = bmake_malloc(len + 1); for (i = 0; i < len + 1; i++) res[i] = ch_toupper(str[i]); return res; } static char * str_tolower(const char *str) { char *res; size_t i, len; len = strlen(str); res = bmake_malloc(len + 1); for (i = 0; i < len + 1; i++) res[i] = ch_tolower(str[i]); return res; } /* :tA, :tu, :tl, :ts, etc. */ static ApplyModifierResult ApplyModifier_To(const char **pp, ModChain *ch) { Expr *expr = ch->expr; const char *mod = *pp; assert(mod[0] == 't'); - if (IsDelimiter(mod[1], ch) || mod[1] == '\0') { + if (IsDelimiter(mod[1], ch)) { *pp = mod + 1; return AMR_BAD; /* Found ":t" or ":t:". */ } if (mod[1] == 's') return ApplyModifier_ToSep(pp, ch); - if (!IsDelimiter(mod[2], ch)) { /* :t */ + if (!IsDelimiter(mod[2], ch)) { /* :t */ *pp = mod + 1; return AMR_BAD; } if (mod[1] == 'A') { /* :tA */ *pp = mod + 2; ModifyWords(ch, ModifyWord_Realpath, NULL, ch->oneBigWord); return AMR_OK; } if (mod[1] == 'u') { /* :tu */ *pp = mod + 2; if (Expr_ShouldEval(expr)) Expr_SetValueOwn(expr, str_toupper(Expr_Str(expr))); return AMR_OK; } if (mod[1] == 'l') { /* :tl */ *pp = mod + 2; if (Expr_ShouldEval(expr)) Expr_SetValueOwn(expr, str_tolower(Expr_Str(expr))); return AMR_OK; } if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */ *pp = mod + 2; ch->oneBigWord = mod[1] == 'W'; return AMR_OK; } /* Found ":t:" or ":t". */ *pp = mod + 1; /* XXX: unnecessary but observable */ return AMR_BAD; } /* :[#], :[1], :[-1..1], etc. */ static ApplyModifierResult ApplyModifier_Words(const char **pp, ModChain *ch) { Expr *expr = ch->expr; const char *estr; int first, last; VarParseResult res; const char *p; LazyBuf estrBuf; FStr festr; (*pp)++; /* skip the '[' */ res = ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf); if (res != VPR_OK) return AMR_CLEANUP; festr = LazyBuf_DoneGet(&estrBuf); estr = festr.str; if (!IsDelimiter(**pp, ch)) goto bad_modifier; /* Found junk after ']' */ if (!ModChain_ShouldEval(ch)) goto ok; if (estr[0] == '\0') goto bad_modifier; /* Found ":[]". */ if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ if (ch->oneBigWord) { Expr_SetValueRefer(expr, "1"); } else { Buffer buf; SubstringWords words = Expr_Words(expr); size_t ac = words.len; SubstringWords_Free(words); /* 3 digits + '\0' is usually enough */ Buf_InitSize(&buf, 4); Buf_AddInt(&buf, (int)ac); Expr_SetValueOwn(expr, Buf_DoneData(&buf)); } goto ok; } if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */ ch->oneBigWord = true; goto ok; } if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */ ch->oneBigWord = false; goto ok; } /* * We expect estr to contain a single integer for :[N], or two * integers separated by ".." for :[start..end]. */ p = estr; if (!TryParseIntBase0(&p, &first)) goto bad_modifier; /* Found junk instead of a number */ if (p[0] == '\0') { /* Found only one integer in :[N] */ last = first; } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') { /* Expecting another integer after ".." */ p += 2; if (!TryParseIntBase0(&p, &last) || *p != '\0') goto bad_modifier; /* Found junk after ".." */ } else goto bad_modifier; /* Found junk instead of ".." */ /* * Now first and last are properly filled in, but we still have to * check for 0 as a special case. */ if (first == 0 && last == 0) { /* ":[0]" or perhaps ":[0..0]" */ ch->oneBigWord = true; goto ok; } /* ":[0..N]" or ":[N..0]" */ if (first == 0 || last == 0) goto bad_modifier; /* Normal case: select the words described by first and last. */ Expr_SetValueOwn(expr, VarSelectWords(Expr_Str(expr), first, last, ch->sep, ch->oneBigWord)); ok: FStr_Done(&festr); return AMR_OK; bad_modifier: FStr_Done(&festr); return AMR_BAD; } #if __STDC__ >= 199901L || defined(HAVE_LONG_LONG_INT) # define NUM_TYPE long long # define PARSE_NUM_TYPE strtoll #else # define NUM_TYPE long # define PARSE_NUM_TYPE strtol #endif static NUM_TYPE num_val(Substring s) { NUM_TYPE val; char *ep; val = PARSE_NUM_TYPE(s.start, &ep, 0); if (ep != s.start) { switch (*ep) { case 'K': case 'k': val <<= 10; break; case 'M': case 'm': val <<= 20; break; case 'G': case 'g': val <<= 30; break; } } return val; } static int SubNumAsc(const void *sa, const void *sb) { NUM_TYPE a, b; a = num_val(*((const Substring *)sa)); b = num_val(*((const Substring *)sb)); return (a > b) ? 1 : (b > a) ? -1 : 0; } static int SubNumDesc(const void *sa, const void *sb) { return SubNumAsc(sb, sa); } static int SubStrAsc(const void *sa, const void *sb) { return strcmp( ((const Substring *)sa)->start, ((const Substring *)sb)->start); } static int SubStrDesc(const void *sa, const void *sb) { return SubStrAsc(sb, sa); } static void ShuffleSubstrings(Substring *strs, size_t n) { size_t i; for (i = n - 1; i > 0; i--) { size_t rndidx = (size_t)random() % (i + 1); Substring t = strs[i]; strs[i] = strs[rndidx]; strs[rndidx] = t; } } /* * :O order ascending * :Or order descending * :Ox shuffle * :On numeric ascending * :Onr, :Orn numeric descending */ static ApplyModifierResult ApplyModifier_Order(const char **pp, ModChain *ch) { const char *mod = *pp; SubstringWords words; int (*cmp)(const void *, const void *); - if (IsDelimiter(mod[1], ch) || mod[1] == '\0') { + if (IsDelimiter(mod[1], ch)) { cmp = SubStrAsc; (*pp)++; - } else if (IsDelimiter(mod[2], ch) || mod[2] == '\0') { + } else if (IsDelimiter(mod[2], ch)) { if (mod[1] == 'n') cmp = SubNumAsc; else if (mod[1] == 'r') cmp = SubStrDesc; else if (mod[1] == 'x') cmp = NULL; else goto bad; *pp += 2; - } else if (IsDelimiter(mod[3], ch) || mod[3] == '\0') { + } else if (IsDelimiter(mod[3], ch)) { if ((mod[1] == 'n' && mod[2] == 'r') || (mod[1] == 'r' && mod[2] == 'n')) cmp = SubNumDesc; else goto bad; *pp += 3; } else goto bad; if (!ModChain_ShouldEval(ch)) return AMR_OK; words = Expr_Words(ch->expr); if (cmp == NULL) ShuffleSubstrings(words.words, words.len); else { assert(words.words[0].end[0] == '\0'); qsort(words.words, words.len, sizeof(words.words[0]), cmp); } Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words)); return AMR_OK; bad: (*pp)++; return AMR_BAD; } /* :? then : else */ static ApplyModifierResult ApplyModifier_IfElse(const char **pp, ModChain *ch) { Expr *expr = ch->expr; VarParseResult res; LazyBuf thenBuf; LazyBuf elseBuf; VarEvalMode then_emode = VARE_PARSE_ONLY; VarEvalMode else_emode = VARE_PARSE_ONLY; CondResult cond_rc = CR_TRUE; /* just not CR_ERROR */ if (Expr_ShouldEval(expr)) { cond_rc = Cond_EvalCondition(expr->name); if (cond_rc == CR_TRUE) then_emode = expr->emode; if (cond_rc == CR_FALSE) else_emode = expr->emode; } (*pp)++; /* skip past the '?' */ res = ParseModifierPart(pp, ':', then_emode, ch, &thenBuf); if (res != VPR_OK) return AMR_CLEANUP; res = ParseModifierPart(pp, ch->endc, else_emode, ch, &elseBuf); if (res != VPR_OK) { LazyBuf_Done(&thenBuf); return AMR_CLEANUP; } (*pp)--; /* Go back to the ch->endc. */ if (cond_rc == CR_ERROR) { Substring thenExpr = LazyBuf_Get(&thenBuf); Substring elseExpr = LazyBuf_Get(&elseBuf); Error("Bad conditional expression '%s' in '%s?%.*s:%.*s'", expr->name, expr->name, (int)Substring_Length(thenExpr), thenExpr.start, (int)Substring_Length(elseExpr), elseExpr.start); LazyBuf_Done(&thenBuf); LazyBuf_Done(&elseBuf); return AMR_CLEANUP; } if (!Expr_ShouldEval(expr)) { LazyBuf_Done(&thenBuf); LazyBuf_Done(&elseBuf); } else if (cond_rc == CR_TRUE) { Expr_SetValue(expr, LazyBuf_DoneGet(&thenBuf)); LazyBuf_Done(&elseBuf); } else { LazyBuf_Done(&thenBuf); Expr_SetValue(expr, LazyBuf_DoneGet(&elseBuf)); } Expr_Define(expr); return AMR_OK; } /* * The ::= modifiers are special in that they do not read the variable value * but instead assign to that variable. They always expand to an empty * string. * * Their main purpose is in supporting .for loops that generate shell commands * since an ordinary variable assignment at that point would terminate the * dependency group for these targets. For example: * * list-targets: .USE * .for i in ${.TARGET} ${.TARGET:R}.gz * @${t::=$i} * @echo 'The target is ${t:T}.' * .endfor * * ::= Assigns as the new value of variable. * ::?= Assigns as value of variable if * it was not already set. * ::+= Appends to variable. * ::!= Assigns output of as the new value of * variable. */ static ApplyModifierResult ApplyModifier_Assign(const char **pp, ModChain *ch) { Expr *expr = ch->expr; GNode *scope; FStr val; VarParseResult res; LazyBuf buf; const char *mod = *pp; const char *op = mod + 1; if (op[0] == '=') goto found_op; if ((op[0] == '+' || op[0] == '?' || op[0] == '!') && op[1] == '=') goto found_op; return AMR_UNKNOWN; /* "::" */ found_op: if (expr->name[0] == '\0') { *pp = mod + 1; return AMR_BAD; } *pp = mod + (op[0] == '+' || op[0] == '?' || op[0] == '!' ? 3 : 2); res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf); if (res != VPR_OK) return AMR_CLEANUP; val = LazyBuf_DoneGet(&buf); (*pp)--; /* Go back to the ch->endc. */ if (!Expr_ShouldEval(expr)) goto done; scope = expr->scope; /* scope where v belongs */ if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) { Var *v = VarFind(expr->name, expr->scope, false); if (v == NULL) scope = SCOPE_GLOBAL; else VarFreeShortLived(v); } if (op[0] == '+') Var_Append(scope, expr->name, val.str); else if (op[0] == '!') { char *output, *error; output = Cmd_Exec(val.str, &error); if (error != NULL) { Error("%s", error); free(error); } else Var_Set(scope, expr->name, output); free(output); } else if (op[0] == '?' && expr->defined == DEF_REGULAR) { /* Do nothing. */ } else Var_Set(scope, expr->name, val.str); Expr_SetValueRefer(expr, ""); done: FStr_Done(&val); return AMR_OK; } /* * :_=... * remember current value */ static ApplyModifierResult ApplyModifier_Remember(const char **pp, ModChain *ch) { Expr *expr = ch->expr; const char *mod = *pp; FStr name; if (!ModMatchEq(mod, "_", ch)) return AMR_UNKNOWN; name = FStr_InitRefer("_"); if (mod[1] == '=') { /* * XXX: This ad-hoc call to strcspn deviates from the usual * behavior defined in ParseModifierPart. This creates an * unnecessary, undocumented inconsistency in make. */ const char *arg = mod + 2; size_t argLen = strcspn(arg, ":)}"); *pp = arg + argLen; name = FStr_InitOwn(bmake_strldup(arg, argLen)); } else *pp = mod + 1; if (Expr_ShouldEval(expr)) Var_Set(expr->scope, name.str, Expr_Str(expr)); FStr_Done(&name); return AMR_OK; } /* * Apply the given function to each word of the variable value, * for a single-letter modifier such as :H, :T. */ static ApplyModifierResult ApplyModifier_WordFunc(const char **pp, ModChain *ch, ModifyWordProc modifyWord) { if (!IsDelimiter((*pp)[1], ch)) return AMR_UNKNOWN; (*pp)++; if (ModChain_ShouldEval(ch)) ModifyWords(ch, modifyWord, NULL, ch->oneBigWord); return AMR_OK; } /* Remove adjacent duplicate words. */ static ApplyModifierResult ApplyModifier_Unique(const char **pp, ModChain *ch) { SubstringWords words; if (!IsDelimiter((*pp)[1], ch)) return AMR_UNKNOWN; (*pp)++; if (!ModChain_ShouldEval(ch)) return AMR_OK; words = Expr_Words(ch->expr); if (words.len > 1) { size_t si, di; di = 0; for (si = 1; si < words.len; si++) { if (!Substring_Eq(words.words[si], words.words[di])) { di++; if (di != si) words.words[di] = words.words[si]; } } words.len = di + 1; } Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words)); return AMR_OK; } #ifdef SYSVVARSUB /* :from=to */ static ApplyModifierResult ApplyModifier_SysV(const char **pp, ModChain *ch) { Expr *expr = ch->expr; VarParseResult res; LazyBuf lhsBuf, rhsBuf; FStr rhs; struct ModifyWord_SysVSubstArgs args; Substring lhs; const char *lhsSuffix; const char *mod = *pp; bool eqFound = false; /* * First we make a pass through the string trying to verify it is a * SysV-make-style translation. It must be: = */ int depth = 1; const char *p = mod; while (*p != '\0' && depth > 0) { if (*p == '=') { /* XXX: should also test depth == 1 */ eqFound = true; /* continue looking for ch->endc */ } else if (*p == ch->endc) depth--; else if (*p == ch->startc) depth++; if (depth > 0) p++; } if (*p != ch->endc || !eqFound) return AMR_UNKNOWN; res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf); if (res != VPR_OK) return AMR_CLEANUP; /* * The SysV modifier lasts until the end of the variable expression. */ res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf); if (res != VPR_OK) { LazyBuf_Done(&lhsBuf); return AMR_CLEANUP; } rhs = LazyBuf_DoneGet(&rhsBuf); (*pp)--; /* Go back to the ch->endc. */ /* Do not turn an empty expression into non-empty. */ if (lhsBuf.len == 0 && Expr_Str(expr)[0] == '\0') goto done; lhs = LazyBuf_Get(&lhsBuf); lhsSuffix = Substring_SkipFirst(lhs, '%'); args.scope = expr->scope; args.lhsPrefix = Substring_Init(lhs.start, lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start); args.lhsPercent = lhsSuffix != lhs.start; args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end); args.rhs = rhs.str; ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord); done: LazyBuf_Done(&lhsBuf); return AMR_OK; } #endif #ifdef SUNSHCMD /* :sh */ static ApplyModifierResult ApplyModifier_SunShell(const char **pp, ModChain *ch) { Expr *expr = ch->expr; const char *p = *pp; if (!(p[1] == 'h' && IsDelimiter(p[2], ch))) return AMR_UNKNOWN; *pp = p + 2; if (Expr_ShouldEval(expr)) { char *output, *error; output = Cmd_Exec(Expr_Str(expr), &error); if (error != NULL) { Error("%s", error); free(error); } Expr_SetValueOwn(expr, output); } return AMR_OK; } #endif static void LogBeforeApply(const ModChain *ch, const char *mod) { const Expr *expr = ch->expr; bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch); /* * At this point, only the first character of the modifier can * be used since the end of the modifier is not yet known. */ if (!Expr_ShouldEval(expr)) { debug_printf("Parsing modifier ${%s:%c%s}\n", expr->name, mod[0], is_single_char ? "" : "..."); return; } if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) && expr->defined == DEF_REGULAR) { debug_printf( "Evaluating modifier ${%s:%c%s} on value \"%s\"\n", expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr)); return; } debug_printf( "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n", expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr), VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]); } static void LogAfterApply(const ModChain *ch, const char *p, const char *mod) { const Expr *expr = ch->expr; const char *value = Expr_Str(expr); const char *quot = value == var_Error ? "" : "\""; if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) && expr->defined == DEF_REGULAR) { debug_printf("Result of ${%s:%.*s} is %s%s%s\n", expr->name, (int)(p - mod), mod, quot, value == var_Error ? "error" : value, quot); return; } debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n", expr->name, (int)(p - mod), mod, quot, value == var_Error ? "error" : value, quot, VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]); } static ApplyModifierResult ApplyModifier(const char **pp, ModChain *ch) { switch (**pp) { case '!': return ApplyModifier_ShellCommand(pp, ch); case ':': return ApplyModifier_Assign(pp, ch); case '?': return ApplyModifier_IfElse(pp, ch); case '@': return ApplyModifier_Loop(pp, ch); case '[': return ApplyModifier_Words(pp, ch); case '_': return ApplyModifier_Remember(pp, ch); #ifndef NO_REGEX case 'C': return ApplyModifier_Regex(pp, ch); #endif case 'D': case 'U': return ApplyModifier_Defined(pp, ch); case 'E': return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix); case 'g': case 'l': return ApplyModifier_Time(pp, ch); case 'H': return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head); case 'h': return ApplyModifier_Hash(pp, ch); case 'L': return ApplyModifier_Literal(pp, ch); case 'M': case 'N': return ApplyModifier_Match(pp, ch); case 'O': return ApplyModifier_Order(pp, ch); case 'P': return ApplyModifier_Path(pp, ch); case 'Q': case 'q': return ApplyModifier_Quote(pp, ch); case 'R': return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root); case 'r': return ApplyModifier_Range(pp, ch); case 'S': return ApplyModifier_Subst(pp, ch); #ifdef SUNSHCMD case 's': return ApplyModifier_SunShell(pp, ch); #endif case 'T': return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail); case 't': return ApplyModifier_To(pp, ch); case 'u': return ApplyModifier_Unique(pp, ch); default: return AMR_UNKNOWN; } } static void ApplyModifiers(Expr *, const char **, char, char); typedef enum ApplyModifiersIndirectResult { /* The indirect modifiers have been applied successfully. */ AMIR_CONTINUE, /* Fall back to the SysV modifier. */ AMIR_SYSV, /* Error out. */ AMIR_OUT } ApplyModifiersIndirectResult; /* * While expanding a variable expression, expand and apply indirect modifiers, * such as in ${VAR:${M_indirect}}. * * All indirect modifiers of a group must come from a single variable * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not. * * Multiple groups of indirect modifiers can be chained by separating them * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers. * * If the variable expression is not followed by ch->endc or ':', fall * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}. */ static ApplyModifiersIndirectResult ApplyModifiersIndirect(ModChain *ch, const char **pp) { Expr *expr = ch->expr; const char *p = *pp; FStr mods; (void)Var_Parse(&p, expr->scope, expr->emode, &mods); /* TODO: handle errors */ - if (mods.str[0] != '\0' && *p != '\0' && !IsDelimiter(*p, ch)) { + if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) { FStr_Done(&mods); return AMIR_SYSV; } DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n", mods.str, (int)(p - *pp), *pp); if (mods.str[0] != '\0') { const char *modsp = mods.str; ApplyModifiers(expr, &modsp, '\0', '\0'); if (Expr_Str(expr) == var_Error || *modsp != '\0') { FStr_Done(&mods); *pp = p; return AMIR_OUT; /* error already reported */ } } FStr_Done(&mods); if (*p == ':') p++; else if (*p == '\0' && ch->endc != '\0') { Error("Unclosed variable expression after indirect " "modifier, expecting '%c' for variable \"%s\"", ch->endc, expr->name); *pp = p; return AMIR_OUT; } *pp = p; return AMIR_CONTINUE; } static ApplyModifierResult ApplySingleModifier(const char **pp, ModChain *ch) { ApplyModifierResult res; const char *mod = *pp; const char *p = *pp; if (DEBUG(VAR)) LogBeforeApply(ch, mod); res = ApplyModifier(&p, ch); #ifdef SYSVVARSUB if (res == AMR_UNKNOWN) { assert(p == mod); res = ApplyModifier_SysV(&p, ch); } #endif if (res == AMR_UNKNOWN) { /* * Guess the end of the current modifier. * XXX: Skipping the rest of the modifier hides * errors and leads to wrong results. * Parsing should rather stop here. */ - for (p++; !IsDelimiter(*p, ch) && *p != '\0'; p++) + for (p++; !IsDelimiter(*p, ch); p++) continue; Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"", (int)(p - mod), mod); Expr_SetValueRefer(ch->expr, var_Error); } if (res == AMR_CLEANUP || res == AMR_BAD) { *pp = p; return res; } if (DEBUG(VAR)) LogAfterApply(ch, p, mod); if (*p == '\0' && ch->endc != '\0') { Error( "Unclosed variable expression, expecting '%c' for " "modifier \"%.*s\" of variable \"%s\" with value \"%s\"", ch->endc, (int)(p - mod), mod, ch->expr->name, Expr_Str(ch->expr)); } else if (*p == ':') { p++; } else if (opts.strict && *p != '\0' && *p != ch->endc) { Parse_Error(PARSE_FATAL, "Missing delimiter ':' after modifier \"%.*s\"", (int)(p - mod), mod); /* * TODO: propagate parse error to the enclosing * expression */ } *pp = p; return AMR_OK; } #if __STDC_VERSION__ >= 199901L #define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \ (ModChain) { expr, startc, endc, sep, oneBigWord } #else MAKE_INLINE ModChain ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord) { ModChain ch; ch.expr = expr; ch.startc = startc; ch.endc = endc; ch.sep = sep; ch.oneBigWord = oneBigWord; return ch; } #endif /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ static void ApplyModifiers( Expr *expr, const char **pp, /* the parsing position, updated upon return */ char startc, /* '(' or '{'; or '\0' for indirect modifiers */ char endc /* ')' or '}'; or '\0' for indirect modifiers */ ) { ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false); const char *p; const char *mod; assert(startc == '(' || startc == '{' || startc == '\0'); assert(endc == ')' || endc == '}' || endc == '\0'); assert(Expr_Str(expr) != NULL); p = *pp; if (*p == '\0' && endc != '\0') { Error( "Unclosed variable expression (expecting '%c') for \"%s\"", ch.endc, expr->name); goto cleanup; } while (*p != '\0' && *p != endc) { ApplyModifierResult res; if (*p == '$') { ApplyModifiersIndirectResult amir = ApplyModifiersIndirect(&ch, &p); if (amir == AMIR_CONTINUE) continue; if (amir == AMIR_OUT) break; /* * It's neither '${VAR}:' nor '${VAR}}'. Try to parse * it as a SysV modifier, as that is the only modifier * that can start with '$'. */ } mod = p; res = ApplySingleModifier(&p, &ch); if (res == AMR_CLEANUP) goto cleanup; if (res == AMR_BAD) goto bad_modifier; } *pp = p; assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */ return; bad_modifier: /* XXX: The modifier end is only guessed. */ Error("Bad modifier \":%.*s\" for variable \"%s\"", (int)strcspn(mod, ":)}"), mod, expr->name); cleanup: /* * TODO: Use p + strlen(p) instead, to stop parsing immediately. * * In the unit tests, this generates a few shell commands with * unbalanced quotes. Instead of producing these incomplete strings, * commands with evaluation errors should not be run at all. * * To make that happen, Var_Subst must report the actual errors * instead of returning VPR_OK unconditionally. */ *pp = p; Expr_SetValueRefer(expr, var_Error); } /* * Only 4 of the 7 built-in local variables are treated specially as they are * the only ones that will be set when dynamic sources are expanded. */ static bool VarnameIsDynamic(Substring varname) { const char *name; size_t len; name = varname.start; len = Substring_Length(varname); if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) { switch (name[0]) { case '@': case '%': case '*': case '!': return true; } return false; } if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) { return Substring_Equals(varname, ".TARGET") || Substring_Equals(varname, ".ARCHIVE") || Substring_Equals(varname, ".PREFIX") || Substring_Equals(varname, ".MEMBER"); } return false; } static const char * UndefinedShortVarValue(char varname, const GNode *scope) { if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) { /* * If substituting a local variable in a non-local scope, * assume it's for dynamic source stuff. We have to handle * this specially and return the longhand for the variable * with the dollar sign escaped so it makes it back to the * caller. Only four of the local variables are treated * specially as they are the only four that will be set * when dynamic sources are expanded. */ switch (varname) { case '@': return "$(.TARGET)"; case '%': return "$(.MEMBER)"; case '*': return "$(.PREFIX)"; case '!': return "$(.ARCHIVE)"; } } return NULL; } /* * Parse a variable name, until the end character or a colon, whichever * comes first. */ static void ParseVarname(const char **pp, char startc, char endc, GNode *scope, VarEvalMode emode, LazyBuf *buf) { const char *p = *pp; int depth = 0; /* Track depth so we can spot parse errors. */ LazyBuf_Init(buf, p); while (*p != '\0') { if ((*p == endc || *p == ':') && depth == 0) break; if (*p == startc) depth++; if (*p == endc) depth--; /* A variable inside a variable, expand. */ if (*p == '$') { FStr nested_val; (void)Var_Parse(&p, scope, emode, &nested_val); /* TODO: handle errors */ LazyBuf_AddStr(buf, nested_val.str); FStr_Done(&nested_val); } else { LazyBuf_Add(buf, *p); p++; } } *pp = p; } static bool IsShortVarnameValid(char varname, const char *start) { if (varname != '$' && varname != ':' && varname != '}' && varname != ')' && varname != '\0') return true; if (!opts.strict) return false; /* XXX: Missing error message */ if (varname == '$') Parse_Error(PARSE_FATAL, "To escape a dollar, use \\$, not $$, at \"%s\"", start); else if (varname == '\0') Parse_Error(PARSE_FATAL, "Dollar followed by nothing"); else Parse_Error(PARSE_FATAL, "Invalid variable name '%c', at \"%s\"", varname, start); return false; } /* * Parse a single-character variable name such as in $V or $@. * Return whether to continue parsing. */ static bool ParseVarnameShort(char varname, const char **pp, GNode *scope, VarEvalMode emode, VarParseResult *out_false_res, const char **out_false_val, Var **out_true_var) { char name[2]; Var *v; const char *val; if (!IsShortVarnameValid(varname, *pp)) { (*pp)++; /* only skip the '$' */ *out_false_res = VPR_ERR; *out_false_val = var_Error; return false; } name[0] = varname; name[1] = '\0'; v = VarFind(name, scope, true); if (v != NULL) { /* No need to advance *pp, the calling code handles this. */ *out_true_var = v; return true; } *pp += 2; val = UndefinedShortVarValue(varname, scope); if (val == NULL) val = emode == VARE_UNDEFERR ? var_Error : varUndefined; if (opts.strict && val == var_Error) { Parse_Error(PARSE_FATAL, "Variable \"%s\" is undefined", name); *out_false_res = VPR_ERR; *out_false_val = val; return false; } /* * XXX: This looks completely wrong. * * If undefined expressions are not allowed, this should * rather be VPR_ERR instead of VPR_UNDEF, together with an * error message. * * If undefined expressions are allowed, this should rather * be VPR_UNDEF instead of VPR_OK. */ *out_false_res = emode == VARE_UNDEFERR ? VPR_UNDEF : VPR_OK; *out_false_val = val; return false; } /* Find variables like @F or ", varname.start[0]) == NULL) return NULL; v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false); if (v == NULL) return NULL; *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:"; return v; } static VarParseResult EvalUndefined(bool dynamic, const char *start, const char *p, Substring varname, VarEvalMode emode, FStr *out_val) { if (dynamic) { *out_val = FStr_InitOwn(bmake_strsedup(start, p)); return VPR_OK; } if (emode == VARE_UNDEFERR && opts.strict) { Parse_Error(PARSE_FATAL, "Variable \"%.*s\" is undefined", (int)Substring_Length(varname), varname.start); *out_val = FStr_InitRefer(var_Error); return VPR_ERR; } if (emode == VARE_UNDEFERR) { *out_val = FStr_InitRefer(var_Error); return VPR_UNDEF; /* XXX: Should be VPR_ERR instead. */ } *out_val = FStr_InitRefer(varUndefined); return VPR_OK; } /* * Parse a long variable name enclosed in braces or parentheses such as $(VAR) * or ${VAR}, up to the closing brace or parenthesis, or in the case of * ${VAR:Modifiers}, up to the ':' that starts the modifiers. * Return whether to continue parsing. */ static bool ParseVarnameLong( const char **pp, char startc, GNode *scope, VarEvalMode emode, const char **out_false_pp, VarParseResult *out_false_res, FStr *out_false_val, char *out_true_endc, Var **out_true_v, bool *out_true_haveModifier, const char **out_true_extraModifiers, bool *out_true_dynamic, ExprDefined *out_true_exprDefined ) { LazyBuf varname; Substring name; Var *v; bool haveModifier; bool dynamic = false; const char *p = *pp; const char *const start = p; char endc = startc == '(' ? ')' : '}'; p += 2; /* skip "${" or "$(" or "y(" */ ParseVarname(&p, startc, endc, scope, emode, &varname); name = LazyBuf_Get(&varname); if (*p == ':') { haveModifier = true; } else if (*p == endc) { haveModifier = false; } else { Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"", (int)Substring_Length(name), name.start); LazyBuf_Done(&varname); *out_false_pp = p; *out_false_val = FStr_InitRefer(var_Error); *out_false_res = VPR_ERR; return false; } v = VarFindSubstring(name, scope, true); /* * At this point, p points just after the variable name, either at * ':' or at endc. */ if (v == NULL && Substring_Equals(name, ".SUFFIXES")) { char *suffixes = Suff_NamesStr(); v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes, true, false, true); free(suffixes); } else if (v == NULL) v = FindLocalLegacyVar(name, scope, out_true_extraModifiers); if (v == NULL) { /* * Defer expansion of dynamic variables if they appear in * non-local scope since they are not defined there. */ dynamic = VarnameIsDynamic(name) && (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL); if (!haveModifier) { p++; /* skip endc */ *out_false_pp = p; *out_false_res = EvalUndefined(dynamic, start, p, name, emode, out_false_val); LazyBuf_Done(&varname); return false; } /* * The variable expression is based on an undefined variable. * Nevertheless it needs a Var, for modifiers that access the * variable name, such as :L or :?. * * Most modifiers leave this expression in the "undefined" * state (VES_UNDEF), only a few modifiers like :D, :U, :L, * :P turn this undefined expression into a defined * expression (VES_DEF). * * In the end, after applying all modifiers, if the expression * is still undefined, Var_Parse will return an empty string * instead of the actually computed value. */ v = VarNew(LazyBuf_DoneGet(&varname), "", true, false, false); *out_true_exprDefined = DEF_UNDEF; } else LazyBuf_Done(&varname); *pp = p; *out_true_endc = endc; *out_true_v = v; *out_true_haveModifier = haveModifier; *out_true_dynamic = dynamic; return true; } #if __STDC_VERSION__ >= 199901L #define Expr_Literal(name, value, emode, scope, defined) \ { name, value, emode, scope, defined } #else MAKE_INLINE Expr Expr_Literal(const char *name, FStr value, VarEvalMode emode, GNode *scope, ExprDefined defined) { Expr expr; expr.name = name; expr.value = value; expr.emode = emode; expr.scope = scope; expr.defined = defined; return expr; } #endif /* * Expressions of the form ${:U...} with a trivial value are often generated * by .for loops and are boring, therefore parse and evaluate them in a fast * lane without debug logging. */ static bool Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value) { const char *p; p = *pp; if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U')) return false; p += 4; while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' && *p != '}' && *p != '\0') p++; if (*p != '}') return false; if (emode == VARE_PARSE_ONLY) *out_value = FStr_InitRefer(""); else *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p)); *pp = p + 1; return true; } /* * Given the start of a variable expression (such as $v, $(VAR), * ${VAR:Mpattern}), extract the variable name and value, and the modifiers, * if any. While doing that, apply the modifiers to the value of the * expression, forming its final value. A few of the modifiers such as :!cmd! * or ::= have side effects. * * Input: * *pp The string to parse. * When called from CondParser_FuncCallEmpty, it can * also point to the "y" of "empty(VARNAME:Modifiers)". * scope The scope for finding variables * emode Controls the exact details of parsing and evaluation * * Output: * *pp The position where to continue parsing. * TODO: After a parse error, the value of *pp is * unspecified. It may not have been updated at all, * point to some random character in the string, to the * location of the parse error, or at the end of the * string. * *out_val The value of the variable expression, never NULL. * *out_val var_Error if there was a parse error. * *out_val var_Error if the base variable of the expression was * undefined, emode is VARE_UNDEFERR, and none of * the modifiers turned the undefined expression into a * defined expression. * XXX: It is not guaranteed that an error message has * been printed. * *out_val varUndefined if the base variable of the expression * was undefined, emode was not VARE_UNDEFERR, * and none of the modifiers turned the undefined * expression into a defined expression. * XXX: It is not guaranteed that an error message has * been printed. */ VarParseResult Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val) { const char *p = *pp; const char *const start = p; bool haveModifier; /* true for ${VAR:...}, false for ${VAR} */ char startc; /* the actual '{' or '(' or '\0' */ char endc; /* the expected '}' or ')' or '\0' */ /* * true if the expression is based on one of the 7 predefined * variables that are local to a target, and the expression is * expanded in a non-local scope. The result is the text of the * expression, unaltered. This is needed to support dynamic sources. */ bool dynamic; const char *extramodifiers; Var *v; Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode, scope, DEF_REGULAR); if (Var_Parse_FastLane(pp, emode, out_val)) return VPR_OK; + /* TODO: Reduce computations in parse-only mode. */ + DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]); *out_val = FStr_InitRefer(NULL); extramodifiers = NULL; /* extra modifiers to apply first */ dynamic = false; endc = '\0'; /* Appease GCC. */ startc = p[1]; if (startc != '(' && startc != '{') { VarParseResult res; if (!ParseVarnameShort(startc, pp, scope, emode, &res, &out_val->str, &v)) return res; haveModifier = false; p++; } else { VarParseResult res; if (!ParseVarnameLong(&p, startc, scope, emode, pp, &res, out_val, &endc, &v, &haveModifier, &extramodifiers, &dynamic, &expr.defined)) return res; } expr.name = v->name.str; - if (v->inUse) { + if (v->inUse && VarEvalMode_ShouldEval(emode)) { if (scope->fname != NULL) { fprintf(stderr, "In a command near "); PrintLocation(stderr, false, scope); } Fatal("Variable %s is recursive.", v->name.str); } /* * XXX: This assignment creates an alias to the current value of the * variable. This means that as long as the value of the expression * stays the same, the value of the variable must not change. * Using the '::=' modifier, it could be possible to trigger exactly * this situation. * * At the bottom of this function, the resulting value is compared to * the then-current value of the variable. This might also invoke * undefined behavior. */ expr.value = FStr_InitRefer(v->val.data); /* * Before applying any modifiers, expand any nested expressions from * the variable value. */ if (VarEvalMode_ShouldEval(emode) && strchr(Expr_Str(&expr), '$') != NULL) { char *expanded; VarEvalMode nested_emode = emode; if (opts.strict) nested_emode = VarEvalMode_UndefOk(nested_emode); v->inUse = true; (void)Var_Subst(Expr_Str(&expr), scope, nested_emode, &expanded); v->inUse = false; /* TODO: handle errors */ Expr_SetValueOwn(&expr, expanded); } if (extramodifiers != NULL) { const char *em = extramodifiers; ApplyModifiers(&expr, &em, '\0', '\0'); } if (haveModifier) { p++; /* Skip initial colon. */ ApplyModifiers(&expr, &p, startc, endc); } if (*p != '\0') /* Skip past endc if possible. */ p++; *pp = p; if (expr.defined == DEF_UNDEF) { if (dynamic) Expr_SetValueOwn(&expr, bmake_strsedup(start, p)); else { /* * The expression is still undefined, therefore * discard the actual value and return an error marker * instead. */ Expr_SetValueRefer(&expr, emode == VARE_UNDEFERR ? var_Error : varUndefined); } } if (v->shortLived) { if (expr.value.str == v->val.data) { /* move ownership */ expr.value.freeIt = v->val.data; v->val.data = NULL; } VarFreeShortLived(v); } *out_val = expr.value; return VPR_OK; /* XXX: Is not correct in all cases */ } static void VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode) { /* A dollar sign may be escaped with another dollar sign. */ if (save_dollars && VarEvalMode_ShouldKeepDollar(emode)) Buf_AddByte(res, '$'); Buf_AddByte(res, '$'); *pp += 2; } static void VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, VarEvalMode emode, bool *inout_errorReported) { const char *p = *pp; const char *nested_p = p; FStr val; (void)Var_Parse(&nested_p, scope, emode, &val); /* TODO: handle errors */ if (val.str == var_Error || val.str == varUndefined) { if (!VarEvalMode_ShouldKeepUndef(emode)) { p = nested_p; } else if (val.str == var_Error) { /* * XXX: This condition is wrong. If val == var_Error, * this doesn't necessarily mean there was an undefined * variable. It could equally well be a parse error; * see unit-tests/varmod-order.exp. */ /* * If variable is undefined, complain and skip the * variable. The complaint will stop us from doing * anything when the file is parsed. */ if (!*inout_errorReported) { Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"", (int)(size_t)(nested_p - p), p); } p = nested_p; *inout_errorReported = true; } else { /* * Copy the initial '$' of the undefined expression, * thereby deferring expansion of the expression, but * expand nested expressions if already possible. See * unit-tests/varparse-undef-partial.mk. */ Buf_AddByte(buf, *p); p++; } } else { p = nested_p; Buf_AddStr(buf, val.str); } FStr_Done(&val); *pp = p; } /* * Skip as many characters as possible -- either to the end of the string * or to the next dollar sign (variable expression). */ static void VarSubstPlain(const char **pp, Buffer *res) { const char *p = *pp; const char *start = p; for (p++; *p != '$' && *p != '\0'; p++) continue; Buf_AddBytesBetween(res, start, p); *pp = p; } /* * Expand all variable expressions like $V, ${VAR}, $(VAR:Modifiers) in the * given string. * * Input: * str The string in which the variable expressions are * expanded. * scope The scope in which to start searching for * variables. The other scopes are searched as well. * emode The mode for parsing or evaluating subexpressions. */ VarParseResult Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res) { const char *p = str; Buffer res; /* * Set true if an error has already been reported, to prevent a * plethora of messages when recursing */ /* See varparse-errors.mk for why the 'static' is necessary here. */ static bool errorReported; Buf_Init(&res); errorReported = false; while (*p != '\0') { if (p[0] == '$' && p[1] == '$') VarSubstDollarDollar(&p, &res, emode); else if (p[0] == '$') VarSubstExpr(&p, &res, scope, emode, &errorReported); else VarSubstPlain(&p, &res); } *out_res = Buf_DoneDataCompact(&res); return VPR_OK; } void Var_Expand(FStr *str, GNode *scope, VarEvalMode emode) { char *expanded; if (strchr(str->str, '$') == NULL) return; (void)Var_Subst(str->str, scope, emode, &expanded); /* TODO: handle errors */ FStr_Done(str); *str = FStr_InitOwn(expanded); } /* Initialize the variables module. */ void Var_Init(void) { SCOPE_INTERNAL = GNode_New("Internal"); SCOPE_GLOBAL = GNode_New("Global"); SCOPE_CMDLINE = GNode_New("Command"); } /* Clean up the variables module. */ void Var_End(void) { Var_Stats(); } void Var_Stats(void) { HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); } static int StrAsc(const void *sa, const void *sb) { return strcmp( *((const char *const *)sa), *((const char *const *)sb)); } /* Print all variables in a scope, sorted by name. */ void Var_Dump(GNode *scope) { Vector /* of const char * */ vec; HashIter hi; size_t i; const char **varnames; Vector_Init(&vec, sizeof(const char *)); HashIter_Init(&hi, &scope->vars); while (HashIter_Next(&hi) != NULL) *(const char **)Vector_Push(&vec) = hi.entry->key; varnames = vec.items; qsort(varnames, vec.len, sizeof varnames[0], StrAsc); for (i = 0; i < vec.len; i++) { const char *varname = varnames[i]; - Var *var = HashTable_FindValue(&scope->vars, varname); + const Var *var = HashTable_FindValue(&scope->vars, varname); debug_printf("%-16s = %s%s\n", varname, var->val.data, ValueDescription(var->val.data)); } Vector_Done(&vec); } diff --git a/usr.bin/bmake/Makefile b/usr.bin/bmake/Makefile index c8d2dbd63b2b..722de897acbb 100644 --- a/usr.bin/bmake/Makefile +++ b/usr.bin/bmake/Makefile @@ -1,181 +1,181 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ SRCTOP?= ${.CURDIR:H:H} # look here first for config.h CFLAGS+= -I${.CURDIR} # for after-import CLEANDIRS+= FreeBSD CLEANFILES+= bootstrap -# $Id: Makefile,v 1.120 2022/07/26 23:02:54 sjg Exp $ +# $Id: Makefile,v 1.122 2022/10/08 02:53:30 sjg Exp $ PROG?= ${.CURDIR:T} SRCS= \ arch.c \ buf.c \ compat.c \ cond.c \ dir.c \ for.c \ hash.c \ job.c \ lst.c \ main.c \ make.c \ make_malloc.c \ meta.c \ metachar.c \ parse.c \ str.c \ suff.c \ targ.c \ trace.c \ util.c \ var.c .sinclude "Makefile.inc" # this file gets generated by configure .sinclude "Makefile.config" .if !empty(LIBOBJS) SRCS+= ${LIBOBJS:T:.o=.c} .endif # just in case prefix?= /usr srcdir?= ${.CURDIR} DEFAULT_SYS_PATH?= ${prefix}/share/mk CPPFLAGS+= -DUSE_META CFLAGS+= ${CPPFLAGS} CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}} COPTS.main.c+= "-DMAKE_VERSION=\"${_MAKE_VERSION}\"" .for x in FORCE_MACHINE FORCE_MACHINE_ARCH .ifdef $x COPTS.main.c+= "-D$x=\"${$x}\"" .endif .endfor # meta mode can be useful even without filemon # should be set by now USE_FILEMON ?= no .if ${USE_FILEMON:tl} != "no" .PATH: ${srcdir}/filemon SRCS+= filemon_${USE_FILEMON}.c COPTS.meta.c+= -DUSE_FILEMON -DUSE_FILEMON_${USE_FILEMON:tu} COPTS.job.c+= ${COPTS.meta.c} .if ${USE_FILEMON} == "dev" FILEMON_H ?= /usr/include/dev/filemon/filemon.h .if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h" COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} .endif .endif # USE_FILEMON == dev .endif # USE_FILEMON .PATH: ${srcdir} .if make(obj) || make(clean) SUBDIR+= unit-tests .endif MAN= ${PROG}.1 MAN1= ${MAN} .if ${PROG} != "make" CLEANFILES+= my.history .if make(${MAN}) || !exists(${srcdir}/${MAN}) my.history: @(echo ".Nm"; \ echo "is derived from NetBSD"; \ echo ".Xr make 1 ."; \ echo "It uses autoconf to facilitate portability to other platforms."; \ echo ".Pp") > $@ .NOPATH: ${MAN} ${MAN}: make.1 my.history @echo making $@ @sed \ -e '/^.Dt/s/MAKE/${PROG:tu}/' \ -e 's/^.Nx/NetBSD/' \ -e '/^.Nm/s/make/${PROG}/' \ -e '/^.Sh HISTORY/rmy.history' \ -e '/^.Sh HISTORY/,$$s,^.Nm,make,' ${srcdir}/make.1 > $@ all beforeinstall: ${MAN} _mfromdir=. .endif .endif MANTARGET?= cat MANDEST?= ${MANDIR}/${MANTARGET}1 .if ${MANTARGET} == "cat" _mfromdir=${srcdir} .endif .include CPPFLAGS+= -DMAKE_NATIVE -DHAVE_CONFIG_H COPTS.var.c += -Wno-cast-qual COPTS.job.c += -Wno-format-nonliteral COPTS.parse.c += -Wno-format-nonliteral COPTS.var.c += -Wno-format-nonliteral # Force these SHAREDIR= ${SHAREDIR.bmake:U${prefix}/share} BINDIR= ${BINDIR.bmake:U${prefix}/bin} MANDIR= ${MANDIR.bmake:U${SHAREDIR}/man} .if !exists(.depend) ${OBJS}: config.h .endif # A simple unit-test driver to help catch regressions TEST_MAKE ?= ${.OBJDIR}/${PROG:T} accept test: .NOMETA cd ${.CURDIR}/unit-tests && \ MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}} .if make(test) && ${MK_AUTO_OBJ} == "yes" # The test target above visits unit-tests with -r -m / # which prevents MK_AUTO_OBJ doing its job # so do it here .if defined(MAKEOBJDIRPREFIX) || ${MAKEOBJDIR:U:M*/*} != "" _utobj = ${.OBJDIR}/unit-tests .else _utobj = ${.CURDIR}/unit-tests/${MAKEOBJDIR:Uobj} .endif utobj: .NOMETA @test -d ${_utobj} && exit 0; \ echo "[Creating ${_utobj}...]"; \ umask ${OBJDIR_UMASK:U002}; \ mkdir -p ${_utobj} test: utobj .endif # override some simple things BINDIR= /usr/bin MANDIR= /usr/share/man/man # make sure we get this CFLAGS+= ${COPTS.${.IMPSRC:T}} after-import: ${SRCTOP}/contrib/bmake/bsd.after-import.mk cd ${.CURDIR} && ${.MAKE} -f ${SRCTOP}/contrib/bmake/bsd.after-import.mk diff --git a/usr.bin/bmake/Makefile.config b/usr.bin/bmake/Makefile.config index f92abbc61dbf..fab0991419ff 100644 --- a/usr.bin/bmake/Makefile.config +++ b/usr.bin/bmake/Makefile.config @@ -1,27 +1,28 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ SRCTOP?= ${.CURDIR:H:H} # things set by configure -_MAKE_VERSION?=20220726 +_MAKE_VERSION?=20230126 prefix?= /usr srcdir= ${SRCTOP}/contrib/bmake CC?= cc DEFAULT_SYS_PATH?= .../share/mk:/usr/share/mk +EGREP = egrep CPPFLAGS+= CFLAGS+= ${CPPFLAGS} -DHAVE_CONFIG_H LDFLAGS+= LIBOBJS+= ${LIBOBJDIR}stresep$U.o LDADD+= USE_META?= yes USE_FILEMON?= dev FILEMON_H?= /usr/include/dev/filemon/filemon.h BMAKE_PATH_MAX?= 1024 # used if MAXPATHLEN not defined CPPFLAGS+= -DBMAKE_PATH_MAX=${BMAKE_PATH_MAX} diff --git a/usr.bin/bmake/config.h b/usr.bin/bmake/config.h index 6c053cf96cfd..baaa1f38ea54 100644 --- a/usr.bin/bmake/config.h +++ b/usr.bin/bmake/config.h @@ -1,395 +1,446 @@ /* $FreeBSD$ */ /* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.in by autoheader. */ /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* Path of default shell */ /* #undef DEFSHELL_CUSTOM */ /* Shell spec to use by default */ /* #undef DEFSHELL_INDEX */ /* Path of default shell */ /* #undef DEFSHELL_PATH */ /* Define to 1 if you have the header file. */ #define HAVE_AR_H 1 /* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you don't. */ #define HAVE_DECL_SYS_SIGLIST 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #define HAVE_DIRENT_H 1 /* Define to 1 if you have the `dirname' function. */ #define HAVE_DIRNAME 1 /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ /* #undef HAVE_DOPRNT */ /* Define to 1 if you have the `err' function. */ #define HAVE_ERR 1 /* Define to 1 if you have the `errx' function. */ #define HAVE_ERRX 1 /* Define to 1 if you have the header file. */ #define HAVE_ERR_H 1 /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the `fork' function. */ #define HAVE_FORK 1 /* Define to 1 if you have the `getcwd' function. */ #define HAVE_GETCWD 1 /* Define to 1 if you have the `getenv' function. */ #define HAVE_GETENV 1 /* Define to 1 if you have the `getopt' function. */ #define HAVE_GETOPT 1 /* Define to 1 if you have the `getwd' function. */ #define HAVE_GETWD 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `killpg' function. */ #define HAVE_KILLPG 1 /* Define to 1 if you have the header file. */ #define HAVE_LIBGEN_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if the system has the type `long long int'. */ #define HAVE_LONG_LONG_INT 1 -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MINIX_CONFIG_H */ /* Define to 1 if you have the `mmap' function. */ #define HAVE_MMAP 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_NDIR_H */ /* Define to 1 if you have the header file. */ #define HAVE_PATHS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_POLL_H 1 /* Define to 1 if you have the `putenv' function. */ #define HAVE_PUTENV 1 /* Define to 1 if you have the header file. */ #define HAVE_RANLIB_H 1 /* Define to 1 if you have the `realpath' function. */ #define HAVE_REALPATH 1 /* Define to 1 if you have the `select' function. */ #define HAVE_SELECT 1 /* Define to 1 if you have the `setenv' function. */ #define HAVE_SETENV 1 /* Define to 1 if you have the `setpgid' function. */ #define HAVE_SETPGID 1 /* Define to 1 if you have the `setrlimit' function. */ #define HAVE_SETRLIMIT 1 /* Define to 1 if you have the `setsid' function. */ #define HAVE_SETSID 1 /* Define to 1 if you have the `sigaction' function. */ #define HAVE_SIGACTION 1 /* Define to 1 if you have the `sigaddset' function. */ #define HAVE_SIGADDSET 1 /* Define to 1 if you have the `sigpending' function. */ #define HAVE_SIGPENDING 1 /* Define to 1 if you have the `sigprocmask' function. */ #define HAVE_SIGPROCMASK 1 /* Define to 1 if you have the `sigsetmask' function. */ #define HAVE_SIGSETMASK 1 /* Define to 1 if you have the `sigsuspend' function. */ #define HAVE_SIGSUSPEND 1 /* Define to 1 if you have the `sigvec' function. */ #define HAVE_SIGVEC 1 /* Define to 1 if the system has the type `sig_atomic_t'. */ #define HAVE_SIG_ATOMIC_T 1 /* Define to 1 if you have the `snprintf' function. */ #define HAVE_SNPRINTF 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the `stresep' function. */ /* #undef HAVE_STRESEP */ /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strlcpy' function. */ #define HAVE_STRLCPY 1 /* Define to 1 if you have the `strsep' function. */ #define HAVE_STRSEP 1 /* Define to 1 if you have the `strtod' function. */ #define HAVE_STRTOD 1 /* Define to 1 if you have the `strtol' function. */ #define HAVE_STRTOL 1 /* Define to 1 if you have the `strtoll' function. */ #define HAVE_STRTOLL 1 /* Define to 1 if you have the `strtoul' function. */ #define HAVE_STRTOUL 1 -/* Define to 1 if `st_rdev' is a member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_RDEV 1 - -/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use - `HAVE_STRUCT_STAT_ST_RDEV' instead. */ -#define HAVE_ST_RDEV 1 - /* Define to 1 if you have the `sysctl' function. */ #define HAVE_SYSCTL 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_SYS_DIR_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ /* #undef HAVE_SYS_NDIR_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_PARAM_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_SELECT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_SOCKET_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_SYSCTL_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_UIO_H 1 /* Define to 1 if you have that is POSIX.1 compatible. */ #define HAVE_SYS_WAIT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `unsetenv' function. */ #define HAVE_UNSETENV 1 /* Define to 1 if the system has the type `unsigned long long int'. */ #define HAVE_UNSIGNED_LONG_LONG_INT 1 /* Define to 1 if you have the header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if you have the `vfork' function. */ #define HAVE_VFORK 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_VFORK_H */ /* Define to 1 if you have the `vprintf' function. */ #define HAVE_VPRINTF 1 /* Define to 1 if you have the `vsnprintf' function. */ #define HAVE_VSNPRINTF 1 /* Define to 1 if you have the `wait3' function. */ #define HAVE_WAIT3 1 /* Define to 1 if you have the `wait4' function. */ #define HAVE_WAIT4 1 /* Define to 1 if you have the `waitpid' function. */ #define HAVE_WAITPID 1 /* Define to 1 if you have the `warn' function. */ #define HAVE_WARN 1 /* Define to 1 if you have the `warnx' function. */ #define HAVE_WARNX 1 +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + /* Define to 1 if `fork' works. */ #define HAVE_WORKING_FORK 1 /* Define to 1 if `vfork' works. */ #define HAVE_WORKING_VFORK 1 /* define if your compiler has __attribute__ */ #define HAVE___ATTRIBUTE__ 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "sjg@NetBSD.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "bmake" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "bmake 20211020" +#define PACKAGE_STRING "bmake 20220909" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "bmake" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "20211020" - -/* Define as the return type of signal handlers (`int' or `void'). */ -#define RETSIGTYPE void +#define PACKAGE_VERSION "20220909" /* Define to 1 if the `S_IS*' macros in do not work properly. */ /* #undef STAT_MACROS_BROKEN */ -/* Define to 1 if you have the ANSI C header files. */ +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ #define STDC_HEADERS 1 -/* Define to 1 if you can safely include both and . */ -#define TIME_WITH_SYS_TIME 1 - /* Define to 1 if your declares `struct tm'. */ /* #undef TM_IN_SYS_TIME */ /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # define _ALL_SOURCE 1 #endif +/* Enable general extensions on macOS. */ +#ifndef _DARWIN_C_SOURCE +# define _DARWIN_C_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif -/* Enable threading extensions on Solaris. */ +/* Enable X/Open compliant socket functions that do not require linking + with -lxnet on HP-UX 11.11. */ +#ifndef _HPUX_ALT_XOPEN_SOCKET_API +# define _HPUX_ALT_XOPEN_SOCKET_API 1 +#endif +/* Identify the host operating system as Minix. + This macro does not affect the system headers' behavior. + A future release of Autoconf may stop defining this macro. */ +#ifndef _MINIX +/* # undef _MINIX */ +#endif +/* Enable general extensions on NetBSD. + Enable NetBSD compatibility extensions on Minix. */ +#ifndef _NETBSD_SOURCE +# define _NETBSD_SOURCE 1 +#endif +/* Enable OpenBSD compatibility extensions on NetBSD. + Oddly enough, this does nothing on OpenBSD. */ +#ifndef _OPENBSD_SOURCE +# define _OPENBSD_SOURCE 1 +#endif +/* Define to 1 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_SOURCE +/* # undef _POSIX_SOURCE */ +#endif +/* Define to 2 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_1_SOURCE +/* # undef _POSIX_1_SOURCE */ +#endif +/* Enable POSIX-compatible threading on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # define _POSIX_PTHREAD_SEMANTICS 1 #endif +/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ +#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ +#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ +# define __STDC_WANT_IEC_60559_BFP_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ +#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ +# define __STDC_WANT_IEC_60559_DFP_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ +#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ +# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 +#endif +/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ +#ifndef __STDC_WANT_LIB_EXT2__ +# define __STDC_WANT_LIB_EXT2__ 1 +#endif +/* Enable extensions specified by ISO/IEC 24747:2009. */ +#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ +# define __STDC_WANT_MATH_SPEC_FUNCS__ 1 +#endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # define _TANDEM_SOURCE 1 #endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# define __EXTENSIONS__ 1 +/* Enable X/Open extensions. Define to 500 only if necessary + to make mbstate_t available. */ +#ifndef _XOPEN_SOURCE +/* # undef _XOPEN_SOURCE */ #endif /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN /* # undef WORDS_BIGENDIAN */ # endif #endif -/* Define to 1 if on MINIX. */ -/* #undef _MINIX */ - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -/* #undef _POSIX_1_SOURCE */ - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -/* #undef _POSIX_SOURCE */ - /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ /* #undef _UINT32_T */ /* C99 function name */ /* #undef __func__ */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus /* #undef inline */ #endif /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ /* #undef int64_t */ /* Define to `int' if does not define. */ /* #undef mode_t */ /* Define to `long int' if does not define. */ /* #undef off_t */ -/* Define to `int' if does not define. */ +/* Define as a signed integer type capable of holding a process identifier. */ /* #undef pid_t */ /* type that signal handlers can safely frob */ /* #undef sig_atomic_t */ /* Define to `unsigned int' if does not define. */ /* #undef size_t */ /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ /* #undef uint32_t */ /* Define as `fork' if `vfork' does not work. */ /* #undef vfork */ diff --git a/usr.bin/bmake/unit-tests/Makefile b/usr.bin/bmake/unit-tests/Makefile index 2a0bbcb3c441..3214dacda5fc 100644 --- a/usr.bin/bmake/unit-tests/Makefile +++ b/usr.bin/bmake/unit-tests/Makefile @@ -1,829 +1,850 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ -# $Id: Makefile,v 1.182 2022/07/26 19:39:32 sjg Exp $ +# $Id: Makefile,v 1.191 2023/01/24 06:09:49 sjg Exp $ # -# $NetBSD: Makefile,v 1.318 2022/06/10 21:28:50 rillig Exp $ +# $NetBSD: Makefile,v 1.331 2023/01/24 00:24:02 sjg Exp $ # # Unit tests for make(1) # # The main targets are: # # all: # run all the tests # test: # run 'all', and compare to expected results # accept: # move generated output to expected results # # Settable variables # # TEST_MAKE # The make program to be tested. # # # Adding a test case # # Each feature should get its own set of tests in its own suitably # named makefile (*.mk), with its own set of expected results (*.exp), # and it should be added to the TESTS list. # -# A few *.mk files are helper files for other tests (such as include-sub.mk) -# and are thus not added to TESTS. Such files must be ignored in -# src/tests/usr.bin/make/t_make.sh. -# .MAIN: all # 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+= cmd-errors TESTS+= cmd-errors-jobs TESTS+= cmd-errors-lint TESTS+= cmd-interrupt TESTS+= cmdline TESTS+= cmdline-redirect-stdin TESTS+= cmdline-undefined TESTS+= comment TESTS+= compat-error TESTS+= cond-cmp-numeric TESTS+= cond-cmp-numeric-eq TESTS+= cond-cmp-numeric-ge TESTS+= cond-cmp-numeric-gt TESTS+= cond-cmp-numeric-le TESTS+= cond-cmp-numeric-lt TESTS+= cond-cmp-numeric-ne TESTS+= cond-cmp-string TESTS+= cond-cmp-unary TESTS+= cond-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 TESTS+= cond-op-and TESTS+= cond-op-and-lint TESTS+= cond-op-not TESTS+= cond-op-or TESTS+= cond-op-or-lint TESTS+= cond-op-parentheses TESTS+= cond-short TESTS+= cond-token-number TESTS+= cond-token-plain TESTS+= cond-token-string TESTS+= cond-token-var TESTS+= cond-undef-lint TESTS+= counter TESTS+= counter-append TESTS+= dep TESTS+= dep-colon TESTS+= dep-colon-bug-cross-file TESTS+= dep-double-colon TESTS+= dep-double-colon-indep TESTS+= dep-duplicate TESTS+= dep-exclam TESTS+= dep-none TESTS+= dep-op-missing TESTS+= dep-percent TESTS+= dep-var TESTS+= dep-wildcards TESTS+= depsrc TESTS+= depsrc-end TESTS+= depsrc-exec TESTS+= depsrc-ignore TESTS+= depsrc-made TESTS+= depsrc-make TESTS+= depsrc-meta TESTS+= depsrc-nometa TESTS+= depsrc-nometa_cmp TESTS+= depsrc-nopath TESTS+= depsrc-notmain TESTS+= depsrc-optional TESTS+= depsrc-phony TESTS+= depsrc-precious TESTS+= depsrc-recursive TESTS+= depsrc-silent TESTS+= depsrc-use TESTS+= depsrc-usebefore 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 TESTS+= deptgt-interrupt TESTS+= deptgt-main TESTS+= deptgt-makeflags TESTS+= deptgt-no_parallel TESTS+= deptgt-nopath TESTS+= deptgt-notparallel TESTS+= deptgt-objdir TESTS+= deptgt-order TESTS+= deptgt-path TESTS+= deptgt-path-suffix TESTS+= deptgt-phony TESTS+= deptgt-posix TESTS+= deptgt-precious TESTS+= deptgt-shell TESTS+= deptgt-silent TESTS+= deptgt-silent-jobs TESTS+= deptgt-stale TESTS+= deptgt-suffixes TESTS+= dir TESTS+= dir-expand-path TESTS+= directive TESTS+= directive-dinclude TESTS+= directive-elif TESTS+= directive-elifdef 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-break TESTS+= directive-for-empty TESTS+= directive-for-errors TESTS+= directive-for-escape TESTS+= directive-for-generating-endif TESTS+= directive-for-if TESTS+= directive-for-lines TESTS+= directive-for-null TESTS+= directive-hyphen-include TESTS+= directive-if TESTS+= directive-if-nested TESTS+= directive-ifdef TESTS+= directive-ifmake TESTS+= directive-ifndef 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 TESTS+= directive-unexport-env TESTS+= directive-warning TESTS+= dollar TESTS+= doterror TESTS+= dotwait TESTS+= error TESTS+= # escape # broken by reverting POSIX changes TESTS+= export TESTS+= export-all TESTS+= export-env TESTS+= export-variants TESTS+= forloop TESTS+= forsubst TESTS+= gnode-submake TESTS+= hanoi-include TESTS+= impsrc TESTS+= include-main TESTS+= job-flags #TESTS+= job-output-long-lines TESTS+= job-output-null TESTS+= jobs-empty-commands TESTS+= jobs-empty-commands-error 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 .if ${.MAKE.UID} > 0 TESTS+= objdir-writable .endif TESTS+= opt TESTS+= opt-backwards TESTS+= opt-chdir TESTS+= opt-debug TESTS+= opt-debug-all TESTS+= opt-debug-archive TESTS+= opt-debug-curdir TESTS+= opt-debug-cond TESTS+= opt-debug-dir TESTS+= opt-debug-errors TESTS+= opt-debug-errors-jobs TESTS+= opt-debug-file TESTS+= opt-debug-for TESTS+= opt-debug-graph1 TESTS+= opt-debug-graph2 TESTS+= opt-debug-graph3 TESTS+= opt-debug-hash #TESTS+= opt-debug-jobs TESTS+= opt-debug-lint TESTS+= opt-debug-loud TESTS+= opt-debug-meta TESTS+= opt-debug-making TESTS+= opt-debug-no-rm TESTS+= opt-debug-parse TESTS+= opt-debug-suff TESTS+= opt-debug-targets TESTS+= opt-debug-varraw TESTS+= opt-debug-var TESTS+= opt-debug-x-trace TESTS+= opt-define TESTS+= opt-env TESTS+= opt-file 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-indirect 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-no-action-touch TESTS+= opt-query TESTS+= opt-raw TESTS+= opt-silent TESTS+= opt-touch TESTS+= opt-touch-jobs TESTS+= opt-tracefile TESTS+= opt-var-expanded TESTS+= opt-var-literal TESTS+= opt-version TESTS+= opt-warnings-as-errors TESTS+= opt-where-am-i TESTS+= opt-x-reduce-exported TESTS+= order TESTS+= parse TESTS+= parse-var TESTS+= phony-end TESTS+= posix TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= recursive TESTS+= sh TESTS+= sh-dots TESTS+= sh-errctl TESTS+= sh-flags TESTS+= sh-jobs TESTS+= sh-jobs-error TESTS+= sh-leading-at TESTS+= sh-leading-hyphen TESTS+= sh-leading-plus TESTS+= sh-meta-chars TESTS+= sh-multi-line TESTS+= sh-single-line TESTS+= shell-csh TESTS+= shell-custom .if exists(/bin/ksh) TESTS+= shell-ksh .endif 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 TESTS+= suff-use TESTS+= sunshcmd TESTS+= ternary TESTS+= unexport TESTS+= unexport-env TESTS+= use-inference +TESTS+= var-readonly TESTS+= var-scope TESTS+= var-scope-cmdline TESTS+= var-scope-env TESTS+= var-scope-global TESTS+= var-scope-local TESTS+= var-scope-local-legacy TESTS+= var-eval-short TESTS+= var-op TESTS+= var-op-append TESTS+= var-op-assign TESTS+= var-op-default TESTS+= var-op-expand TESTS+= var-op-shell TESTS+= var-op-sunsh TESTS+= var-recursive TESTS+= varcmd TESTS+= vardebug TESTS+= varfind TESTS+= varmisc TESTS+= varmod TESTS+= varmod-assign TESTS+= varmod-assign-shell TESTS+= varmod-defined TESTS+= varmod-edge TESTS+= varmod-exclam-shell TESTS+= varmod-extension 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 TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape TESTS+= varmod-no-match TESTS+= varmod-order TESTS+= varmod-order-numeric TESTS+= varmod-order-reverse TESTS+= varmod-order-shuffle TESTS+= varmod-order-string TESTS+= varmod-path TESTS+= varmod-quote TESTS+= varmod-quote-dollar TESTS+= varmod-range TESTS+= varmod-remember TESTS+= varmod-root TESTS+= varmod-select-words TESTS+= varmod-shell TESTS+= varmod-subst TESTS+= varmod-subst-regex TESTS+= varmod-sun-shell TESTS+= varmod-sysv TESTS+= varmod-tail TESTS+= varmod-to-abs TESTS+= varmod-to-lower TESTS+= varmod-to-many-words TESTS+= varmod-to-one-word TESTS+= varmod-to-separator TESTS+= varmod-to-upper TESTS+= varmod-undefined TESTS+= varmod-unique TESTS+= varname TESTS+= varname-dollar TESTS+= varname-dot-alltargets TESTS+= varname-dot-curdir TESTS+= varname-dot-includes TESTS+= varname-dot-includedfromdir TESTS+= varname-dot-includedfromfile TESTS+= varname-dot-libs TESTS+= varname-dot-make-dependfile TESTS+= varname-dot-make-expand_variables TESTS+= varname-dot-make-exported TESTS+= varname-dot-make-jobs TESTS+= varname-dot-make-jobs-prefix TESTS+= varname-dot-make-level TESTS+= varname-dot-make-makefile_preference TESTS+= varname-dot-make-makefiles TESTS+= varname-dot-make-meta-bailiwick TESTS+= varname-dot-make-meta-created TESTS+= varname-dot-make-meta-files TESTS+= varname-dot-make-meta-ignore_filter TESTS+= varname-dot-make-meta-ignore_paths TESTS+= varname-dot-make-meta-ignore_patterns TESTS+= varname-dot-make-meta-prefix TESTS+= varname-dot-make-mode 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 TESTS+= varname-dot-parsedir TESTS+= varname-dot-parsefile TESTS+= varname-dot-path TESTS+= varname-dot-shell TESTS+= varname-dot-suffixes TESTS+= varname-dot-targets TESTS+= varname-empty TESTS+= varname-make TESTS+= varname-make_print_var_on_error TESTS+= varname-make_print_var_on_error-jobs TESTS+= varname-makefile TESTS+= varname-makeflags TESTS+= varname-pwd TESTS+= varname-vpath TESTS+= varparse-dynamic TESTS+= varparse-errors TESTS+= varparse-mod TESTS+= varparse-undef-partial # some shells have quirks _shell := ${.SHELL:tA:T} .if ${_shell} == "dash" # dash fails -x output BROKEN_TESTS+= opt-debug-x-trace .elif ${_shell} == "ksh" BROKEN_TESTS+= sh-flags .endif .if ${.MAKE.OS:NDarwin} == "" BROKEN_TESTS+= shell-ksh .endif .if ${.MAKE.OS} == "SCO_SV" BROKEN_TESTS+= \ opt-debug-graph[23] \ varmod-localtime \ varmod-to-separator \ .if ${_shell} == "bash" BROKEN_TESTS+= job-output-null .else BROKEN_TESTS+= \ cmd-interrupt \ job-flags \ .endif .endif # Some tests just do not work on some platforms or environments # so allow for some filtering. .if !empty(BROKEN_TESTS) .warning Skipping broken tests: ${BROKEN_TESTS:O:u} TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}} .endif # Ideas for more tests: # char-0020-space.mk # char-005C-backslash.mk # escape-cond-str.mk # escape-cond-func-arg.mk # escape-varmod.mk # escape-varmod-define.mk # escape-varmod-match.mk # escape-varname.mk # escape-varassign-varname.mk # escape-varassign-varname-cmdline.mk # escape-varassign-value.mk # escape-varassign-value-cmdline.mk # escape-dependency-source.mk # escape-dependency-target.mk # escape-for-varname.mk # escape-for-item.mk # posix-*.mk (see posix.mk and posix1.mk) # Additional environment variables for some of the tests. # The base environment is -i PATH="$PATH". ENV.depsrc-optional+= TZ=UTC ENV.deptgt-phony+= MAKESYSPATH=. ENV.directive-undef= ENV_VAR=env-value -ENV.envfirst= FROM_ENV=value-from-env +ENV.opt-env= FROM_ENV=value-from-env ENV.opt-m-include-dir= ${MAKEOBJDIR:DMAKEOBJDIR=${MAKEOBJDIR}} ENV.varmisc= FROM_ENV=env ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_AFTER=env ENV.varmod-localtime+= TZ=${UTC_1:UEurope/Berlin} ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2 # Override make flags for some of the tests; default is -k. # If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of # settings FLAGS.test=-dv here, since that is closer to the test code. FLAGS.cond-func-make= via-cmdline FLAGS.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.deptgt-phony= ${STD_SED_CMDS.dd} SED_CMDS.dir= ${STD_SED_CMDS.dd} 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' \ ${:D Plain output lines are ok as well. } \ ${:D They may come in multiples of 1024 or as 10000. } \ -e '/^aa*$$/d' \ -e '/^bb*$$/d' \ ${:D The following lines should rather not occur since the job } \ ${:D marker should always be at the beginning of the line. } \ -e '/^aa*--- job-b ---$$/d' \ -e '/^bb*--- job-a ---$$/d' SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \ -e '/name/s,file,File,' \ -e 's,no such,No such,' \ -e 's,Filename,File name,' SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1} SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2} SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3} SED_CMDS.opt-debug-hash= -e 's,\(numEntries\)=[1-9][0-9],\1=,' SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(),' SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid ,' SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process ,' SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: ,' SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: ,' # The "-q" may be there or not, see jobs.c, variable shells. SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: \) -q,\1,' SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex} SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output} SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output} SED_CMDS.opt-where-am-i= -e '/usr.obj/d' # For Compat_RunCommand, useShell == false. SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,,' # For Compat_RunCommand, useShell == true. SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,,' SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1,' SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj} SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output} SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space} +SED_CMDS.sh-leading-hyphen= ${STD_SED_CMDS.shell} 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+= ${STD_SED_CMDS.shell} SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,' SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space} SED_CMDS.vardebug+= -e 's,${.SHELL},,' SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex} +SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp} SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: ",' SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: ",' SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g' SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g' SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g' -SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@} +SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL .SYSPATH:L:@v@-e '/\\$v/d'@} # Some tests need an additional round of postprocessing. POSTPROC.depsrc-wait= sed -e '/^---/d' -e 's,^\(: Making 3[abc]\)[123]$$,\1,' POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' POSTPROC.varname-dot-make-mode= sed 's,^\(: Making [abc]\)[123]$$,\1,' # Some tests reuse other tests, which makes them unnecessarily fragile. export-all.rawout: export.mk unexport.rawout: export.mk unexport-env.rawout: export.mk # End of the configuration section. # Some standard sed commands, to be used in the SED_CMDS above. # In tests that use the debugging option -dd, ignore debugging output that is # only logged in -DCLEANUP mode. STD_SED_CMDS.dd= -e '/^OpenDirs_Done:/d' STD_SED_CMDS.dd+= -e '/^CachedDir /d' # Omit details such as process IDs from the output of the -dg1 option. STD_SED_CMDS.dg1= -e '/\#.* \.$$/d' STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d' -STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d' +STD_SED_CMDS.dg1+= -e '/^\#.*\/mk/d' STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, ,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1
,' +STD_SED_CMDS.dg1+= -e '/\.SYSPATH/d' STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1} STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 ,' STD_SED_CMDS.dg3= ${STD_SED_CMDS.dg2} # 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(),' \ -e 's,^([0-9][0-9]*) \(withdrew token\),() \1,' \ -e 's, \(pid\) [0-9][0-9]*, \1 ,' \ -e 's,^\( Command:\) .*,\1 ,' # 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,,' # Normalize the output for error messages from the shell. # # $shell -c '...' # NetBSD sh ...: not found # NetBSD ksh ksh: ...: not found # bash 5.0.18 bash: ...: command not found # bash 5.1.0 bash: line 1: ...: command not found # dash dash: 1: ...: not found # # $shell -c '< /nonexistent' # NetBSD sh sh: cannot open /nonexistent: no such file # NetBSD ksh ksh: cannot open /nonexistent: No such file or directory # bash 5.0.18 bash: /nonexistent: No such file or directory # bash 5.1.0 bash: line 1: /nonexistent: No such file or directory # dash dash: 1: cannot open /nonexistent: No such file # # echo '< /nonexistent' | $shell # NetBSD sh sh: cannot open /nonexistent: no such file # NetBSD ksh ksh: [1]: cannot open /nonexistent: No such file or directory # bash 5.0.18 bash: line 1: /nonexistent: No such file or directory # bash 5.1.0 bash: line 1: /nonexistent: No such file or directory # dash dash: 1: cannot open /nonexistent: No such file # +STD_SED_CMDS.shell+= -e 's,^${.SHELL},${.SHELL:T},' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,' +STD_SED_CMDS.shell+= -e 's,: command not found,: not found,' STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,' # The actual error messages for a failed regcomp or regexec differ between the # implementations. STD_SED_CMDS.regex= \ -e 's,\(Regex compilation error:\).*,\1 (details omitted),' +# Normalize timestamps from ':gmtime' or ':localtime' to ''. +# See STD_SED_CMDS.dg2 for timestamps from the debug log. +STD_SED_CMDS.timestamp= \ + -e 's,[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [12][0-9][0-9][0-9],,' + # End of the configuration helpers section. .sinclude "Makefile.inc" .sinclude "Makefile.config" UNIT_TESTS:= ${srcdir} .PATH: ${UNIT_TESTS} .if ${USE_ABSOLUTE_TESTNAMES:Uno} == yes OUTFILES= ${TESTS:@test@${.CURDIR:tA}/${test}.out@} .else OUTFILES= ${TESTS:=.out} .endif all: ${OUTFILES} CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp CLEANFILES+= obj*.[och] lib*.a # posix1.mk CLEANFILES+= issue* .[ab]* # suffixes.mk CLEANDIRS= dir dummy # posix1.mk clean: rm -f ${CLEANFILES} rm -rf ${CLEANDIRS} TEST_MAKE?= ${.MAKE} TOOL_SED?= sed TOOL_TR?= tr TOOL_DIFF?= diff DIFF_FLAGS?= -u .if defined(.PARSEDIR) # ensure consistent results from sort(1) LC_ALL= C LANG= C .export LANG LC_ALL .endif .if ${.MAKE.MODE:Unormal:Mmeta} != "" # we don't need the noise _MKMSG_TEST= : .endif # for many tests we need a TMPDIR that will not collide # with other users. .if ${.OBJDIR} != ${.CURDIR} # easy TMPDIR:= ${.OBJDIR}/tmp .elif defined(TMPDIR) TMPDIR:= ${TMPDIR}/uid${.MAKE.UID} .else TMPDIR:= /tmp/uid${.MAKE.UID} .endif # make sure it exists .if !exist(${TMPDIR}) _!= mkdir -p ${TMPDIR} .endif -MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100 +# Some Linux systems such as Fedora have deprecated egrep in favor of grep -E. +.if ${.MAKE.OS:NLinux} == "" +EGREP= grep -E +.endif +# Keep the classical definition for all other systems. Just as the bmake code +# is kept compatible with C90, the tests are kept compatible with systems that +# are several decades old and don't follow modern POSIX standards. +EGREP?= egrep + +MAKE_TEST_ENV= EGREP="${EGREP}" +MAKE_TEST_ENV+= MALLOC_OPTIONS="JA" # for jemalloc 100 MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510 MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" LIMIT_RESOURCES?= ulimit -v 300000 .endif LIMIT_RESOURCES?= : # Each test is run in a sub-make, to keep the tests from interfering with # each other, and because they use different environment variables and # command line options. .SUFFIXES: .mk .rawout .out .mk.rawout: @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} @set -eu; \ ${LIMIT_RESOURCES}; \ cd ${.OBJDIR}; \ env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \ ${TEST_MAKE} \ -r -C ${.CURDIR} -f ${.IMPSRC} \ ${FLAGS.${.PREFIX:T}:U-k} \ > ${.TARGET}.tmp 2>&1 \ && status=$$? || status=$$?; \ echo $$status > ${.TARGET:R}.status @mv ${.TARGET}.tmp ${.TARGET} # Postprocess the test output to make the output platform-independent. # # always pretend .MAKE was called 'make' _SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' _SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' # Allow the test files to be placed anywhere. _SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = ,' _SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = ,' _SED_CMDS+= -e 's,${TMPDIR},,g' # canonicalize ${.OBJDIR} and ${.CURDIR} .if ${.OBJDIR} != ${.CURDIR} # yes this is inaccurate but none of the tests expect anywhere # which we get depending on how MAKEOBJDIR is set. _SED_CMDS+= -e 's,${.OBJDIR},,g' .endif _SED_CMDS+= -e 's,${.CURDIR},,g' _SED_CMDS+= -e 's,/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' +_SED_CMDS+= -e '/MAKE_VERSION/d' +_SED_CMDS+= -e '/EGREP=/d' + # on AT&T derived systems: false exits 255 not 1 .if ${.MAKE.OS:N*BSD} != "" _SED_CMDS+= -e 's,\(Error code\) 255,\1 1,' .endif .if ${.SHELL:T} == "ksh" _SED_CMDS+= -e '/^set [+-]v/d' .endif .rawout.out: @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \ < ${.IMPSRC} > ${.TARGET}.tmp1 @${POSTPROC.${.PREFIX:T}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 @rm ${.TARGET}.tmp1 @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 ; \ if [ -n "$${failed}" ]; then \ echo "Failed tests: $${failed}" ; false ; \ else \ echo "All tests passed" ; \ fi accept: @for test in ${TESTS}; do \ cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ || { echo "Replacing $${test}.exp" ; \ cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ done .if exists(${TEST_MAKE}) ${TESTS:=.rawout}: ${TEST_MAKE} # in meta mode, we *know* if a target script is impacted # by a makefile change. .if ${.MAKE.MODE:Unormal:Mmeta} == "" ${TESTS:=.rawout}: ${.PARSEDIR}/Makefile .endif .endif .sinclude diff --git a/usr.bin/bmake/unit-tests/Makefile.config b/usr.bin/bmake/unit-tests/Makefile.config index 17ccf593b495..1e71a3dd5ef6 100644 --- a/usr.bin/bmake/unit-tests/Makefile.config +++ b/usr.bin/bmake/unit-tests/Makefile.config @@ -1,13 +1,14 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ SRCTOP?= ${.CURDIR:H:H:H} -# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $ +# $Id: Makefile.config.in,v 1.4 2022/09/09 18:44:56 sjg Exp $ srcdir= ${SRCTOP}/contrib/bmake/unit-tests +EGREP= egrep TOOL_DIFF?= diff DIFF_FLAGS?= -u UTC_1= Europe/Berlin