Problem
On a musl based linux system , cross compilation will fail when using proc-macro
Steps
1.Cargo.toml:
[package]
name = "demo"
version = "0.1.0"
edition = "2018"
[dependencies]
serde= { version = "1.0", features = ["derive"] }
2.src/main.rs:
fn main(){}
3.
$ RUSTFLAGS="-C target-feature=-crt-static" cargo build --target=wasm32-wasi
error: cannot produce proc-macro for `serde_derive v1.0.102` as the target `x86_64-unknown-linux-musl` does not support these crate types
Possible Solution(s)
Problem is : https://github.com/rust-lang/cargo/blob/master/src/cargo/core/compiler/build_context/target_info.rs#L406
proc-macro can't build without RUSTFLAGS=-C target-feature=-crt-static
Notes
Output of cargo version: cargo 1.40.0-nightly (5da4b4d47 2019-10-28)
AFAIK, rustc musl host fundamentally does not support proc-macros. Some more information may be found at https://github.com/rust-lang/rust/issues/40174.
Musl target does support proc-macros.
The issue here is proc-macros are dylibs and Rust doesn't support linking dylibs to static CRT on targets other than Windows. Historically musl target links static CRT by default so one has to disable it with target-feature=-crt-static to get proc-macro. Binary will be build dynamically linked so it requires musl to be installed on the target to run (it loses portability).
Oh, I think I see. I've never been able to get rustc musl to work on any system. Do you know, is there any information on how to get it to work? Whatever I've tried, I get all sorts of errors (symbol not found, libgcc_s.so.1 not found, etc) just running rustc -V.
@ehuss you need musl compiler (musl-gcc) in the path to build for musl.
Whatever I've tried, I get all sorts of errors (symbol not found, libgcc_s.so.1 not found, etc) just running rustc -V.
You have probably installed musl host toolchain on glibc based linux, it's possible to run it there but sketchy. You'd have more luck running it on Alpine Linux or musl variant Void Linux.
Slightly offtopic but I want you to understand what is going on:
You can cross compile code from OP on linux-gnu to linux-musl or wasi just fine because proc macro runs on linux-gnu and get static binary.
Compiling it on linux-musl for any target won't work out of the box because user have to disable crt-static. I think it won't hurt for wasi but this prevents user from creating static binary on musl whenever proc-macros are involved.
I don't know how to fix it, generally proc-macros always have to be compiled dynamically for host assuming it supports dylibs. Probably this has to be fixed in rustc itself but I don't remember that code well enough to tell for sure.
You have probably installed musl host toolchain on glibc based linux, it's possible to run it there but sketchy. You'd have more luck running it on Alpine Linux or musl variant Void Linux.
On a fresh install of Alpine Linux, I get the exact same errors as other distros trying to run rustc for stable-x86_64-unknown-linux-musl: https://gist.github.com/ehuss/57a81703bdfb4abcd64ddfbdc4d371c0
I just tried with the latest nightly, with the same result. Any ideas what I'm missing? This is using rustup to install. Do I need to install a special package? Are there dependencies I need to install?
Maybe a bit overkill but installing g++ or build-base should give you everything you need.
On a fresh install of Alpine Linux
Using container in Docker/Podman is enough to try it BTW.
Ah, adding g++ did the trick, thanks!
Just ran into this and want to make sure I'm understanding this correctly -- it sure seems like it's impossible to build a static binary (or at least the closest we can get to it on linux w/ musl) if you have any dependency that uses macros?
It seems like the only workaround is to dynamically link libgcc...
You don't need to use a MUSL host toolchain to target MUSL.
~/.cargo/config
[build]
rustflags = "-C target-feature=-crt-static"
OR
$>RUSTFLAGS="-C target-feature=-crt-static" cargo build
Thanks @paul-sedun/@sfackler
Maybe I wasn't clear about my misunderstanding -- it wasn't whether you needed musl installed or how to specifically to do it, but more about the fact if you choose to do use crt-static you must then the generated binary must at runtime be dynamically linked.
I'm going directly off what @mati865 said (reproduced below):
The issue here is proc-macros are dylibs and Rust doesn't support linking dylibs to static CRT on targets other than Windows. Historically musl target links static CRT by default so one has to disable it with target-feature=-crt-static to get proc-macro. Binary will be build dynamically linked so it requires musl to be installed on the target to run (it loses portability).
If I'm reading this correctly, then it means that at the moment Rust cannot generate the closest thing we have to a static binary on linux if the project being built uses macros.
[EDIT] - Just trying to make sure this is clear but I'm not trying to slight rust here, just want to make sure that I arrived at the right conclusion.
Compiling binary dynamically linked to musl on musl host (need RUSTFLAGS set) or binary statically linked to musl on glibc host will works.
This issue is about the fact that, when a crate use proc-macro, on a musl host
(1) we can't cross compile it for other targets
(2) we can't compile it statically linked to musl.
We can't do (1) because cargo set RUSTFLAGS to empty when compile proc-macro.
we can't do (2) because it will let proc-macro compile statically, which is wrong.
The problem is cargo don't have a concept about HOSTRUSTFLAGS that only apply to build script and proc-macro, but the behavior of crt-static is control by RUSTFLAGS.
However, RUSTFLAGS isn't a out of box solution. On every platform that have rustc built and runs, proc-macro is compiled dynamically, so the better solution is rustc don't check whether a target can compile proc-macro (as it always can), and compile it dynamically directly
Thank you for the explanation, I think I understand now!
I hit the same error message, and here's what worked for me to build a Rust Rocket app in a Dockerfile using Alpine and Rust nightly. I welcome feedback -- this is my first time trying Docker + Rust nightly + Alpine musl.
FROM alpine:latest as builder-system
RUN apk update
RUN apk add binutils build-base ca-certificates curl file g++ gcc libressl-dev make patch rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
RUN . ~/.cargo/env && rustup toolchain install nightly && rustup default nightly
RUN . ~/.cargo/env && rustup target add x86_64-unknown-linux-musl
WORKDIR /usr/src/demo_rust_rocket
COPY Cargo.toml Cargo.toml
RUN mkdir src/
RUN echo "fn main() {println!(\"if you see this, the build broke\")}" > src/main.rs
RUN . ~/.cargo/env && RUSTFLAGS="-C linker=musl-gcc -C target-feature=-crt-static" cargo build --release --target=x86_64-unknown-linux-musl
Most helpful comment
Compiling binary dynamically linked to musl on musl host (need RUSTFLAGS set) or binary statically linked to musl on glibc host will works.
This issue is about the fact that, when a crate use proc-macro, on a musl host
(1) we can't cross compile it for other targets
(2) we can't compile it statically linked to musl.
We can't do (1) because cargo set RUSTFLAGS to empty when compile
proc-macro.we can't do (2) because it will let
proc-macrocompile statically, which is wrong.The problem is cargo don't have a concept about
HOSTRUSTFLAGSthat only apply to build script andproc-macro, but the behavior ofcrt-staticis control by RUSTFLAGS.However, RUSTFLAGS isn't a out of box solution. On every platform that have rustc built and runs,
proc-macrois compiled dynamically, so the better solution is rustc don't check whether a target can compile proc-macro (as it always can), and compile it dynamically directly