1 /* Provide prerequisite shuffle support.
2 Copyright (C) 2022 Free Software Foundation, Inc.
3 This file is part of GNU Make.
5 GNU Make is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3 of the License, or (at your option) any later
10 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along with
15 this program. If not, see <https://www.gnu.org/licenses/>. */
24 /* Supported shuffle modes. */
25 static void random_shuffle_array (void ** a, size_t len);
26 static void reverse_shuffle_array (void ** a, size_t len);
27 static void identity_shuffle_array (void ** a, size_t len);
29 /* The way goals and rules are shuffled during update. */
32 /* No shuffle data is populated or used. */
34 /* Random within dependency list. */
38 /* identity order. Differs from SM_NONE by explicitly populating
39 the traversal order. */
43 /* Shuffle configuration. */
46 enum shuffle_mode mode;
48 void (*shuffler) (void **a, size_t len);
49 char strval[INTSTR_LENGTH + 1];
50 } config = { sm_none, 0, NULL, "" };
52 /* Return string value of --shuffle= option passed.
53 If none was passed or --shuffle=none was used function
58 return config.strval[0] == '\0' ? NULL : config.strval;
62 shuffle_set_mode (const char *cmdarg)
64 /* Parse supported '--shuffle' mode. */
65 if (strcasecmp (cmdarg, "reverse") == 0)
67 config.mode = sm_reverse;
68 config.shuffler = reverse_shuffle_array;
69 strcpy (config.strval, "reverse");
71 else if (strcasecmp (cmdarg, "identity") == 0)
73 config.mode = sm_identity;
74 config.shuffler = identity_shuffle_array;
75 strcpy (config.strval, "identity");
77 else if (strcasecmp (cmdarg, "none") == 0)
79 config.mode = sm_none;
80 config.shuffler = NULL;
81 config.strval[0] = '\0';
85 if (strcasecmp (cmdarg, "random") == 0)
86 config.seed = make_rand ();
89 /* Assume explicit seed. */
91 config.seed = make_toui (cmdarg, &err);
93 OSS (fatal, NILF, _("invalid shuffle mode: %s: '%s'"), err, cmdarg);
96 config.mode = sm_random;
97 config.shuffler = random_shuffle_array;
98 sprintf (config.strval, "%u", config.seed);
102 /* Shuffle array elements using RAND(). */
104 random_shuffle_array (void **a, size_t len)
107 for (i = 0; i < len; i++)
111 /* Pick random element and swap. */
112 unsigned int j = make_rand () % len;
123 /* Shuffle array elements using reverse order. */
125 reverse_shuffle_array (void **a, size_t len)
128 for (i = 0; i < len / 2; i++)
132 /* Pick mirror and swap. */
133 size_t j = len - 1 - i;
142 /* Shuffle array elements using identity order. */
144 identity_shuffle_array (void **a UNUSED, size_t len UNUSED)
149 /* Shuffle list of dependencies by populating '->shuf'
150 field in each 'struct dep'. */
152 shuffle_deps (struct dep *deps)
159 for (dep = deps; dep; dep = dep->next)
161 /* Do not reshuffle prerequisites if any .WAIT is present. */
171 /* Allocate array of all deps, store, shuffle, write back. */
172 da = xmalloc (sizeof (struct dep *) * ndeps);
175 for (dep = deps, dp = da; dep; dep = dep->next, dp++)
179 config.shuffler (da, ndeps);
182 for (dep = deps, dp = da; dep; dep = dep->next, dp++)
188 /* Shuffle 'deps' of each 'file' recursively. */
190 shuffle_file_deps_recursive (struct file *f)
194 /* Implicit rules do not always provide any depends. */
198 /* Avoid repeated shuffles and loops. */
203 shuffle_deps (f->deps);
205 /* Shuffle dependencies. */
206 for (dep = f->deps; dep; dep = dep->next)
207 shuffle_file_deps_recursive (dep->file);
210 /* Shuffle goal dependencies first, then shuffle dependency list
211 of each file reachable from goaldep recursively. Used by
212 --shuffle flag to introduce artificial non-determinism in build
216 shuffle_deps_recursive (struct dep *deps)
220 /* Exit early if shuffling was not requested. */
221 if (config.mode == sm_none)
224 /* Do not reshuffle prerequisites if .NOTPARALLEL was specified. */
228 /* Set specific seed at the top level of recursion. */
229 if (config.mode == sm_random)
230 make_seed (config.seed);
234 /* Shuffle dependencies. */
235 for (dep = deps; dep; dep = dep->next)
236 shuffle_file_deps_recursive (dep->file);