diff --git a/compiler-rt.be0 b/compiler-rt.be0 index ef3f17a0c..b4e8f94d7 100755 --- a/compiler-rt.be0 +++ b/compiler-rt.be0 @@ -1,6 +1,6 @@ #!/usr/bin/env beesh -# BEE_VERSION compiler-rt-23.1.3-0 +# BEE_VERSION compiler-rt-23.1.3-1 #SRCURL[0]="https://github.com/llvm/llvm-project/releases/download/llvmorg-${PKGVERSION}/llvm-project-${PKGVERSION}.src.tar.xz" SRCURL[0]="https://beehive.molgen.mpg.de/d7e227fe471d80d5e2d411127a0123b1/llvm-project-23.1.3.src.tar.xz" @@ -11,6 +11,19 @@ SRCURL[0]="https://beehive.molgen.mpg.de/d7e227fe471d80d5e2d411127a0123b1/llvm-p sourcesubdir_append compiler-rt +# Bare-metal Arm builtins, one build per multilib variant: the directory below +# ${LIBDIR}/clang-runtimes, the target, and the flags. multilib.yaml, written +# in mee_install(), lets clang pick a variant from -mcpu/-mfloat-abi/-mfpu. +# picolibc.be0 installs the C library into the same directories. +BAREMETAL_VARIANTS=( + "arm-none-eabi/armv6m_soft_nofp thumbv6m-none-eabi -mfloat-abi=soft" + "arm-none-eabi/armv7m_soft_nofp thumbv7m-none-eabi -mfloat-abi=soft" + "arm-none-eabi/armv7em_soft_fpv4_sp_d16 thumbv7em-none-eabi -mfloat-abi=softfp -mfpu=fpv4-sp-d16" + "arm-none-eabi/armv7em_hard_fpv4_sp_d16 thumbv7em-none-eabihf -mfloat-abi=hard -mfpu=fpv4-sp-d16" + "arm-none-eabi/armv8m.main_soft_fp thumbv8m.main-none-eabi -mfloat-abi=softfp -mfpu=fpv5-sp-d16" + "arm-none-eabi/armv8m.main_hard_fp thumbv8m.main-none-eabihf -mfloat-abi=hard -mfpu=fpv5-sp-d16" +) + #mee_extract() { # bee_extract "${@}" #} @@ -33,12 +46,102 @@ mee_configure() { -DCMAKE_CXX_COMPILER=clang++ \ -Wno-dev \ -G Ninja + + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + start_cmd cmake \ + -S ${S}/lib/builtins \ + -B ${B}/baremetal/${dir} \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SYSTEM_NAME=Generic \ + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_ASM_COMPILER=clang \ + -DCMAKE_C_COMPILER_TARGET=${target} \ + -DCMAKE_ASM_COMPILER_TARGET=${target} \ + -DCMAKE_C_FLAGS="${flags}" \ + -DCMAKE_ASM_FLAGS="${flags}" \ + -DCMAKE_AR=$(command -v llvm-ar) \ + -DCMAKE_RANLIB=$(command -v llvm-ranlib) \ + -DCOMPILER_RT_STANDALONE_BUILD=ON \ + -DCOMPILER_RT_BAREMETAL_BUILD=ON \ + -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ + -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON \ + -DCOMPILER_RT_INSTALL_LIBRARY_DIR=${LIBDIR}/clang-runtimes/${dir}/lib \ + -DLLVM_CMAKE_DIR=$(llvm-config --cmakedir) \ + -Wno-dev \ + -G Ninja + done } mee_build() { start_cmd ninja -v -C ${B} ${BEE_NINJAFLAGS} "${@}" + + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + start_cmd ninja -v -C ${B}/baremetal/${dir} ${BEE_NINJAFLAGS} "${@}" + done } mee_install() { DESTDIR=${D} start_cmd ninja -C ${B} install "${@}" + + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + DESTDIR=${D} start_cmd ninja -C ${B}/baremetal/${dir} install "${@}" + + # compiler-rt appends the normalized target to the library + # directory, but clang only searches the variant's lib directory. + local libdir=${D}${LIBDIR}/clang-runtimes/${dir}/lib + start_cmd mv ${libdir}/*/libclang_rt.builtins.a ${libdir}/ + start_cmd rmdir ${libdir}/*/ + done + + # A variant matches if clang's flags (see -print-multi-flags-experimental) + # contain all of its flags, and of the matching variants the last one + # wins. The mappings let compatible cores use them: a library runs on any + # core of the same or a later architecture, and an FPv5 unit executes + # FPv4 code. The variants without FPU require -mfpu=none, as their crt0 + # does not enable the FPU, which code built with -mfloat-abi=softfp for + # a core with FPU uses. + cat > ${D}${LIBDIR}/clang-runtimes/multilib.yaml <<-'EOF' + MultilibVersion: 1.0 + + Groups: + - Name: stdlibs + Type: Exclusive + + Variants: + - Dir: arm-none-eabi/armv6m_soft_nofp + Flags: [--target=thumbv6m-unknown-none-eabi, -mfpu=none] + Group: stdlibs + - Dir: arm-none-eabi/armv7m_soft_nofp + Flags: [--target=thumbv7m-unknown-none-eabi, -mfpu=none] + Group: stdlibs + - Dir: arm-none-eabi/armv7em_soft_fpv4_sp_d16 + Flags: [--target=thumbv7em-unknown-none-eabi, -mfpu=fpv4-sp-d16] + Group: stdlibs + - Dir: arm-none-eabi/armv7em_hard_fpv4_sp_d16 + Flags: [--target=thumbv7em-unknown-none-eabihf, -mfpu=fpv4-sp-d16] + Group: stdlibs + - Dir: arm-none-eabi/armv8m.main_soft_fp + Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=fpv5-sp-d16] + Group: stdlibs + - Dir: arm-none-eabi/armv8m.main_hard_fp + Flags: [--target=thumbv8m.main-unknown-none-eabihf, -mfpu=fpv5-sp-d16] + Group: stdlibs + + Mappings: + - Match: --target=thumbv8m\.base-unknown-none-eabi + Flags: [--target=thumbv6m-unknown-none-eabi] + - Match: --target=thumbv(7em|8m\.main|8\.1m\.main)-unknown-none-eabi + Flags: [--target=thumbv7m-unknown-none-eabi] + - Match: -mfpu=fpv5-(sp-)?d16 + Flags: [-mfpu=fpv4-sp-d16] + - Match: -mfpu=fpv5-d16 + Flags: [-mfpu=fpv5-sp-d16] + EOF } diff --git a/picolibc.be0 b/picolibc.be0 new file mode 100755 index 000000000..ab0b9d118 --- /dev/null +++ b/picolibc.be0 @@ -0,0 +1,91 @@ +#!/usr/bin/env beesh + +# BEE_VERSION picolibc-1.8.12-0 + +# SRCURL[0]="https://github.com/picolibc/picolibc/releases/download/${PKGVERSION}/picolibc-${PKGVERSION}.tar.xz" +SRCURL[0]="https://beehive.molgen.mpg.de/7219ddf0193c83528f22b7b8fbc1fff4/picolibc-1.8.12.tar.xz" + +# PATCHURL+=() + +# build_in_sourcedir + +# sourcesubdir_append src + +# Bare-metal Arm C library, one build per multilib variant: the directory +# below ${LIBDIR}/clang-runtimes, the target, and the flags. Keep the list in +# sync with compiler-rt.be0, which builds the builtins for these variants and +# installs the multilib.yaml that lets clang pick one. +BAREMETAL_VARIANTS=( + "arm-none-eabi/armv6m_soft_nofp thumbv6m-none-eabi -mfloat-abi=soft" + "arm-none-eabi/armv7m_soft_nofp thumbv7m-none-eabi -mfloat-abi=soft" + "arm-none-eabi/armv7em_soft_fpv4_sp_d16 thumbv7em-none-eabi -mfloat-abi=softfp -mfpu=fpv4-sp-d16" + "arm-none-eabi/armv7em_hard_fpv4_sp_d16 thumbv7em-none-eabihf -mfloat-abi=hard -mfpu=fpv4-sp-d16" + "arm-none-eabi/armv8m.main_soft_fp thumbv8m.main-none-eabi -mfloat-abi=softfp -mfpu=fpv5-sp-d16" + "arm-none-eabi/armv8m.main_hard_fp thumbv8m.main-none-eabihf -mfloat-abi=hard -mfpu=fpv5-sp-d16" +) + +#mee_extract() { +# bee_extract "${@}" +#} + +#mee_patch() { +# bee_patch "${@}" +#} + +mee_configure() { + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + + # -nostdlibinc keeps the headers of an installed picolibc out of + # the build, -nostdlib the libraries. clang's bare-metal driver + # defaults to GNU ld, which on MarIuX only knows x86. + mkdir -p ${B}/cross/${dir%/*} + cat > ${B}/cross/${dir}.txt <<-EOF + [binaries] + c = ['clang', '--target=${target}', $(printf "'%s', " ${flags})'-nostdlibinc', '-nostdlib'] + c_ld = 'lld' + ar = 'llvm-ar' + nm = 'llvm-nm' + strip = 'llvm-strip' + + [host_machine] + system = 'none' + cpu_family = 'arm' + cpu = 'arm' + endian = 'little' + + [properties] + skip_sanity_check = true + EOF + + # Without -Dspecsdir=none the GCC specs file would be installed + # into clang's install directory, /usr/bin. + start_cmd meson setup \ + --cross-file ${B}/cross/${dir}.txt \ + --prefix=${LIBDIR}/clang-runtimes/${dir} \ + --includedir=include \ + --libdir=lib \ + --wrap-mode=nofallback \ + -Dmultilib=false \ + -Dspecsdir=none \ + ${B}/${dir} \ + ${S} + done +} + +mee_build() { + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + start_cmd ninja -v -C ${B}/${dir} ${BEE_NINJAFLAGS} "${@}" + done +} + +mee_install() { + local dir target flags + for variant in "${BAREMETAL_VARIANTS[@]}" ; do + read -r dir target flags <<< "${variant}" + DESTDIR=${D} start_cmd ninja -C ${B}/${dir} install "${@}" + done +} diff --git a/scripts/smoke-tests/baremetal-arm-smoke-test b/scripts/smoke-tests/baremetal-arm-smoke-test new file mode 100755 index 000000000..b18c9ba99 --- /dev/null +++ b/scripts/smoke-tests/baremetal-arm-smoke-test @@ -0,0 +1,377 @@ +#! /usr/bin/env python3 + +# Smoke-test the bare-metal Arm runtimes: compiler-rt's Cortex-M builtins and +# picolibc below /usr/lib/clang-runtimes, selected by its multilib.yaml. +# +# scripts/smoke-tests/baremetal-arm-smoke-test +# TMPDIR=/dev/shm scripts/smoke-tests/baremetal-arm-smoke-test \ +# --qemu /pkg/qemu-11.1.0-0/bin/qemu-system-arm \ +# --prefix /src/mariux/beeroot/packages/compiler-rt-23.1.3-1.x86_64.bee.tar.bz2 \ +# --prefix /src/mariux/beeroot/packages/picolibc-1.8.12-0.x86_64.bee.tar.bz2 +# +# compiler-rt.be0 and picolibc.be0 each build the same list of multilib +# variants into /usr/lib/clang-runtimes/arm-none-eabi/, and clang picks +# one from -mcpu, -mfloat-abi and -mfpu. A wrong pick still links, and only +# fails on the hardware: with only soft-float variants, code built with the +# default -mfloat-abi=softfp for a Cortex-M33 got the crt0 of a variant without +# FPU, which never enables it, and hard-faulted on the first VLDR. So this +# +# - checks which variant clang selects for each core and float ABI, and that +# a core without a variant gets -Wmissing-multilib instead of a wrong one, +# - checks that each variant has crt0, libc, the builtins, the linker script +# and the headers, +# - links a program doing 64-bit division and float and double printf for +# each core and runs it in QEMU with semihosting, +# - links once without naming crt0, so that clang adds crt0.o, -lc and the +# builtins on its own. +# +# Every link passes -fuse-ld=lld: for bare-metal targets clang defaults to the +# GNU ld in /usr/bin, which on MarIuX only knows x86 ("unrecognised emulation +# mode: armelf"). That is clang's configuration, not a fault of the runtimes, +# so it is not checked here. +# +# --prefix (repeatable) takes the image directory of a build, or a .bee.tar.bz2 +# package, which is unpacked below $TMPDIR. They are mounted read-only over /usr +# in a user namespace, so packages can be checked before they are installed, +# without root: clang looks for the runtimes in ../lib/clang-runtimes relative +# to its binary, so they have to show up below the same /usr as clang. +# +# --qemu names qemu-system-arm. The QEMU 5.1.0 in /usr/bin has no mps2-an386 +# (Cortex-M4) or mps2-an500 (Cortex-M7); cores without a board are linked but +# not run, and that is reported as WARN. Failures exit non-zero. + +import argparse +import os +import re +import shlex +import shutil +import subprocess +import sys +import tempfile + +# How --prefix survives the re-exec into the mount namespace. +PREFIX_VARIABLE = "BAREMETAL_ARM_SMOKE_TEST_PREFIX" + +RUNTIMES = "/usr/lib/clang-runtimes" + +HELLO = r""" +#include +int main(void) +{ + volatile unsigned long long a = 1234567890123ULL, b = 7; + volatile float f = 2.5f; + volatile double d = 1.0 / 3.0; + printf("hello: %llu %.3f %.6f\n", a / b, (double)(f * f), d); + return 0; +} +""" +EXPECT = "hello: 176366841446 6.250 0.333333" + +VARIANTS = [ + "arm-none-eabi/armv6m_soft_nofp", + "arm-none-eabi/armv7m_soft_nofp", + "arm-none-eabi/armv7em_soft_fpv4_sp_d16", + "arm-none-eabi/armv7em_hard_fpv4_sp_d16", + "arm-none-eabi/armv8m.main_soft_fp", + "arm-none-eabi/armv8m.main_hard_fp", +] + +# Name, clang flags, expected variant (None: no variant, -Wmissing-multilib). +SELECTIONS = [ + ("Cortex-M0+", "--target=arm-none-eabi -mcpu=cortex-m0plus", VARIANTS[0]), + ("Cortex-M23", "--target=arm-none-eabi -mcpu=cortex-m23", VARIANTS[0]), + ("thumbv6m triple", "--target=thumbv6m-none-eabi", VARIANTS[0]), + ("Cortex-M3", "--target=arm-none-eabi -mcpu=cortex-m3", VARIANTS[1]), + ("Cortex-M4 soft", "--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=soft", + VARIANTS[1]), + ("Cortex-M4 softfp", "--target=arm-none-eabi -mcpu=cortex-m4", VARIANTS[2]), + ("Cortex-M4 hard", "--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=hard", + VARIANTS[3]), + ("armv7em-eabihf triple", "--target=armv7em-none-eabihf", VARIANTS[3]), + ("thumbv7em-eabihf triple", "--target=thumbv7em-none-eabihf", VARIANTS[3]), + ("Cortex-M7 softfp", "--target=arm-none-eabi -mcpu=cortex-m7", VARIANTS[2]), + ("Cortex-M7 hard", "--target=arm-none-eabi -mcpu=cortex-m7 -mfloat-abi=hard", + VARIANTS[3]), + ("Cortex-M33 soft", + "--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=soft", VARIANTS[1]), + ("Cortex-M33 softfp", "--target=arm-none-eabi -mcpu=cortex-m33", VARIANTS[4]), + ("Cortex-M33 hard", + "--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard", VARIANTS[5]), + ("Cortex-M33 without FPU", "--target=arm-none-eabi -mcpu=cortex-m33+nofp", + VARIANTS[1]), + ("Cortex-M55 softfp", "--target=arm-none-eabi -mcpu=cortex-m55", None), + ("Cortex-M55 hard", + "--target=arm-none-eabi -mcpu=cortex-m55 -mfloat-abi=hard", None), +] + +# Memory maps: flash, flash size, RAM, RAM size. The micro:bit has 16 KiB of +# RAM; a stack placed above it locks the core up before main(). +MICROBIT = ("0x0", "0x40000", "0x20000000", "0x4000") +MPS2 = ("0x0", "0x100000", "0x20000000", "0x100000") +MPS2_AN505 = ("0x10000000", "0x100000", "0x38000000", "0x100000") + +# Name, clang flags, QEMU machine, memory map. The micro:bit's Cortex-M0 runs +# Armv6-M code just like a Cortex-M0+. +PROGRAMS = [ + ("Cortex-M0+", "--target=arm-none-eabi -mcpu=cortex-m0plus", "microbit", + MICROBIT), + ("Cortex-M3", "--target=arm-none-eabi -mcpu=cortex-m3", "mps2-an385", MPS2), + ("Cortex-M4 soft", "--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=soft", + "mps2-an386", MPS2), + ("Cortex-M4 softfp", "--target=arm-none-eabi -mcpu=cortex-m4", "mps2-an386", + MPS2), + ("Cortex-M4 hard", "--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=hard", + "mps2-an386", MPS2), + ("thumbv7em-eabihf triple", "--target=thumbv7em-none-eabihf", "mps2-an386", + MPS2), + ("Cortex-M7 softfp", "--target=arm-none-eabi -mcpu=cortex-m7", "mps2-an500", + MPS2), + ("Cortex-M7 hard", "--target=arm-none-eabi -mcpu=cortex-m7 -mfloat-abi=hard", + "mps2-an500", MPS2), + ("Cortex-M33 soft", + "--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=soft", "mps2-an505", + MPS2_AN505), + ("Cortex-M33 softfp", "--target=arm-none-eabi -mcpu=cortex-m33", + "mps2-an505", MPS2_AN505), + ("Cortex-M33 hard", + "--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard", "mps2-an505", + 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) + + +# ----------------------------------------------------------------- checks + +def check_layout(): + if not os.path.isfile(RUNTIMES + "/multilib.yaml"): + report("fail", "no %s/multilib.yaml" % RUNTIMES) + for variant in VARIANTS: + directory = os.path.join(RUNTIMES, variant) + missing = [name for name in ("lib/crt0.o", "lib/crt0-semihost.o", + "lib/libc.a", "lib/libsemihost.a", + "lib/libclang_rt.builtins.a", + "lib/picolibc.ld", "include/stdio.h") + if not os.path.isfile(os.path.join(directory, name))] + if missing: + report("fail", "%s lacks %s" % (variant, ", ".join(missing))) + else: + report("ok", "%s: crt0, libc, builtins, picolibc.ld, headers" + % variant) + + +def check_selection(name, flags, expected): + process = run(["clang"] + flags.split() + ["-print-multi-directory"]) + selected = process.stdout.strip() + warned = "no multilib found" in process.stderr + if expected is None: + if warned and not selected: + report("ok", "%s (%s): no variant, -Wmissing-multilib" + % (name, flags)) + else: + report("fail", "%s (%s): expected -Wmissing-multilib, got %r" + % (name, flags, selected)) + elif selected == expected and not warned: + report("ok", "%s (%s) -> %s" % (name, flags, selected)) + else: + report("fail", "%s (%s): expected %s, got %r%s" + % (name, flags, expected, selected, + ", -Wmissing-multilib" if warned else "")) + + +def link_flags(memory): + flash, flash_size, ram, ram_size = memory + return ["-O2", "-fuse-ld=lld", "-T", "picolibc.ld", + "-Wl,--defsym=__flash=" + flash, + "-Wl,--defsym=__flash_size=" + flash_size, + "-Wl,--defsym=__ram=" + ram, + "-Wl,--defsym=__ram_size=" + ram_size] + + +def qemu_machines(qemu): + process = run([qemu, "-M", "help"]) if qemu else None + if not process or process.returncode: + return set() + return {line.split()[0] for line in process.stdout.splitlines()[1:] + if line.strip()} + + +def check_program(name, flags, machine, memory, qemu, machines, directory): + flags = flags.split() + variant = run(["clang"] + flags + ["-print-multi-directory"]).stdout.strip() + output = re.sub(r"\W+", "-", name.lower()) + ".elf" + # crt0-semihost.o makes main()'s return value QEMU's exit status. + process = run(["clang"] + flags + link_flags(memory) + + ["-nostartfiles", + 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))) + return + if machine not in machines: + report("warn", "%s: linked, but not run: %s has no %s board" + % (name, qemu or "no qemu-system-arm", machine)) + return + process = run(["timeout", "30", qemu, "-M", machine, "-nographic", + "-monitor", "none", "-serial", "none", + "-semihosting-config", "enable=on,target=native", + "-kernel", output], cwd=directory) + # QEMU 11 writes the semihosting console to stderr unless it is a tty. + printed = process.stdout + process.stderr + 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 + printed.strip())) + else: + report("ok", "%s runs on %s with %s" % (name, machine, variant)) + + +def check_default_link(directory): + """Let clang add crt0.o, -lc and the builtins itself.""" + name = "default crt0, libc and builtins" + flags = ["--target=arm-none-eabi", "-mcpu=cortex-m4", "-mfloat-abi=hard"] + command = ["clang"] + flags + link_flags(MPS2) + \ + ["-o", "default.elf", "hello.c", "-lsemihost"] + process = run(command, cwd=directory) + if process.returncode: + report("fail", "%s: link failed: %s" % (name, last_line(process))) + return + variant = os.path.join(RUNTIMES, VARIANTS[3], "lib") + # clang spells the paths /usr/bin/../lib/clang-runtimes/... + arguments = set() + for line in run(command + ["-###"], cwd=directory).stderr.splitlines(): + if "ld.lld" in line: + arguments = {os.path.normpath(argument) + for argument in shlex.split(line)} + missing = [item for item in (variant + "/crt0.o", "-lc", + variant + "/libclang_rt.builtins.a") + if item not in arguments] + if missing: + report("fail", "%s: link line lacks %s" % (name, ", ".join(missing))) + else: + report("ok", "%s from %s" % (name, VARIANTS[3])) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--prefix", action="append", default=[], + metavar="DIR|PACKAGE", + help="test the image directory DIR of a build, or the" + " .bee.tar.bz2 PACKAGE, instead of what is" + " installed; may be repeated") + parser.add_argument("--qemu", default=shutil.which("qemu-system-arm"), + help="qemu-system-arm to run the programs with" + " (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(":", " ")) + + machines = qemu_machines(arguments.qemu) + if arguments.qemu: + version = run([arguments.qemu, "--version"]).stdout.splitlines() + print(" running with %s (%s)" + % (arguments.qemu, version[0] if version else "unknown version")) + + directory = tempfile.mkdtemp(prefix="baremetal-arm-smoke-") + try: + with open(os.path.join(directory, "hello.c"), "w") as source: + source.write(HELLO) + check_layout() + for name, flags, expected in SELECTIONS: + check_selection(name, flags, expected) + for name, flags, machine, memory in PROGRAMS: + check_program(name, flags, machine, memory, arguments.qemu, + machines, directory) + check_default_link(directory) + 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 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/smoke-tests/llvm-smoke-test b/scripts/smoke-tests/llvm-smoke-test index 252e4c854..23141297c 100755 --- a/scripts/smoke-tests/llvm-smoke-test +++ b/scripts/smoke-tests/llvm-smoke-test @@ -173,6 +173,7 @@ def run_in_overlay(lowers): 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: diff --git a/scripts/smoke-tests/mesa-smoke-test b/scripts/smoke-tests/mesa-smoke-test index 4017ef51c..dbe645413 100755 --- a/scripts/smoke-tests/mesa-smoke-test +++ b/scripts/smoke-tests/mesa-smoke-test @@ -111,6 +111,7 @@ def run_in_overlay(lowers): environment = dict(os.environ) environment[PREFIX_VARIABLE] = ":".join(lowers) command = ["unshare", "--user", "--map-root-user", "--mount", + "--propagation", "slave", "sh", "-c", "\n".join(setup), "sh", sys.executable, os.path.abspath(__file__)] + sys.argv[1:] try: diff --git a/scripts/smoke-tests/webkitgtk-smoke-test b/scripts/smoke-tests/webkitgtk-smoke-test index 3b921f294..c885650b1 100755 --- a/scripts/smoke-tests/webkitgtk-smoke-test +++ b/scripts/smoke-tests/webkitgtk-smoke-test @@ -478,6 +478,7 @@ ln -s %s /usr/libexec/%s exec "$@" """ % (shlex.quote(libexec), api["subdir"]) command = ["unshare", "--user", "--map-root-user", "--mount", + "--propagation", "slave", "sh", "-c", setup, "sh", sys.executable, os.path.abspath(__file__)] + sys.argv[1:] try: