Module Name: src Committed By: rillig Date: Sat May 7 17:49:47 UTC 2022
Modified Files: src/usr.bin/make: compat.c main.c make.1 make.c make.h src/usr.bin/make/unit-tests: Makefile depsrc-wait.exp depsrc-wait.mk varname-dot-make-mode.exp varname-dot-make-mode.mk Log Message: make: allow to randomize build order of targets In complex dependency structures, when a build fails, a probable cause is a missing dependency declaration between some files. In compat mode, the build order is deterministic, in jobs mode, it is somewhat deterministic. To explore more edge cases, add the line ".MAKE.MODE += randomize-targets" somewhere in the makefile. Fixes PR bin/45226 by riastradh. Reviewed by christos. To generate a diff of this commit: cvs rdiff -u -r1.239 -r1.240 src/usr.bin/make/compat.c cvs rdiff -u -r1.581 -r1.582 src/usr.bin/make/main.c cvs rdiff -u -r1.308 -r1.309 src/usr.bin/make/make.1 cvs rdiff -u -r1.254 -r1.255 src/usr.bin/make/make.c cvs rdiff -u -r1.301 -r1.302 src/usr.bin/make/make.h cvs rdiff -u -r1.312 -r1.313 src/usr.bin/make/unit-tests/Makefile cvs rdiff -u -r1.2 -r1.3 src/usr.bin/make/unit-tests/depsrc-wait.exp \ src/usr.bin/make/unit-tests/varname-dot-make-mode.mk cvs rdiff -u -r1.3 -r1.4 src/usr.bin/make/unit-tests/depsrc-wait.mk cvs rdiff -u -r1.1 -r1.2 \ src/usr.bin/make/unit-tests/varname-dot-make-mode.exp Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/make/compat.c diff -u src/usr.bin/make/compat.c:1.239 src/usr.bin/make/compat.c:1.240 --- src/usr.bin/make/compat.c:1.239 Sat May 7 08:01:20 2022 +++ src/usr.bin/make/compat.c Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: compat.c,v 1.239 2022/05/07 08:01:20 rillig Exp $ */ +/* $NetBSD: compat.c,v 1.240 2022/05/07 17:49:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -91,7 +91,7 @@ #include "pathnames.h" /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: compat.c,v 1.239 2022/05/07 08:01:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: compat.c,v 1.240 2022/05/07 17:49:47 rillig Exp $"); static GNode *curTarg = NULL; static pid_t compatChild; @@ -459,13 +459,65 @@ RunCommands(GNode *gn) } static void +MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn) +{ + GNode **it; + size_t r; + + for (r = (size_t)(end - gnodes); r >= 2; r--) { + /* Biased, but irrelevant in practice. */ + size_t i = (size_t)random() % r; + GNode *t = gnodes[r - 1]; + gnodes[r - 1] = gnodes[i]; + gnodes[i] = t; + } + + for (it = gnodes; it != end; it++) + Compat_Make(*it, pgn); +} + +static void +MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn) +{ + Vector vec; + GNodeListNode *ln; + GNode **nodes; + size_t i, n, start; + + Vector_Init(&vec, sizeof(GNode *)); + for (ln = gnodes->first; ln != NULL; ln = ln->next) + *(GNode **)Vector_Push(&vec) = ln->datum; + nodes = vec.items; + n = vec.len; + + start = 0; + for (i = 0; i < n; i++) { + if (nodes[i]->type & OP_WAIT) { + MakeInRandomOrder(nodes + start, nodes + i, pgn); + Compat_Make(nodes[i], pgn); + start = i + 1; + } + } + MakeInRandomOrder(nodes + start, nodes + i, pgn); + + Vector_Done(&vec); +} + +static void MakeNodes(GNodeList *gnodes, GNode *pgn) { GNodeListNode *ln; + if (Lst_IsEmpty(gnodes)) + return; + if (opts.randomizeTargets) { + MakeWaitGroupsInRandomOrder(gnodes, pgn); + return; + } + for (ln = gnodes->first; ln != NULL; ln = ln->next) { - GNode *cohort = ln->datum; - Compat_Make(cohort, pgn); + GNode *cgn = ln->datum; + Compat_Make(cgn, pgn); } } Index: src/usr.bin/make/main.c diff -u src/usr.bin/make/main.c:1.581 src/usr.bin/make/main.c:1.582 --- src/usr.bin/make/main.c:1.581 Sat May 7 08:01:20 2022 +++ src/usr.bin/make/main.c Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.581 2022/05/07 08:01:20 rillig Exp $ */ +/* $NetBSD: main.c,v 1.582 2022/05/07 17:49:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -111,7 +111,7 @@ #include "trace.h" /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: main.c,v 1.581 2022/05/07 08:01:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: main.c,v 1.582 2022/05/07 17:49:47 rillig Exp $"); #if defined(MAKE_NATIVE) && !defined(lint) __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " "The Regents of the University of California. " @@ -799,6 +799,8 @@ MakeMode(void) if (strstr(mode, "meta") != NULL) meta_mode_init(mode); #endif + if (strstr(mode, "randomize-targets") != NULL) + opts.randomizeTargets = true; } free(mode); Index: src/usr.bin/make/make.1 diff -u src/usr.bin/make/make.1:1.308 src/usr.bin/make/make.1:1.309 --- src/usr.bin/make/make.1:1.308 Mon Apr 18 15:06:27 2022 +++ src/usr.bin/make/make.1 Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.308 2022/04/18 15:06:27 rillig Exp $ +.\" $NetBSD: make.1,v 1.309 2022/05/07 17:49:47 rillig Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd April 18, 2022 +.Dd May 7, 2022 .Dt MAKE 1 .Os .Sh NAME @@ -978,6 +978,10 @@ If .Va bf is True, when a .meta file is created, mark the target .Ic .SILENT . +.It Pa randomize-targets +In both compat and parallel mode, do not make the targets in the usual order, +but instead randomize their order. +This mode can be used to detect undeclared dependencies between files. .El .It Va .MAKE.META.BAILIWICK In "meta" mode, provides a list of prefixes which @@ -2250,8 +2254,9 @@ will to it and update the value of .Ql Va .OBJDIR . .It Ic .ORDER -The named targets are made in sequence. +In parallel mode, the named targets are made in sequence. This ordering does not add targets to the list of targets to be made. +.Pp Since the dependents of a target do not get built until the target itself could be built, unless .Ql a @@ -2262,9 +2267,6 @@ the following is a dependency loop: 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. Index: src/usr.bin/make/make.c diff -u src/usr.bin/make/make.c:1.254 src/usr.bin/make/make.c:1.255 --- src/usr.bin/make/make.c:1.254 Sat May 7 10:05:49 2022 +++ src/usr.bin/make/make.c Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: make.c,v 1.254 2022/05/07 10:05:49 rillig Exp $ */ +/* $NetBSD: make.c,v 1.255 2022/05/07 17:49:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -104,7 +104,7 @@ #include "job.h" /* "@(#)make.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: make.c,v 1.254 2022/05/07 10:05:49 rillig Exp $"); +MAKE_RCSID("$NetBSD: make.c,v 1.255 2022/05/07 17:49:47 rillig Exp $"); /* Sequence # to detect recursion. */ static unsigned int checked_seqno = 1; @@ -932,6 +932,28 @@ GNode_SetLocalVars(GNode *gn) gn->flags.doneAllsrc = true; } +static void +ScheduleRandomly(GNode *gn) +{ + GNodeListNode *ln; + size_t i, n; + + n = 0; + for (ln = toBeMade.first; ln != NULL; ln = ln->next) + n++; + i = n > 0 ? (size_t)random() % (n + 1) : 0; + + if (i == 0) { + Lst_Append(&toBeMade, gn); + return; + } + i--; + + for (ln = toBeMade.first; i > 0; ln = ln->next) + i--; + Lst_InsertBefore(&toBeMade, ln, gn); +} + static bool MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) { @@ -957,7 +979,9 @@ MakeBuildChild(GNode *cn, GNodeListNode cn->name, cn->cohort_num); cn->made = REQUESTED; - if (toBeMadeNext == NULL) + if (opts.randomizeTargets && !(cn->type & OP_WAIT)) + ScheduleRandomly(cn); + else if (toBeMadeNext == NULL) Lst_Append(&toBeMade, cn); else Lst_InsertBefore(&toBeMade, toBeMadeNext, cn); Index: src/usr.bin/make/make.h diff -u src/usr.bin/make/make.h:1.301 src/usr.bin/make/make.h:1.302 --- src/usr.bin/make/make.h:1.301 Sat May 7 08:01:20 2022 +++ src/usr.bin/make/make.h Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: make.h,v 1.301 2022/05/07 08:01:20 rillig Exp $ */ +/* $NetBSD: make.h,v 1.302 2022/05/07 17:49:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -769,6 +769,11 @@ typedef struct CmdOpts { */ StringList create; + /* + * Randomize the order in which the targets from toBeMade are made, + * to catch undeclared dependencies. + */ + bool randomizeTargets; } CmdOpts; extern CmdOpts opts; Index: src/usr.bin/make/unit-tests/Makefile diff -u src/usr.bin/make/unit-tests/Makefile:1.312 src/usr.bin/make/unit-tests/Makefile:1.313 --- src/usr.bin/make/unit-tests/Makefile:1.312 Mon Apr 18 15:06:28 2022 +++ src/usr.bin/make/unit-tests/Makefile Sat May 7 17:49:47 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.312 2022/04/18 15:06:28 rillig Exp $ +# $NetBSD: Makefile,v 1.313 2022/05/07 17:49:47 rillig Exp $ # # Unit tests for make(1) # @@ -541,8 +541,10 @@ SED_CMDS.varname-dot-shell+= -e 's,\[/[^ SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@} # Some tests need an additional round of postprocessing. +POSTPROC.depsrc-wait= sed -e '/^---/d' -e 's,^\(: Making 3[abc]\)[123]$$,\1,' POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' +POSTPROC.varname-dot-make-mode= sed 's,^\(: Making [abc]\)[123]$$,\1,' # Some tests reuse other tests, which makes them unnecessarily fragile. export-all.rawout: export.mk Index: src/usr.bin/make/unit-tests/depsrc-wait.exp diff -u src/usr.bin/make/unit-tests/depsrc-wait.exp:1.2 src/usr.bin/make/unit-tests/depsrc-wait.exp:1.3 --- src/usr.bin/make/unit-tests/depsrc-wait.exp:1.2 Mon Sep 7 18:40:32 2020 +++ src/usr.bin/make/unit-tests/depsrc-wait.exp Sat May 7 17:49:47 2022 @@ -1,13 +1,18 @@ ---- a --- echo a a ---- b1 --- echo b1 b1 ---- b --- echo b b ---- x --- echo x x +: Making 3a +: Making 3a +: Making 3a +: Making 3b +: Making 3b +: Making 3b +: Making 3c +: Making 3c +: Making 3c exit status 0 Index: src/usr.bin/make/unit-tests/varname-dot-make-mode.mk diff -u src/usr.bin/make/unit-tests/varname-dot-make-mode.mk:1.2 src/usr.bin/make/unit-tests/varname-dot-make-mode.mk:1.3 --- src/usr.bin/make/unit-tests/varname-dot-make-mode.mk:1.2 Sun Aug 16 14:25:16 2020 +++ src/usr.bin/make/unit-tests/varname-dot-make-mode.mk Sat May 7 17:49:47 2022 @@ -1,8 +1,41 @@ -# $NetBSD: varname-dot-make-mode.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-mode.mk,v 1.3 2022/05/07 17:49:47 rillig Exp $ # # Tests for the special .MAKE.MODE variable. -# TODO: Implementation +# TODO: test .MAKE.MODE "meta", or see meta mode tests. +# TODO: test .MAKE.MODE "compat" -all: - @:; + +# See Makefile, POSTPROC for the postprocessing that takes place. +# See the .rawout file for the raw output before stripping the digits. +all: .PHONY make-mode-randomize-targets + + +# By adding the word "randomize-targets" to the variable .MAKE.MODE, the +# targets are not made in declaration order, but rather in random order. This +# mode helps to find undeclared dependencies between files. +# +# History +# Added on 2022-05-07. +# +# See also +# https://gnats.netbsd.org/45226 +make-mode-randomize-targets: .PHONY + @echo "randomize compat mode:" + @${MAKE} -r -f ${MAKEFILE} randomize-targets + + @echo "randomize jobs mode (-j1):" + @${MAKE} -r -f ${MAKEFILE} -j1 randomize-targets + + @echo "randomize jobs mode (-j5):" + @${MAKE} -r -f ${MAKEFILE} -j5 randomize-targets | grep '^:' + +.if make(randomize-targets) +randomize-targets: .WAIT a1 a2 a3 .WAIT b1 b2 b3 .WAIT c1 c2 c3 .WAIT +a1 a2 a3 b1 b2 b3 c1 c2 c3: + : Making ${.TARGET} + +# .MAKE.MODE is evaluated after parsing all files, so it suffices to switch +# the mode after defining the targets. +.MAKE.MODE+= randomize-targets +.endif Index: src/usr.bin/make/unit-tests/depsrc-wait.mk diff -u src/usr.bin/make/unit-tests/depsrc-wait.mk:1.3 src/usr.bin/make/unit-tests/depsrc-wait.mk:1.4 --- src/usr.bin/make/unit-tests/depsrc-wait.mk:1.3 Mon Sep 7 18:40:32 2020 +++ src/usr.bin/make/unit-tests/depsrc-wait.mk Sat May 7 17:49:47 2022 @@ -1,9 +1,15 @@ -# $NetBSD: depsrc-wait.mk,v 1.3 2020/09/07 18:40:32 rillig Exp $ +# $NetBSD: depsrc-wait.mk,v 1.4 2022/05/07 17:49:47 rillig Exp $ # # Tests for the special source .WAIT in dependency declarations, # which adds a sequence point between the nodes to its left and the nodes # to its right. +all: .PHONY + @${MAKE} -r -f ${MAKEFILE} x + @${MAKE} -r -f ${MAKEFILE} three-by-three + + +.if make(x) # Even though the build could run massively parallel, the .WAIT imposes a # strict ordering in this example, which forces the targets to be made in # exactly this order. @@ -19,3 +25,17 @@ b: b1 echo b b1: echo b1 +.endif + + +# There are 3 groups of 3 targets, with .WAIT barriers in between. Each of +# these groups has to be made completely before starting the next group. +# See Makefile, POSTPROC for the postprocessing that takes place. +.if make(three-by-three) +.MAKEFLAGS: -j5 +.MAKE.MODE+= randomize-targets + +three-by-three: .WAIT 3a1 3a2 3a3 .WAIT 3b1 3b2 3b3 .WAIT 3c1 3c2 3c3 .WAIT +3a1 3a2 3a3 3b1 3b2 3b3 3c1 3c2 3c3: + : Making ${.TARGET} +.endif Index: src/usr.bin/make/unit-tests/varname-dot-make-mode.exp diff -u src/usr.bin/make/unit-tests/varname-dot-make-mode.exp:1.1 src/usr.bin/make/unit-tests/varname-dot-make-mode.exp:1.2 --- src/usr.bin/make/unit-tests/varname-dot-make-mode.exp:1.1 Sun Aug 16 12:07:52 2020 +++ src/usr.bin/make/unit-tests/varname-dot-make-mode.exp Sat May 7 17:49:47 2022 @@ -1 +1,31 @@ +randomize compat mode: +: Making a +: Making a +: Making a +: Making b +: Making b +: Making b +: Making c +: Making c +: Making c +randomize jobs mode (-j1): +: Making a +: Making a +: Making a +: Making b +: Making b +: Making b +: Making c +: Making c +: Making c +randomize jobs mode (-j5): +: Making a +: Making a +: Making a +: Making b +: Making b +: Making b +: Making c +: Making c +: Making c exit status 0