1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
#!/usr/bin/env python3
# This differs from matrix-check in the following ways:
# - it only tests one combination of features, as specified on the command line
# - it doesn't have crate-specific hacks (or any knowledge of our workspace contents)
# (but it does read some ad-hoc parseable comments from Cargo.toml's).
# - it runs `cargo test`
import argparse
import subprocess
import sys
import list_crates
def test_crate(args, c):
conditional_options = []
for line in open(c.subdir + "/Cargo.toml"):
# TODO do something more formal here
#
# We need this because some crates don't compile without a runtime selected.
#
# Ideally, if the crate doesn't compile without any features selected,
# the manifest should have a `minimal` feature we can use, or something.
if line.startswith("# @@ test-all-crates ignore"):
print(
"""(
(((((((((( skipping %s ))))))))))
)""" % c.name,
file=sys.stderr,
)
return
conditional_option_directive = "# @@ test-all-crates conditional-option "
# One option per line, so it can contain spaces
if line.startswith(conditional_option_directive):
key, option = (
line[len(conditional_option_directive) :].rstrip().split(maxsplit=1)
)
if key in args.enable_conditional_options:
conditional_options.append(option)
continue
command_sh = 'p=$1; shift; set -x; $CARGO test -p $p "$@"'
print(
""":
:::::::::: %s ::::::::::
:""" % c.name,
file=sys.stderr,
)
# We run a separate build command for each one, to defeat cargo feature unification.
command_l = (
[
"sh",
"-ec",
': "${CARGO:=cargo --locked}"; ' + command_sh,
"x",
c.name,
]
+ args.cargo_option
+ conditional_options
)
child = subprocess.run(command_l)
if child.returncode != 0:
print(
"""failed command %s
"return code" %s
failed to test crate %s""" % (repr(command_l), child.returncode, c.name),
file=sys.stderr,
)
sys.exit(1)
# https://stackoverflow.com/questions/24483182/python-split-list-into-n-chunks
def chunks(lst, n):
"""
Yield n number of chunks from lst.
This is a striped chunk, meaning that rather than making contiguous chunks, each chunk is made
up of elements that are n spaces apart in the input list. This has the advantage of making
sure that the lists are as close to evenly sized as possible, even if the input list length is
not divisible by n.
"""
for i in range(0, n):
yield lst[i::n]
def main():
parser = argparse.ArgumentParser(prog="test-all-crates")
parser.add_argument("cargo_option", nargs="*")
parser.add_argument("--enable-conditional-options", action="append", default=[])
parser.add_argument("--skip-until", action="store", default=None)
parser.add_argument(
"--parallel",
type=str,
default="1,0",
help="Split the checking into multiple chunks and run only a single one of the chunks. "
"Takes two comma-separated integers, the first is the number of chunks, "
"the second is which chunk to run (zero-indexed)."
"This assumes that the set of crates that exist is the same between all invocations.",
)
args = parser.parse_args()
parallel_chunks, parallel_n = map(int, args.parallel.split(","))
assert parallel_n < parallel_chunks
skip_until = args.skip_until
crates = sorted(list_crates.list_crates(), key=lambda crate: crate.name)
for crate in list(chunks(crates, parallel_chunks))[parallel_n]:
if skip_until is not None:
if skip_until == crate.name:
skip_until = None
else:
print(
"skipping due to --skip-until %s: %s" % (skip_until, crate.name),
file=sys.stderr,
)
continue
test_crate(args, crate)
main()
|