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
112 changes: 21 additions & 91 deletions scripts/smoke-tests/baremetal-arm-smoke-test
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,26 @@
# not run, and that is reported as WARN. Failures exit non-zero.

import argparse
import functools
import os
import re
import shlex
import shutil
import subprocess
import sys
import tempfile

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
sys.dont_write_bytecode = True
import smoketest # noqa: E402
from smoketest import (last_line, overlay_prefixes, report, # noqa: E402
summary)

# How --prefix survives the re-exec into the mount namespace.
PREFIX_VARIABLE = "BAREMETAL_ARM_SMOKE_TEST_PREFIX"

# What says why a link or a QEMU run failed, for last_line.
ERROR = r"error|cannot find|undefined symbol|unrecognised|fault"

RUNTIMES = "/usr/lib/clang-runtimes"

HELLO = r"""
Expand Down Expand Up @@ -138,80 +147,8 @@ PROGRAMS = [
MPS2_AN505),
]

status = {"fail": 0, "warn": 0}


def report(state, message):
if state != "ok":
status[state] += 1
print("%-5s %s" % (state.upper(), message))


def run(command, **kwargs):
"""Run command, capturing its output, never raising for its exit status."""
try:
return subprocess.run(command, capture_output=True, text=True,
timeout=120, **kwargs)
except (OSError, subprocess.TimeoutExpired) as error:
return subprocess.CompletedProcess(command, 127, "", str(error))


def last_line(process):
"""The line of the output that says what went wrong."""
lines = (process.stderr or process.stdout).strip().splitlines()
for line in lines:
# The linker says why before clang says that it failed.
if re.search(r"error|cannot find|undefined symbol|unrecognised|fault",
line) and \
"linker command failed" not in line:
return line.strip()
return lines[-1] if lines else "exit status %d" % process.returncode


# ---------------------------------------------------------------- --prefix

def unpack(items, workdir):
"""Return the directories to overlay, unpacking package archives."""
lowers = []
for number, item in enumerate(items):
if os.path.isdir(item):
path = os.path.abspath(item.rstrip("/"))
elif os.path.isfile(item):
path = os.path.join(workdir, str(number))
os.mkdir(path)
print(" unpacking %s" % item, flush=True)
process = subprocess.run(["tar", "-C", path, "-xf", item],
capture_output=True, text=True)
if process.returncode:
sys.exit("cannot unpack %s: %s" % (item, process.stderr))
else:
sys.exit("no such directory or package: %s" % item)
if ":" in path or "," in path:
sys.exit("cannot overlay %s: overlayfs separates layers by ':'"
" and options by ','" % path)
if not os.path.isdir(path + "/usr"):
sys.exit("%s has no usr/" % item)
lowers.append(path)
return lowers


def run_in_overlay(lowers):
"""Run this script again with lowers mounted over /usr."""
layers = ":".join([lower + "/usr" for lower in lowers] + ["/usr"])
setup = "set -e\nmount -t overlay overlay -o lowerdir=%s /usr\nexec \"$@\"" \
% shlex.quote(layers)
environment = dict(os.environ)
environment[PREFIX_VARIABLE] = ":".join(lowers)
command = ["unshare", "--user", "--map-root-user", "--mount",
"--propagation", "slave",
"sh", "-c", setup, "sh", sys.executable,
os.path.abspath(__file__)] + sys.argv[1:]
try:
return subprocess.run(command, env=environment).returncode
except OSError as error:
sys.exit("cannot re-run in a mount namespace (%s); install the"
" packages and run without --prefix" % error)

# Shorter than smoketest's default: nothing here compiles for long.
run = functools.partial(smoketest.run, timeout=120)

# ----------------------------------------------------------------- checks

Expand Down Expand Up @@ -278,7 +215,8 @@ def check_program(name, flags, machine, memory, qemu, machines, directory):
os.path.join(RUNTIMES, variant, "lib/crt0-semihost.o"),
"-o", output, "hello.c", "-lsemihost"], cwd=directory)
if process.returncode:
report("fail", "%s: link failed: %s" % (name, last_line(process)))
report("fail", "%s: link failed: %s"
% (name, last_line(process, ERROR)))
return
if machine not in machines:
report("warn", "%s: linked, but not run: %s has no %s board"
Expand All @@ -293,7 +231,7 @@ def check_program(name, flags, machine, memory, qemu, machines, directory):
if process.returncode or EXPECT not in printed:
report("fail", "%s on %s: exit status %d: %s"
% (name, machine, process.returncode,
last_line(process) if process.returncode else
last_line(process, ERROR) if process.returncode else
printed.strip()))
else:
report("ok", "%s runs on %s with %s" % (name, machine, variant))
Expand All @@ -307,7 +245,8 @@ def check_default_link(directory):
["-o", "default.elf", "hello.c", "-lsemihost"]
process = run(command, cwd=directory)
if process.returncode:
report("fail", "%s: link failed: %s" % (name, last_line(process)))
report("fail", "%s: link failed: %s"
% (name, last_line(process, ERROR)))
return
variant = os.path.join(RUNTIMES, VARIANTS[3], "lib")
# clang spells the paths /usr/bin/../lib/clang-runtimes/...
Expand Down Expand Up @@ -337,15 +276,9 @@ def main():
" (default: from PATH)")
arguments = parser.parse_args()

if arguments.prefix and PREFIX_VARIABLE not in os.environ:
workdir = tempfile.mkdtemp(prefix="baremetal-arm-smoke-")
try:
return run_in_overlay(unpack(arguments.prefix, workdir))
finally:
shutil.rmtree(workdir, ignore_errors=True)
if PREFIX_VARIABLE in os.environ:
print(" testing, over /usr: %s"
% os.environ[PREFIX_VARIABLE].replace(":", " "))
returncode = overlay_prefixes(arguments.prefix, PREFIX_VARIABLE)
if returncode is not None:
return returncode

machines = qemu_machines(arguments.qemu)
if arguments.qemu:
Expand All @@ -367,10 +300,7 @@ def main():
finally:
shutil.rmtree(directory, ignore_errors=True)

print("%s (%d failed, %d warned)"
% ("FAIL" if status["fail"] else "PASS", status["fail"],
status["warn"]))
return 1 if status["fail"] else 0
return summary()


if __name__ == "__main__":
Expand Down
17 changes: 6 additions & 11 deletions scripts/smoke-tests/freetype-smoke-test
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ import subprocess
import sys
import tempfile

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
sys.dont_write_bytecode = True
from smoketest import report, summary # noqa: E402

# configure links these when it finds them, and 2.10.4 linked all of them.
NEEDED = ["libbz2.so.1.0", "libpng16.so.16", "libz.so.1", "libharfbuzz.so.0",
"libbrotlidec.so."]
Expand Down Expand Up @@ -425,13 +429,6 @@ int main(int argc, char **argv)
"""

root = ""
status = {"fail": 0, "warn": 0}


def report(state, message):
if state in status:
status[state] += 1
print("%-5s %s" % (state.upper(), message))


def environment(tree=None):
Expand Down Expand Up @@ -1041,16 +1038,14 @@ def main():
comparisons = check_rendering(binary, directory, fonts, arguments)
check_fcscan(fonts)

print("%s (%d failed, %d warned), files in %s"
% ("FAIL" if status["fail"] else "PASS",
status["fail"], status["warn"], directory))
returncode = summary(directory)
if comparisons and os.environ.get("DISPLAY") and not arguments.no_view:
try:
view(comparisons)
# ImportError without tkinter, tkinter.TclError without a display.
except Exception as error:
print("cannot show the rendering: %s" % error)
return 1 if status["fail"] else 0
return returncode


if __name__ == "__main__":
Expand Down
115 changes: 15 additions & 100 deletions scripts/smoke-tests/llvm-smoke-test
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,21 @@
import argparse
import os
import re
import shlex
import shutil
import subprocess
import sys
import tempfile

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
sys.dont_write_bytecode = True
from smoketest import (last_line, overlay_prefixes, report, run, # noqa: E402
strict_overcommit, summary)

# How --prefix survives the re-exec into the mount namespace.
PREFIX_VARIABLE = "LLVM_SMOKE_TEST_PREFIX"

# What says why a compile failed, for last_line.
ERROR = r"error|cannot find|undefined reference"

SOURCES = {
"hello.c": r"""
#include <stdio.h>
Expand Down Expand Up @@ -109,80 +115,6 @@ kernel void k(global float *out) {
""",
}

status = {"fail": 0, "warn": 0}


def report(state, message):
if state != "ok":
status[state] += 1
print("%-5s %s" % (state.upper(), message))


def run(command, **kwargs):
"""Run command, capturing its output, never raising for its exit status."""
try:
return subprocess.run(command, capture_output=True, text=True,
timeout=600, **kwargs)
except (OSError, subprocess.TimeoutExpired) as error:
return subprocess.CompletedProcess(command, 127, "", str(error))


def last_line(process):
"""The line of the output that says what went wrong."""
lines = (process.stderr or process.stdout).strip().splitlines()
for line in lines:
# The linker says why before clang says that it failed.
if re.search(r"error|cannot find|undefined reference", line) and \
"linker command failed" not in line:
return line.strip()
return lines[-1] if lines else "exit status %d" % process.returncode


# ---------------------------------------------------------------- --prefix

def unpack(items, workdir):
"""Return the directories to overlay, unpacking package archives."""
lowers = []
for number, item in enumerate(items):
if os.path.isdir(item):
path = os.path.abspath(item.rstrip("/"))
elif os.path.isfile(item):
path = os.path.join(workdir, str(number))
os.mkdir(path)
print(" unpacking %s" % item, flush=True)
process = subprocess.run(["tar", "-C", path, "-xf", item],
capture_output=True, text=True)
if process.returncode:
sys.exit("cannot unpack %s: %s" % (item, process.stderr))
else:
sys.exit("no such directory or package: %s" % item)
if ":" in path or "," in path:
sys.exit("cannot overlay %s: overlayfs separates layers by ':'"
" and options by ','" % path)
if not os.path.isdir(path + "/usr"):
sys.exit("%s has no usr/" % item)
lowers.append(path)
return lowers


def run_in_overlay(lowers):
"""Run this script again with lowers mounted over /usr."""
layers = ":".join([lower + "/usr" for lower in lowers] + ["/usr"])
setup = "set -e\nmount -t overlay overlay -o lowerdir=%s /usr\nexec \"$@\"" \
% shlex.quote(layers)
environment = dict(os.environ)
environment[PREFIX_VARIABLE] = ":".join(lowers)
command = ["unshare", "--user", "--map-root-user", "--mount",
"--propagation", "slave",
"sh", "-c", setup, "sh", sys.executable,
os.path.abspath(__file__)] + sys.argv[1:]
try:
return subprocess.run(command, env=environment).returncode
except OSError as error:
sys.exit("cannot re-run in a mount namespace (%s); install the"
" packages and run without --prefix" % error)


# ----------------------------------------------------------------- checks

def major(version):
Expand Down Expand Up @@ -240,7 +172,7 @@ def build(name, command, directory):
process = run(command, cwd=directory)
if process.returncode:
report("fail", "%s: %s failed: %s"
% (name, " ".join(command[:1]), last_line(process)))
% (name, " ".join(command[:1]), last_line(process, ERROR)))
return False
return True

Expand Down Expand Up @@ -272,14 +204,6 @@ def check_lld(version, directory):
report("ok", "%s, .comment names LLD %s" % (name, version))


def strict_overcommit():
try:
with open("/proc/sys/vm/overcommit_memory") as sysctl:
return sysctl.read().strip() == "2"
except OSError:
return False


def check_sanitizer(name, flags, source, expect, directory):
output = name.lower()
if not build(name, ["clang", "-g"] + flags + ["-o", output, source],
Expand All @@ -294,7 +218,7 @@ def check_sanitizer(name, flags, source, expect, directory):
report("ok", "%s reports %s" % (name, expect))
else:
report("fail", "%s: expected %r, got exit status %d: %s"
% (name, expect, process.returncode, last_line(process)))
% (name, expect, process.returncode, last_line(process, ERROR)))


def check_profile(directory):
Expand All @@ -313,7 +237,7 @@ def check_profile(directory):
env=dict(os.environ, **environment))
if process.returncode:
report("fail", "%s: %s failed: %s"
% (name, command[0], last_line(process)))
% (name, command[0], last_line(process, ERROR)))
return
if "TOTAL" in process.stdout:
report("ok", "%s: compiler-rt profile, llvm-profdata, llvm-cov" % name)
Expand Down Expand Up @@ -358,15 +282,9 @@ def main():
" installed; may be repeated")
arguments = parser.parse_args()

if arguments.prefix and PREFIX_VARIABLE not in os.environ:
workdir = tempfile.mkdtemp(prefix="llvm-smoke-")
try:
return run_in_overlay(unpack(arguments.prefix, workdir))
finally:
shutil.rmtree(workdir, ignore_errors=True)
if PREFIX_VARIABLE in os.environ:
print(" testing, over /usr: %s"
% os.environ[PREFIX_VARIABLE].replace(":", " "))
returncode = overlay_prefixes(arguments.prefix, PREFIX_VARIABLE)
if returncode is not None:
return returncode

version = check_versions()
if not version:
Expand Down Expand Up @@ -398,10 +316,7 @@ def main():
finally:
shutil.rmtree(directory, ignore_errors=True)

print("%s (%d failed, %d warned)"
% ("FAIL" if status["fail"] else "PASS", status["fail"],
status["warn"]))
return 1 if status["fail"] else 0
return summary()


if __name__ == "__main__":
Expand Down
Loading