Wouldn't it be nice if e.g. cargo build --target arm-unknown-linux-gnueabi
Just Worked? Without having to install a C cross toolchain (e.g. arm-linux-gnueabi-gcc
), cross compiled standard crates (i.e. rustup target add
) or cross compiled C libraries (e.g. Glibc) on your system?
Well, I'm here to tell you that such thing might be possible and that cargo test --target arm-unknown-linux-gnueabi
and cargo run --target arm-unknown-linux-gnueabi
might Just Work as well.
Not with Cargo thought, but, instead, with a "transparent" Cargo wrapper like Xargo. Which I'm going to call "Dock".
Dock: ship your Rust code anywhere (*)
How will it work?
TL; DR You'll use it just like Cargo but it'll use Docker and QEMU under the hood.
When you call dock build --target arm-unknown-linux-gnueabi
inside a Cargo project, Dock will fetch a Docker image (from Docker Hub) that contains the C cross toolchain and cross compiled C libraries and then run your system Cargo (and rustc) inside a Docker container based on that image. This container will be destroyed right after the Cargo command ends. Oh, and if necessary Dock will also call rustup target add arm-unknown-linux-gnueabi
outside the container for you.
This ephemeral Docker container will have write access to your Cargo project's target
directory and your system Cargo registry. Note that plain Cargo also has write permissions like these. However, the Docker container will NOT have write access to the rest of your Cargo project (e.g. the src
directory) or any other directory. I believe this is a good idea because your Cargo build shouldn't touch any other part of your Cargo project that's not the target
directory (as that messes up Cargo "freshness" check).
dock test --target arm-unknown-linux-gnueabi
and dock run
will also make use of a Docker container and additionally use QEMU user emulation mode to transparently run cross compiled binaries.
About the Docker images
There will be one Docker image per cross compilation target. Each image will contain all the packages necessary to cross compile and test/run the cross compiled binaries.
For maximum portability of the cross compiled binaries, the Docker images will be based on the oldest stable Ubuntu version to keep the Glibc requirement of the cross compiled binaries as low as possible.
To increase the number of Cargo projects Dock can compile, the images will also contain extra cross compiled C libraries that are widely used by Cargo projects. For example: OpenSSL.
These images will be versioned using tags and Dock will make use of an image whose tag matches Dock's version (dock -V
). This way we'll be able to update the Docker images with each Dock release.
What if I need more cross compiled C libraries?
Dock will let you override the Docker images it uses via a Dock.toml file which it's local to each Cargo project:
[target.arm-unknown-linux-gnueabi]
# this image can be local or be hosted on Docker Hub
# if both exist, Dock will prefer local images
image = "user/image:tag"
It's recommended to base these images on the Docker images Dock would normally use.
FROM japaric/arm-unknown-linux-gnueabi:v0.1.0
RUN apt-get install -y --no-install-recommends packages more_packages
Will all of this actually work?
The "run Cargo inside a different Docker container for each target" approach have been battle tested for a long time in the libc repo and other repos.
The "use QEMU user emulation to transparently run cross compiled Rust programs/test suites" have been proven to work in the compiler-builtins repo.
The answer is: Yes, but someone has to sit down and put all the pieces together.
Dock 0.1.0 milestone will be: being able to dock build
Cargo for x86_64, i686 and ARM. The stretch goal will be being able to dock test
Cargo for the same architectures.
Caveats
-
Dock can only be invoked on x86_64 Linux (Note: you will be able o use Dock on a Travis worker)
-
(*) Dock can only cross compile/test Linux targets. (Targeting Android and MinGW might be possible too)
-
QEMU is good but not perfect and it may explode (read: segfault) while testing "some" Rust code.
-
If using
dock run
ordock test
for the first time (during your current session), you may need to enter your password because transparent QEMU emulation requires installing "binfmt interpreters" in/proc
and that requires root privilege. -
Lots of disk IO? Continuously creating and destroying Docker containers seems wasteful.
Untested, future enhancements
"Wait! It gets better?"
panic=abort
It may be possible to leverage some of Xargo logic to make Dock compile the standard crates (up to std) using the panic=abort codegen option thus making it possible to build binaries 100% free of landing pads (all the official binary releases of the 'std' component are compiled with panic=unwind and thus have landing pads in them). This should produce smaller and faster binaries.
It probably only makes sense to do this if profile.*.panic
have been set to "abort"
in the project's Cargo.toml.
Thoughts? Questions? Would you use something like this? IMO, this could simplify lots of CI setups that have to deal with several, non-native compilation targets.
(It's late; I'm going to answer questions/comments tomorrow)