dockerrustcross-compiling

How to containerize the compilation process for a Rust-based Windows application on Linux?


I'm developing an application in Rust for Windows (the target is: i686-pc-windows-msvc), this application has particular requirements: it uses a build.rs file to specify the Windows manifest.

Compilation worked fine on Windows, but I had to switch to Linux for this project.

Here's the project:

❯ tree
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── src
│   ├── api.rs
│   ├── client.rs
│   ├── main.rs
│   ├── service.rs
│   └── system.rs
└── target

Cargo.toml

[package]
name = "wrapper"
version = "0.1.0"
edition = "2021"
build = "build.rs"

[build-dependencies]
winresource = "0.1"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
named-lock = "0.3.0"
rand = "0.8.5"
reqwest = "0.11.24"
tokio = { version = "1.36.0", features = ["rt", "rt-multi-thread"] }
winapi = { version = "0.3.9", features = ["processthreadsapi", "securitybaseapi", "winnt", "fileapi", "minwinbase", "iphlpapi", "winsock2", "ws2def"] }
windows-service = "0.6.0"

build.rs

extern crate winresource;

fn main() {
    let mut res = winresource::WindowsResource::new();
    res.set_manifest(
        r#"
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly>
"#,
    );
    res.set_ar_path("i686-w64-mingw32-gcc-ar");
    res.set_windres_path("i686-w64-mingw32-windres");
    if let Err(error) = res.compile() {
        eprint!("{error}");
        std::process::exit(1);
    }
}

I therefore tried to containerize the compilation process via a Dockerfile, but without success.

Here's my Dockerfile:

FROM archlinux:multilib-devel-20240101.0.204074

RUN pacman -Syu --noconfirm && \
    pacman -S --needed --noconfirm rustup mingw-w64

RUN rustup default stable && \
    rustup target install i686-pc-windows-msvc

WORKDIR /app

COPY Cargo.toml Cargo.lock build.rs ./
COPY src ./src

RUN cargo build --target i686-pc-windows-msvc
RUN cargo build --release --target i686-pc-windows-msvc

RUN mkdir -p /app/debug/ && \
    cp target/i686-pc-windows-msvc/debug/wrapper.exe /app/debug/

RUN mkdir -p /app/release/ && \
    cp target/i686-pc-windows-msvc/release/wrapper.exe /app/release/

Here's my output :

❯ docker build --tag 'wrapper' .
[...]
Step 7/10 : RUN cargo build --target i686-pc-windows-msvc
[...]
   Compiling wrapper v0.1.0 (/app)
The following warnings were emitted during compilation:

warning: wrapper@0.1.0: unknown Windows target used for cross-compilation; invoking unprefixed windres

error: failed to run custom build command for `wrapper v0.1.0 (/app)`

Caused by:
  process didn't exit successfully: `/app/target/debug/build/wrapper-6dacece8a0e1e7d8/build-script-build` (exit status: 1)
  --- stdout
  package.metadata does not exist
  cargo:warning=unknown Windows target used for cross-compilation; invoking unprefixed windres
  Selected RC path: '/bin\x64\rc.exe'

  --- stderr
  No such file or directory (os error 2)
warning: build failed, waiting for other jobs to finish...
The command '/bin/sh -c cargo build --target i686-pc-windows-msvc' returned a non-zero code: 101

How can I get my Dockerfile to compile properly and return both versions of the program (debug and release)?


Solution

  • The winres crate does not support cross-compiling (see https://github.com/mxre/winres/issues/42).

    Your options are: