Execute a command for the current directory on multiple architectures and operating systems.
Hydra Run, or hydrun, is a thin (<200 SLOC) layer atop Docker buildx and qemu-user-static. It allows one to easily execute a command on different processor architectures and operating systems than the host.
It can, for example, be used for ...
- Cross-compilation that "just works", without having to set up a cross-compiler (at the cost of longer build times)
- Multi-architecture testing
- Building arm64 binaries on GitHub actions, which doesn't support arm64 runners or Linux distros other than Ubuntu
- Quickly getting an interactive arm64 shell for the current directory on an amd64 host or the other way round
- Running binaries built against glibc on an Alpine Linux host
- Making CI release builds locally reproducable and testable, without having to
git pushand wait
Static binaries are also available on GitHub releases.
On Linux, you can install them like so:
$ curl -L -o /tmp/hydrun "https://github.com/pojntfx/hydrun/releases/latest/download/hydrun.linux-$(uname -m)"
$ sudo install /tmp/hydrun /usr/local/binOn macOS, you can use the following:
$ curl -L -o /tmp/hydrun "https://github.com/pojntfx/hydrun/releases/latest/download/hydrun.darwin-$(uname -m)"
$ sudo install /tmp/hydrun /usr/local/binOn Windows, the following should work (using PowerShell as administrator):
PS> Invoke-WebRequest https://github.com/pojntfx/hydrun/releases/latest/download/hydrun.windows-x86_64.exe -OutFile \Windows\System32\hydrun.exeYou can find binaries for more operating systems and architectures on GitHub releases.
Before continuing, please ensure that you have both Docker buildx and qemu-user-static installed on the host.
As described in the Reference, you can get an interactive shell by using the -i flag. The -a parameter corresponds to an architecture such as amd64, arm64 or ppc64le; the -o flag corresponds to a Docker image such as debian, alpine or fedora. To for example run arm64 Debian on an amd64 host, you can do the following:
$ uname -a
Linux dev-tmp 4.19.0-10-cloud-amd64 #1 SMP Debian 4.19.132-1 (2020-07-24) x86_64 GNU/Linux
$ hydrun -a arm64 -o debian -i "bash"
root@81647bd6aa02:/data# uname -a
Linux 81647bd6aa02 4.19.0-10-cloud-amd64 #1 SMP Debian 4.19.132-1 (2020-07-24) aarch64 GNU/Linux
root@81647bd6aa02:/data# ldd $(which ls)
libselinux.so.1 => /lib/aarch64-linux-gnu/libselinux.so.1 (0x0000005501868000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x000000550189e000)
/lib/ld-linux-aarch64.so.1 (0x0000005500000000)
libpcre.so.3 => /lib/aarch64-linux-gnu/libpcre.so.3 (0x0000005501a10000)
libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000005501a83000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000005501a97000)It is very easy to use hydrun to get binaries for many platforms, without having to set up cross-compilation. Consider the following C hello world program:
/* main.c */
#include <stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}Using hydrun, we can now compile it for multiple architectures:
$ ls
ls
main.c
$ hydrun -a amd64,arm64 -o gcc 'gcc -static -o hello-world.linux-$(uname -m) main.c'
$ ls
hello-world.linux-aarch64 hello-world.linux-x86_64 main.c
$ file *
hello-world.linux-aarch64: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.7.0, with debug_info, not stripped
hello-world.linux-x86_64: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, with debug_info, not stripped
main.c: C source, ASCII textIt is also possible to run/test the compiled binaries with it:
$ ls
hello-world.linux-aarch64 hello-world.linux-x86_64 main.c
$ hydrun -i -a arm64 "./hello-world.linux-aarch64"
Hello, world!
$ hydrun -i -a amd64 "./hello-world.linux-x86_64"
Hello, world!When building larger projects or requiring dependencies, it is recommended to put the commands used into a shell script, conventionally named the Hydrunfile:
$ cat <<EOT>Hydrunfile
#!/bin/bash
apt update
apt install -y build-essentials
gcc -static -o hello-world.linux-$(uname -m) main.c
EOT
$ chmod +x ./Hydrunfile
You can now easily build like so:
$ hydrun -a amd64,arm64 "./Hydrunfile"
$ ls
hello-world.linux-aarch64 hello-world.linux-x86_64 Hydrunfile main.cMost of the time you'll probably want to use the Hydrunfile to install toolchains/dependencies and then call your Makefile, the Go compiler, cargo etc.
For an example, check out pojntfx/panrpc.
It is also possible to use hydrun to build multi-architecture binaries in a CI/CD system such as GitHub actions. Continuing with the C example from above, we could automatically build and release amd64 and arm64 binaries to GitHub releases using GitHub actions with the following workflow:
name: hydrun CI
on:
push:
pull_request:
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up hydrun
run: |
curl -L -o /tmp/hydrun https://github.com/pojntfx/hydrun/releases/latest/download/hydrun.linux-$(uname -m)
sudo install /tmp/hydrun /usr/local/bin
- name: Build with hydrun
run: hydrun -a amd64,arm64 ./Hydrunfile
- name: Publish to GitHub releases
if: ${{ github.ref == 'refs/heads/main' }}
uses: marvinpinto/action-automatic-releases@latest
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: false
files: |
*.linux*For an example, check out pojntfx/panrpc.
$ hydrun --help
Execute a command for the current directory on multiple architectures and operating systems.
See https://github.com/pojntfx/hydrun for more information.
Usage: hydrun [OPTION...] "<COMMAND...>"
-a, --arch string Comma-separated list of architectures to run on (default "amd64")
-c, --context string Directory to use in the container (default is the current working directory)
-e, --extra-args string Extra arguments to pass to the Docker command
-i, --it Attach stdin and setup a TTY
-j, --jobs int Maximum amount of parallel jobs (default 1)
-m, --mount Enable mounting the directory specified with the context flag (default true)
-o, --os string Comma-separated list of operating systems (Docker images) to run on (default "debian")
-p, --pull Always pull the specified tags of the operating systems (Docker images)
-q, --quiet Disable logging executed commands
-r, --readyOnly Mount the directory specified as read-onlyTo contribute, please use the GitHub flow and follow our Code of Conduct.
To build hydrun locally, run:
$ git clone https://github.com/pojntfx/hydrun.git
$ cd hydrun
$ go run main.go --helpHave any questions or need help? Chat with us on Matrix!
If you want to quickly cross-compile your Go app, check out bagop!
hydrun (c) 2024 Felicitas Pojtinger and contributors
SPDX-License-Identifier: AGPL-3.0