libabort 0.1.9

A abort function that doesn't require the standard library
Documentation
# Helper for Github Actions to run the `abortcli` example
# and verify it exits with a SIGABORT
#
# Requires POSIX to detect sigabort

import sys
import os
import signal
import subprocess
from pathlib import Path
import re

RUST_VERSION_PATTERN = re.compile(r"(\d+).(\d+).(\d+)")


def parse_rust_version(version):
    if version in ("stable", "beta", "nightly"):
        return version
    else:
        mtch = RUST_VERSION_PATTERN.fullmatch(version)
        if mtch is None:
            fatal(f"Invalid rust version: {version!r}")
        return tuple(map(int, mtch.groups()))


def fatal(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


def main(args):
    if len(args) < 2 or args[0] != "--features":
        fatal("Expected `--features` as first argument")
    else:
        features = args[1].split(",")
        if features == [""]:
            features = []
    if len(args) < 4 or args[2] != "--rust-version":
        fatal("Expected `--rust-version` as third argument")
    else:
        rust_version = parse_rust_version(args[3])
    if os.name != "posix":
        fatal(f"Need POSIX to check signal for example, but have {os.name!r}")
    example_binary = Path("target/release/examples/abortcli")
    if not example_binary.is_file():
        raise FileNotFoundError(f"Missing example binary: {example_binary}")
    print("Running `abortcli` example:")
    result = subprocess.run(
        [example_binary],
    )
    # After Rust 1.59, we support using inline assembly to issue trap instructions
    supports_trap_instruction = isinstance(rust_version, str) or rust_version[1] >= 59
    # For some reason Rust 1.59 and 1.31 trigger SIGILL on double panics
    double_panic_triggers_trap = rust_version[1] in range(31, 60)
    # If our only feature is `abort-via-trap
    if (
        features == ["abort-via-trap"]
        and (supports_trap_instruction or double_panic_triggers_trap)
    ) or (features == [] and double_panic_triggers_trap):
        expected_signal = "SIGILL"
    else:
        expected_signal = "SIGABRT"
    expected_signal_code = getattr(signal, expected_signal)
    if result.returncode == -expected_signal_code:
        print(f"Detected a {expected_signal}, as expected")
    else:
        fatal(
            f"Expected a `{expected_signal}` exit code (-{expected_signal_code}), but got {result.returncode}"
        )


if __name__ == "__main__":
    main(sys.argv[1:])