diff --git a/Makefile.inc1 b/Makefile.inc1 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1071,10 +1071,12 @@ # tree changes, particularly with respect to removing source files and # replacing generated files. Handle these cases here in an ad-hoc fashion. _cleanobj_fast_depend_hack: .PHONY +.if ${MK_DEPEND_CLEANUP} != "no" @echo ">>> Deleting stale dependencies..."; MACHINE=${MACHINE} MACHINE_ARCH=${MACHINE_ARCH} \ - ALL_libcompats=${_ALL_libcompats:Q} \ + ALL_libcompats=${_ALL_libcompats:Q} MAKE=${MAKE} \ sh ${.CURDIR}/tools/build/depend-cleanup.sh ${OBJTOP} ${SRCTOP} +.endif _cleanworldtmp: .PHONY .if ${MK_CLEAN} == "yes" @@ -1168,7 +1170,7 @@ .endfor .else ${_+_}cd ${.CURDIR}; env CLEANMK="_NO_INCLUDE_COMPILERMK=t ${CLEANDIR}" \ - MAKE=${MAKE} ${WMAKE} _cleanobj_fast_depend_hack + ${WMAKE} _cleanobj_fast_depend_hack .endif # ${MK_CLEAN} == "yes" _obj: @echo diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5 --- a/share/man/man5/src.conf.5 +++ b/share/man/man5/src.conf.5 @@ -1,5 +1,5 @@ .\" DO NOT EDIT-- this file is @generated by tools/build/options/makeman. -.Dd October 22, 2025 +.Dd December 7, 2025 .Dt SRC.CONF 5 .Os .Sh NAME @@ -449,6 +449,14 @@ Note that recording a new epoch in .Pa .clean_build_epoch in the root of the source tree will also force a clean world build. +When set, these options are also in effect: +.Pp +.Bl -inset -compact +.It Va WITHOUT_DEPEND_CLEANUP +(unless +.Va WITH_DEPEND_CLEANUP +is set explicitly) +.El .It Va WITHOUT_CPP Do not build .Xr cpp 1 . @@ -537,6 +545,12 @@ .It Va WITHOUT_DEBUG_FILES Avoid building or installing standalone debug files for each executable binary and shared library. +.It Va WITHOUT_DEPEND_CLEANUP +Do not attempt to detect if the object tree needs cleaning in part or in +whole before building. +This speeds up incremental builds, especially when experimenting with +build options, but may cause the build to inexplicably fail or produce +non-functioning binaries. .It Va WITH_DETECT_TZ_CHANGES Make the time handling code detect changes to the timezone files. .It Va WITH_DIALOG diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -86,6 +86,7 @@ CRYPT \ CUSE \ CXGBETOOL \ + DEPEND_CLEANUP \ DICT \ DMAGENT \ DTRACE \ @@ -513,6 +514,10 @@ MK_LOADER_VERIEXEC_PASS_MANIFEST := no .endif +.if ${MK_CLEAN} == "yes" +MK_DEPEND_CLEANUP:= no +.endif + # # MK_* options whose default value depends on another option. # diff --git a/tools/build/depend-cleanup.sh b/tools/build/depend-cleanup.sh --- a/tools/build/depend-cleanup.sh +++ b/tools/build/depend-cleanup.sh @@ -124,10 +124,8 @@ fi : ${CLEANMK=""} -if [ -n "$CLEANMK" ]; then - if [ -z "${MAKE+set}" ]; then - err "MAKE not set" - fi +if [ -z "${MAKE+set}" ]; then + err "MAKE not set" fi if [ -z "${MACHINE+set}" ]; then @@ -202,9 +200,41 @@ awk 'int($1) > 0 { epoch = $1 } END { print epoch }' "$1" } +# Regular expression matching the names of src.conf(5) options which +# don't affect the build. +# +# This filter is applied to both the current options and the cached +# options so we don't force a rebuild just because the filter itself +# changed. +IGNORED_OPTS="CLEAN|DEPEND_CLEANUP|EXAMPLES|MAN|TESTS|WARNS|WERROR" +IGNORED_OPTS="${IGNORED_OPTS}|INSTALL.*|STAGING.*" +# Also ignore TOOLCHAIN and the options it forces if set. It is +# commonly used to speed up a build and is safe to toggle. +IGNORED_OPTS="${IGNORED_OPTS}|TOOLCHAIN|CLANG.*|LLDB?|LLVM_(BIN|COV).*" + +extract_src_opts() +{ + $MAKE -C "$SRCTOP" -f "$SRCTOP"/Makefile.inc1 \ + -V $'SRC_OPT_LIST:O:ts\n' | + egrep -v "^WITH(OUT)?_(${IGNORED_OPTS})=" +} + +extract_obj_opts() +{ + for fn; do + if [ -f "${fn}" ]; then + cat "${fn}" + else + echo "# ${fn}" + fi + done | + egrep -v "^WITH(OUT)?_(${IGNORED_OPTS})=" +} + clean_world() { local buildepoch="$1" + local srcopts="$2" # The caller may set CLEANMK in the environment to make target(s) that # should be invoked instead of just destroying everything. This is @@ -227,34 +257,45 @@ mkdir -p "$OBJTOP" echo "$buildepoch" > "$OBJTOP"/.clean_build_epoch + echo "$srcopts" > "$OBJTOP"/.src_opts exit 0 } -check_epoch() +check_epoch_and_opts() { local srcepoch objepoch + local srcopts objopts srcepoch=$(extract_epoch "$SRCTOP"/.clean_build_epoch) if [ -z "$srcepoch" ]; then err "Malformed .clean_build_epoch; please validate the last line" fi + srcopts=$(extract_src_opts) + if [ -z "$srcopts" ]; then + err "Unable to extract source options" + fi + # We don't discriminate between the varying degrees of difference # between epochs. If it went backwards we could be bisecting across # epochs, in which case the original need to clean likely still stands. objepoch=$(extract_epoch "$OBJTOP"/.clean_build_epoch) if [ -z "$objepoch" ] || [ "$srcepoch" -ne "$objepoch" ]; then - if [ "$VERBOSE" ]; then - echo "Cleaning - src epoch: $srcepoch, objdir epoch: ${objepoch:-unknown}" - fi + echo "Cleaning - src epoch: $srcepoch, objdir epoch: ${objepoch:-unknown}" + clean_world "$srcepoch" "$srcopts" + # NORETURN + fi - clean_world "$srcepoch" + objopts=$(extract_obj_opts "$OBJTOP"/.src_opts) + if [ "$srcopts" != "$objopts" ]; then + echo "Cleaning - build options have changed" + clean_world "$srcepoch" "$srcopts" # NORETURN fi } -check_epoch +check_epoch_and_opts #### Typical dependency cleanup begins here. diff --git a/tools/build/options/WITHOUT_DEPEND_CLEANUP b/tools/build/options/WITHOUT_DEPEND_CLEANUP new file mode 100644 --- /dev/null +++ b/tools/build/options/WITHOUT_DEPEND_CLEANUP @@ -0,0 +1,5 @@ +Do not attempt to detect if the object tree needs cleaning in part or in +whole before building. +This speeds up incremental builds, especially when experimenting with +build options, but may cause the build to inexplicably fail or produce +non-functioning binaries.