home ~ socials ~ projects ~ rss

Output a Rust Binary to a Specific Architecture and Version Number Directory

August 2025

Doing It Locally

I'm working on my process for creating release binaries for my rust projects. I've seen how to do it with GitHub actions. That's fine. It's the Continuous Integration way to do things. It's also overkill for most of my work.

I don't want to have to make a GitHub repo for everything. I just want version specific binary files. I'm doing that using this cargo command:

cargo -Z unstable-options build --bins --release --artifact-dir "./releases/$(rustc --print host-tuple)/$(cargo metadata --no-deps | jq -r '.packages[0].version' | tr -d '\n')"

That results in directory structure that looks something like:

- releases/
  - aarch64-apple-darwin/
    - 0.1.2/
      - the-binary-file

- src/
  - {all the source code}

- target/
  - {compilation stuff}

The most important parts being that my binaries are in directories with version numbers and the binary is the only thing in that directory.

Bonus is that the target directory can be ignored and the binaries will still be included in the repo commit inside releases.

No Nightly Required

This approach uses the --artifact-dir parameter which currently requires using rust nightly and setting the -Z unstable-options flag. It's possible to do a similar approach using --target-dir from the mainline stable branch instead. It looks like this:

cargo build --bins --release --target-dir "./target/$(rustc --print host-tuple)/$(cargo metadata --no-deps | jq -r '.packages[0].version' | tr -d '\n')"

The big difference is that the binary file ends up in the same directory as all the compilation related files. The output directory path also changes a little to:

./target/aarch64-apple-darwin/0.1.2/release

Not a big deal. Just a little messier and would make putting the binary in the repo a little harder since you can't just allow the top level directory.

Getting Started

I've just started using this approach. So far, I'm loving it. A single command that provides a full, versioned archive of my binaries without having to mess with GitHub Actions or Releases is hard to beat.

- a

end of line

Endnotes

I don't run the command directly. I drop it in a bash script at the root of my projects called build-release that looks like this:

#!/bin/bash

cargo -Z unstable-options build --bins --release --artifact-dir "./releases/$(rustc --print host-tuple)/$(cargo metadata --no-deps | jq -r '.packages[0].version' | tr -d '\n')"

After doing a chmod u+x build-release I just run it when I'm ready to make the release binary.

There are various ways this could be automated, set up to have a dev direction, etc... TBD if I end up doing any of that stuff.

The version number is pulled from the Cargo.toml metadata file.

I also like that the releases directory sits with everything else in the directory tree view instead of being in the Releases section of the sidebar on GitHub. Makes it feel like it's more a part of the project.

I expect there will be some sorting weirdness if you have version numbers that jump an order of magnitude. e.g.

1.1.0
1.10.0
1.2.0

I'm fine with that as a minor annoyance trade off.

This is the process that I use on my mac.

The process works by piping output to two commands jq and tr. The first required an install. The second comes along with basic macOS.

I've seen a lot of back and forth about if you should put binaries in your repos or not. Some folks have Very Strong Opinions about it.

Personally, I like having the artifacts stored with the code. To each their own.

References

Share link:
https://www.alanwsmith.com/en/31/sg/ed/cd/?output-a-rust-binary-to-a-specific-architecture-and-version-number-directory