|
| 1 | +# ******************************************************************************* |
| 2 | +# Copyright (c) 2026 Contributors to the Eclipse Foundation |
| 3 | +# |
| 4 | +# See the NOTICE file(s) distributed with this work for additional |
| 5 | +# information regarding copyright ownership. |
| 6 | +# |
| 7 | +# This program and the accompanying materials are made available under the |
| 8 | +# terms of the Apache License Version 2.0 which is available at |
| 9 | +# https://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# SPDX-License-Identifier: Apache-2.0 |
| 12 | +# ******************************************************************************* |
| 13 | + |
| 14 | +""" |
| 15 | +Rule for creating a QEMU user-mode emulation wrapper script. |
| 16 | +
|
| 17 | +QEMU user-mode emulation allows running Linux executables compiled for one CPU |
| 18 | +architecture on a host with a different CPU architecture (e.g. running aarch64 |
| 19 | +binaries on an x86_64 host). The generated script is intended to be used with |
| 20 | +Bazel's --run_under flag to transparently execute cross-compiled test binaries. |
| 21 | +
|
| 22 | +The sysroot path is passed to QEMU via the -L flag so that the emulator can |
| 23 | +locate the target architecture's shared libraries at runtime. |
| 24 | +
|
| 25 | +Prerequisites: |
| 26 | + The QEMU user-mode emulator for the target architecture must be installed on |
| 27 | + the host system and available on $PATH. For example, to emulate aarch64 |
| 28 | + binaries on Debian/Ubuntu, install the ``qemu-user-static`` package. |
| 29 | +""" |
| 30 | + |
| 31 | +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") |
| 32 | + |
| 33 | +def _qemu_emulator_wrapper_impl(ctx): |
| 34 | + # Get sysroot from explicit attr or from the CC toolchain |
| 35 | + if ctx.attr.sysroot: |
| 36 | + sysroot = ctx.attr.sysroot[DefaultInfo].files.to_list()[0].path |
| 37 | + else: |
| 38 | + cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] |
| 39 | + sysroot = cc_toolchain.sysroot |
| 40 | + |
| 41 | + script = ctx.actions.declare_file(ctx.label.name) |
| 42 | + ctx.actions.write( |
| 43 | + output = script, |
| 44 | + content = """#!/bin/bash |
| 45 | +set -euo pipefail |
| 46 | +# Resolve symlinks to find the real execroot, then locate the sysroot. |
| 47 | +REAL_SCRIPT="$(readlink -f "$0")" |
| 48 | +EXECROOT="${{REAL_SCRIPT%/bazel-out/*}}" |
| 49 | +exec qemu-{arch} -L "$EXECROOT/{sysroot}" "$@" |
| 50 | +""".format( |
| 51 | + arch = ctx.attr.arch, |
| 52 | + sysroot = sysroot, |
| 53 | + ), |
| 54 | + is_executable = True, |
| 55 | + ) |
| 56 | + |
| 57 | + return [DefaultInfo(executable = script)] |
| 58 | + |
| 59 | +qemu_emulator_wrapper = rule( |
| 60 | + implementation = _qemu_emulator_wrapper_impl, |
| 61 | + attrs = { |
| 62 | + "arch": attr.string( |
| 63 | + mandatory = True, |
| 64 | + doc = "Target CPU architecture (e.g. 'aarch64').", |
| 65 | + ), |
| 66 | + "sysroot": attr.label( |
| 67 | + mandatory = False, |
| 68 | + doc = "Label pointing to the sysroot directory that is passed to QEMU. If not provided, uses the CC toolchain's sysroot.", |
| 69 | + ), |
| 70 | + "_cc_toolchain": attr.label( |
| 71 | + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), |
| 72 | + ), |
| 73 | + }, |
| 74 | + executable = True, |
| 75 | + doc = "Generates a wrapper script for running tests under qemu with the correct sysroot.", |
| 76 | +) |
| 77 | + |
| 78 | +def qemu_aarch64(name = "qemu_aarch64", sysroot = None, **kwargs): |
| 79 | + """Convenience macro for creating a QEMU aarch64 emulator wrapper. |
| 80 | +
|
| 81 | + Args: |
| 82 | + name: Optional name of the wrapper target. |
| 83 | + sysroot: Optional sysroot label. |
| 84 | + **kwargs: Additional arguments passed to qemu_emulator_wrapper. |
| 85 | + """ |
| 86 | + qemu_emulator_wrapper( |
| 87 | + name = name, |
| 88 | + arch = "aarch64", |
| 89 | + sysroot = sysroot, |
| 90 | + **kwargs |
| 91 | + ) |
0 commit comments