Cross-compile a Rust application from Linux to Windows
Other answers, while technically correct, are more difficult than they need to be. There's no need to use rustc
(in fact it's discouraged, just use cargo
), you only need rustup
, cargo
and your distribution's mingw-w64.
Add the target (you can also change this for whatever target you're cross compiling for):
rustup target add x86_64-pc-windows-gnu
You can build your crate easily with:
cargo build --target x86_64-pc-windows-gnu
No need for messing around with ~/.cargo/config
or anything else.
EDIT: Just wanted to add that while you can use the above it can also sometimes be a headache. I wanted to add that the rust tools team also maintains a project called cross: https://github.com/rust-embedded/cross
This might be another solution that you want to look into
Crosscompiling Rust from Fedora Linux host to Windows target does not find dependencies
After a little thought I was able to solve the problem to cross compile projects in Rust from the Linux host to the Windows target without using cross and similar tools but simply by specifying specific target in the cargo. Of course testing and running the project will fail (if you have no any emulator like wine installed), but just creating the Windows target goes smoothly. BTW Rust was developed to be cross compiler initially and therefore having the full working project and passing all the tests in Linux one can be almost sure that a simple cross compilation of this project in Windows will not break it in any way.
Since I run Fedora Linux 31 all of the following applies to this distribution. But I do not think that there are significant differences among other Linux distributions. And I only consider pc-windows-gnu Rust targets, pc-windows-msvc targets are not of my interest.
First, make sure all the necessary MinGW packages for cross compilation are installed:
[pfemidi@pfemidi ~]$ rpm -qa | grep mingw | sort
mingw32-binutils-2.32-6.fc31.x86_64
mingw32-cpp-9.2.1-1.fc31.x86_64
mingw32-crt-6.0.0-2.fc31.noarch
mingw32-filesystem-110-1.fc31.noarch
mingw32-gcc-9.2.1-1.fc31.x86_64
mingw32-headers-6.0.0-2.fc31.noarch
mingw32-winpthreads-6.0.0-2.fc31.noarch
mingw32-winpthreads-static-6.0.0-2.fc31.noarch
mingw64-binutils-2.32-6.fc31.x86_64
mingw64-cpp-9.2.1-1.fc31.x86_64
mingw64-crt-6.0.0-2.fc31.noarch
mingw64-filesystem-110-1.fc31.noarch
mingw64-gcc-9.2.1-1.fc31.x86_64
mingw64-headers-6.0.0-2.fc31.noarch
mingw64-winpthreads-6.0.0-2.fc31.noarch
mingw64-winpthreads-static-6.0.0-2.fc31.noarch
mingw-binutils-generic-2.32-6.fc31.x86_64
mingw-filesystem-base-110-1.fc31.noarch
[pfemidi@pfemidi ~]$
Lets create the simplest project in the Rust language:
[pfemidi@pfemidi rust]$ cargo new foobar
Created binary (application) `foobar` package
[pfemidi@pfemidi rust]$ cat foobar/src/main.rs
fn main() {
println!("Hello, world!");
}
[pfemidi@pfemidi rust]$
Create the .cargo directory with the config file in the project directory with the following contents:
[pfemidi@pfemidi rust]$ cd foobar
[pfemidi@pfemidi foobar]$ mkdir .cargo
[pfemidi@pfemidi foobar]$ cat > .cargo/config
[target.i686-pc-windows-gnu]
linker = "i686-w64-mingw32-gcc"
ar = "i686-w64-mingw32-ar"
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"
[pfemidi@pfemidi foobar]$
This step is necessary so that when building Windows targets Rust uses not the linker from the default gcc installed but the MinGW linker.
Now try to build the project as the i686-pc-windows-gnu target:
[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
|
= note: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"
...
= note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x75): undefined reference to `__onexitend'
/usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x7a): undefined reference to `__onexitbegin'
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$
Error! Got "undefined reference to __onexitend
" and "undefined reference to __onexitbegin
" in the crt2.o file. The fact is that Rust component for pc-windows-gnu target has been built with MinGW 6.3.0 but there is MinGW version 9.2.1 in Fedora Linux 31. So there is a difference in the CRT due compiler versions mismatch. Ok, lets copy crt2.o from Fedora Linux MinGW repository to the Rust directory for the i686-pc-windows-gnu component. And along with crt2.o we also copy dllcrt2.o, it is the entry point for dynamic libraries as crt2.o is for standalone executable files. Just in case, we save the original files:
[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$
Now try to build the project as the i686-pc-windows-gnu target again:
[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
[pfemidi@pfemidi foobar]$
It worked!
Now try to do the same for x86_64-pc-windows-gnu target:
[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
|
= note: "x86_64-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib" "-m64"
...
= note: /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitbegin[.refptr.__onexitbegin]+0x0): undefined reference to `__onexitbegin'
/usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitend[.refptr.__onexitend]+0x0): undefined reference to `__onexitend'
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$
The error is exactly the same as for the i686-pc-windows-gnu target was noted before. Lets copy the files crt2.o and dllcrt2.o by analogy with the 32bit target above:
[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$
And try to build the project as the x86_64-pc-windows-gnu target again:
[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 0.20s
[pfemidi@pfemidi foobar]$
Yes! It worked also! So now we can easily build targets for Windows using the standard cargo included in the Rust distribution! And for x86_64 targets this is completely true. But for x86 targets everything will be fine only until we use in our project any functions that panic (f.e. macro panic!, expect functions, etc.).
Lets add panic to our simple project:
[pfemidi@pfemidi foobar]$ cat src/main.rs
fn main() {
println!("Hello, world!");
panic!("I'm panicked!"); // <-- here it is
}
[pfemidi@pfemidi foobar]$
Build it as x86_64 target:
[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 0.16s
[pfemidi@pfemidi foobar]$
It is going perfectly (and works just as well, I checked). Now try to do the same for the x86 target:
[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1
|
= note: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"
...
= note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `ZN12panic_unwind3imp5panic17h03027a0e504502cdE':
/rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/src\libpanic_unwind/gcc.rs:73: undefined reference to `_Unwind_RaiseException'
/usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `rust_eh_unwind_resume':
/rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/src\libpanic_unwind/gcc.rs:327: undefined reference to `_Unwind_Resume'
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `foobar`.
To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$
Oops! We got "undefined reference to _Unwind_RaiseException
" and "undefined reference to _Unwind_Resume
" in the libpanic_unwind module of the Rust standard library. But this problem is also solved, though not as simple as the previous one which is a mere replacement of CRT crt2.o and dllcrt2.o files.
For the stack unwinding Rust uses the dwarf method for 32bit Windows targets and seh method for 64bit Windows targets while MinGW from the standard Fedora Linux distribution uses the sjlj method for 32bit Windows targets and seh method for 64bit Windows targets (read about the difference here). Therefore 64bit targets are linked by MinGW linker without any problem but for 32bit ones there are no necessary symbols and object files to be linked correctly. To get these files and symbols it's necessary to rebuild MinGW with dwarf support instead of sjlj support by default for 32bit Windows targets.
I will not go into details on how to rebuild MinGW here, I will say only one thing: after MinGW is rebuilt with the dwarf stack unwinding instead of sjlj method you need to pick only one file called libgcc_eh.a from the MinGW just built and put it in the library directory for the Rust i686-pc-windows-gnu target. After doing that projects with any panic functions will build with no errors not only for 64bit Windows targets, but also for 32bit ones:
[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ cp ~/rpmbuild/BUILD/gcc-9.2.1-20190827/build_win32/i686-w64-mingw32/libgcc/libgcc_eh.a .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ cargo build --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
[pfemidi@pfemidi foobar]$
Happy RUSTing! :-)
Compile Rust application for Windows with cross and sqlite3 dependecy
It is because it is not bundled by default with rusqlite
. You could enable bundled
through a feature gate. You will need to add this to your Cargo.toml
file.
[features]
windows = ["rusqlite/bundled"]
or by linking to winsqlite3
[features]
windows = ["rusqlite/winsqlite3"]
Then when cross-compiling enable that feature in windows. Using the command line options
cross build --target x86_64-pc-windows-gnu --features windows
Cross-compiling Rust from Windows to ARM Linux
Thanks to @Notlikethat's comment:
a) Yes you need to provide your own GCC cross-compiler.
b) You can get one here (select a mingw32
build).
Just unzip linaro's GCC then point cargo to it:
[target.armv7-unknown-linux-gnueabihf]
linker = "C:/Users/me/gcc-linaro-5.3.1-2016.05-i686-mingw32_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc.exe"
It seems to work even though it is arm-
and not armv7-
. I guess linking doesn't depend on the ISA. Actually I haven't run it yet, but it builds without errors!
Edit:
You can now use armv7-unknown-linux-musleabihf
instead and get an actually portable binary (i.e. it doesn't depend on the GNU C library which often causes compatibility issues).
Cross-compile gtk crate for Windows
I managed to compile it by using this awesome tool.
Basically it is dockerized Linux Fedora with all necessary dependencies pre-installed.
Related Topics
Git Status Ignore Line Endings/Identical Files/Windows & Linux Environment/Dropbox/Meld
"Max Open Files" for Working Process
How to Check If Sed Has Changed a File
Linux Shell Script to Add Leading Zeros to File Names
Change Path Permanently on Ubuntu
How to Have Simple and Double Quotes in a Scripted Ssh Command
Why Does Printf Overwrite the Ecx Register
How to Get Position of Cursor in Terminal
Running 32 Bit Assembly Code on a 64 Bit Linux & 64 Bit Processor:Explain the Anomaly
How to Run Crontab Job Every Week on Sunday
Generating a Sha-256 Hash from the Linux Command Line
How Clear and Invalidate Arm V7 Processor Cache from User Mode on Linux 2.6.35
How to Do Memory Test on Arm Architecture Hardware? (Something Like Memtest86)
When to Check for Eintr and Repeat the Function Call