cosmopolitan/third_party/make/shuffle.c
Justine Tunney 14bf57180f
Import GNU Make 4.4.1
Landlock Make hasn't been working well on AARCH64 systems. Let's do this
over the right way, using our new build tools.
2023-11-30 20:54:52 -08:00

237 lines
5.6 KiB
C

/* Provide prerequisite shuffle support.
Copyright (C) 2022-2023 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.
GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>. */
#include "makeint.h"
#include "shuffle.h"
#include "filedef.h"
#include "dep.h"
/* Supported shuffle modes. */
static void random_shuffle_array (void ** a, size_t len);
static void reverse_shuffle_array (void ** a, size_t len);
static void identity_shuffle_array (void ** a, size_t len);
/* The way goals and rules are shuffled during update. */
enum shuffle_mode
{
/* No shuffle data is populated or used. */
sm_none,
/* Random within dependency list. */
sm_random,
/* Inverse order. */
sm_reverse,
/* identity order. Differs from SM_NONE by explicitly populating
the traversal order. */
sm_identity,
};
/* Shuffle configuration. */
static struct
{
enum shuffle_mode mode;
unsigned int seed;
void (*shuffler) (void **a, size_t len);
char strval[INTSTR_LENGTH + 1];
} config = { sm_none, 0, NULL, "" };
/* Return string value of --shuffle= option passed.
If none was passed or --shuffle=none was used function
returns NULL. */
const char *
shuffle_get_mode ()
{
return config.strval[0] == '\0' ? NULL : config.strval;
}
void
shuffle_set_mode (const char *cmdarg)
{
/* Parse supported '--shuffle' mode. */
if (strcasecmp (cmdarg, "reverse") == 0)
{
config.mode = sm_reverse;
config.shuffler = reverse_shuffle_array;
strcpy (config.strval, "reverse");
}
else if (strcasecmp (cmdarg, "identity") == 0)
{
config.mode = sm_identity;
config.shuffler = identity_shuffle_array;
strcpy (config.strval, "identity");
}
else if (strcasecmp (cmdarg, "none") == 0)
{
config.mode = sm_none;
config.shuffler = NULL;
config.strval[0] = '\0';
}
else
{
if (strcasecmp (cmdarg, "random") == 0)
config.seed = make_rand ();
else
{
/* Assume explicit seed. */
const char *err;
config.seed = make_toui (cmdarg, &err);
if (err)
OSS (fatal, NILF, _("invalid shuffle mode: %s: '%s'"), err, cmdarg);
}
config.mode = sm_random;
config.shuffler = random_shuffle_array;
sprintf (config.strval, "%u", config.seed);
}
}
/* Shuffle array elements using RAND(). */
static void
random_shuffle_array (void **a, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
{
void *t;
/* Pick random element and swap. */
unsigned int j = make_rand () % len;
if (i == j)
continue;
/* Swap. */
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
/* Shuffle array elements using reverse order. */
static void
reverse_shuffle_array (void **a, size_t len)
{
size_t i;
for (i = 0; i < len / 2; i++)
{
void *t;
/* Pick mirror and swap. */
size_t j = len - 1 - i;
/* Swap. */
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
/* Shuffle array elements using identity order. */
static void
identity_shuffle_array (void **a UNUSED, size_t len UNUSED)
{
/* No-op! */
}
/* Shuffle list of dependencies by populating '->shuf'
field in each 'struct dep'. */
static void
shuffle_deps (struct dep *deps)
{
size_t ndeps = 0;
struct dep *dep;
void **da;
void **dp;
for (dep = deps; dep; dep = dep->next)
{
/* Do not reshuffle prerequisites if any .WAIT is present. */
if (dep->wait_here)
return;
ndeps++;
}
if (ndeps == 0)
return;
/* Allocate array of all deps, store, shuffle, write back. */
da = xmalloc (sizeof (struct dep *) * ndeps);
/* Store locally. */
for (dep = deps, dp = da; dep; dep = dep->next, dp++)
*dp = dep;
/* Shuffle. */
config.shuffler (da, ndeps);
/* Write back. */
for (dep = deps, dp = da; dep; dep = dep->next, dp++)
dep->shuf = *dp;
free (da);
}
/* Shuffle 'deps' of each 'file' recursively. */
static void
shuffle_file_deps_recursive (struct file *f)
{
struct dep *dep;
/* Implicit rules do not always provide any depends. */
if (!f)
return;
/* Avoid repeated shuffles and loops. */
if (f->was_shuffled)
return;
f->was_shuffled = 1;
shuffle_deps (f->deps);
/* Shuffle dependencies. */
for (dep = f->deps; dep; dep = dep->next)
shuffle_file_deps_recursive (dep->file);
}
/* Shuffle goal dependencies first, then shuffle dependency list
of each file reachable from goaldep recursively. Used by
--shuffle flag to introduce artificial non-determinism in build
order. .*/
void
shuffle_deps_recursive (struct dep *deps)
{
struct dep *dep;
/* Exit early if shuffling was not requested. */
if (config.mode == sm_none)
return;
/* Do not reshuffle prerequisites if .NOTPARALLEL was specified. */
if (not_parallel)
return;
/* Set specific seed at the top level of recursion. */
if (config.mode == sm_random)
make_seed (config.seed);
shuffle_deps (deps);
/* Shuffle dependencies. */
for (dep = deps; dep; dep = dep->next)
shuffle_file_deps_recursive (dep->file);
}