Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
- name: Target size after restoring cache
run: du -sh target | sort -k 2 || true

- name: Execute build.sh
- name: Execute build check
run: |
set -eux
if [ "${{ matrix.toolchain }}" = "1.63.0" ]; then
Expand All @@ -92,7 +92,7 @@ jobs:
perl -i -ne 'print unless /"ctest(-test)?",/ || /"libc-test",/' Cargo.toml
fi

./ci/verify-build.sh
python3 ci/verify-build.py --toolchain "$TOOLCHAIN"
- name: Target size after job completion
run: du -sh target | sort -k 2

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ but this is not guaranteed.
You can see the platform(target)-specific docs on [docs.rs], select a platform
you want to see.

See [`ci/verify-build.sh`](https://github.com/rust-lang/libc/blob/HEAD/ci/verify-build.sh) for
See [`ci/verify-build.py`](https://github.com/rust-lang/libc/blob/HEAD/ci/verify-build.py) for
the platforms on which `libc` is guaranteed to build for each Rust toolchain.
The test-matrix at [GitHub Actions] and [Cirrus CI] show the platforms in which
`libc` tests are run.
Expand Down
362 changes: 362 additions & 0 deletions ci/verify-build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,362 @@
#!/usr/bin/env python3

import re
import os
import argparse
import subprocess as sp
import sys
import platform
from typing import Optional
from enum import Enum, IntEnum
from dataclasses import dataclass, field


ESC_CYAN = "\033[1;36m"
ESC_END = "\033[0m"


class Os(Enum):
LINUX = "Linux"
WINDOWS = "Windows"
DARWIN = "Darwin"


class Toolchain(IntEnum):
OTHER = 0 # msrv
STABLE = 1
BETA = 2
NIGHTLY = 3


@dataclass
class Cfg:
toolchain_name: str
toolchain: Toolchain = field(init=False)
host_target: str = field(init=False)
os_: Os = field(init=False)

def __post_init__(self):
rustc_output = check_output(["rustc", f"+{self.toolchain_name}", "-vV"])
self.host_target = re.findall(r"host: (.*)", rustc_output)[0]
if "nightly" in self.toolchain_name:
self.toolchain = Toolchain.NIGHTLY
elif "beta" in self.toolchain_name:
self.toolchain = Toolchain.BETA
elif "stable" in self.toolchain_name:
self.toolchain = Toolchain.STABLE
else:
self.toolchain = Toolchain.OTHER
self.os_ = Os(platform.system())
eprint(f"Testing Rust {self.toolchain_name} on {self.os_}")

def nightly(self) -> bool:
return self.toolchain == Toolchain.NIGHTLY


@dataclass
class Target:
name: str
dist: bool = True
min_toolchain: Toolchain = Toolchain.OTHER

def __post_init__(self):
if not self.dist:
# We will need to use build-std
self.min_toolchain = Toolchain.NIGHTLY

def run_on(self) -> Os:
"""MacOS CI runs all apple targets, Windows CI runs all Windows targets,
Linux CI handles everything else."""

if "apple" in self.name:
return Os.DARWIN
elif "windows" in self.name:
return Os.WINDOWS
return Os.LINUX


FREEBSD_VERSIONS = [11, 12, 13, 14, 15]

TARGETS = [
# linux
Target("aarch64-linux-android"),
Target("aarch64-unknown-linux-gnu"),
Target("aarch64-unknown-linux-musl"),
Target("arm-linux-androideabi"),
Target("arm-unknown-linux-gnueabi"),
Target("arm-unknown-linux-gnueabihf"),
Target("arm-unknown-linux-musleabi"),
Target("arm-unknown-linux-musleabihf"),
Target("armv7-linux-androideabi"),
Target("armv7-unknown-linux-gnueabihf"),
Target("armv7-unknown-linux-musleabihf"),
Target("i586-unknown-linux-gnu"),
Target("i586-unknown-linux-musl"),
Target("i686-linux-android"),
Target("i686-unknown-freebsd"),
Target("i686-unknown-linux-gnu"),
Target("i686-unknown-linux-musl"),
Target("powerpc-unknown-linux-gnu"),
Target("powerpc64-unknown-linux-gnu"),
Target("powerpc64le-unknown-linux-gnu"),
Target("s390x-unknown-linux-gnu"),
Target("sparc64-unknown-linux-gnu"),
Target("sparcv9-sun-solaris"),
Target("wasm32-unknown-emscripten"),
Target("wasm32-unknown-unknown"),
# Target was renamed
Target("wasm32-wasip1", min_toolchain=Toolchain.STABLE),
Target("wasm32-wasip2", min_toolchain=Toolchain.STABLE),
Target("x86_64-linux-android"),
Target("x86_64-unknown-freebsd"),
Target("x86_64-unknown-linux-gnu"),
Target("x86_64-unknown-linux-musl"),
Target("x86_64-unknown-netbsd"),
# nightly linux
# FIXME(powerpc64le): powerpc64le-unknown-linux-musl is tier 2 since 1.85 and
# can be moved to rust_linux_targets once MSRV is increased
Target("aarch64-unknown-fuchsia", min_toolchain=Toolchain.NIGHTLY),
Target("armv5te-unknown-linux-gnueabi", min_toolchain=Toolchain.NIGHTLY),
Target("armv5te-unknown-linux-musleabi", min_toolchain=Toolchain.NIGHTLY),
Target("i686-pc-windows-gnu", min_toolchain=Toolchain.NIGHTLY),
Target("powerpc64le-unknown-linux-musl", min_toolchain=Toolchain.NIGHTLY),
Target("riscv64gc-unknown-linux-gnu", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-fortanix-unknown-sgx", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-pc-solaris", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-pc-windows-gnu", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-unknown-fuchsia", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-unknown-illumos", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-unknown-linux-gnux32", min_toolchain=Toolchain.NIGHTLY),
Target("x86_64-unknown-redox", min_toolchain=Toolchain.NIGHTLY),
# apple
Target("aarch64-apple-darwin"),
Target("aarch64-apple-ios"),
# windows
Target("x86_64-pc-windows-msvc"),
Target("x86_64-pc-windows-gnu"),
Target("i686-pc-windows-msvc"),
# linux nodist
# Targets which are not available via rustup and must be built with -Zbuild-std
# FIXME(hexagon): hexagon-unknown-linux-musl should be tested but currently has
# duplicate symbol errors from `compiler_builtins`.
Target("aarch64-pc-windows-msvc", dist=False),
Target("aarch64-unknown-freebsd", dist=False),
Target("aarch64-unknown-hermit", dist=False),
Target("aarch64-unknown-netbsd", dist=False),
Target("aarch64-unknown-openbsd", dist=False),
Target("aarch64-wrs-vxworks", dist=False),
Target("armebv7r-none-eabi", dist=False),
Target("armebv7r-none-eabihf", dist=False),
Target("armv7-wrs-vxworks-eabihf", dist=False),
Target("armv7r-none-eabi", dist=False),
Target("armv7r-none-eabihf", dist=False),
Target("i686-pc-windows-msvc", dist=False),
Target("i686-unknown-haiku", dist=False),
Target("i686-unknown-netbsd", dist=False),
Target("i686-unknown-openbsd", dist=False),
Target("i686-wrs-vxworks", dist=False),
Target("mips-unknown-linux-gnu", dist=False),
Target("mips-unknown-linux-musl", dist=False),
Target("mips64-unknown-linux-gnuabi64", dist=False),
Target("mips64-unknown-linux-muslabi64", dist=False),
Target("mips64el-unknown-linux-gnuabi64", dist=False),
Target("mips64el-unknown-linux-muslabi64", dist=False),
Target("mipsel-unknown-linux-gnu", dist=False),
Target("mipsel-unknown-linux-musl", dist=False),
Target("nvptx64-nvidia-cuda", dist=False),
Target("powerpc-unknown-linux-gnuspe", dist=False),
Target("powerpc-unknown-netbsd", dist=False),
Target("powerpc-wrs-vxworks", dist=False),
Target("powerpc-wrs-vxworks-spe", dist=False),
Target("powerpc64-unknown-freebsd", dist=False),
Target("powerpc64-wrs-vxworks", dist=False),
Target("riscv32i-unknown-none-elf", dist=False),
Target("riscv32imac-unknown-none-elf", dist=False),
Target("riscv32imc-unknown-none-elf", dist=False),
Target("riscv32gc-unknown-linux-gnu", dist=False),
Target("riscv32-wrs-vxworks", dist=False),
Target("riscv64gc-unknown-freebsd", dist=False),
Target("riscv64gc-unknown-hermit", dist=False),
Target("riscv64gc-unknown-linux-musl", dist=False),
Target("riscv64gc-unknown-none-elf", dist=False),
Target("riscv64imac-unknown-none-elf", dist=False),
Target("riscv64-wrs-vxworks", dist=False),
Target("s390x-unknown-linux-musl", dist=False),
Target("sparc-unknown-linux-gnu", dist=False),
Target("sparc64-unknown-netbsd", dist=False),
Target("thumbv6m-none-eabi", dist=False),
Target("thumbv7em-none-eabi", dist=False),
Target("thumbv7em-none-eabihf", dist=False),
Target("thumbv7m-none-eabi", dist=False),
Target("thumbv7neon-linux-androideabi", dist=False),
Target("thumbv7neon-unknown-linux-gnueabihf", dist=False),
Target("thumbv8m.main-none-eabi", dist=False),
Target("x86_64-pc-windows-msvc", dist=False),
Target("x86_64-unknown-dragonfly", dist=False),
Target("x86_64-unknown-haiku", dist=False),
Target("x86_64-unknown-hermit", dist=False),
Target("x86_64-unknown-l4re-uclibc", dist=False),
Target("x86_64-unknown-openbsd", dist=False),
Target("x86_64-wrs-vxworks", dist=False),
# apple nodist
Target("armv7s-apple-ios", dist=False),
Target("i686-apple-darwin", dist=False),
Target("i386-apple-ios", dist=False),
]


def eprint(*args, **kw):
print(*args, file=sys.stderr, **kw)


def xtrace(args: list[str], /, env: Optional[dict[str, str]]):
"""Print commands before running them."""
astr = " ".join(args)
if env is None:
eprint(f"+ {astr}")
else:
envdiff = set(env.items()) - set(os.environ.items())
estr = " ".join(f"{k}='{v}'" for (k, v) in envdiff)
eprint(f"+ {estr} {astr}")


def check_output(args: list[str], /, env: Optional[dict[str, str]] = None) -> str:
xtrace(args, env=env)
return sp.check_output(args, env=env, encoding="utf8")


def run(args: list[str], /, env: Optional[dict[str, str]] = None):
xtrace(args, env=env)
sp.run(args, env=env, check=True)


def test_target(cfg: Cfg, target: Target):
env = os.environ.copy()
env.setdefault("RUSTFLAGS", "")

tname = target.name
target_cfg = check_output(["rustc", "--print=cfg", "--target", tname])
target_env = re.findall(r'target_env="(.*)"', target_cfg)
target_os = re.findall(r'target_os="(.*)"', target_cfg)
target_bits = re.findall(r'target_pointer_width="(.*)"', target_cfg)[0]
assert target_bits in ["32", "64"]
eprint(f"env {target_env}, os {target_os}, bits {target_bits}")

cmd = ["cargo", f"+{cfg.toolchain_name}", "build", "--target", tname]

if not target.dist:
# If we can't download a `core`, we need to build it
cmd += ["-Zbuild-std=core"]
# FIXME: With `build-std` feature, `compiler_builtins` emits a lot of lint warnings.
env["RUSTFLAGS"] += " -Aimproper_ctypes_definitions"
else:
run(["rustup", "target", "add", tname, "--toolchain", cfg.toolchain_name])

# Test with expected combinations of features
run(cmd, env=env)
run(cmd + ["--features=extra_traits"], env=env)

# Check with different env for 64-bit time_t
if target_os == "linux" and target_bits == "32":
# Equivalent of __USE_TIME_BITS64
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64": "1"})

if "gnu" in target_env and target_bits == "32":
# Equivalent of _FILE_OFFSET_BITS=64
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS": "64"})
# Equivalent of _TIME_BITS=64
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_GNU_TIME_BITS": "64"})

# Test again without default features, i.e. without `std`
run(cmd + ["--no-default-features"])
run(cmd + ["--no-default-features", "--features=extra_traits"])

# Ensure the crate will build when used as a dependency of `std`
if cfg.nightly():
run(cmd + ["--no-default-features", "--features=rustc-dep-of-std"])

# For freebsd targets, check with the different versions we support
# if on nightly or stable
if "freebsd" in tname and cfg.toolchain >= Toolchain.STABLE:
for version in FREEBSD_VERSIONS:
run(cmd, env=env | {"RUST_LIBC_UNSTABLE_FREEBSD_VERSION": str(version)})
run(
cmd + ["--no-default-features"],
env=env | {"RUST_LIBC_UNSTABLE_FREEBSD_VERSION": str(version)},
)

is_stable = cfg.toolchain == Toolchain.STABLE
# FIXME(semver): can't pass `--target` to `cargo-semver-checks` so we restrict to
# the host target
is_host = tname == cfg.host_target
if is_stable and is_host:
eprint("Running semver checks")
run(
[
"cargo",
"semver-checks",
"--only-explicit-features",
"--features=std,extra_traits",
]
)
else:
eprint("Skipping semver checks")

eprint(f"Finished checking target {tname}")


def main():
p = argparse.ArgumentParser()
p.add_argument("--toolchain", required=True, help="Rust toolchain")
p.add_argument("--only", help="only targets matching this regex")
p.add_argument("--skip", help="skip targets matching this regex")
args = p.parse_args()

cfg = Cfg(toolchain_name=args.toolchain)
eprint(f"Config: {cfg}")
eprint("Python version: ", sys.version)

if cfg.nightly():
# Needed for build-std
run(["rustup", "component", "add", "rust-src"])

targets = TARGETS
eprint(f"Total checked targets across platforms: {len(targets)}")

if not cfg.nightly():
# Non-dist targets need nightly for build-std
targets = [t for t in targets if t.dist]

# Filter to targets supported on the current toolchain
targets = [t for t in targets if cfg.toolchain >= t.min_toolchain]
eprint(f"Targets checkable with this toolchain: {len(targets)}")

# Targets get split among the diferent CI runners
targets = [t for t in targets if t.run_on() == cfg.os_]
eprint(f"Targets checked on this OS: {len(targets)}")

# Apply filtering
if args.only:
targets = [t for t in targets if re.match(args.only, t.name)]
if args.skip:
targets = [t for t in targets if not re.match(args.skip, t.name)]

total = len(targets)
eprint(f"Targets to run: {total}")
assert total > 0, "some tests should be run"

for i, target in enumerate(targets):
# HACK: We need to install the toolchain by name for most Windows toolchains,
# but not when cross compiling.
if cfg.os_ == Os.WINDOWS and "aarch64" not in target.name:
run(
["sh", "./ci/install-rust.sh"], env=os.environ | {"TARGET": target.name}
)

eprint(f"::group::Target: {target.name} ({i}/{total})")
eprint(f"{ESC_CYAN}Checking target {target} ({i}/{total}){ESC_END}")
test_target(cfg, target)
eprint("::endgroup::")


main()
Loading