diff --git a/share/mk/auto.obj.mk b/share/mk/auto.obj.mk index cfe60ef9c4d6..4b8c5325b71f 100644 --- a/share/mk/auto.obj.mk +++ b/share/mk/auto.obj.mk @@ -1,78 +1,81 @@ # SPDX-License-Identifier: BSD-2-Clause # -# $Id: auto.obj.mk,v 1.17 2024/02/17 17:26:57 sjg Exp $ +# $Id: auto.obj.mk,v 1.20 2025/05/17 15:29:55 sjg Exp $ # -# @(#) Copyright (c) 2004, Simon J. Gerraty +# @(#) Copyright (c) 2004-2025, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # ECHO_TRACE ?= echo .ifndef Mkdirs # A race condition in some versions of mkdir, means that it can bail # if another process made a dir that mkdir expected to. # We repeat the mkdir -p a number of times to try and work around this. # We stop looping as soon as the dir exists. # If we get to the end of the loop, a plain mkdir will issue an error. Mkdirs= Mkdirs() { \ for d in $$*; do \ for i in 1 2 3 4 5 6; do \ mkdir -p $$d; \ test -d $$d && return 0; \ done > /dev/null 2>&1; \ mkdir $$d || exit $$?; \ done; } .endif # if MKOBJDIRS is set to auto (and NOOBJ isn't defined) do some magic... # This will automatically create objdirs as needed. # Skip it if we are just doing 'clean'. .if ${MK_AUTO_OBJ:Uno} == "yes" MKOBJDIRS= auto .endif .if !defined(NOOBJ) && !defined(NO_OBJ) && ${MKOBJDIRS:Uno} == auto # Use __objdir here so it is easier to tweak without impacting # the logic. .if !empty(MAKEOBJDIRPREFIX) .if ${.CURDIR:M${MAKEOBJDIRPREFIX}/*} != "" # we are already in obj tree! __objdir?= ${.CURDIR} .endif __objdir?= ${MAKEOBJDIRPREFIX}${.CURDIR} .endif __objdir?= ${MAKEOBJDIR:Uobj} -__objdir:= ${__objdir} +# relative dirs can cause trouble below +# keep it simple and convert to absolute path now if needed +.if ${__objdir:M/*} == "" +# avoid ugly ${.CURDIR}/./obj etc. +__objdir:= ${.CURDIR}/${__objdir:S,^./,,} +.endif .if ${.OBJDIR:tA} != ${__objdir:tA} # We need to chdir, make the directory if needed .if !exists(${__objdir}/) && \ (${.TARGETS} == "" || ${.TARGETS:Nclean*:N*clean:Ndestroy*} != "") # This will actually make it... __objdir_made != echo ${__objdir}/; umask ${OBJDIR_UMASK:U002}; \ ${ECHO_TRACE} "[Creating objdir ${__objdir}...]" >&2; \ ${Mkdirs}; Mkdirs ${__objdir} .endif # This causes make to use the specified directory as .OBJDIR .OBJDIR: ${__objdir} .if ${.OBJDIR:tA} != ${__objdir:tA} # we did not get what we want - do we care? .if ${__objdir_made:Uno:M${__objdir}/*} != "" -# watch out for __objdir being relative path -.if !(${__objdir:M/*} == "" && ${.OBJDIR:tA} == ${${.CURDIR}/${__objdir}:L:tA}) +# we attempted to make ${__objdir} and failed .error could not use ${__objdir}: .OBJDIR=${.OBJDIR} .endif -.endif # apparently we can live with it # make sure we know what we have .OBJDIR: ${.CURDIR} .endif .endif .endif diff --git a/share/mk/jobs.mk b/share/mk/jobs.mk index 0643e6481082..e304e16a9c56 100644 --- a/share/mk/jobs.mk +++ b/share/mk/jobs.mk @@ -1,105 +1,108 @@ -# $Id: jobs.mk,v 1.14 2023/09/11 16:52:44 sjg Exp $ +# SPDX-License-Identifier: BSD-2-Clause # -# @(#) Copyright (c) 2012-2023, Simon J. Gerraty +# $Id: jobs.mk,v 1.19 2025/02/03 21:18:44 sjg Exp $ +# +# @(#) Copyright (c) 2012-2025, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # This makefile is used by top-level makefile. # With the following: # # .if make(*-jobs) # .include # .endif # # # Then if you do: # # mk target-jobs # # We will run: # # ${MAKE} -j${JOB_MAX} target > ${JOB_LOGDIR}/target.log 2>&1 # # JOB_MAX should be something like 1.2 - 1.5 times the number of # available CPUs. # If bmake sets .MAKE.JOBS.C=yes we can use -jC and # JOB_MAX defaults to JOB_MAX_C (default 1.33C). # Otherwise we use 8. # now_utc ?= ${%s:L:localtime} .if !defined(start_utc) start_utc := ${now_utc} .endif .if make(*-jobs) .info ${.newline}${TIME_STAMP} Start ${.TARGETS} JOB_LOGDIR ?= ${SRCTOP:H} JOB_LOG = ${JOB_LOGDIR}/${.TARGET:S,-jobs,,:S,/,_,g}.log JOB_LOG_GENS ?= 4 # we like to rotate logs .if empty(NEWLOG_SH) .for d in ${.SYSPATH:U${.PARSEDIR}:@x@$x $x/scripts@} .if exists($d/newlog.sh) NEWLOG_SH := $d/newlog.sh .if ${MAKE_VERSION} > 20220924 .break .endif .endif .endfor .if empty(NEWLOG_SH) .ifdef M_whence NEWLOG_SH := ${newlog.sh:L:${M_whence}} .else NEWLOG_SH := ${(type newlog.sh) 2> /dev/null:L:sh:M/*} .endif .endif .endif .if !empty(NEWLOG_SH) && exists(${NEWLOG_SH}) -NEWLOG := sh ${NEWLOG_SH} +NEWLOG := ${.SHELL:Ush} ${NEWLOG_SH} JOB_NEWLOG_ARGS ?= -S -n ${JOB_LOG_GENS} .else NEWLOG = : .endif .if ${.MAKE.JOBS:U0} > 0 JOB_MAX = ${.MAKE.JOBS} .else # This should be derrived from number of cpu's .if ${.MAKE.JOBS.C:Uno} == "yes" # 1.2 - 1.5 times nCPU works well on most machines that support -jC -JOB_MAX_C ?= 1.33C +# if the factor is floating point, the C suffix isn't needed +JOB_MAX_C ?= 1.33 JOB_MAX ?= ${JOB_MAX_C} .endif JOB_MAX ?= 8 JOB_ARGS += -j${JOB_MAX} .endif # we need to reset .MAKE.LEVEL to 0 so that # build orchestration works as expected (DIRDEPS_BUILD) ${.TARGETS:M*-jobs}: @${NEWLOG} ${JOB_NEWLOG_ARGS} ${JOB_LOG} @echo "${TIME_STAMP} Start ${.TARGET:S,-jobs,,} ${JOB_ARGS} ${JOB_LOG_START} log=${JOB_LOG}" | tee ${JOB_LOG} @cd ${.CURDIR} && env MAKELEVEL=0 \ ${.MAKE} ${JOB_ARGS} _TARGETS=${.TARGET:S,-jobs,,} ${.TARGET:S,-jobs,,} >> ${JOB_LOG} 2>&1 .endif .END: _build_finish .ERROR: _build_failed _build_finish: .NOMETA @echo "${TIME_STAMP} Finished ${.TARGETS} seconds=`expr ${now_utc} - ${start_utc}`" _build_failed: .NOMETA @echo "${TIME_STAMP} Failed ${.TARGETS} seconds=`expr ${now_utc} - ${start_utc}`" diff --git a/share/mk/src.sys.obj.mk b/share/mk/src.sys.obj.mk index 708559edcdb8..e4fe3fa9a2aa 100644 --- a/share/mk/src.sys.obj.mk +++ b/share/mk/src.sys.obj.mk @@ -1,224 +1,230 @@ # # Early setup of MAKEOBJDIR # # Default format is: /usr/obj/usr/src/[${TARGET}.${TARGET_ARCH}/]bin/sh # MAKEOBJDIRPREFIX is /usr/obj # OBJROOT is /usr/obj/usr/src/ # OBJTOP is /usr/obj/usr/src/[${TARGET}.${TARGET_ARCH}/] # MAKEOBJDIR is /usr/obj/usr/src/[${TARGET}.${TARGET_ARCH}/]bin/sh # # MAKEOBJDIRPREFIX will override the default pattern above and internally # set MAKEOBJDIR. If OBJROOT is set then MAKEOBJDIRPREFIX is rooted inside # of there. # # If MK_UNIFIED_OBJDIR is no then OBJROOT will always match OBJTOP. # # If .MAKE.LEVEL == 0 then the TARGET.TARGET_ARCH is potentially added on. # If .MAKE.LEVEL > 0 and MAKEOBJDIRPREFIX is set then it will not get # TARGET.TARGET_ARCH added in as it assumes that MAKEOBJDIRPREFIX is # nested in the existing OBJTOP with TARGET.TARGET_ARCH in it. # # The expected OBJDIR is stored in __objdir for auto.obj.mk to use. # # AUTO_OBJ is opportunistically enabled if the computed .OBJDIR is writable # by the current user. Some top-level targets disable this behavior in # Makefile.sys.inc. # _default_makeobjdirprefix?= /usr/obj _default_makeobjdir= $${.CURDIR:S,^$${SRCTOP},$${OBJTOP},} .include .if ${.MAKE.LEVEL} == 0 || empty(OBJROOT) .if ${MK_UNIFIED_OBJDIR} == "no" && ${MK_DIRDEPS_BUILD} == "no" # Fall back to historical behavior. # We always want to set a default MAKEOBJDIRPREFIX... MAKEOBJDIRPREFIX?= ${_default_makeobjdirprefix} # but don't enforce TARGET.TARGET_ARCH unless we're at the top-level directory. .if ${.CURDIR} == ${SRCTOP} && \ !(defined(TARGET) && defined(TARGET_ARCH) && \ ${MACHINE} == ${TARGET} && ${MACHINE_ARCH} == ${TARGET_ARCH} && \ !defined(CROSS_BUILD_TESTING)) MAKEOBJDIRPREFIX:= ${MAKEOBJDIRPREFIX}${TARGET:D/${TARGET}.${TARGET_ARCH}} .endif .endif # ${MK_UNIFIED_OBJDIR} == "no" .if !empty(MAKEOBJDIRPREFIX) # put things approximately where they want OBJROOT:= ${MAKEOBJDIRPREFIX}${SRCTOP}/ MAKEOBJDIRPREFIX= # export but do not track .export-env MAKEOBJDIRPREFIX .endif .if empty(MAKEOBJDIR) # OBJTOP set below MAKEOBJDIR= ${_default_makeobjdir} # export but do not track .export-env MAKEOBJDIR # Expand for our own use MAKEOBJDIR:= ${MAKEOBJDIR} .endif # SB documented at http://www.crufty.net/sjg/docs/sb-tools.htm .if !empty(SB) SB_OBJROOT?= ${SB}/obj/ # this is what we use below OBJROOT?= ${SB_OBJROOT} .endif OBJROOT?= ${_default_makeobjdirprefix}${SRCTOP}/ .if ${OBJROOT:M*/} != "" OBJROOT:= ${OBJROOT:H:tA}/ .else OBJROOT:= ${OBJROOT:H:tA}/${OBJROOT:T} .endif # Must export since OBJDIR will dynamically be based on it .export OBJROOT SRCTOP +# if we didn't get SB_OBJROOT from env, +# it is handy to set it now, so we can remember it +.if empty(SB_OBJROOT) +SB_OBJROOT:= ${OBJROOT} +.export SB_OBJROOT +.endif .endif .if ${MK_DIRDEPS_BUILD} == "no" .if empty(OBJTOP) # SRCTOP == OBJROOT only happens with clever MAKEOBJDIRPREFIX=/. Don't # append TARGET.TARGET_ARCH for that case since the user wants to build # in the source tree. .if ${MK_UNIFIED_OBJDIR} == "yes" && ${SRCTOP} != ${OBJROOT:tA} .if defined(TARGET) && defined(TARGET_ARCH) OBJTOP:= ${OBJROOT}${TARGET}.${TARGET_ARCH} .elif defined(TARGET) && ${.CURDIR} == ${SRCTOP} # Not enough information, just use basic OBJDIR. This can happen with some # 'make universe' targets or if TARGET is not being used as expected. OBJTOP:= ${OBJROOT:H} .else OBJTOP:= ${OBJROOT}${MACHINE}.${MACHINE_ARCH} .endif .else # TARGET.TARGET_ARCH handled in OBJROOT already. OBJTOP:= ${OBJROOT:H} .endif # ${MK_UNIFIED_OBJDIR} == "yes" .endif # empty(OBJTOP) # Fixup OBJROOT/OBJTOP if using MAKEOBJDIRPREFIX. # This intenionally comes after adding TARGET.TARGET_ARCH so that is truncated # away for nested objdirs. This logic also will not trigger if the OBJROOT # block above unsets MAKEOBJDIRPREFIX. .if !empty(MAKEOBJDIRPREFIX) OBJTOP:= ${MAKEOBJDIRPREFIX}${SRCTOP} OBJROOT:= ${OBJTOP}/ .endif # Wait to validate MAKEOBJDIR until OBJTOP is set. .if defined(MAKEOBJDIR) .if ${MAKEOBJDIR:M/*} == "" .error Cannot use MAKEOBJDIR=${MAKEOBJDIR}${.newline}Unset MAKEOBJDIR to get default: MAKEOBJDIR='${_default_makeobjdir}' .endif .endif # __objdir is the expected .OBJDIR we want to use and that auto.obj.mk will # try to create. .if !empty(MAKEOBJDIRPREFIX) .if ${.CURDIR:M${MAKEOBJDIRPREFIX}/*} != "" # we are already in obj tree! __objdir= ${.CURDIR} .else __objdir:= ${MAKEOBJDIRPREFIX}${.CURDIR} .endif .elif !empty(MAKEOBJDIR) __objdir:= ${MAKEOBJDIR} .endif # Try to enable MK_AUTO_OBJ by default if we can write to the __objdir. Only # do this if AUTO_OBJ is not disabled by the user, and this is the first make # ran. .if ${.MAKE.LEVEL} == 0 && \ ${MK_AUTO_OBJ} == "no" && empty(.MAKEOVERRIDES:MMK_AUTO_OBJ) && \ !defined(WITHOUT_AUTO_OBJ) && !make(showconfig) && !make(print-dir) && \ !make(test-system-*) && \ !defined(NO_OBJ) && \ empty(RELDIR:Msys/*/compile/*) # Find the last existing directory component and check if we can write to it. # If the last component is a symlink then recurse on the new path. CheckAutoObj= \ DirIsCreatable() { \ if [ -w "$${1}" ]; then \ [ -d "$${1}" ] || return 1; \ return 0; \ fi; \ d="$${1}"; \ IFS=/; \ set -- $${d}; \ unset dir; \ while [ $$\# -gt 0 ]; do \ d="$${1}"; \ shift; \ if [ ! -d "$${dir}$${d}/" ]; then \ if [ -L "$${dir}$${d}" ]; then \ dir="$$(readlink "$${dir}$${d}")/"; \ for d in "$${@}"; do \ dir="$${dir}$${d}/"; \ done; \ ret=0; \ DirIsCreatable "$${dir%/}" || ret=$$?; \ return $${ret}; \ elif [ -e "/$${dir}$${d}" ]; then \ return 1; \ else \ break; \ fi; \ fi; \ dir="$${dir}$${d}/"; \ done; \ [ -w "$${dir}" ] && [ -d "$${dir}" ] && return 0; \ return 1; \ }; \ CheckAutoObj() { \ if DirIsCreatable "$${1}"; then \ echo yes; \ else \ echo no; \ fi; \ } .if !empty(__objdir) .if ${.CURDIR} == ${__objdir} || \ (exists(${__objdir}) && ${.TARGETS:M*install*} == ${.TARGETS}) __objdir_writable?= yes .elif empty(__objdir_writable) __objdir_writable!= \ ${CheckAutoObj}; CheckAutoObj "${__objdir}" || echo no .endif .endif __objdir_writable?= no # Export the decision to sub-makes. MK_AUTO_OBJ:= ${__objdir_writable} .export MK_AUTO_OBJ .elif make(showconfig) # Need to export for showconfig internally running make -dg1. It is enabled # in sys.mk by default. .export MK_AUTO_OBJ .endif # ${MK_AUTO_OBJ} == "no" && ... # Assign this directory as .OBJDIR if possible. # # The expected OBJDIR already exists, set it as .OBJDIR. .if !empty(__objdir) && exists(${__objdir}) .OBJDIR: ${__objdir} .else # The OBJDIR we wanted does not yet exist, ensure we default to safe .CURDIR # in case make started with a bogus MAKEOBJDIR, that expanded before OBJTOP # was set, that happened to match some unexpected directory. Either # auto.obj.mk or bsd.obj.mk will create the directory and fix .OBJDIR later. .OBJDIR: ${.CURDIR} .endif # Ensure .OBJDIR=.CURDIR cases have a proper OBJTOP and .OBJDIR .if defined(NO_OBJ) || ${__objdir_writable:Uunknown} == "no" || \ ${__objdir} == ${.CURDIR} OBJTOP= ${SRCTOP} OBJROOT= ${SRCTOP}/ # Compare only to avoid an unneeded chdir(2), :tA purposely left out. .if ${.OBJDIR} != ${.CURDIR} .OBJDIR: ${.CURDIR} .endif .endif # defined(NO_OBJ) HOST_OBJTOP?= ${OBJROOT}${HOST_TARGET} .endif # ${MK_DIRDEPS_BUILD} == "no" diff --git a/share/mk/stage-install.sh b/share/mk/stage-install.sh index 64d044fa048c..d9182e32feff 100755 --- a/share/mk/stage-install.sh +++ b/share/mk/stage-install.sh @@ -1,98 +1,141 @@ #!/bin/sh # NAME: # stage-install.sh - wrapper around install # # SYNOPSIS: # stage-install.sh [variable="value"] "args" "dest" # # DESCRIPTION: # This script is a wrapper around the normal install(1). # Its role is to add '.dirdep' files to the destination. # The variables we might use are: # # INSTALL # Path to actual install(1), default is # $REAL_INSTALL # # OBJDIR # Path to the dir where '.dirdep' was generated, # default is '.' # # _DIRDEP # Path to actual '.dirdep' file, default is # $OBJDIR/.dirdep # # The "args" and "dest" are passed as is to install(1), and if a # '.dirdep' file exists it will be linked or copied to each # "file".dirdep placed in "dest" or "dest".dirdep if it happed # to be a file rather than a directory. # +# Before we run install(1), we check if "dest" needs to be a +# directory (more than one file in "args") and create it +# if necessary. +# # SEE ALSO: # meta.stage.mk -# +# # RCSid: -# $Id: stage-install.sh,v 1.5 2013/04/19 16:32:24 sjg Exp $ +# $Id: stage-install.sh,v 1.11 2024/02/17 17:26:57 sjg Exp $ # -# @(#) Copyright (c) 2013, Simon J. Gerraty +# SPDX-License-Identifier: BSD-2-Clause +# +# @(#) Copyright (c) 2013-2020, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # INSTALL=${REAL_INSTALL:-install} OBJDIR=. while : do case "$1" in *=*) eval "$1"; shift;; *) break;; esac done +# get last entry from "$@" without side effects +last_entry() { + while [ $# -gt 8 ] + do + shift 8 + done + eval last=\$$# + echo $last +} + +# mkdir $dest if needed (more than one file) +mkdir_if_needed() { + ( + lf= + while [ $# -gt 8 ] + do + shift 4 + done + for f in "$@" + do + [ -f $f ] || continue + [ $f = $dest ] && continue + if [ -n "$lf" ]; then + # dest must be a directory + mkdir -p $dest + break + fi + lf=$f + done + ) +} + +args="$@" +dest=`last_entry "$@"` +case " $args " in +*" -d "*) ;; +*) [ -e $dest ] || mkdir_if_needed "$@";; +esac + # if .dirdep doesn't exist, just run install and be done _DIRDEP=${_DIRDEP:-$OBJDIR/.dirdep} [ -s $_DIRDEP ] && EXEC= || EXEC=exec $EXEC $INSTALL "$@" || exit 1 # from meta.stage.mk LnCp() { rm -f $2 2> /dev/null ln $1 $2 2> /dev/null || cp -p $1 $2 } StageDirdep() { t=$1 if [ -s $t.dirdep ]; then cmp -s $_DIRDEP $t.dirdep && return - echo "ERROR: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2 - exit 1 + case "${STAGE_CONFLICT:-error}" in + [Ee]*) STAGE_CONFLICT=ERROR action=exit;; + *) STAGE_CONFLICT=WARNING action=: ;; + esac + echo "$STAGE_CONFLICT: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2 + $action 1 fi LnCp $_DIRDEP $t.dirdep || exit 1 } -args="$@" -while [ $# -gt 8 ] -do - shift 8 -done -eval dest=\$$# if [ -f $dest ]; then # a file, there can be only one .dirdep needed StageDirdep $dest elif [ -d $dest ]; then for f in $args do test -f $f || continue StageDirdep $dest/${f##*/} done fi