Skip to content

Commit e67cc71

Browse files
committed
ci: Switch verify-build to a python script
It would be nice to do a few more things with this script that are starting to get cumbersome in bash. Switch to python so we gain some flexibility. There are some target inaccuracies in the lists here, but fixing these is left for a future change. (backport <#4778>) (cherry picked from commit f82e7fa)
1 parent e7e02f4 commit e67cc71

File tree

4 files changed

+365
-347
lines changed

4 files changed

+365
-347
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ jobs:
7878
- name: Target size after restoring cache
7979
run: du -sh target | sort -k 2 || true
8080

81-
- name: Execute build.sh
81+
- name: Execute build check
8282
run: |
8383
set -eux
8484
# Remove `-Dwarnings` at the MSRV since lints may be different
8585
[ "${{ matrix.toolchain }}" = "1.63.0" ] && export RUSTFLAGS=""
86-
./ci/verify-build.sh
86+
python3 ci/verify-build.py --toolchain "$TOOLCHAIN"
8787
- name: Target size after job completion
8888
run: du -sh target | sort -k 2
8989

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ but this is not guaranteed.
7171
You can see the platform(target)-specific docs on [docs.rs], select a platform
7272
you want to see.
7373

74-
See [`ci/verify-build.sh`](https://github.com/rust-lang/libc/blob/HEAD/ci/verify-build.sh) for
74+
See [`ci/verify-build.py`](https://github.com/rust-lang/libc/blob/HEAD/ci/verify-build.py) for
7575
the platforms on which `libc` is guaranteed to build for each Rust toolchain.
7676
The test-matrix at [GitHub Actions] and [Cirrus CI] show the platforms in which
7777
`libc` tests are run.

ci/verify-build.py

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#!/usr/bin/env python3
2+
3+
import re
4+
import os
5+
import argparse
6+
import subprocess as sp
7+
import sys
8+
import platform
9+
from typing import Optional
10+
from enum import Enum, IntEnum
11+
from dataclasses import dataclass, field
12+
13+
14+
ESC_CYAN = "\033[1;36m"
15+
ESC_END = "\033[0m"
16+
17+
18+
class Os(Enum):
19+
LINUX = "Linux"
20+
WINDOWS = "Windows"
21+
DARWIN = "Darwin"
22+
23+
24+
class Toolchain(IntEnum):
25+
OTHER = 0 # msrv
26+
STABLE = 1
27+
BETA = 2
28+
NIGHTLY = 3
29+
30+
31+
@dataclass
32+
class Cfg:
33+
toolchain_name: str
34+
toolchain: Toolchain = field(init=False)
35+
host_target: str = field(init=False)
36+
os_: Os = field(init=False)
37+
38+
def __post_init__(self):
39+
rustc_output = check_output(["rustc", f"+{self.toolchain_name}", "-vV"])
40+
self.host_target = re.findall(r"host: (.*)", rustc_output)[0]
41+
if "nightly" in self.toolchain_name:
42+
self.toolchain = Toolchain.NIGHTLY
43+
elif "beta" in self.toolchain_name:
44+
self.toolchain = Toolchain.BETA
45+
elif "stable" in self.toolchain_name:
46+
self.toolchain = Toolchain.STABLE
47+
else:
48+
self.toolchain = Toolchain.OTHER
49+
self.os_ = Os(platform.system())
50+
eprint(f"Testing Rust {self.toolchain_name} on {self.os_}")
51+
52+
def nightly(self) -> bool:
53+
return self.toolchain == Toolchain.NIGHTLY
54+
55+
56+
@dataclass
57+
class Target:
58+
name: str
59+
dist: bool = True
60+
min_toolchain: Toolchain = Toolchain.OTHER
61+
62+
def __post_init__(self):
63+
if not self.dist:
64+
# We will need to use build-std
65+
self.min_toolchain = Toolchain.NIGHTLY
66+
67+
def run_on(self) -> Os:
68+
"""MacOS CI runs all apple targets, Windows CI runs all Windows targets,
69+
Linux CI handles everything else."""
70+
71+
if "apple" in self.name:
72+
return Os.DARWIN
73+
elif "windows" in self.name:
74+
return Os.WINDOWS
75+
return Os.LINUX
76+
77+
78+
FREEBSD_VERSIONS = [11, 12, 13, 14, 15]
79+
80+
TARGETS = [
81+
# linux
82+
Target("aarch64-linux-android"),
83+
Target("aarch64-unknown-linux-gnu"),
84+
Target("aarch64-unknown-linux-musl"),
85+
Target("arm-linux-androideabi"),
86+
Target("arm-unknown-linux-gnueabi"),
87+
Target("arm-unknown-linux-gnueabihf"),
88+
Target("arm-unknown-linux-musleabi"),
89+
Target("arm-unknown-linux-musleabihf"),
90+
Target("armv7-linux-androideabi"),
91+
Target("armv7-unknown-linux-gnueabihf"),
92+
Target("armv7-unknown-linux-musleabihf"),
93+
Target("i586-unknown-linux-gnu"),
94+
Target("i586-unknown-linux-musl"),
95+
Target("i686-linux-android"),
96+
Target("i686-unknown-freebsd"),
97+
Target("i686-unknown-linux-gnu"),
98+
Target("i686-unknown-linux-musl"),
99+
Target("powerpc-unknown-linux-gnu"),
100+
Target("powerpc64-unknown-linux-gnu"),
101+
Target("powerpc64le-unknown-linux-gnu"),
102+
Target("s390x-unknown-linux-gnu"),
103+
Target("sparc64-unknown-linux-gnu"),
104+
Target("sparcv9-sun-solaris"),
105+
Target("wasm32-unknown-emscripten"),
106+
Target("wasm32-unknown-unknown"),
107+
# Target was renamed
108+
Target("wasm32-wasip1", min_toolchain=Toolchain.STABLE),
109+
Target("wasm32-wasip2", min_toolchain=Toolchain.STABLE),
110+
Target("x86_64-linux-android"),
111+
Target("x86_64-unknown-freebsd"),
112+
Target("x86_64-unknown-linux-gnu"),
113+
Target("x86_64-unknown-linux-musl"),
114+
Target("x86_64-unknown-netbsd"),
115+
# nightly linux
116+
# FIXME(powerpc64le): powerpc64le-unknown-linux-musl is tier 2 since 1.85 and
117+
# can be moved to rust_linux_targets once MSRV is increased
118+
Target("aarch64-unknown-fuchsia", min_toolchain=Toolchain.NIGHTLY),
119+
Target("armv5te-unknown-linux-gnueabi", min_toolchain=Toolchain.NIGHTLY),
120+
Target("armv5te-unknown-linux-musleabi", min_toolchain=Toolchain.NIGHTLY),
121+
Target("i686-pc-windows-gnu", min_toolchain=Toolchain.NIGHTLY),
122+
Target("powerpc64le-unknown-linux-musl", min_toolchain=Toolchain.NIGHTLY),
123+
Target("riscv64gc-unknown-linux-gnu", min_toolchain=Toolchain.NIGHTLY),
124+
Target("x86_64-fortanix-unknown-sgx", min_toolchain=Toolchain.NIGHTLY),
125+
Target("x86_64-pc-solaris", min_toolchain=Toolchain.NIGHTLY),
126+
Target("x86_64-pc-windows-gnu", min_toolchain=Toolchain.NIGHTLY),
127+
Target("x86_64-unknown-fuchsia", min_toolchain=Toolchain.NIGHTLY),
128+
Target("x86_64-unknown-illumos", min_toolchain=Toolchain.NIGHTLY),
129+
Target("x86_64-unknown-linux-gnux32", min_toolchain=Toolchain.NIGHTLY),
130+
Target("x86_64-unknown-redox", min_toolchain=Toolchain.NIGHTLY),
131+
# apple
132+
Target("aarch64-apple-darwin"),
133+
Target("aarch64-apple-ios"),
134+
# windows
135+
Target("x86_64-pc-windows-msvc"),
136+
Target("x86_64-pc-windows-gnu"),
137+
Target("i686-pc-windows-msvc"),
138+
# linux nodist
139+
# Targets which are not available via rustup and must be built with -Zbuild-std
140+
# FIXME(hexagon): hexagon-unknown-linux-musl should be tested but currently has
141+
# duplicate symbol errors from `compiler_builtins`.
142+
Target("aarch64-pc-windows-msvc", dist=False),
143+
Target("aarch64-unknown-freebsd", dist=False),
144+
Target("aarch64-unknown-hermit", dist=False),
145+
Target("aarch64-unknown-netbsd", dist=False),
146+
Target("aarch64-unknown-openbsd", dist=False),
147+
Target("aarch64-wrs-vxworks", dist=False),
148+
Target("armebv7r-none-eabi", dist=False),
149+
Target("armebv7r-none-eabihf", dist=False),
150+
Target("armv7-wrs-vxworks-eabihf", dist=False),
151+
Target("armv7r-none-eabi", dist=False),
152+
Target("armv7r-none-eabihf", dist=False),
153+
Target("i686-pc-windows-msvc", dist=False),
154+
Target("i686-unknown-haiku", dist=False),
155+
Target("i686-unknown-netbsd", dist=False),
156+
Target("i686-unknown-openbsd", dist=False),
157+
Target("i686-wrs-vxworks", dist=False),
158+
Target("mips-unknown-linux-gnu", dist=False),
159+
Target("mips-unknown-linux-musl", dist=False),
160+
Target("mips64-unknown-linux-gnuabi64", dist=False),
161+
Target("mips64-unknown-linux-muslabi64", dist=False),
162+
Target("mips64el-unknown-linux-gnuabi64", dist=False),
163+
Target("mips64el-unknown-linux-muslabi64", dist=False),
164+
Target("mipsel-unknown-linux-gnu", dist=False),
165+
Target("mipsel-unknown-linux-musl", dist=False),
166+
Target("nvptx64-nvidia-cuda", dist=False),
167+
Target("powerpc-unknown-linux-gnuspe", dist=False),
168+
Target("powerpc-unknown-netbsd", dist=False),
169+
Target("powerpc-wrs-vxworks", dist=False),
170+
Target("powerpc-wrs-vxworks-spe", dist=False),
171+
Target("powerpc64-unknown-freebsd", dist=False),
172+
Target("powerpc64-wrs-vxworks", dist=False),
173+
Target("riscv32i-unknown-none-elf", dist=False),
174+
Target("riscv32imac-unknown-none-elf", dist=False),
175+
Target("riscv32imc-unknown-none-elf", dist=False),
176+
Target("riscv32gc-unknown-linux-gnu", dist=False),
177+
Target("riscv32-wrs-vxworks", dist=False),
178+
Target("riscv64gc-unknown-freebsd", dist=False),
179+
Target("riscv64gc-unknown-hermit", dist=False),
180+
Target("riscv64gc-unknown-linux-musl", dist=False),
181+
Target("riscv64gc-unknown-none-elf", dist=False),
182+
Target("riscv64imac-unknown-none-elf", dist=False),
183+
Target("riscv64-wrs-vxworks", dist=False),
184+
Target("s390x-unknown-linux-musl", dist=False),
185+
Target("sparc-unknown-linux-gnu", dist=False),
186+
Target("sparc64-unknown-netbsd", dist=False),
187+
Target("thumbv6m-none-eabi", dist=False),
188+
Target("thumbv7em-none-eabi", dist=False),
189+
Target("thumbv7em-none-eabihf", dist=False),
190+
Target("thumbv7m-none-eabi", dist=False),
191+
Target("thumbv7neon-linux-androideabi", dist=False),
192+
Target("thumbv7neon-unknown-linux-gnueabihf", dist=False),
193+
Target("thumbv8m.main-none-eabi", dist=False),
194+
Target("x86_64-pc-windows-msvc", dist=False),
195+
Target("x86_64-unknown-dragonfly", dist=False),
196+
Target("x86_64-unknown-haiku", dist=False),
197+
Target("x86_64-unknown-hermit", dist=False),
198+
Target("x86_64-unknown-l4re-uclibc", dist=False),
199+
Target("x86_64-unknown-openbsd", dist=False),
200+
Target("x86_64-wrs-vxworks", dist=False),
201+
# apple nodist
202+
Target("armv7s-apple-ios", dist=False),
203+
Target("i686-apple-darwin", dist=False),
204+
Target("i386-apple-ios", dist=False),
205+
]
206+
207+
208+
def eprint(*args, **kw):
209+
print(*args, file=sys.stderr, **kw)
210+
211+
212+
def xtrace(args: list[str], /, env: Optional[dict[str, str]]):
213+
"""Print commands before running them."""
214+
astr = " ".join(args)
215+
if env is None:
216+
eprint(f"+ {astr}")
217+
else:
218+
envdiff = set(env.items()) - set(os.environ.items())
219+
estr = " ".join(f"{k}='{v}'" for (k, v) in envdiff)
220+
eprint(f"+ {estr} {astr}")
221+
222+
223+
def check_output(args: list[str], /, env: Optional[dict[str, str]] = None) -> str:
224+
xtrace(args, env=env)
225+
return sp.check_output(args, env=env, encoding="utf8")
226+
227+
228+
def run(args: list[str], /, env: Optional[dict[str, str]] = None):
229+
xtrace(args, env=env)
230+
sp.run(args, env=env, check=True)
231+
232+
233+
def test_target(cfg: Cfg, target: Target):
234+
env = os.environ.copy()
235+
env.setdefault("RUSTFLAGS", "")
236+
237+
tname = target.name
238+
target_cfg = check_output(["rustc", "--print=cfg", "--target", tname])
239+
target_env = re.findall(r'target_env="(.*)"', target_cfg)
240+
target_os = re.findall(r'target_os="(.*)"', target_cfg)
241+
target_bits = re.findall(r'target_pointer_width="(.*)"', target_cfg)[0]
242+
assert target_bits in ["32", "64"]
243+
eprint(f"env {target_env}, os {target_os}, bits {target_bits}")
244+
245+
cmd = ["cargo", f"+{cfg.toolchain_name}", "build", "--target", tname]
246+
247+
if not target.dist:
248+
# If we can't download a `core`, we need to build it
249+
cmd += ["-Zbuild-std=core"]
250+
# FIXME: With `build-std` feature, `compiler_builtins` emits a lot of lint warnings.
251+
env["RUSTFLAGS"] += " -Aimproper_ctypes_definitions"
252+
else:
253+
run(["rustup", "target", "add", tname, "--toolchain", cfg.toolchain_name])
254+
255+
# Test with expected combinations of features
256+
run(cmd, env=env)
257+
run(cmd + ["--features=extra_traits"], env=env)
258+
259+
# Check with different env for 64-bit time_t
260+
if target_os == "linux" and target_bits == "32":
261+
# Equivalent of __USE_TIME_BITS64
262+
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64": "1"})
263+
264+
if "gnu" in target_env and target_bits == "32":
265+
# Equivalent of _FILE_OFFSET_BITS=64
266+
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS": "64"})
267+
# Equivalent of _TIME_BITS=64
268+
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_GNU_TIME_BITS": "64"})
269+
270+
# Test again without default features, i.e. without `std`
271+
run(cmd + ["--no-default-features"])
272+
run(cmd + ["--no-default-features", "--features=extra_traits"])
273+
274+
# Ensure the crate will build when used as a dependency of `std`
275+
if cfg.nightly():
276+
run(cmd + ["--no-default-features", "--features=rustc-dep-of-std"])
277+
278+
# For freebsd targets, check with the different versions we support
279+
# if on nightly or stable
280+
if "freebsd" in tname and cfg.toolchain >= Toolchain.STABLE:
281+
for version in FREEBSD_VERSIONS:
282+
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_FREEBSD_VERSION": str(version)})
283+
run(
284+
cmd + ["--no-default-features"],
285+
env=env | {"RUST_LIBC_UNSTABLE_FREEBSD_VERSION": str(version)},
286+
)
287+
288+
is_stable = cfg.toolchain == Toolchain.STABLE
289+
# FIXME(semver): can't pass `--target` to `cargo-semver-checks` so we restrict to
290+
# the host target
291+
is_host = tname == cfg.host_target
292+
if is_stable and is_host:
293+
eprint("Running semver checks")
294+
run(
295+
[
296+
"cargo",
297+
"semver-checks",
298+
"--only-explicit-features",
299+
"--features=std,extra_traits",
300+
]
301+
)
302+
else:
303+
eprint("Skipping semver checks")
304+
305+
eprint(f"Finished checking target {tname}")
306+
307+
308+
def main():
309+
p = argparse.ArgumentParser()
310+
p.add_argument("--toolchain", required=True, help="Rust toolchain")
311+
p.add_argument("--only", help="only targets matching this regex")
312+
p.add_argument("--skip", help="skip targets matching this regex")
313+
args = p.parse_args()
314+
315+
cfg = Cfg(toolchain_name=args.toolchain)
316+
eprint(f"Config: {cfg}")
317+
eprint("Python version: ", sys.version)
318+
319+
if cfg.nightly():
320+
# Needed for build-std
321+
run(["rustup", "component", "add", "rust-src"])
322+
323+
targets = TARGETS
324+
eprint(f"Total checked targets across platforms: {len(targets)}")
325+
326+
if not cfg.nightly():
327+
# Non-dist targets need nightly for build-std
328+
targets = [t for t in targets if t.dist]
329+
330+
# Filter to targets supported on the current toolchain
331+
targets = [t for t in targets if cfg.toolchain >= t.min_toolchain]
332+
eprint(f"Targets checkable with this toolchain: {len(targets)}")
333+
334+
# Targets get split among the diferent CI runners
335+
targets = [t for t in targets if t.run_on() == cfg.os_]
336+
eprint(f"Targets checked on this OS: {len(targets)}")
337+
338+
# Apply filtering
339+
if args.only:
340+
targets = [t for t in targets if re.match(args.only, t.name)]
341+
if args.skip:
342+
targets = [t for t in targets if not re.match(args.skip, t.name)]
343+
344+
total = len(targets)
345+
eprint(f"Targets to run: {total}")
346+
assert total > 0, "some tests should be run"
347+
348+
for i, target in enumerate(targets):
349+
# HACK: We need to install the toolchain by name for most Windows toolchains,
350+
# but not when cross compiling.
351+
if cfg.os_ == Os.WINDOWS and "aarch64" not in target.name:
352+
run(
353+
["sh", "./ci/install-rust.sh"], env=os.environ | {"TARGET": target.name}
354+
)
355+
356+
eprint(f"::group::Target: {target.name} ({i}/{total})")
357+
eprint(f"{ESC_CYAN}Checking target {target} ({i}/{total}){ESC_END}")
358+
test_target(cfg, target)
359+
eprint("::endgroup::")
360+
361+
362+
main()

0 commit comments

Comments
 (0)