Show HN: Cross-Platform GitHub Action (github.com)
I've created a GitHub Action for running commands on multiple platforms. This includes platforms that GitHub Actions don't natively support. It currently supports FreeBSD, OpenBSD and NetBSD. OpenBSD can run on x86-64 and ARM64, the other operating systems run on x86-64.
Some of the features that are supported include:
* Multiple operating system with one single action
* Multiple versions of each operating system
* Allows to use default shell or Bash shell
* Low boot overhead
* Fast execution
* Runs on both macOS and Linux runners
Compared to similar solutions like https://github.com/vmactions/freebsd-vm, the boot time is around a fifth and the full execution time for the same job is around half of freebsd-vm (last time I tried).
The readme contains more information about how it all works under the hood.
28 comments
[ 2.7 ms ] story [ 71.1 ms ] threadFor a while, the blocker in GitHub Actions for providing ARM support was that Azure doesn't have ARM support. In this way, the Azure cloud offering may determine the habits of AWS consumers.
Notably, I compile for CentOS 6 (ancient, right?) on whatever the latest version of Ubuntu is--using the most recent versions of clang and rust and whatever--and simply build my sysroot using this trivial script.
https://github.com/OrchidTechnologies/orchid/blob/6958658c25...
(You can do this in some sense even easier using Docker--though in other senses it is a lot more complex as now you need docker--if you are into that sort of thing. You don't run the compiler in docker: you just install the dependency packages and then docker export the filesystem as your sysroot.)
Using this script and clang I can actually compile to target CentOS 6 on macOS (and I get the exact same binary if I use a consistent version of clang; I actually have a GitHub action that verifies that I reproduce the same binary compiling on both macOS and Ubuntu).
We compile in a Docker container of the distro. It's the same concept as having a sysroot.
>as it makes having a consistent toolchain a lot more difficult.
>using the most recent versions of clang and rust and whatever
The external dependencies I mentioned are libraries like glibc or openssl, where you want to use the distro version. Similarly the packages should be consistent with what the users of the distro would build themselves if they used rpmbuild / debbuild / whatever. The toolchain not being consistent across distros is the point.
Also, none of this is relevant to cross-compiling for ARM. As I said, RHEL 7 doesn't have a cross compiler - specifically it has gcc but not glibc, because the cross compiler is only meant for compiling the kernel.
It isn't the same concept as using a sysroot, as you are now using the compiler from that potentially-ancient different distribution. I see people constantly twisting themselves into knots being like "CentOS has some broken/limited build of gcc/clang so I can't use X" and it is like "if there is one thing you as a developer control it is the compiler".
> The external dependencies I mentioned are libraries like glibc or openssl, where you want to use the distro version.
I understood this: that's what you put in your sysroot (and is easily seen in the example script I showed for CentOS 6; my scripts for building Ubuntu sysroots are much more complicated as I am trying to avoid nesting docker and so now have some crazy fallback involving debootstrap and proot).
> Similarly the packages should be consistent with what the users of the distro would build themselves if they used rpmbuild / debbuild / whatever.
Ok, this is where we differ, then: I am not trying to simultaneously support users building on CentOS using "rpmbuild". I want to be able to use the latest build of C++ and Rust and Python and essentially have a "modern"/easy development stack but "target"/support other distributions so they can install my software as packages.
Given the goal of being able to provide source code that can itself be compiled on these random distributions I see the problem. I personally feel like it would still be better to work around that by requesting (which can itself be automated by your build) that the user compile their own toolchain that has more functionality, though.
>I understood this: that's what you put in your sysroot
You're advocating mixing compilers and libraries that are not from the same distro release. While it may work for you it's generally a recipe for disaster.
The correct and least-mental-overhead way of building packages for $distro is to build on $distro. The fact that users of $distro can then build your package themselves using the standard distro method is an extra benefit.
What libraries on Linux don't use the SystemV C ABI on x86?
We've been happily compiling a single executable using ~latest clang for years with very few problems beyond using a symbol that doesn't exist on older distros.
So first, you said:
>>What libraries on Linux don't use the SystemV C ABI on x86?
... even though the conversation was about ARM.
Second, you seem to assume that there could be no other problems other than ABI, even though...
>with very few problems beyond using a symbol that doesn't exist on older distros.
... Oh, I shouldn't even need to explain it, because you're already aware of it. So why are you acting contrarian?
When you use gcc version X then it is allowed to generate calls to libgcc version X. If the distro has libgcc version Y, then either you put libgcc version Y in the sysroot and force gcc version X to use it, which gcc version X is not prepared for, or you let gcc version X link the binary to symbols from libgcc version X, and the binary explodes when running against libgcc version Y on the actual system.
Furthermore, if a distro ships gcc version Y and some distro library version Z, it expects you to compile code that uses distro library version Z with gcc version Y. If there are compiler bugs or library bugs, the distro might have patched one or the other to ensure that they work together. By instead compiling with gcc version X (from a different distro, no less) you're breaking that guarantee and again can have silent miscompilations or other runtime issues.
I'm amazed I even have to argue about such basic knowledge. If you don't believe me, maybe you'll believe OSDev?
https://wiki.osdev.org/Libgcc
>Can I use the Linux libgcc?
>You must use the correct libgcc that came with your cross-compiler. Whatever else libgcc you found likely has a different target, was built with different machine compile options, has dependencies on the standard library, is part of a different compiler revision (your distribution may have patched its gcc, even). It is possible that using a different libgcc will work, but perhaps not reliably.
So congratulations on having YOLO'd with no problems "for years", but doing it properly is much easier for correctness and peace of mind, so excuse me if I don't follow suit.
Apologies, I wanted to be specific which standard is being followed. Nonetheless afaik the ABI is the same for all arm distros.
>> with very few problems beyond using a symbol that doesn't exist on older distros. > ... Oh, I shouldn't even need to explain it, because you're already aware of it.
A missing symbol is a problem regardless of where you're compiling. If you're linking against something that doesn't exist it'll fail; whether that's on your client's machine running an old distro or when you're compiling on that old distro yourself. You could argue it's better to fail early here, but the work in fixing this bug is approximately the same.
> I'm amazed I even have to argue about such basic knowledge. If you don't believe me, maybe you'll believe OSDev?
OSDev is incorrect here. Take it from GNU themselves: https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html. Symbols in libgcc are versioned; either your code runs and links against the exact correct version or it fails. If you want to run on old distros with libgccs that don't have newer symbols either use an older gcc, statically link libgcc or use clang.
Did you mean cross-compilations for compiled languages? It doesn't fit Python.
That is, unless you use Docker's cross platform build capability (buildx). Which was still considered experimental the last time I looked at it (about a year ago).
Or you can register manually by writing to /proc/sys/fs/binfmt_misc/register . Note that you'll want to register the statically linked version of qemu (qemu-user-static or whatever your distro calls it), and that you'll want to use at least the O and F flags so that the binary works inside containers automatically insead of needing to be mounted from the host.
Note that this can screw up other kinds of builds, as the autoconf check for cross-compilation relies on a cross-compiled executable being unrunnable. (Seen with a wine binfmt handler and a MinGW cross compiler.)
Behind the scenes we are running the same engine as Docker, BuildKit, on VMs in AWS for both architectures. We combine native architecture support with a 50GB persistent SSD cache that is automatically available across builds, so no more saving/loading cache over the network.
All those things combined makes multi-platform or Arm image builds in GitHub Actions ~15x faster using Depot (example: https://depot.dev/benchmark/temporal). And it's a one line change, swap `docker build` for `depot build` or use our `depot/build-push-action` instead of `docker/build-push-action`.
Here is my direct email if anyone wants to chat/learn/share more about this problem space, kyle [at] depot.dev.
GitHub has Apple Silicon on the roadmap, but IIRC it was at the end of this year. They already support Apple Silicon for self hosted runners [2].
[1] https://github.com/jacob-carlborg/lime/blob/f4d9c8c4265b61b2...
[2] https://github.blog/changelog/2022-08-09-github-actions-self...