Some checks failed
Lint / Check that code is lint clean using precious (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:false can-execute:true cross-version:19be834 expect-cross:--expect-cross expect-cross-version:19be834 expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 with cross 19be834 (no cache) runs-on:ubuntu-24.04 targ… (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:false can-execute:true cross-version:v0.2.3 expect-cross:--expect-cross expect-cross-version:0.2.3 expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 with cross v0.2.3 (no cache) runs-on:ubuntu-24.04 target:a… (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:false can-execute:true expect-cross:--expect-cross expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 (no cache) runs-on:ubuntu-24.04 target:aarch64-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:false expect-cross: expect-file-re:Mach-O.+arm64 expect-stripped:--expect-stripped name:macOS-aarch64 runs-on:macOS-latest target:aarch64-apple-darwin]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:false expect-cross: expect-file-re:Windows.+ARM64 expect-stripped: name:Windows-aarch64 runs-on:windows-latest target:aarch64-pc-windows-msvc]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:false expect-cross:--expect-cross expect-file-re:x86-64.+FreeBSD expect-stripped: name:FreeBSD-x86_64 runs-on:ubuntu-24.04 target:x86_64-unknown-freebsd]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:false expect-cross:--expect-cross expect-file-re:x86-64.+NetBSD expect-stripped: name:NetBSD-x86_64 runs-on:ubuntu-24.04 target:x86_64-unknown-netbsd]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true cross-version:19be834 expect-cross:--expect-cross expect-cross-version:19be834 expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 with cross 19be834 runs-on:ubuntu-24.04 target:aarch64-u… (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true cross-version:v0.2.3 expect-cross:--expect-cross expect-cross-version:0.2.3 expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 with cross v0.2.3 runs-on:ubuntu-24.04 target:aarch64-unkno… (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:ELF.+x86-64 expect-stripped:--expect-stripped name:Linux-x86_64 (beta) runs-on:ubuntu-24.04 target:x86_64-unknown-linux-musl toolchain:beta]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:ELF.+x86-64 expect-stripped:--expect-stripped name:Linux-x86_64 (nightly) runs-on:ubuntu-24.04 target:x86_64-unknown-linux-musl toolchain:nightly]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:ELF.+x86-64 expect-stripped:--expect-stripped name:Linux-x86_64 (stable 8 months ago) runs-on:ubuntu-24.04 target:x86_64-unknown-linux-gnu toolchain:stable 8 months … (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:ELF.+x86-64 expect-stripped:--expect-stripped name:Linux-x86_64 runs-on:ubuntu-24.04 target:x86_64-unknown-linux-musl]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:Mach-O.+x86_64 expect-stripped:--expect-stripped name:macOS-x86_64 runs-on:macOS-latest target:x86_64-apple-darwin]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:Windows.+Intel i386 expect-stripped:--expect-stripped name:Windows-i686 runs-on:windows-latest target:i686-pc-windows-msvc]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross: expect-file-re:aarch64 expect-stripped:--expect-stripped name:Linux-aarch64 with arm64 host runs-on:ubuntu-24.04-arm target:aarch64-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:32.+ARM expect-stripped: name:Linux-arm runs-on:ubuntu-24.04 target:arm-unknown-linux-gnueabi]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:32.+PowerPC expect-stripped: name:Linux-powerpc runs-on:ubuntu-24.04 target:powerpc-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:64.+PowerPC expect-stripped: name:Linux-powerpc64 runs-on:ubuntu-24.04 target:powerpc64-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:64.+PowerPC expect-stripped: name:Linux-powerpc64le runs-on:ubuntu-24.04 target:powerpc64le-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:64.+RISC-V expect-stripped: name:Linux-riscv64 runs-on:ubuntu-24.04 target:riscv64gc-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:64.+S/390 expect-stripped: name:Linux-s390x runs-on:ubuntu-24.04 target:s390x-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:ELF.+80386 expect-stripped: name:Linux-i586 runs-on:ubuntu-24.04 target:i586-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:ELF.+80386 expect-stripped: name:Linux-i686 runs-on:ubuntu-24.04 target:i686-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:ELF.+x86-64 expect-stripped: force-use-cross:true name:Linux-x86_64 (force cross) runs-on:ubuntu-24.04 target:x86_64-unknown-linux-gnu]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-cross:--expect-cross expect-file-re:aarch64 expect-stripped: name:Linux-aarch64 runs-on:ubuntu-24.04 target:aarch64-unknown-linux-musl]) (push) Has been cancelled
Self-test / Test action (map[cache-cross-binary:true can-execute:true expect-file-re:Windows.+x86-64 expect-stripped:--expect-stripped name:Windows-x86_64 runs-on:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
Self-test / Test validate-inputs (push) Has been cancelled
Self-test / Test a "cargo foo" command (push) Has been cancelled
204 lines
12 KiB
Markdown
204 lines
12 KiB
Markdown
# GitHub Action to Cross Compile Rust Projects
|
|
|
|
This action lets you easily cross-compile Rust projects using
|
|
[cross](https://github.com/cross-rs/cross).
|
|
|
|
Here's a simplified example from the test and release workflow for
|
|
[my tool `ubi`](https://github.com/houseabsolute/ubi):
|
|
|
|
```yaml
|
|
jobs:
|
|
release:
|
|
name: Release - ${{ matrix.platform.os-name }}
|
|
strategy:
|
|
matrix:
|
|
platform:
|
|
- os-name: FreeBSD-x86_64
|
|
runs-on: ubuntu-24.04
|
|
target: x86_64-unknown-freebsd
|
|
|
|
- os-name: Linux-x86_64
|
|
runs-on: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
|
|
- os-name: Linux-aarch64
|
|
runs-on: ubuntu-24.04
|
|
target: aarch64-unknown-linux-musl
|
|
|
|
- os-name: Linux-riscv64
|
|
runs-on: ubuntu-24.04
|
|
target: riscv64gc-unknown-linux-gnu
|
|
|
|
- os-name: Windows-x86_64
|
|
runs-on: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
|
|
- os-name: macOS-x86_64
|
|
runs-on: macOS-latest
|
|
target: x86_64-apple-darwin
|
|
|
|
# more targets here ...
|
|
|
|
runs-on: ${{ matrix.platform.runs-on }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
- name: Build binary
|
|
uses: houseabsolute/actions-rust-cross@v1
|
|
with:
|
|
command: build
|
|
target: ${{ matrix.platform.target }}
|
|
args: "--locked --release"
|
|
strip: true
|
|
- name: Publish artifacts and release
|
|
uses: houseabsolute/actions-rust-release@v0
|
|
with:
|
|
executable-name: ubi
|
|
target: ${{ matrix.platform.target }}
|
|
```
|
|
|
|
Note that for Linux or BSD targets, you should always set the `runs-on` key to a Linux x86-64
|
|
architecture runner.
|
|
|
|
If you _only_ want to do native ARM compilation, for example using the `ubuntu-24.04-arm` runner,
|
|
then there's no need to use this action. However, if you want to compile for _many_ platforms,
|
|
including Linux ARM, using this action will simplify your config. This action is only tested on
|
|
Ubuntu x86-64, Windows, and macOS runners.
|
|
|
|
## Input Parameters
|
|
|
|
This action takes the following parameters:
|
|
|
|
| Key | Type | Required? | Description |
|
|
| ----------------------- | --------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `command` | string | no | The command(s) to run. The default is `build`. Running the `test` command will fail with \*BSD targets and non-x86 Windows. You can use any command supported by `cargo` and/or `cross`. For example, if you install `cargo-deb`, the command can be `deb`. Use the special string "both" to run both `build` and `test. |
|
|
| `target` | string | yes | The target triple to compile for. This should be one of the targets found by running `rustup target list`. |
|
|
| `working-directory` | string | no | The working directory in which to run the `cargo` or `cross` commands. Defaults to the current directory (`.`). |
|
|
| `toolchain` | string) | no | The Rust toolchain version to install. This is passed directly to [dtolnay/rust-toolchain](https://github.com/dtolnay/rust-toolchain), which accepts many different options. See its documentation for more details. The default is `stable`. |
|
|
| `GITHUB_TOKEN` | string | no | Defaults to the value of `${{ github.token }}`. |
|
|
| `args` | string | no | A string-separated list of arguments to be passed to `cross build`, like `--release --locked`. |
|
|
| `strip` | boolean (`true` or `false`) | no | If this is true, then the resulting binaries will be stripped if possible. This is only possible for binaries which weren't cross-compiled. |
|
|
| `cross-version` | string | no | This can be used to set the version of `cross` to use. If specified, it should be a specific `cross` release tag (like `v0.2.3`) or a git ref (commit hash, `HEAD`, etc.). If this is not set then the latest released version will always be used. If this is set to a git ref then the version corresponding to that ref will be installed. |
|
|
| `force-use-cross` | boolean (`true` or `false`) | no | If this is true, then the action will use `cross` even if it is not needed for the given target. If this is set to `true`, then the resulting binary will not be stripped, regardless of whether `strip` is `true` or not. This only works on Linux hosts. Forcing the use of `cross` on other hosts is not supported. |
|
|
| `use-rust-cache` | boolean | no | Whether or not to use [the `Swatinem/rust-cache@v2` action](https://github.com/Swatinem/rust-cache). This defaults to true. |
|
|
| `rust-cache-parameters` | string (containing JSON) | no | This must be a string containing valid JSON. The JSON should be an object where the keys are the parameters for [the `Swatinem/rust-cache@v2` action](https://github.com/Swatinem/rust-cache). |
|
|
|
|
### Setting Environment Variables
|
|
|
|
By default, `cross` passes most rust-related environment variables through when it runs `cargo` in a
|
|
Docker image. This means you can simply set an `env` key in the workflow step that uses this action.
|
|
|
|
```
|
|
- name: Run build command
|
|
uses: houseabsolute/actions-rust-cross@v1
|
|
env:
|
|
CARGO_LOG: debug
|
|
RUSTFLAGS: "-g"
|
|
with:
|
|
command: build
|
|
```
|
|
|
|
If you want to pass other environment variables through, you will need to configure `cross` to do
|
|
see. See the
|
|
[`cross` docs](https://github.com/cross-rs/cross/blob/main/docs/environment_variables.md#environment-variable-passthrough)
|
|
for more details.
|
|
|
|
## How it Works
|
|
|
|
Under the hood, this action will compile your binaries with either `cargo` or `cross`, depending on
|
|
the host machine and target. For Linux builds, it will always use `cross` except for builds
|
|
targeting an x86 architecture like `x86_64` or `i686`.
|
|
|
|
On Windows and macOS, it's possible to compile for all supported targets out of the box, so `cross`
|
|
will not be used on those platforms.
|
|
|
|
If it needs to install `cross`, it will install the latest version by downloading a release using
|
|
[my tool `ubi`](https://github.com/houseabsolute/ubi). This is much faster than using `cargo` to
|
|
build `cross`.
|
|
|
|
When compiling on Windows, it will do so in a Powershell environment, which can matter in some
|
|
corner cases, like compiling the `openssl` crate with the `vendored` feature.
|
|
|
|
When running `cargo` on a Linux system, it will also include the output of running
|
|
`lsb_release --short --description` in the cache key. This is important for crates that link against
|
|
system libraries. If those library versions change across OS versions (e.g. Ubuntu 20.04 to 22.04),
|
|
then the cache will be broken for these cases.
|
|
|
|
When running `cross`, the hash of the `cross` binary will be included in the cache key. This is done
|
|
because the Docker images that `cross` uses can change when `cross` is updated. We want to make sure
|
|
that we do not re-use the cache across changes when these images change.
|
|
|
|
Finally, it will run `strip` to strip the binaries it builds if the `strip` parameter is true. This
|
|
is only possible for builds that are not done via `cross`. In addition, Windows builds for `aarch64`
|
|
cannot be stripped either.
|
|
|
|
### Caching
|
|
|
|
By default, this action will use
|
|
[the `Swatinem/rust-cache@v2` action](https://github.com/Swatinem/rust-cache) to cache compiled
|
|
dependencies for a crate. Note that per the documentation for the `rust-cache` action, it has fairly
|
|
limited value for crates without a `Cargo.lock` file. The `key` parameter passed to this action will
|
|
always include the value of the `target` input. If you specify a `key` parameter in
|
|
`rust-cache-parameters`, then the `target` input will be appended to the value you specify.
|
|
|
|
#### Weird Caching Issue with Multiple Crates
|
|
|
|
In my testing, it seemed like in some cases restoring the cache would delete existing files in a
|
|
`target` directory. This manifested with this sequence of actions:
|
|
|
|
1. Run `actions-rust-cross` to compile a crate in a top-level directory.
|
|
2. Run `actions-rust-cross` to compile a crate in a subdirectory.
|
|
|
|
After step 2, the compiled binaries from step 1 were no longer present, _sometimes_. I'm not sure
|
|
exactly what's going on here, but my recommendation is to structure your workflows so that this
|
|
cannot affect you.
|
|
|
|
For example, if you have multiple crates, each of which builds a binary you want to release, then
|
|
you can avoid this issue by structuring your workflow as follows:
|
|
|
|
1. Run `actions-rust-cross` to compile crate A.
|
|
2. Run the release steps for crate A.
|
|
3. Run `actions-rust-cross` to compile crate B.
|
|
4. Run the release steps for crate B.
|
|
|
|
When structured this way, it does not matter if the output of crate A is deleted in step 3.
|
|
|
|
## Cross-Compiling from Linux ARM Runners
|
|
|
|
In theory, this should work, and this action does implement some of the necessary work for this.
|
|
However, there are a couple issues with this:
|
|
|
|
1. As of 2025-02-17, the `cross` project does not publish Linux ARM binary releases. That means that
|
|
in order to use `cross` on a Linux ARM runner as part of this action, you must set
|
|
`cross-version` to a more recent commit from the `cross` repo.
|
|
2. There is
|
|
[a bug in `cross` that means you must use a custom Docker image](https://github.com/cross-rs/cross/issues/1628)
|
|
when cross-compiling from a Linux ARM runner. See
|
|
[this other `cross` issue](https://github.com/cross-rs/cross/issues/751) for more details.
|
|
|
|
## Linting and Tidying this Code
|
|
|
|
The code in this repo is linted and tidied with
|
|
[`precious`](https://github.com/houseabsolute/precious). This repo contains a `mise.toml` file.
|
|
[Mise](https://mise.jdx.dev/) is a tool for managing dev tools with per-repo configuration. You can
|
|
install `mise` and use it to run `precious` as follows:
|
|
|
|
```
|
|
# Installs mise
|
|
curl https://mise.run | sh
|
|
# Installs precious and other dev tools
|
|
mise install
|
|
```
|
|
|
|
Once this is done, you can run `precious` via `mise`:
|
|
|
|
```
|
|
# Lints all code
|
|
mise exec -- precious lint -a
|
|
# Tidies all code
|
|
mise exec -- precious tidy -a
|
|
```
|
|
|
|
If you want to use `mise` for other projects, see [its documentation](https://mise.jdx.dev/) for
|
|
more details on how you can configure your shell to always activate `mise`.
|