diff --git a/ChangeLog b/ChangeLog index 653cf14f8802..c4d43ef82e62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3934 +1,3952 @@ +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/FILES b/FILES index 5e0c387885fb..ecdd3327a9de 100644 --- a/FILES +++ b/FILES @@ -1,869 +1,871 @@ 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/cond1.exp unit-tests/cond1.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-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/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-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 unit-tests/varquote.exp unit-tests/varquote.mk util.c var.c wait.h diff --git a/VERSION b/VERSION index 9945186def18..ed97681843b1 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20220330 +_MAKE_VERSION=20220418 diff --git a/bmake.1 b/bmake.1 index 29acad9ff0e9..8ebd54ed6e31 100644 --- a/bmake.1 +++ b/bmake.1 @@ -1,2547 +1,2545 @@ -.\" $NetBSD: make.1,v 1.307 2022/03/26 15:39:58 sjg Exp $ +.\" $NetBSD: make.1,v 1.308 2022/04/18 15:06:27 rillig 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 March 24, 2022 +.Dd April 18, 2022 .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 ... .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, .Nm will try to open .Ql Pa makefile then .Ql Pa Makefile in order to find the specifications. If the file .Ql Pa .depend 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" . .Pp .Nm will prepend the contents of the .Va 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. .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 Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by .Ql \- they are added to the .Va 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 .Ar 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: .Bl -tag -width Ds .It Ar A Print all possible debugging information; equivalent to specifying all of the debugging flags. .It Ar a Print debugging information about archive searching and caching. .It Ar C Print debugging information about current working directory. .It Ar c Print debugging information about conditional evaluation. .It Ar d Print debugging information about directory searching and caching. .It Ar e Print debugging information about failed commands and targets. .It Ar F Ns Oo Sy \&+ 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 flag is .Ql \&+ , then the file will be opened in append mode; otherwise the file will be 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 \&+ option has no effect). Otherwise, the output will be written to the named file. If the file name ends .Ql .%d then the .Ql %d is replaced by the pid. .It Ar f Print debugging information about loop evaluation. .It Ar "g1" Print the input graph before making anything. .It Ar "g2" Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. .It Ar h Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. .It Ar 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 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 Print debugging information about making targets, including modification dates. .It Ar 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 Print debugging information about makefile parsing. .It Ar s Print debugging information about suffix-transformation rules. .It Ar t Print debugging information about target list maintenance. .It Ar V Force the .Fl V 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 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. .It Fl f Ar makefile Specify a makefile to read instead of the default .Ql Pa makefile . If .Ar makefile is .Ql Fl , 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 .Ql Fl 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 .Ar 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 .Ar B flag 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 .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 .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). .Pp If a file or directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string .Qq \&.../ then .Nm 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 successful, then the resulting directory replaces the .Qq \&.../ specification in the .Fl m argument. If used, 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 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 .Ql Ic + . .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 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. .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 .Ql 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, 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. 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 .Va .MAKE.EXPAND_VARIABLES is set to true and the .Fl 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 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 the variable is always expanded to its complete value. .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 environment variable. This option may be useful on systems which have a small limit on the size of command arguments. .It Ar variable=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 specifications, shell commands, variable assignments, include statements, conditional directives, for loops, and comments. .Pp In general, 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 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 \&:, for dependency lines with no sources, the attached shell commands are always run. Also unlike .Ic \&:, the target will not be removed if .Nm is interrupted. .El 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. 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 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. If the .Ql Ic \&:: operator is used, however, all rules may include scripts and the 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 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 .Ql Ic @ , .Ql Ic + , or .Ql Ic \- , the command is treated specially. A .Ql Ic @ causes the command not to be echoed before it is executed. A .Ql Ic + causes the command to be executed even when .Fl 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. A .Ql Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. .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. 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 .Nm will attempt direct execution. If a line starts with .Ql Ic \- and the shell has ErrCtl enabled then failure of the command line will be ignored as in compatibility mode. Otherwise .Ql 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. .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 .Dq cd or .Dq chdir without potentially 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 the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \e (cd ${.CURDIR} && ${MAKE} $@); \e echo Back in `pwd` .Ed .Pp Since .Nm will .Xr chdir 2 to .Ql Va .OBJDIR before executing any targets, each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradition, consist of all upper-case letters. .Ss Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overridden. .It Ic \&+= Append the value to the current value of the variable. .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. .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. .It Ic \&!= Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. .El .Pp Any white-space before the assigned .Ar value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. .Pp Variables are expanded by surrounding the variable name with either curly braces .Pq Ql {} or parentheses .Pq Ql () and preceding it with a dollar sign .Pq Ql \&$ . If the variable name contains only a single letter, the surrounding 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. This allows almost arbitrary variable names, however names containing dollar, braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ the string is expanded again. .Pp Variable substitution occurs at three 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 shell commands are expanded when the shell command is executed. .It .Dq .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the following example code: .Bd -literal -offset indent .Dv .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .Dv .endfor all: @echo ${a} @echo ${b} .Ed will print: .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} contains .Dq ${j} ${j} ${j} which expands to .Dq 3 3 3 since after the loop completes ${j} contains .Dq 3 . .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 .Va .MAKE.TARGET_LOCAL_VARIABLES is not set to .Ql false . The rest of the line (which will already have 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 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 .It Va .ALLSRC The list of all sources for this target; also known as .Ql Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Ql 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 .Ql Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Ql Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Ql Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Ql Va * . The suffix must be one of the known suffixes declared with .Ic .SUFFIXES or it will not be recognized. .It Va .TARGET The name of the target; also known as .Ql Va @ . For compatibility with other makes this is an alias for .Ic .ARCHIVE in archive member rules. .El .Pp The shorter forms .Ql ( Va > , .Ql Va \&! , .Ql Va < , .Ql Va % , .Ql Va \&? , .Ql Va * , and .Ql Va @ ) 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. .Ql Va $(@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 .Ql Va .TARGET , .Ql Va .PREFIX , .Ql Va .ARCHIVE , and .Ql Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: .Bl -tag -width .MAKEOVERRIDES .It Va \&$ A single dollar sign .Ql \&$ , i.e. .Ql \&$$ expands to a single dollar sign. .It Va .ALLTARGETS The list of all targets encountered in the Makefile. 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 .Ql Ev PWD for more details. .It Va .INCLUDEDFROMDIR 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 name that .Nm was executed with .Pq Va argv[0] . For compatibility .Nm also sets .Va .MAKE with the same value. The preferred variable to use is the environment variable .Ev MAKE because it is more compatible with other versions of .Nm and cannot be confused with the special target with the same name. .It Va .MAKE.DEPENDFILE Names the makefile (default .Ql Pa .depend ) from which generated dependencies are read. .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 .MAKE.JOB.PREFIX If .Nm is run with .Ar j then output for each target is prefixed with a token .Ql --- target --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. .br For example: .Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like .Ql ---make[1234] target --- 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 .Ql 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 .Ql Ev MAKEFLAGS variable which is then entered into the environment for all programs which .Nm executes. .It Va .MAKE.LEVEL The recursion depth of .Nm . The initial instance of .Nm will be 0, and an incremented value is put into the environment to be seen by the next generation. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 to protect things which should only be evaluated in the initial instance of .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Ql Pa makefile , .Ql Pa Makefile ) that .Nm will look 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.MODE Processed after reading all makefiles. Can affect the mode that .Nm runs in. It can contain a number of keywords: .Bl -hang -width missing-filemon=bf. .It Pa compat Like .Fl B , puts .Nm into "compat" mode. .It Pa meta Puts .Nm into "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 .Nm will not create .meta files in .Ql Va .CURDIR . This can be overridden by setting .Va bf to a value which represents True. .It Pa missing-meta= Ar bf If .Va bf is True, then a missing .meta file makes the target out-of-date. .It Pa missing-filemon= Ar bf If .Va bf is True, then missing filemon data makes the target out-of-date. .It Pa nofilemon Do not use .Xr filemon 4 . .It Pa 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. This is useful if the build is otherwise running silently. The message printed the value of: .Va .MAKE.META.PREFIX . .It Pa 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 .Ic .NOMETA_CMP . .It Pa silent= Ar bf If .Va bf is True, when a .meta file is created, mark the target .Ic .SILENT . .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: .Ql 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 .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 .Ql Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Ql Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Ql Va .MAKEOVERRIDES . .Ql Ev MAKEFLAGS is re-exported whenever .Ql 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 .Nm . .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 .Nm . .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Ql Va .ERROR_TARGET to the name of the target that failed, .Ql Va .ERROR_CMD to the commands of the failed target, and in "meta" mode, it also sets .Ql Va .ERROR_CWD to the .Xr getcwd 3 , and .Ql 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 .Ql Va .CURDIR as well as the value of any variables named in .Ql Va MAKE_PRINT_VAR_ON_ERROR . .It Va .newline This variable is simply assigned a newline character as its value. 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 .Ql Va MAKE_PRINT_VAR_ON_ERROR could be done as ${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} .Pp (Only if .Ql Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It .Ev ${MAKEOBJDIR} .Pp (Only if .Ql Ev MAKEOBJDIR is set in the environment or on the command line.) .It .Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} .It .Ev ${.CURDIR} Ns Pa /obj .It .Pa /usr/obj/ Ns Ev ${.CURDIR} .It .Ev ${.CURDIR} .El .Pp Variable expansion is performed on the value before it's used, so expressions such as .Dl ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Ql Ev MAKEOBJDIR . .Pp .Ql Va .OBJDIR may be modified in the makefile via the special target .Ql Ic .OBJDIR . In all cases, .Nm will .Xr chdir 2 to the specified directory if it exists, and set .Ql Va .OBJDIR and .Ql Ev PWD to that directory before executing any targets. .Pp Except in the case of an explicit .Ql Ic .OBJDIR target, .Nm will check that the specified directory is writable and ignore it if not. This check can be skipped by setting the environment variable .Ql Ev MAKE_OBJDIR_CHECK_WRITABLE to "no". . .It Va .PARSEDIR A path to the directory of the current .Ql Pa Makefile being parsed. .It Va .PARSEFILE The basename of the current .Ql Pa Makefile being parsed. This variable and .Ql Va .PARSEDIR are both set only while the .Ql Pa Makefiles are being parsed. If you want to retain their current values, assign them to a variable using assignment with expansion: .Pq Ql Cm \&:= . .It Va .PATH A variable that represents the list of directories that .Nm will search for files. The search list should be updated using the target .Ql Va .PATH rather than the variable. .It Ev PWD Alternate path to the current directory. .Nm normally sets .Ql Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Ql Ev PWD is set and gives a path to the current directory, then .Nm sets .Ql Va .CURDIR to the value of .Ql Ev PWD instead. This behavior is disabled if .Ql Ev MAKEOBJDIRPREFIX is set or .Ql Ev MAKEOBJDIR contains a variable transform. .Ql Ev PWD is set to the value of .Ql Va .OBJDIR for all programs which .Nm executes. .It Ev .SHELL The pathname of the shell used to run target scripts. It is read-only. .It Ev .SUFFIXES The list of known suffixes. It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH Colon-separated .Pq Dq \&: lists of directories that .Nm will search for files. The variable is supported for compatibility with old make programs only, use .Ql 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: .Pp .Dl ${variable[:modifier[:...]]} .Pp Each modifier begins with a colon, which may be escaped with a backslash .Pq Ql \e . .Pp A set of modifiers can be specified via a variable, as follows: .Pp .Dl modifier_variable=modifier[:...] .Dl ${variable:${modifier_variable}[:...]} .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 .Pq Ql $ , these must be doubled to avoid early expansion. .Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql Oo Oc ) 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. . .It Cm \&:N Ns Ar pattern This is identical to .Ql Cm \&:M , but selects all words which do not match .Ar pattern . .It Cm \&:O Orders every word in variable alphabetically. .It Cm \&:On Orders every word in variable 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)). Both upper- and lower-case letters are accepted. .It Cm \&:Or Orders every word in variable in reverse alphabetical order. .It Cm \&:Orn Orders every word in variable in reverse numerical order. .It Cm \&: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 .Pq Ql 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 safely to the shell. .It Cm \&:q Quotes every shell meta-character in the variable, 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 . .It Cm \&:R Replaces each word in the variable with everything but its suffix. .It Cm \&:range[=count] The value is an integer sequence representing the words of the original value, or the supplied .Va count . .It Cm \&:gmtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr gmtime 3 . If a .Va utc 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[=utc] The value is a format string for .Xr strftime 3 , using .Xr localtime 3 . If a .Va utc 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. .It Cm \&:tl Converts variable 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 .Ar c . If .Ar c is omitted, then no separator is used. The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW Causes the value to be treated as a single word (possibly containing embedded white space). See also .Ql Cm \&:[*] . .It Cm \&:tw Causes the value to be treated as a sequence of words delimited by white space. See also .Ql 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 .Ar new_string . If a .Ql g 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. 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). 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 .Ql ^ or .Ql \&$ ) . 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 .Pq Ql \e . .Pp Variable expansion occurs in the normal fashion inside 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. .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 .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 .Ar pattern 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). .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. .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 .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. 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: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the .At V style variable substitution. It must be the last modifier specified. If .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 .Ar old_string contains the pattern matching character .Ar % , and .Ar old_string matches, then the result is the .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 .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. .Pp Variable expansion occurs in the normal fashion inside 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. .Sm off .It Cm \&:@ Ar temp 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 .Ar string . The ODE convention is that .Ar temp 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: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] Saves the current variable value in .Ql $_ or the named .Va 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. In order for this modifier to work, the name (node) must at least have appeared on the rhs 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. .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. .Pp The .Ql Cm \&:: helps avoid false matches with the .At V style .Cm \&:= modifier and since substitution always occurs the .Cm \&::= 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. .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 .Ql 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, .Ql 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. For example, .Ql 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 .Ql Cm \&:Or instead of .Ql Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of \&"$*\&" in Bourne shell. .\" :[0] .It 0 Means the same as .Ql Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words delimited by white space. Analogous to the effect of \&"$@\&" 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 .Pq Ql \&. character. 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 ... 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. .Pp If the include statement is written as .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 .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: .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 ... 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. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. .It Ic .export-env Ar variable ... 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 ... 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 ... The opposite of .Ql .export . The specified global .Va variable will be 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, so should be used sparingly. Testing for .Va .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: .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 .Ql Ev PATH , which is the minimal useful environment. Actually .Ql Ev .MAKE.LEVEL will also be pushed into the new environment. .It Ic .warning Ar message The message prefixed by .Ql Pa 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 ... 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 .else Reverse the sense of the last conditional. .It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... A combination of .Ql Ic .else followed by .Ql Ic .if . .It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifdef . .It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifndef . .It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifmake . .It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql 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 \&|\&| Logical OR. .It Cm \&&& Logical .Tn AND ; of higher precedence than .Dq \&|\&| . .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. The boolean operator .Ql Ic \&! may be used to logically negate an entire conditional. It is of higher precedence than .Ql 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 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 .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 .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. .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 .Ql Ic == or .Ql 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. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters a (white-space 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. If the form is .Ql Ic .ifdef , .Ql Ic .ifndef , or .Ql Ic .if the .Dq defined expression is applied. Similarly, if the form is .Ql Ic .ifmake or .Ql Ic .ifnmake , the .Dq make expression is applied. .Pp If the conditional evaluates to true the parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a .Ql Ic .else or .Ql Ic .endif is found. .Pp 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 \&.endfor .El .Pp After the for .Ic expression is evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each .Ic variable , in order, and these .Ic variables are substituted into the .Ic 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. .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. .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. .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. 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 .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 . .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 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 .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 .Ic .USE , but 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 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 .Nm can't figure out any other way to create. Only the shell script is used. The .Ic .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's 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. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The .Ic .ERROR_TARGET variable is set to the target that failed. See also .Ic 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. .It Ic .MAIN If no target is specified when .Nm is invoked, this target will be built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm when the makefile is used. The flags are as if typed to the shell, though the .Fl f option will have 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. .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 .OBJDIR The source is a new value for .Ql Va .OBJDIR . If it exists, .Nm will .Xr chdir 2 to it and update the value of .Ql Va .OBJDIR . .It Ic .ORDER 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 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 The ordering imposed by .Ic .ORDER is only relevant for parallel makes. .\" 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. .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 the source is the special .Ic .DOTLAST target, then the current working directory is searched last. .It Ic .PATH. Ns Va 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 -This should be the first non-comment line in a Makefile. -It results in the variable +If this is the first non-comment line in the main makefile, +the variable .Va %POSIX -being defined with the value -.Ql 1003.2 . -The first time -.Ic .POSIX -is encountered, the makefile -.Ql posix.mk -will be included if possible, -to provide POSIX compatible default rules. +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 .Ql posix.mk will contribute 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. .It Ic .SHELL Sets the shell that .Nm will use to execute commands. The sources are a set of .Ar field=value pairs. .Bl -tag -width hasErrCtls .It Ar name This is the minimal specification, used to select one of the built-in shell specs; .Ar sh , .Ar ksh , and .Ar csh . .It Ar path Specifies the path to the shell. .It Ar hasErrCtl Indicates whether the shell supports exit on error. .It Ar check The command to turn on error checking. .It Ar ignore The command to disable error checking. .It Ar echo The command to turn on echoing of commands executed. .It Ar quiet The command to turn off echoing of commands executed. .It Ar filter The output to filter after issuing the .Ar quiet command. It is typically identical to .Ar quiet . .It Ar errFlag The flag to pass the shell to enable error checking. .It Ar echoFlag The flag to pass the shell to enable command echoing. .It Ar 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 \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed .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 .Ql 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 .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; 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 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.) .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 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 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. 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 has been used to FoRCe rebuilding (since the target/dependency does not exist... unless someone creates an .Dq FRC file). .Sh 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. .Pp There is no way of escaping a space character in a filename. diff --git a/bmake.cat1 b/bmake.cat1 index 8e58f40309ff..b4442ea67b2e 100644 --- a/bmake.cat1 +++ b/bmake.cat1 @@ -1,1632 +1,1631 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) NAME 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 ...] DESCRIPTION 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)). 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. bmake will prepend 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. -C directory 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 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 specifying all of the debugging flags. a Print debugging information about archive searching and caching. C Print debugging information about current working direc- tory. c Print debugging information about conditional evaluation. d Print debugging information about directory searching and caching. e Print debugging information about failed commands and targets. 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. f Print debugging information about loop evaluation. g1 Print the input graph before making anything. g2 Print the input graph after making everything, or before exiting on error. g3 Print the input graph before exiting on error. h Print debugging information about hash table operations. 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 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 about targets. m Print debugging information about making targets, includ- ing modification dates. 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- 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. s Print debugging information about suffix-transformation rules. t Print debugging information about target list mainte- nance. 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. 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. -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. -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. -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- bility mode off, unless the B flag is also specified. When com- patibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the tradi- tional 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. -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 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). -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 `+'. -N Display the commands which 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. -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. -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- 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 .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. -v variable Like -V but the variable is always expanded to its complete value. -W Treat any warnings during makefile parsing as errors. -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- ment individually. Variables passed on the command line are 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 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- 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 specifications, shell commands, variable assignments, include statements, conditional directives, for loops, 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. 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: : 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- 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- 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- 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. SHELL COMMANDS 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- 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. 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. 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. 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 whole script one command. For example: avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \ (cd ${.CURDIR} && ${MAKE} $@); \ 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. VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradi- tion, consist of all upper-case letters. Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: = Assign the value to the variable. Any previous value is overrid- den. += Append the value to the current value of the variable. ?= 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. 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. Any white-space before the assigned value is removed; if the value is be- ing appended, a single space is inserted between the previous contents of the variable and the appended value. Variables are expanded by surrounding the variable name with either curly braces (`{}') or parentheses (`()') and preceding it with a dollar sign (`$'). If the variable name contains only a single letter, the surround- ing 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 avoided! If the result of expanding a variable contains a dollar sign (`$') the string is expanded again. Variable substitution occurs at three 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 shell commands are expanded when the shell command is executed. 3. ".for" loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the fol- lowing example code: .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .endfor all: @echo ${a} @echo ${b} will print: 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". Variable classes 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: 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. 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: .ALLSRC The list of all sources for this target; 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 in explicit rules. .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 `?'. .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- nized. .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 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 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'. Additional built-in variables In addition, bmake sets or knows about the following variables: $ A single dollar sign `$', i.e. `$$' expands to a single dollar sign. .ALLTARGETS The list of all targets encountered in the Makefile. If evaluated during Makefile parsing, lists only those tar- gets encountered thus far. .CURDIR A path to the directory where bmake was executed. Refer to the description of `PWD' for more details. .INCLUDEDFROMDIR The directory of the file this Makefile was included from. .INCLUDEDFROMFILE The filename of the file this Makefile was included from. 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.DEPENDFILE Names the makefile (default `.depend') from which gener- ated dependencies are read. .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. .MAKE.EXPORTED The list of variables exported by bmake. .MAKE.JOBS The argument to the -j option. .MAKE.JOB.PREFIX If bmake is run with j then 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: .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like `---make[1234] target ---' mak- ing 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. .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default `makefile', `Makefile') that bmake will look 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. .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. .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. .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. .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. .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' .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. .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. .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. .MAKE.PID The process-id of bmake. .MAKE.PPID The parent process-id of bmake. .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. .MAKE.UID The user-id running bmake. .MAKE.GID The group-id running bmake. 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. 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}@}. .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: 1. ${MAKEOBJDIRPREFIX}${.CURDIR} (Only if `MAKEOBJDIRPREFIX' is set in the environ- ment or on the command line.) 2. ${MAKEOBJDIR} (Only if `MAKEOBJDIR' is set in the environment or on the command line.) 3. ${.CURDIR}/obj.${MACHINE} 4. ${.CURDIR}/obj 5. /usr/obj/${.CURDIR} 6. ${.CURDIR} 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'. `.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. 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". .PARSEDIR A path to the directory of the current `Makefile' being parsed. .PARSEFILE The basename of the current `Makefile' being parsed. This variable and `.PARSEDIR' are both set only while the `Makefiles' are being parsed. If you want to retain their current values, assign them to a variable using as- signment with expansion: (`:='). .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, 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. .SUFFIXES The list of known suffixes. It is read-only. .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. 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: ${variable[:modifier[:...]]} Each modifier begins with a colon, which may be escaped with a backslash (`\'). A set of modifiers can be specified via a variable, as follows: modifier_variable=modifier[:...] ${variable:${modifier_variable}[:...]} 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. The supported modifiers are: :E Replaces each word in the variable with its suffix. :H Replaces each word in the variable with everything but the last com- ponent. :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. :Npattern This is identical to `:M', but selects all words which do not match pattern. :O Orders every word in variable alphabetically. :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. :Or Orders every word in variable in reverse alphabetical order. :Orn Orders every word in variable 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, 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 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'. :R Replaces each word in the variable with everything but its suffix. :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. :hash Computes a 32-bit hash of the value and encode it as 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. :tA Attempts to convert variable to an absolute path using realpath(3), if that fails, the value is unchanged. :tl Converts variable 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. :tu Converts variable to upper-case letters. :tW Causes the value to be treated as a single word (possibly containing embedded white space). See also `:[*]'. :tw Causes the value to be treated as a sequence of words delimited by white space. 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. :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). 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. :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- 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 ${NUMBERS:M42:?match:no} 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- 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. ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} However a single character 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 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'. :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- 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. :!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. ::=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. 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- 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]'. * Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of "$*" in Bourne shell. 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. # 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- 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- 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. 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. Conditional expressions are also preceded by a single dot as the first character of a line. The possible conditionals are as follows: .error message The message is printed along with the name of the makefile and line number, then bmake will exit immediately. .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 flag, so should be used with caution. For compatibility with other bmake programs `export variable=value' is also accepted. 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- 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 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-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: .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. .warning message The message prefixed by `warning:' is printed along with the name of the makefile and line number. .if [!]expression [operator expression ...] Test the value of an expression. .ifdef [!]variable [operator variable ...] Test the value of a variable. .ifndef [!]variable [operator variable ...] Test the value of a variable. .ifmake [!]target [operator target ...] Test the target being built. .ifnmake [!] target [operator target ...] Test the target being built. .else Reverse the sense of the last conditional. .elif [!] expression [operator expression ...] A combination of `.else' followed by `.if'. .elifdef [!]variable [operator variable ...] A combination of `.else' followed by `.ifdef'. .elifndef [!]variable [operator variable ...] A combination of `.else' followed by `.ifndef'. .elifmake [!]target [operator target ...] A combination of `.else' followed by `.ifmake'. .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 AND; 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: defined Takes a variable name as an argument and evaluates to true if the variable has been defined. 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 Takes a variable, with possible modifiers, and evaluates to true if the expansion of the variable would result in an empty string. 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 Takes a target name as an argument and evaluates to true if the target has been defined. 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, 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. 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 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. 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 .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. COMMENTS 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- way. .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. .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: skip-compare-for-some: @echo this will be compared @echo this will not ${.OODATE:M.NOMETA_CMP} @echo this will also be compared The :M pattern suppresses any expansion of the unwanted vari- able. .NOPATH Do not search for the target in the directories specified by .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. .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. .PRECIOUS When bmake is interrupted, it normally removes any partially 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 (`@'). .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. .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- 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 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 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- ing left around and corrupting future rebuilds. .END Any command lines attached to this target are executed after ev- erything else is done. .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. .MAIN If no target is specified when bmake is invoked, this target will be 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. .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'. .ORDER The named targets are made in sequence. This ordering does not add targets to the list of targets to be made. Since the depen- dents of a target do not get built until the target 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 The ordering imposed by .ORDER is only relevant for parallel makes. .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. .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 This should be the first non-comment line in a Makefile. It re- - sults in the variable %POSIX being defined with the value - `1003.2'. The first time .POSIX is encountered, the makefile - `posix.mk' will be included if possible, to provide POSIX com- - patible default rules. If bmake is run with the -r flag, then - only `posix.mk' will contribute to the default rules. + .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. .PRECIOUS Apply the .PRECIOUS attribute to any specified sources. If no 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. 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. hasErrCtl Indicates whether the shell supports exit on error. check The command to turn on error checking. ignore The command to disable error checking. echo The command to turn on echoing of commands executed. 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. errFlag The flag to pass the shell to enable error checking. echoFlag The flag to pass the shell to enable command echo- ing. 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- 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 .c.o: cc -o ${.TARGET} -c ${.IMPSRC} 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. FILES .depend list of dependencies Makefile list of dependencies makefile list of dependencies 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. 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- 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- 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- tably: +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.) +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.) +o All built-in variables 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. +o Variable modifiers, except for the :old=new string substitution, which does not portably support globbing with `%' and historically only works on declared suffixes. +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- 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- 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 Berkeley. It was designed to be a parallel distributed make running jobs 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). 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. There is no way of escaping a space character in a filename. -FreeBSD 13.0 March 24, 2022 FreeBSD 13.0 +FreeBSD 13.0 April 18, 2022 FreeBSD 13.0 diff --git a/cond.c b/cond.c index 5217b41a62cc..3dd177d5acb8 100644 --- a/cond.c +++ b/cond.c @@ -1,1277 +1,1277 @@ -/* $NetBSD: cond.c,v 1.333 2022/03/03 19:46:31 rillig Exp $ */ +/* $NetBSD: cond.c,v 1.334 2022/04/15 09:33:20 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. */ #include #include "make.h" #include "dir.h" /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: cond.c,v 1.333 2022/03/03 19:46:31 rillig Exp $"); +MAKE_RCSID("$NetBSD: cond.c,v 1.334 2022/04/15 09:33:20 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 */ /* 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: 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 !="); 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 'defined(${file})'. */ +/* 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) { 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) { 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) { 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) { unsigned int open_conds = cond_depth - cond_min_depth; if (open_conds != 0 || saved_depth > cond_depth) { Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds, open_conds == 1 ? "" : "s"); cond_depth = cond_min_depth; } 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/main.c b/main.c index 1f8e9481adc9..04ac7589027f 100644 --- a/main.c +++ b/main.c @@ -1,2196 +1,2197 @@ -/* $NetBSD: main.c,v 1.579 2022/03/22 23:37:09 rillig Exp $ */ +/* $NetBSD: main.c,v 1.580 2022/04/18 15:06:27 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. */ /* * 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.579 2022/03/22 23:37:09 rillig Exp $"); +MAKE_RCSID("$NetBSD: main.c,v 1.580 2022/04/18 15:06:27 rillig Exp $"); #if defined(MAKE_NATIVE) && !defined(lint) __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " "The Regents of the University of California. " "All rights reserved."); #endif #ifndef __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); } 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; } /* 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 } 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_Init will take care of creating all the * targets as well as initializing the module. */ Compat_Run(&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("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 */ #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"); 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); /* 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); snprintf(buf, sizeof buf, "%u", getppid()); Global_Set(".MAKE.PPID", buf); snprintf(buf, sizeof buf, "%u", getuid()); Global_Set(".MAKE.UID", buf); snprintf(buf, sizeof buf, "%u", getgid()); Global_Set(".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 (!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 unlink_file(const char *file) { struct stat st; if (lstat(file, &st) == -1) return false; if (S_ISDIR(st.st_mode)) { errno = EISDIR; return false; } return unlink(file) == 0; } 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/make.1 b/make.1 index cc740811b8a8..7c44356eefc7 100644 --- a/make.1 +++ b/make.1 @@ -1,2542 +1,2540 @@ -.\" $NetBSD: make.1,v 1.307 2022/03/26 15:39:58 sjg Exp $ +.\" $NetBSD: make.1,v 1.308 2022/04/18 15:06:27 rillig 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 March 24, 2022 +.Dd April 18, 2022 .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 ... .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, .Nm will try to open .Ql Pa makefile then .Ql Pa Makefile in order to find the specifications. If the file .Ql Pa .depend 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" . .Pp .Nm will prepend the contents of the .Va 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. .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 Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by .Ql \- they are added to the .Va 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 .Ar 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: .Bl -tag -width Ds .It Ar A Print all possible debugging information; equivalent to specifying all of the debugging flags. .It Ar a Print debugging information about archive searching and caching. .It Ar C Print debugging information about current working directory. .It Ar c Print debugging information about conditional evaluation. .It Ar d Print debugging information about directory searching and caching. .It Ar e Print debugging information about failed commands and targets. .It Ar F Ns Oo Sy \&+ 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 flag is .Ql \&+ , then the file will be opened in append mode; otherwise the file will be 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 \&+ option has no effect). Otherwise, the output will be written to the named file. If the file name ends .Ql .%d then the .Ql %d is replaced by the pid. .It Ar f Print debugging information about loop evaluation. .It Ar "g1" Print the input graph before making anything. .It Ar "g2" Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. .It Ar h Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. .It Ar 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 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 Print debugging information about making targets, including modification dates. .It Ar 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 Print debugging information about makefile parsing. .It Ar s Print debugging information about suffix-transformation rules. .It Ar t Print debugging information about target list maintenance. .It Ar V Force the .Fl V 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 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. .It Fl f Ar makefile Specify a makefile to read instead of the default .Ql Pa makefile . If .Ar makefile is .Ql Fl , 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 .Ql Fl 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 .Ar 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 .Ar B flag 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 .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 .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). .Pp If a file or directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string .Qq \&.../ then .Nm 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 successful, then the resulting directory replaces the .Qq \&.../ specification in the .Fl m argument. If used, 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 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 .Ql Ic + . .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 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. .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 .Ql 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, 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. 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 .Va .MAKE.EXPAND_VARIABLES is set to true and the .Fl 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 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 the variable is always expanded to its complete value. .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 environment variable. This option may be useful on systems which have a small limit on the size of command arguments. .It Ar variable=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 specifications, shell commands, variable assignments, include statements, conditional directives, for loops, and comments. .Pp In general, 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 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 \&:, for dependency lines with no sources, the attached shell commands are always run. Also unlike .Ic \&:, the target will not be removed if .Nm is interrupted. .El 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. 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 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. If the .Ql Ic \&:: operator is used, however, all rules may include scripts and the 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 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 .Ql Ic @ , .Ql Ic + , or .Ql Ic \- , the command is treated specially. A .Ql Ic @ causes the command not to be echoed before it is executed. A .Ql Ic + causes the command to be executed even when .Fl 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. A .Ql Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. .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. 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 .Nm will attempt direct execution. If a line starts with .Ql Ic \- and the shell has ErrCtl enabled then failure of the command line will be ignored as in compatibility mode. Otherwise .Ql 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. .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 .Dq cd or .Dq chdir without potentially 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 the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \e (cd ${.CURDIR} && ${MAKE} $@); \e echo Back in `pwd` .Ed .Pp Since .Nm will .Xr chdir 2 to .Ql Va .OBJDIR before executing any targets, each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradition, consist of all upper-case letters. .Ss Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overridden. .It Ic \&+= Append the value to the current value of the variable. .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. .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. .It Ic \&!= Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. .El .Pp Any white-space before the assigned .Ar value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. .Pp Variables are expanded by surrounding the variable name with either curly braces .Pq Ql {} or parentheses .Pq Ql () and preceding it with a dollar sign .Pq Ql \&$ . If the variable name contains only a single letter, the surrounding 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. This allows almost arbitrary variable names, however names containing dollar, braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ the string is expanded again. .Pp Variable substitution occurs at three 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 shell commands are expanded when the shell command is executed. .It .Dq .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the following example code: .Bd -literal -offset indent .Dv .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .Dv .endfor all: @echo ${a} @echo ${b} .Ed will print: .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} contains .Dq ${j} ${j} ${j} which expands to .Dq 3 3 3 since after the loop completes ${j} contains .Dq 3 . .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 .Va .MAKE.TARGET_LOCAL_VARIABLES is not set to .Ql false . The rest of the line (which will already have 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 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 .It Va .ALLSRC The list of all sources for this target; also known as .Ql Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Ql 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 .Ql Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Ql Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Ql Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Ql Va * . The suffix must be one of the known suffixes declared with .Ic .SUFFIXES or it will not be recognized. .It Va .TARGET The name of the target; also known as .Ql Va @ . For compatibility with other makes this is an alias for .Ic .ARCHIVE in archive member rules. .El .Pp The shorter forms .Ql ( Va > , .Ql Va \&! , .Ql Va < , .Ql Va % , .Ql Va \&? , .Ql Va * , and .Ql Va @ ) 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. .Ql Va $(@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 .Ql Va .TARGET , .Ql Va .PREFIX , .Ql Va .ARCHIVE , and .Ql Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: .Bl -tag -width .MAKEOVERRIDES .It Va \&$ A single dollar sign .Ql \&$ , i.e. .Ql \&$$ expands to a single dollar sign. .It Va .ALLTARGETS The list of all targets encountered in the Makefile. 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 .Ql Ev PWD for more details. .It Va .INCLUDEDFROMDIR 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 name that .Nm was executed with .Pq Va argv[0] . For compatibility .Nm also sets .Va .MAKE with the same value. The preferred variable to use is the environment variable .Ev MAKE because it is more compatible with other versions of .Nm and cannot be confused with the special target with the same name. .It Va .MAKE.DEPENDFILE Names the makefile (default .Ql Pa .depend ) from which generated dependencies are read. .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 .MAKE.JOB.PREFIX If .Nm is run with .Ar j then output for each target is prefixed with a token .Ql --- target --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. .br For example: .Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like .Ql ---make[1234] target --- 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 .Ql 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 .Ql Ev MAKEFLAGS variable which is then entered into the environment for all programs which .Nm executes. .It Va .MAKE.LEVEL The recursion depth of .Nm . The initial instance of .Nm will be 0, and an incremented value is put into the environment to be seen by the next generation. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 to protect things which should only be evaluated in the initial instance of .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Ql Pa makefile , .Ql Pa Makefile ) that .Nm will look 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.MODE Processed after reading all makefiles. Can affect the mode that .Nm runs in. It can contain a number of keywords: .Bl -hang -width missing-filemon=bf. .It Pa compat Like .Fl B , puts .Nm into "compat" mode. .It Pa meta Puts .Nm into "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 .Nm will not create .meta files in .Ql Va .CURDIR . This can be overridden by setting .Va bf to a value which represents True. .It Pa missing-meta= Ar bf If .Va bf is True, then a missing .meta file makes the target out-of-date. .It Pa missing-filemon= Ar bf If .Va bf is True, then missing filemon data makes the target out-of-date. .It Pa nofilemon Do not use .Xr filemon 4 . .It Pa 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. This is useful if the build is otherwise running silently. The message printed the value of: .Va .MAKE.META.PREFIX . .It Pa 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 .Ic .NOMETA_CMP . .It Pa silent= Ar bf If .Va bf is True, when a .meta file is created, mark the target .Ic .SILENT . .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: .Ql 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 .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 .Ql Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Ql Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Ql Va .MAKEOVERRIDES . .Ql Ev MAKEFLAGS is re-exported whenever .Ql 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 .Nm . .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 .Nm . .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Ql Va .ERROR_TARGET to the name of the target that failed, .Ql Va .ERROR_CMD to the commands of the failed target, and in "meta" mode, it also sets .Ql Va .ERROR_CWD to the .Xr getcwd 3 , and .Ql 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 .Ql Va .CURDIR as well as the value of any variables named in .Ql Va MAKE_PRINT_VAR_ON_ERROR . .It Va .newline This variable is simply assigned a newline character as its value. 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 .Ql Va MAKE_PRINT_VAR_ON_ERROR could be done as ${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} .Pp (Only if .Ql Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It .Ev ${MAKEOBJDIR} .Pp (Only if .Ql Ev MAKEOBJDIR is set in the environment or on the command line.) .It .Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} .It .Ev ${.CURDIR} Ns Pa /obj .It .Pa /usr/obj/ Ns Ev ${.CURDIR} .It .Ev ${.CURDIR} .El .Pp Variable expansion is performed on the value before it's used, so expressions such as .Dl ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Ql Ev MAKEOBJDIR . .Pp .Ql Va .OBJDIR may be modified in the makefile via the special target .Ql Ic .OBJDIR . In all cases, .Nm will .Xr chdir 2 to the specified directory if it exists, and set .Ql Va .OBJDIR and .Ql Ev PWD to that directory before executing any targets. .Pp Except in the case of an explicit .Ql Ic .OBJDIR target, .Nm will check that the specified directory is writable and ignore it if not. This check can be skipped by setting the environment variable .Ql Ev MAKE_OBJDIR_CHECK_WRITABLE to "no". . .It Va .PARSEDIR A path to the directory of the current .Ql Pa Makefile being parsed. .It Va .PARSEFILE The basename of the current .Ql Pa Makefile being parsed. This variable and .Ql Va .PARSEDIR are both set only while the .Ql Pa Makefiles are being parsed. If you want to retain their current values, assign them to a variable using assignment with expansion: .Pq Ql Cm \&:= . .It Va .PATH A variable that represents the list of directories that .Nm will search for files. The search list should be updated using the target .Ql Va .PATH rather than the variable. .It Ev PWD Alternate path to the current directory. .Nm normally sets .Ql Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Ql Ev PWD is set and gives a path to the current directory, then .Nm sets .Ql Va .CURDIR to the value of .Ql Ev PWD instead. This behavior is disabled if .Ql Ev MAKEOBJDIRPREFIX is set or .Ql Ev MAKEOBJDIR contains a variable transform. .Ql Ev PWD is set to the value of .Ql Va .OBJDIR for all programs which .Nm executes. .It Ev .SHELL The pathname of the shell used to run target scripts. It is read-only. .It Ev .SUFFIXES The list of known suffixes. It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH Colon-separated .Pq Dq \&: lists of directories that .Nm will search for files. The variable is supported for compatibility with old make programs only, use .Ql 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: .Pp .Dl ${variable[:modifier[:...]]} .Pp Each modifier begins with a colon, which may be escaped with a backslash .Pq Ql \e . .Pp A set of modifiers can be specified via a variable, as follows: .Pp .Dl modifier_variable=modifier[:...] .Dl ${variable:${modifier_variable}[:...]} .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 .Pq Ql $ , these must be doubled to avoid early expansion. .Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql Oo Oc ) 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. . .It Cm \&:N Ns Ar pattern This is identical to .Ql Cm \&:M , but selects all words which do not match .Ar pattern . .It Cm \&:O Orders every word in variable alphabetically. .It Cm \&:On Orders every word in variable 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)). Both upper- and lower-case letters are accepted. .It Cm \&:Or Orders every word in variable in reverse alphabetical order. .It Cm \&:Orn Orders every word in variable in reverse numerical order. .It Cm \&: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 .Pq Ql 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 safely to the shell. .It Cm \&:q Quotes every shell meta-character in the variable, 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 . .It Cm \&:R Replaces each word in the variable with everything but its suffix. .It Cm \&:range[=count] The value is an integer sequence representing the words of the original value, or the supplied .Va count . .It Cm \&:gmtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr gmtime 3 . If a .Va utc 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[=utc] The value is a format string for .Xr strftime 3 , using .Xr localtime 3 . If a .Va utc 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. .It Cm \&:tl Converts variable 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 .Ar c . If .Ar c is omitted, then no separator is used. The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW Causes the value to be treated as a single word (possibly containing embedded white space). See also .Ql Cm \&:[*] . .It Cm \&:tw Causes the value to be treated as a sequence of words delimited by white space. See also .Ql 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 .Ar new_string . If a .Ql g 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. 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). 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 .Ql ^ or .Ql \&$ ) . 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 .Pq Ql \e . .Pp Variable expansion occurs in the normal fashion inside 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. .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 .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 .Ar pattern 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). .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. .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 .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. 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: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the .At V style variable substitution. It must be the last modifier specified. If .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 .Ar old_string contains the pattern matching character .Ar % , and .Ar old_string matches, then the result is the .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 .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. .Pp Variable expansion occurs in the normal fashion inside 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. .Sm off .It Cm \&:@ Ar temp 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 .Ar string . The ODE convention is that .Ar temp 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: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] Saves the current variable value in .Ql $_ or the named .Va 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. In order for this modifier to work, the name (node) must at least have appeared on the rhs 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. .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. .Pp The .Ql Cm \&:: helps avoid false matches with the .At V style .Cm \&:= modifier and since substitution always occurs the .Cm \&::= 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. .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 .Ql 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, .Ql 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. For example, .Ql 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 .Ql Cm \&:Or instead of .Ql Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of \&"$*\&" in Bourne shell. .\" :[0] .It 0 Means the same as .Ql Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words delimited by white space. Analogous to the effect of \&"$@\&" 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 .Pq Ql \&. character. 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 ... 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. .Pp If the include statement is written as .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 .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: .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 ... 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. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. .It Ic .export-env Ar variable ... 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 ... 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 ... The opposite of .Ql .export . The specified global .Va variable will be 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, so should be used sparingly. Testing for .Va .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: .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 .Ql Ev PATH , which is the minimal useful environment. Actually .Ql Ev .MAKE.LEVEL will also be pushed into the new environment. .It Ic .warning Ar message The message prefixed by .Ql Pa 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 ... 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 .else Reverse the sense of the last conditional. .It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... A combination of .Ql Ic .else followed by .Ql Ic .if . .It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifdef . .It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifndef . .It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifmake . .It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql 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 \&|\&| Logical OR. .It Cm \&&& Logical .Tn AND ; of higher precedence than .Dq \&|\&| . .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. The boolean operator .Ql Ic \&! may be used to logically negate an entire conditional. It is of higher precedence than .Ql 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 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 .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 .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. .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 .Ql Ic == or .Ql 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. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters a (white-space 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. If the form is .Ql Ic .ifdef , .Ql Ic .ifndef , or .Ql Ic .if the .Dq defined expression is applied. Similarly, if the form is .Ql Ic .ifmake or .Ql Ic .ifnmake , the .Dq make expression is applied. .Pp If the conditional evaluates to true the parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a .Ql Ic .else or .Ql Ic .endif is found. .Pp 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 \&.endfor .El .Pp After the for .Ic expression is evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each .Ic variable , in order, and these .Ic variables are substituted into the .Ic 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. .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. .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. .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. 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 .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 . .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 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 .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 .Ic .USE , but 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 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 .Nm can't figure out any other way to create. Only the shell script is used. The .Ic .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's 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. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The .Ic .ERROR_TARGET variable is set to the target that failed. See also .Ic 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. .It Ic .MAIN If no target is specified when .Nm is invoked, this target will be built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm when the makefile is used. The flags are as if typed to the shell, though the .Fl f option will have 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. .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 .OBJDIR The source is a new value for .Ql Va .OBJDIR . If it exists, .Nm will .Xr chdir 2 to it and update the value of .Ql Va .OBJDIR . .It Ic .ORDER 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 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 The ordering imposed by .Ic .ORDER is only relevant for parallel makes. .\" 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. .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 the source is the special .Ic .DOTLAST target, then the current working directory is searched last. .It Ic .PATH. Ns Va 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 -This should be the first non-comment line in a Makefile. -It results in the variable +If this is the first non-comment line in the main makefile, +the variable .Va %POSIX -being defined with the value -.Ql 1003.2 . -The first time -.Ic .POSIX -is encountered, the makefile -.Ql posix.mk -will be included if possible, -to provide POSIX compatible default rules. +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 .Ql posix.mk will contribute 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. .It Ic .SHELL Sets the shell that .Nm will use to execute commands. The sources are a set of .Ar field=value pairs. .Bl -tag -width hasErrCtls .It Ar name This is the minimal specification, used to select one of the built-in shell specs; .Ar sh , .Ar ksh , and .Ar csh . .It Ar path Specifies the path to the shell. .It Ar hasErrCtl Indicates whether the shell supports exit on error. .It Ar check The command to turn on error checking. .It Ar ignore The command to disable error checking. .It Ar echo The command to turn on echoing of commands executed. .It Ar quiet The command to turn off echoing of commands executed. .It Ar filter The output to filter after issuing the .Ar quiet command. It is typically identical to .Ar quiet . .It Ar errFlag The flag to pass the shell to enable error checking. .It Ar echoFlag The flag to pass the shell to enable command echoing. .It Ar 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 \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed .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 .Ql 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 .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; 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 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.) .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 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 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 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. 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 has been used to FoRCe rebuilding (since the target/dependency does not exist... unless someone creates an .Dq FRC file). .Sh BUGS The .Nm 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 .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/make.h b/make.h index b9538a1fd7d5..9b73b04abb74 100644 --- a/make.h +++ b/make.h @@ -1,1228 +1,1239 @@ -/* $NetBSD: make.h,v 1.299 2022/03/26 14:02:40 rillig Exp $ */ +/* $NetBSD: make.h,v 1.300 2022/04/18 15:06:27 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. * * 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; } 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_Run(GNodeList *); void Compat_Make(GNode *, GNode *); /* cond.c */ 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; /* 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 *); /* 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; 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 char *, unsigned); 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; /* 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 Global_Set(const char *, const char *); void Global_Append(const char *, const char *); void Global_Delete(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; /* 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/meta.c b/meta.c index 86155b10afb0..f400485d25c0 100644 --- a/meta.c +++ b/meta.c @@ -1,1731 +1,1729 @@ -/* $NetBSD: meta.c,v 1.199 2022/03/04 23:17:16 sjg Exp $ */ +/* $NetBSD: meta.c,v 1.200 2022/04/15 12:28:16 rillig 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 { ldname = strlen(dname); if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/') snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]); else snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); /* * 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(_type) do { \ - if ((gn->type & __CONCAT(OP_, _type))) { \ - if (verbose) { \ - debug_printf("Skipping meta for %s: .%s\n", \ - gn->name, __STRING(_type)); \ - } \ +#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(NOMETA); + SKIP_META_TYPE(OP_NOMETA, "NOMETA"); /* Unless it is explicitly flagged as .META */ if (!(gn->type & OP_META)) { - SKIP_META_TYPE(PHONY); - SKIP_META_TYPE(SPECIAL); - SKIP_META_TYPE(MAKE); + 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); 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 (strcmp(latestdir, lcwd) != 0) { /* Check vs lcwd */ snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p); sdirs[sdx++] = fname2; } if (strcmp(lcwd, cwd) != 0) { /* Check vs cwd */ snprintf(fname3, sizeof fname3, "%s/%s", cwd, p); 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/parse.c b/parse.c index 06243d008e64..398c594f523e 100644 --- a/parse.c +++ b/parse.c @@ -1,2978 +1,2974 @@ -/* $NetBSD: parse.c,v 1.668 2022/03/25 21:16:04 sjg Exp $ */ +/* $NetBSD: parse.c,v 1.670 2022/04/18 16:09:05 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.668 2022/03/25 21:16:04 sjg Exp $"); +MAKE_RCSID("$NetBSD: parse.c,v 1.670 2022/04/18 16:09:05 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 */ 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_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_SHELL, /* .SHELL */ SP_SILENT, /* .SILENT */ SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */ SP_STALE, /* .STALE */ SP_SUFFIXES, /* .SUFFIXES */ 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 }, { ".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 }, { ".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 }, { ".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) { return Vector_Get(&includes, i); } /* The file that is currently being read. */ static IncludedFile * CurFile(void) { return GetInclude(includes.len - 1); } 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; 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 char *fname, unsigned lineno) { char dirbuf[MAXPATHLEN + 1]; FStr dir, base; 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(6, 0) ParseVErrorInternal(FILE *f, bool useVars, const char *fname, unsigned lineno, ParseErrorLevel level, const char *fmt, va_list ap) { static bool fatal_warning_error_printed = false; (void)fprintf(f, "%s: ", progname); if (fname != NULL) PrintLocation(f, useVars, fname, lineno); 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(4, 5) ParseErrorInternal(const char *fname, unsigned lineno, ParseErrorLevel level, const char *fmt, ...) { va_list ap; (void)fflush(stdout); va_start(ap, fmt); ParseVErrorInternal(stderr, false, fname, lineno, level, fmt, ap); va_end(ap); if (opts.debug_file != stdout && opts.debug_file != stderr) { va_start(ap, fmt); ParseVErrorInternal(opts.debug_file, false, fname, lineno, 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; const char *fname; unsigned lineno; if (includes.len == 0) { fname = NULL; lineno = 0; } else { IncludedFile *curFile = CurFile(); fname = curFile->name.str; lineno = curFile->lineno; } (void)fflush(stdout); va_start(ap, fmt); ParseVErrorInternal(stderr, true, fname, lineno, level, fmt, ap); va_end(ap); if (opts.debug_file != stdout && opts.debug_file != stderr) { va_start(ap, fmt); ParseVErrorInternal(opts.debug_file, true, fname, lineno, 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; 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 wait_src[16]; GNode *gn; snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); gn = Targ_NewInternalNode(wait_src); 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_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 HandleDependencyTargetMundane(char *targetName) { StringList targetNames = LST_INIT; if (Dir_HasWildcards(targetName)) { SearchPath *emptyPath = SearchPath_New(); SearchPath_Expand(emptyPath, targetName, &targetNames); SearchPath_Free(emptyPath); } else Lst_Append(&targetNames, targetName); while (!Lst_IsEmpty(&targetNames)) { char *targName = Lst_Dequeue(&targetNames); GNode *gn = Suff_IsTransform(targName) ? Suff_AddTransform(targName) : Targ_GetNode(targName); if (doing_depend) RememberLocation(gn); Lst_Append(targets, gn); } } 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"); *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)[1] == ':') return *pp += 2, OP_DOUBLEDEP; else return (*pp)++, OP_DEPENDS; } static void ClearPaths(SearchPathList *paths) { if (paths != NULL) { SearchPathListNode *ln; for (ln = paths->first; ln != NULL; ln = ln->next) SearchPath_Clear(ln->datum); } Dir_SetPATH(); } /* * 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 */ char *newName; char *slash, *incdir; int fd; int i; fullname = file[0] == '/' ? bmake_strdup(file) : NULL; if (fullname == NULL && !isSystem) { /* * Include files contained in double-quotes are first searched * relative to the including file's location. We don't want to * cd there, of course, so we just tack on the old file's * leading path components and call Dir_FindFile to see if * we can locate the file. */ 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); if (fullname == NULL) { /* * Makefile wasn't found in same directory as included * makefile. * * Search for it first on the -I search path, then on * the .PATH search path, if not found in a -I * directory. If we have a suffix-specific path, we * should use that. */ const char *suff; SearchPath *suffPath = NULL; if ((suff = strrchr(file, '.')) != NULL) { suffPath = Suff_GetPath(suff); if (suffPath != NULL) fullname = Dir_FindFile(file, suffPath); } if (fullname == NULL) { fullname = Dir_FindFile(file, parseIncPath); if (fullname == NULL) fullname = Dir_FindFile(file, &dirSearchPath); } } } /* Looking for a system file or file still not found */ if (fullname == NULL) { /* * Look for it on the system path */ 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; } /* Actually open the file... */ fd = open(fullname, O_RDONLY); if (fd == -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); break; #ifdef POSIX case SP_POSIX: - Global_Set("%POSIX", "1003.2"); - { - static bool first_posix = true; - + if (posix_state == PS_NOW_OR_NEVER) { /* - * Since .POSIX: should be the first - * operative line in a makefile, - * if '-r' flag is used, no default rules have - * been read yet, in which case 'posix.mk' can - * be a substiute for 'sys.mk'. - * If '-r' is not used, then 'posix.mk' acts - * as an extension of 'sys.mk'. + * With '-r', 'posix.mk' (if it exists) + * can effectively substitute for 'sys.mk', + * otherwise it is an extension. */ - if (first_posix) { - first_posix = false; - IncludeFile("posix.mk", true, false, true); - } + 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_NULL: Suff_SetNull(word); break; case SP_OBJDIR: Main_SetObjdir(false, "%s", word); 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, const char *lstart, ParseSpecial *inout_special, GNodeType *inout_targetAttr, SearchPathList **inout_paths) { char *cp = *inout_cp; for (;;) { char *tgt = cp; ParseDependencyTargetWord(&cp, 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)) { Parse_Error(PARSE_FATAL, "Error in archive specification: \"%s\"", tgt); return false; } continue; } if (*cp == '\0') { InvalidLineType(lstart); return false; } if (!ApplyDependencyTarget(tgt, cp, inout_special, inout_targetAttr, inout_paths)) return false; if (*inout_special != SP_NOT && *inout_special != SP_PATH) SkipExtraTargets(&cp, lstart); else pp_skip_whitespace(&cp); if (*cp == '\0') break; if ((*cp == '!' || *cp == ':') && !IsEscaped(lstart, cp)) break; } *inout_cp = cp; 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) { ParseDependencySourcesSpecial(p, special, *inout_paths); if (*inout_paths != NULL) { Lst_Free(*inout_paths); *inout_paths = NULL; } if (special == SP_PATH) Dir_SetPATH(); } else { assert(*inout_paths == NULL); if (!ParseDependencySourcesMundane(p, special, targetAttr)) return; } 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 */ 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); ApplyDependencyOperator(ParseDependencyOp(&p)); 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->fname, gn->lineno, 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(); 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); 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 } } /* * 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")) 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/targ.c b/targ.c index 52c5531845d8..7ce495c73b01 100644 --- a/targ.c +++ b/targ.c @@ -1,604 +1,605 @@ -/* $NetBSD: targ.c,v 1.176 2022/01/07 20:50:35 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.177 2022/04/15 12:19:28 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.176 2022/01/07 20:50:35 rillig Exp $"); +MAKE_RCSID("$NetBSD: targ.c,v 1.177 2022/04/15 12:19:28 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 * 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. * * 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) */ 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). */ 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; } } } diff --git a/unit-tests/Makefile b/unit-tests/Makefile index 7141223714e2..c3b64abad84a 100644 --- a/unit-tests/Makefile +++ b/unit-tests/Makefile @@ -1,827 +1,823 @@ -# $Id: Makefile,v 1.178 2022/03/26 23:10:27 sjg Exp $ +# $Id: Makefile,v 1.180 2022/04/18 21:25:37 sjg Exp $ # -# $NetBSD: Makefile,v 1.311 2022/03/26 12:44:57 rillig Exp $ +# $NetBSD: Makefile,v 1.312 2022/04/18 15:06:28 rillig 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+= cond1 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-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-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 TESTS+= varquote -# for now at least -.if ${.SHELL:T} == "ksh" +# 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} == "Linux" && ${.SHELL:tA:T} != "bash" -.if exists(/etc/os-release) -distro!= . /etc/os-release && echo $$NAME -.endif -# dash fails -x output -# .SHELL is not bash so may be dash -# if distro is Ubuntu or we cannot tell, assume the worst -.if ${distro:U:NUbuntu} == "" -BROKEN_TESTS+= opt-debug-x-trace -.endif -.endif + .if ${.MAKE.OS} == "SCO_SV" BROKEN_TESTS+= \ opt-debug-graph[23] \ varmod-localtime \ varmod-to-separator \ -.if ${.SHELL:T} == "bash" +.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.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.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.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'@} # Some tests need an additional round of postprocessing. POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' # 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 '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.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: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.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),' # 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 MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510 MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" LIMIT_RESOURCES?= ulimit -v 200000 .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 so that the results can be compared. # # always pretend .MAKE was called 'make' _SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' _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' # 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/unit-tests/depsrc-use.exp b/unit-tests/depsrc-use.exp index c9810bda462d..d31b9e70eb30 100644 --- a/unit-tests/depsrc-use.exp +++ b/unit-tests/depsrc-use.exp @@ -1,6 +1,10 @@ first 1 first 2 second 1 second 2 +first-first 1 +first-first 2 +first-second 1 +first-second 2 directly exit status 0 diff --git a/unit-tests/depsrc-use.mk b/unit-tests/depsrc-use.mk index 3f73a5f04ad9..ea2cf25bfb6a 100644 --- a/unit-tests/depsrc-use.mk +++ b/unit-tests/depsrc-use.mk @@ -1,29 +1,39 @@ -# $NetBSD: depsrc-use.mk,v 1.5 2021/12/28 14:22:51 rillig Exp $ +# $NetBSD: depsrc-use.mk,v 1.6 2022/04/18 14:38:24 rillig Exp $ # # Tests for the special source .USE in dependency declarations, # which allows to append common commands to other targets. +# +# See also: +# .USEBEFORE +# depsrc-usebefore.mk # Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally # regarded as a candidate for the main target. On the other hand, a .USE # target was not. not-a-main-candidate: .USE all: action directly -first: .USE +first: .USE first-first first-second @echo first 1 # Using ${.TARGET} here would expand to "action" @echo first 2 +first-first: .USE + @echo first-first 1 + @echo first-first 2 +first-second: .USE + @echo first-second 1 + @echo first-second 2 second: .USE @echo second 1 @echo second 2 # It's possible but uncommon to have a .USE target with no commands. # This may happen as the result of expanding a .for loop. empty: .USE -# It's possible but uncommon to directly make a .USEBEFORE target. +# It's possible but uncommon to directly make a .USE target. directly: .USE @echo directly action: first second empty diff --git a/unit-tests/depsrc-usebefore.exp b/unit-tests/depsrc-usebefore.exp index c9810bda462d..d84299b66275 100644 --- a/unit-tests/depsrc-usebefore.exp +++ b/unit-tests/depsrc-usebefore.exp @@ -1,6 +1,42 @@ -first 1 -first 2 -second 1 -second 2 -directly +after-2-before-2 1 +after-2-before-2 2 +after-2-before-1 1 +after-2-before-1 2 +after-1-before-2 1 +after-1-before-2 2 +after-1-before-1 1 +after-1-before-1 2 +before-2-before-2 1 +before-2-before-2 2 +before-2-before-1 1 +before-2-before-1 2 +before-1-before-2 1 +before-1-before-2 2 +before-1-before-1 1 +before-1-before-1 2 +before-2 1 +before-2 2 +before-1 1 +before-1 2 +after-1 1 +after-1 2 +after-2 1 +after-2 2 +before-1-after-1 1 +before-1-after-1 2 +before-1-after-2 1 +before-1-after-2 2 +before-2-after-1 1 +before-2-after-1 2 +before-2-after-2 1 +before-2-after-2 2 +after-1-after-1 1 +after-1-after-1 2 +after-1-after-2 1 +after-1-after-2 2 +after-2-after-1 1 +after-2-after-1 2 +after-2-after-2 1 +after-2-after-2 2 +`directly' is up to date. exit status 0 diff --git a/unit-tests/depsrc-usebefore.mk b/unit-tests/depsrc-usebefore.mk index 58b3145e4f3f..672fb7144856 100644 --- a/unit-tests/depsrc-usebefore.mk +++ b/unit-tests/depsrc-usebefore.mk @@ -1,33 +1,115 @@ -# $NetBSD: depsrc-usebefore.mk,v 1.7 2021/12/28 14:22:51 rillig Exp $ +# $NetBSD: depsrc-usebefore.mk,v 1.9 2022/04/18 14:41:42 rillig Exp $ # # Tests for the special source .USEBEFORE in dependency declarations, # which allows to prepend common commands to other targets. # +# If a target depends on several .USE or .USEBEFORE nodes, the commands get +# appended or prepended in declaration order. For .USE nodes, this is the +# expected order, for .USEBEFORE nodes the order is somewhat reversed, and for +# .USE or .USEBEFORE nodes that depend on other .USE or .USEBEFORE nodes, it +# gets even more complicated. +# # See also: # .USE # depsrc-use.mk # Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally # regarded as a candidate for the main target. On the other hand, a .USE # target was not. not-a-main-candidate: .USEBEFORE -all: action directly +all: + @${MAKE} -r -f ${MAKEFILE} ordering + @${MAKE} -r -f ${MAKEFILE} directly + +ordering: before-1 before-2 after-1 after-2 + +before-1: .USEBEFORE before-1-before-1 before-1-before-2 before-1-after-1 before-1-after-2 + @echo before-1 1 + @echo before-1 2 + +before-1-before-1: .USEBEFORE + @echo before-1-before-1 1 + @echo before-1-before-1 2 + +before-1-before-2: .USEBEFORE + @echo before-1-before-2 1 + @echo before-1-before-2 2 + +before-1-after-1: .USE + @echo before-1-after-1 1 + @echo before-1-after-1 2 + +before-1-after-2: .USE + @echo before-1-after-2 1 + @echo before-1-after-2 2 + +before-2: .USEBEFORE before-2-before-1 before-2-before-2 before-2-after-1 before-2-after-2 + @echo before-2 1 + @echo before-2 2 + +before-2-before-1: .USEBEFORE + @echo before-2-before-1 1 + @echo before-2-before-1 2 + +before-2-before-2: .USEBEFORE + @echo before-2-before-2 1 + @echo before-2-before-2 2 + +before-2-after-1: .USE + @echo before-2-after-1 1 + @echo before-2-after-1 2 -first: .USEBEFORE - @echo first 1 # Using ${.TARGET} here would expand to "action" - @echo first 2 # Using ${.TARGET} here would expand to "action" +before-2-after-2: .USE + @echo before-2-after-2 1 + @echo before-2-after-2 2 -second: .USEBEFORE - @echo second 1 - @echo second 2 +after-1: .USE after-1-before-1 after-1-before-2 after-1-after-1 after-1-after-2 + @echo after-1 1 + @echo after-1 2 + +after-1-before-1: .USEBEFORE + @echo after-1-before-1 1 + @echo after-1-before-1 2 + +after-1-before-2: .USEBEFORE + @echo after-1-before-2 1 + @echo after-1-before-2 2 + +after-1-after-1: .USE + @echo after-1-after-1 1 + @echo after-1-after-1 2 + +after-1-after-2: .USE + @echo after-1-after-2 1 + @echo after-1-after-2 2 + +after-2: .USE after-2-before-1 after-2-before-2 after-2-after-1 after-2-after-2 + @echo after-2 1 + @echo after-2 2 + +after-2-before-1: .USEBEFORE + @echo after-2-before-1 1 + @echo after-2-before-1 2 + +after-2-before-2: .USEBEFORE + @echo after-2-before-2 1 + @echo after-2-before-2 2 + +after-2-after-1: .USE + @echo after-2-after-1 1 + @echo after-2-after-1 2 + +after-2-after-2: .USE + @echo after-2-after-2 1 + @echo after-2-after-2 2 # It is possible but uncommon to have a .USEBEFORE target with no commands. # This may happen as the result of expanding a .for loop. empty: .USEBEFORE -# It is possible but uncommon to directly make a .USEBEFORE target. +# It is technically possible to directly make a .USEBEFORE target, but it +# doesn't make sense since GNode_IsOODate considers such a target to always be +# up to date. directly: .USEBEFORE @echo directly - -action: second first empty diff --git a/unit-tests/deptgt-posix.exp b/unit-tests/deptgt-posix.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-posix.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-posix.mk b/unit-tests/deptgt-posix.mk new file mode 100644 index 000000000000..ae41af15ffec --- /dev/null +++ b/unit-tests/deptgt-posix.mk @@ -0,0 +1,116 @@ +# $NetBSD: deptgt-posix.mk,v 1.2 2022/04/18 15:59:39 sjg Exp $ +# +# Tests for the special target '.POSIX', which enables POSIX mode. +# +# As of 2022-04-18, this only means that the variable '%POSIX' is defined and +# that the variables and rules specified by POSIX replace the default ones. +# This is done by loading , if available. That file is not included +# in NetBSD, but only in the bmake distribution. As of 2022-04-18, POSIX +# support is not complete. +# +# Implementation node: this test needs to be isolated from the usual test +# to prevent unit-tests/posix.mk from interfering with the posix.mk from the +# system directory that this test uses. +# +# See also: +# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html + +TESTTMP= ${TMPDIR:U/tmp}/make.test.deptgt-posix +SYSDIR= ${TESTTMP}/sysdir +MAIN_MK= ${TESTTMP}/main.mk +INCLUDED_MK= ${TESTTMP}/included.mk + +all: .PHONY +.SILENT: + +set-up-sysdir: .USEBEFORE + mkdir -p ${SYSDIR} + printf '%s\n' > ${SYSDIR}/sys.mk \ + 'CC=sys-cc' \ + 'SEEN_SYS_MK=yes' + printf '%s\n' > ${SYSDIR}/posix.mk \ + 'CC=posix-cc' + +check-is-posix: .USE + printf '%s\n' >> ${MAIN_MK} \ + '.if $${CC} != "posix-cc"' \ + '. error' \ + '.endif' \ + '.if $${%POSIX} != "1003.2"' \ + '. error' \ + '.endif' \ + 'all: .PHONY' + +check-not-posix: .USE + printf '%s\n' >> ${MAIN_MK} \ + '.if $${CC} != "sys-cc"' \ + '. error' \ + '.endif' \ + '.if defined(%POSIX)' \ + '. error' \ + '.endif' \ + 'all: .PHONY' + +check-not-seen-sys-mk: .USE + printf '%s\n' >> ${MAIN_MK} \ + '.if defined(SEEN_SYS_MK)' \ + '. error' \ + '.endif' + +run: .USE + (cd "${TESTTMP}" && MAKEFLAGS=${MAKEFLAGS.${.TARGET}:Q} ${MAKE} \ + -m "${SYSDIR}" -f ${MAIN_MK:T}) + rm -rf ${TESTTMP} + +# If the main makefile has a '.for' loop as its first non-comment line, a +# strict reading of POSIX 2018 makes the makefile non-conforming. +all: after-for +after-for: .PHONY set-up-sysdir check-not-posix run + printf '%s\n' > ${MAIN_MK} \ + '# comment' \ + '' \ + '.for i in once' \ + '.POSIX:' \ + '.endfor' + +# If the main makefile has an '.if' conditional as its first non-comment line, +# a strict reading of POSIX 2018 makes the makefile non-conforming. +all: after-if +after-if: .PHONY set-up-sysdir check-not-posix run + printf '%s\n' > ${MAIN_MK} \ + '# comment' \ + '' \ + '.if 1' \ + '.POSIX:' \ + '.endif' + +# If the main makefile first includes another makefile and that included +# makefile tries to switch to POSIX mode, that's too late. +all: in-included-file +in-included-file: .PHONY set-up-sysdir check-not-posix run + printf 'include included.mk\n' > ${MAIN_MK} + printf '.POSIX:\n' > ${INCLUDED_MK} + +# If the main makefile switches to POSIX mode in its very first line, before +# and comment lines or empty lines, that works. +all: in-first-line +in-first-line: .PHONY set-up-sysdir check-is-posix run + printf '%s\n' > ${MAIN_MK} \ + '.POSIX:' + +# The only allowed lines before switching to POSIX mode are comment lines. +# POSIX defines that empty and blank lines are called comment lines as well. +all: after-comment-lines +after-comment-lines: .PHONY set-up-sysdir check-is-posix run + printf '%s\n' > ${MAIN_MK} \ + '# comment' \ + '' \ + '.POSIX:' + +# Running make with the option '-r' skips the builtin rules from . +# In that mode, '.POSIX:' just loads , which works as well. +MAKEFLAGS.no-builtins= -r +all: no-builtins +no-builtins: .PHONY set-up-sysdir check-is-posix check-not-seen-sys-mk run + printf '%s\n' > ${MAIN_MK} \ + '.POSIX:' diff --git a/unit-tests/deptgt-silent-jobs.mk b/unit-tests/deptgt-silent-jobs.mk index e16cca3b8016..aed85932281b 100644 --- a/unit-tests/deptgt-silent-jobs.mk +++ b/unit-tests/deptgt-silent-jobs.mk @@ -1,35 +1,34 @@ -# $NetBSD: deptgt-silent-jobs.mk,v 1.2 2022/02/12 11:14:48 rillig Exp $ +# $NetBSD: deptgt-silent-jobs.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $ # # Ensure that the special dependency target '.SILENT' only affects the amount # of output, but not the kind of error handling. # # History: # In job.c 1.83 from 2003.12.20.00.18.22, in an attempt to fix # https://gnats.netbsd.org/18573, commands that suppressed error # handling were output in jobs mode, even when the global '.SILENT' # was set. This was fixed in job.c 1.452 from 2022-02-12. # # See also: # https://gnats.netbsd.org/45356 all: compat jobs .PHONY: all compat jobs test .SILENT: test: @echo '${VARIANT}: testing 1' -echo '${VARIANT}: testing 2' echo '${VARIANT}: testing 3' # expect: compat: testing 1 # expect: compat: testing 2 # expect: compat: testing 3 compat: @${MAKE} -r -f ${MAKEFILE} test VARIANT=compat # expect: jobs: testing 1 -# expect: echo 'jobs: testing 2' # expect: jobs: testing 2 # expect: jobs: testing 3 jobs: @${MAKE} -r -f ${MAKEFILE} test VARIANT=jobs -j1 diff --git a/unit-tests/opt-debug-cond.mk b/unit-tests/opt-debug-cond.mk index 056996cf0ece..28ad59587789 100644 --- a/unit-tests/opt-debug-cond.mk +++ b/unit-tests/opt-debug-cond.mk @@ -1,24 +1,24 @@ -# $NetBSD: opt-debug-cond.mk,v 1.2 2022/01/23 16:09:38 rillig Exp $ +# $NetBSD: opt-debug-cond.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $ # # Tests for the -dc command line option, which adds debug logging for the # evaluation of conditional expressions, such as in .if directives and # ${cond:?then:else} expressions. .MAKEFLAGS: -dc # expect: CondParser_Eval: ${:U12345} > ${:U55555} -# expect: lhs = 12345.000000, rhs = 55555.000000, op = > +# expect: Comparing 12345.000000 > 55555.000000 .if ${:U12345} > ${:U55555} # expect: CondParser_Eval: "string" != "string" -# expect: lhs = "string", rhs = "string", op = != +# expect: Comparing "string" != "string" .elif "string" != "string" # expect: CondParser_Eval: "nonempty" .elif "nonempty" .endif .MAKEFLAGS: -d0 all: .PHONY diff --git a/unit-tests/varname-dot-suffixes.mk b/unit-tests/varname-dot-suffixes.mk index babbe20d1c7c..f9f995fcd845 100644 --- a/unit-tests/varname-dot-suffixes.mk +++ b/unit-tests/varname-dot-suffixes.mk @@ -1,106 +1,106 @@ -# $NetBSD: varname-dot-suffixes.mk,v 1.2 2022/01/15 12:35:18 rillig Exp $ +# $NetBSD: varname-dot-suffixes.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $ # # Tests for the special "variable" .SUFFIXES, which lists the suffixes that # have been registered for use in suffix transformation rules. Suffixes are # listed even if there is no actual transformation rule that uses them. # # The name '.SUFFIXES' does not refer to a real variable, instead it can be # used as a starting "variable name" for expressions like ${.SUFFIXES} or # ${.SUFFIXES:M*o}. # In the beginning, there are no suffix rules, the expression is thus empty. .if ${.SUFFIXES} != "" .endif # There is no actual variable named '.SUFFIXES', it is all made up. .if defined(.SUFFIXES) . error .endif # The suffixes list is still empty, and so is the "variable" '.SUFFIXES'. .if !empty(.SUFFIXES) . error .endif .SUFFIXES: .c .o .1 .err # The suffixes are listed in declaration order. .if ${.SUFFIXES} != ".c .o .1 .err" . error .endif # There is still no actual variable named '.SUFFIXES', it is all made up. .if defined(.SUFFIXES) . error .endif # Now the suffixes list is not empty anymore. It may seem strange that there # is no variable named '.SUFFIXES' but evaluating '${.SUFFIXES}' nevertheless # returns something. For all practical use cases, it's good enough though. .if empty(.SUFFIXES) . error .endif .SUFFIXES: .tar.gz # Changes to the suffixes list are reflected immediately. .if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" . error .endif # Deleting .SUFFIXES has no effect since there is no actual variable of that # name. .MAKEFLAGS: -dv -# expect: Global:delete .SUFFIXES (not found) +# expect: Global: delete .SUFFIXES (not found) .undef .SUFFIXES .MAKEFLAGS: -d0 .if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" . error .endif # The list of suffixes can only be modified using dependency declarations, any # attempt at setting the variable named '.SUFFIXES' is rejected. .MAKEFLAGS: -dv # expect: Global: .SUFFIXES = set ignored (read-only) .SUFFIXES= set # expect: Global: .SUFFIXES = append ignored (read-only) .SUFFIXES+= append # expect: Global: .SUFFIXES = assign ignored (read-only) _:= ${.SUFFIXES::=assign} # expect: Global: .SUFFIXES = preserve ignored (read-only) _:= ${preserve:L:_=.SUFFIXES} .MAKEFLAGS: -d0 # Using the name '.SUFFIXES' in a .for loop looks strange because these # variable names are typically in singular form, and .for loops do not use # real variables either, they are made up as well, see directive-for.mk. The # replacement mechanism for the iteration variables takes precedence. .for .SUFFIXES in .c .o . if ${.SUFFIXES} != ".c" && ${.SUFFIXES} != ".o" . error . endif .endfor # After the .for loop, the expression '${.SUFFIXES}' refers to the list of # suffixes again. .if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" . error .endif # Using the name '.SUFFIXES' in the modifier ':@var@body@' does not create an # actual variable either. Like in the .for loop, choosing the name # '.SUFFIXES' for the iteration variable is unusual. In ODE Make, the # convention for these iteration variables is to have dots at both ends, so # the name would be '.SUFFIXES.', furthermore the name of the iteration # variable is typically in singular form. .MAKEFLAGS: -dv # expect: Command: .SUFFIXES = 1 ignored (read-only) # expect: Command: .SUFFIXES = 2 ignored (read-only) # XXX: Missing space after ':' -# expect: Command:delete .SUFFIXES (not found) +# expect: Command: delete .SUFFIXES (not found) .if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" . error .endif .MAKEFLAGS: -d0 all: