Cross-compile a Rust application from Linux to Windows - linux

Basically I'm trying to compile the simplest code to Windows while I am developing on Linux.
fn main() {
println!("Hello, and bye.")
}
I found these commands by searching the internet:
rustc --target=i686-w64-mingw32-gcc main.rs
rustc --target=i686_pc_windows_gnu -C linker=i686-w64-mingw32-gcc main.rs
Sadly, none of them work. It gives me an error about the std crate missing
$ rustc --target=i686_pc_windows_gnu -C linker=i686-w64-mingw32-gcc main.rs
main.rs:1:1: 1:1 error: can't find crate for `std`
main.rs:1 fn main() {
^
error: aborting due to previous error
Is there a way to compile code on Linux that will run on 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

The Rust distribution only provides compiled libraries for the host system. However, according to Arch Linux's wiki page on Rust, you could copy the compiled libraries from the Windows packages in the download directory (note that there are i686 and x86-64 packages) in the appropriate place on your system (in /usr/lib/rustlib or /usr/local/lib/rustlib, depending on where Rust is installed), install mingw-w64-gcc and Wine and you should be able to cross-compile.
If you're using Cargo, you can tell Cargo where to look for ar and the linker by adding this to ~/.cargo/config (where $ARCH is the architecture you use):
[target.$ARCH-pc-windows-gnu]
linker = "/usr/bin/$ARCH-w64-mingw32-gcc"
ar = "/usr/$ARCH-w64-mingw32/bin/ar"
Note: the exact paths can vary based on your distribution. Check the list of files for the mingw-w64 package(s) (GCC and binutils) in your distribution.
Then you can use Cargo like this:
$ # Build
$ cargo build --release --target "$ARCH-pc-windows-gnu"
$ # Run unit tests under wine
$ cargo test --target "$ARCH-pc-windows-gnu"

UPDATE 2019-06-11
This fails for me with:
Running `rustc --crate-name animation examples/animation.rs --color always --crate-type bin --emit=dep-info,link -C debuginfo=2 --cfg 'feature="default"' -C metadata=006e668c6384c29b -C extra-filename=-006e668c6384c29b --out-dir /home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/examples --target x86_64-pc-windows-gnu -C ar=x86_64-w64-mingw32-gcc-ar -C linker=x86_64-w64-mingw32-gcc -C incremental=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/incremental -L dependency=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps -L dependency=/home/roman/projects/rust-sdl2/target/debug/deps --extern bitflags=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/libbitflags-2c7b3e3d10e1e0dd.rlib --extern lazy_static=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/liblazy_static-a80335916d5ac241.rlib --extern libc=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/liblibc-387157ce7a56c1ec.rlib --extern num=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/libnum-18ac2d75a7462b42.rlib --extern rand=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/librand-7cf254de4aeeab70.rlib --extern sdl2=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/libsdl2-3f37ebe30a087396.rlib --extern sdl2_sys=/home/roman/projects/rust-sdl2/target/x86_64-pc-windows-gnu/debug/deps/libsdl2_sys-3edefe52781ad7ef.rlib -L native=/home/roman/.cargo/registry/src/github.com-1ecc6299db9ec823/winapi-x86_64-pc-windows-gnu-0.4.0/lib`
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
Maybe this will help https://github.com/rust-lang/rust/issues/44787
Static compile sdl2
There is option to static-compile sdl but it didn't work for me.
Also mixer is not included when used with bundled.
Let's cross-compile examples from rust-sdl2 project from Ubuntu to Windows x86_64
In ~/.cargo/config
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-gcc-ar"
Then run this:
sudo apt-get install gcc-mingw-w64-x86-64 -y
# use rustup to add target https://github.com/rust-lang/rustup.rs#cross-compilation
rustup target add x86_64-pc-windows-gnu
# Based on instructions from https://github.com/AngryLawyer/rust-sdl2/
# First we need sdl2 libs
# links to packages https://www.libsdl.org/download-2.0.php
sudo apt-get install libsdl2-dev -y
curl -s https://www.libsdl.org/release/SDL2-devel-2.0.9-mingw.tar.gz | tar xvz -C /tmp
# Prepare files for building
mkdir -p ~/projects
cd ~/projects
git clone https://github.com/Rust-SDL2/rust-sdl2
cd rust-sdl2
cp -r /tmp/SDL2-2.0.9/x86_64-w64-mingw32/lib/* ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/
cp /tmp/SDL2-2.0.9/x86_64-w64-mingw32/bin/SDL2.dll .
Build examples at once
cargo build --target=x86_64-pc-windows-gnu --verbose --examples
Or stop after first fail:
echo; for i in examples/*; do [ $? -eq 0 ] && cargo build --target=x86_64-pc-windows-gnu --verbose --example $(basename $i .rs); done
Run
cargo build will put binaries in target/x86_64-pc-windows-gnu/debug/examples/
Copy needed files:
cp /tmp/SDL2-2.0.4/x86_64-w64-mingw32/bin/SDL2.dll target/x86_64-pc-windows-gnu/debug/examples/
cp assets/sine.wav target/x86_64-pc-windows-gnu/debug/examples/
Then copy directory target/x86_64-pc-windows-gnu/debug/examples/ to your Windows machine and run exe files.
Run in cmd.exe
If you want to see the console output when running exe files, you may run them from cmd.exe.
To open cmd.exe in current directory in file explorer, right click with shift on empty place in window and choose Open command window here.
Backtraces with mingw should work now - if not use msvc https://github.com/rust-lang/rust/pull/39234

There is Docker based solution called cross. All the required tools are in virtualized environment so you don't need to install additional packages for your machine. See Supported targets list.
From project's README:
Features
cross will provide all the ingredients needed for cross compilation without touching your system installation.
cross provides an environment, cross toolchain and cross compiled libraries, that produces the most portable binaries.
“cross testing”, cross can test crates for architectures other than i686 and x86_64.
The stable, beta and nightly channels are supported.
Dependencies
rustup
A Linux kernel with binfmt_misc support is required for cross testing.
One of these container engines is required. If both are installed, cross will default to docker.
Docker. Note that on Linux non-sudo users need to be in the docker group. Read the official post-installation steps. Requires version 1.24 or later.
Podman. Requires version 1.6.3 or later.
Installation
$ cargo install cross
Usage
cross has the exact same CLI as Cargo but as it relies on Docker you'll have to start the daemon before you can use it.
# (ONCE PER BOOT)
# Start the Docker daemon, if it's not already running
$ sudo systemctl start docker
# MAGIC! This Just Works
$ cross build --target aarch64-unknown-linux-gnu
# EVEN MORE MAGICAL! This also Just Works
$ cross test --target mips64-unknown-linux-gnuabi64
# Obviously, this also Just Works
$ cross rustc --target powerpc-unknown-linux-gnu --release -- -C lto

The solution that worked for me was. It is similar to one of the accepted answers but I did not require to add the toolchain.
rustup target add x86_64-pc-windows-gnu
cargo build --target x86_64-pc-windows-gnu
Refer to the documentation for more details.

I've had success on Debian (testing) without using Mingw and Wine just following the official instructions. They look scary, but in the end it didn't hurt that much.
The official instructions also contain info on how to cross-compile C/C++ code. I haven't needed that, so it's something I haven't actually tested.
A couple of remarks for individual points in the official instructions. The numbers match the numbers in the official instructions.
Debian: sudo apt-get install lld
Make a symlink named lld-link to lld somewhere in your $PATH. Example: ln -s /usr/bin/lld local_bin/lld-link
I don't cross-compile C/C++, haven't used this point personally.
This is probably the most annoying part. I installed Rust on a Windows box via rustup, and copied the libraries from the directories named in the official docs to the Linux box. Beware, there were sometimes uppercase library filenames, but lld wants them all lowercase (Windows isn't case-sensitive, Linux is). I've used the following to rename all files in current directory to lowercase:
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Personally, I've needed both Kit directories and just one of the VC dirs.
I don't cross-compile C/C++, haven't used this point personally.
Just make $LIB_ROOT in the script at the end of this post point to the lib directory from point 3.
Mandatory
I don't cross-compile C/C++, haven't used this point personally.
Depending the target architecture, either of the following:
rustup target add i686-pc-windows-msvc
rustup target add x86_64-pc-windows-msvc
For cross-building itself, I'm using the following simple script (32-bit version):
#!/bin/sh
# "cargo build" for the 32-bit Windows MSVC architecture.
# Set this to proper directory
LIB_ROOT=~/opt/rust-msvc
# The rest shouldn't need modifications
VS_LIBS="$LIB_ROOT/Microsoft Visual Studio 14.0/VC/lib/"
KIT_8_1_LIBS="$LIB_ROOT/Windows Kits/8.1/Lib/winv6.3/um/x86/"
KIT_10_LIBS="$LIB_ROOT/Windows Kits/10/Lib/10.0.10240.0/ucrt/x86/"
export LIB="$VS_LIBS;$KIT_8_1_LIBS;$KIT_10_LIBS"
cargo build --target=i686-pc-windows-msvc "$#"
I'm using the script the same way I would use cargo build
Hope that helps somebody!

Related

Linker error when cross compiling for raspberry pi

I try to cross-compile a simple rust program with sqlite on Linux for raspberry pi:
Cargo.toml
...
[dependencies]
rusqlite = { version = "0.26.3", features = ["bundled"] }
.cargo/config
[target.arm-unknown-linux-gnueabihf]
linker = "/opt/crosspi/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc"
When trying to build with cargo build --release --target=arm-unknown-linux-gnueabihf rust bails out with a linker error which basically says:
error: linking with `/opt/crosspi/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc` failed: exit status: 1
...
= note: /home/hannenz/pidev/projects/kiddyblaster/webui-rust/target/arm-unknown-linux-gnueabihf/release/deps/liblibsqlite3_sys-950993cbbcc1e3eb.rlib(sqlite3.o):(.data.rel.aSyscall+0x58): undefined reference to `fcntl64'
collect2: error: ld returned 1 exit status
(The ... is the full gcc command line, i can post the whole output if relevant)
Without rusqlite cross-compiling works and compiling for the host target is working as well. I also tried the sqlite crate instead of rusqlite, but that produces the exact same linker error.
Would be glad if someone could point me in the right direction what's goiong wrong here.
Cross compiling can be a bit tricky, luckily the cross crate (https://github.com/cross-rs/cross) makes it simple. Follow these steps:
Install cargo cross: cargo install cross
Create a file called Cross.toml in the root directory (where Cargo.toml is) with this content
[target.aarch64-unknown-linux-gnu]
image = "my-custom-cross"
Create a Dockerfile with this content
FROM rustembedded/cross:aarch64-unknown-linux-gnu
RUN dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install --assume-yes libsqlite3-dev:arm64
Build the docker container: docker buildx build --platform linux/arm64 -t my-custom-cross --load .
Build your program: cross build --target aarch64-unknown-linux-gnu --release
Now you have your binary compiled at target/aarch64-unknown-linux-gnu/release directory
A bit of explanation
The project cross-rs prepared all the environment and put it in a docker container. We created our own container based on this (FROM rustembedded/cross:aarch64-unknown-linux-gnu) and we added the extra libraries we need, in this case the dev sqlite3 (apt-get install --assume-yes libsqlite3-dev:arm64).
Once our docker container is ready, we configured cross to use this container by using the file Cross.toml.
Depending on your raspberry and the operating system you use in your raspberry, you may need to use other arquitecture. In this case just change aarch64 by yours. In my case I'm using rpi3 64 bit. In case you are using 32 bit or an older board, try something like arm-unknown-linux-gnueabihf or go to the "Supported targets" section in the cross documentation https://github.com/cross-rs/cross.
Dont forget to configure Cross.toml and the Dockerfile to use your target architecture!

Cargo fails to build when invoked from inside a python script

I have a python script which will symlink my binary into /usr/bin, and I wanted to add the functionality of building the binary if it is not already built.
# Check if binary exists
if path.exists(src_bin):
# If not, build it
run('cargo build --release', shell=True, check=True)
# Check if symlink already exists
if path.exists(dest_bin):
# Remove it, in case it is outdated
path.remove(dest_bin)
# Symlink binary to /usr/bin
os.symlink(src_bin, dest_bin)
But the cargo command (which works perfectly fine if I run it in the terminal) errors out with this:
no override and no default toolchain set; run 'rustup default stable' to set the stable toolchain as default
My best guess is that cargo relies on some environment variables to tell it which toolchain to use which are not present in the python environment, but I don't know what variables those might be. I also tried
run(['cargo', 'build', '--release'], check=True)
but the same thing happened.
What is causing this, and is there a way around it?
Edit more info:
$ ls ~/.cargo/bin
# no output
$ ls ~/.rustup/toolchains
stable-x86_64-unknown-linux-gnu
$ which cargo
/usr/bin/cargo
I tried
run('rustup run stable cargo build --release', shell=True, check=True)
and got
error: toolchain 'stable-x86_64-unknown-linux-gnu' is not installed
when I run cargo build --release or rustup run stable cargo build --release in the shell, both build sucessfully.

How do I Distribute my Haxe application with Hashlink?

I've got a Haxe Application that I want to make available to people with a Windows system. I use Hashlink to run the Application locally and it works very nicely.
I am wondering if I'm supposed to distribute my Application with Hashlink. Can it build me an .exe?
It looks like generating distributable binary files isn't supported out of the box today (March 10, 2017):
> haxe -main Main -hl main.c
Code generated in main.c automatic native compilation not yet implemented
Hopefully it will be supported soon!
Note: I'm talking about building a final executable using hashlink. An entirely separate approach I do not cover here is the possibility of delivering the hashlink virtual machine with your output hl bitcode.
Sane people stop reading here.
But in the meantime... it is possible to generate binaries with hashlink today if you build hashlink from source.
Warnings:
This isn't a generic, cross-platform answer to your question -- it's just my experience on Linux.
There will probably soon be a better way than this.
But I wanted to jot these notes down even for myself to recall later.
Here's what I had to do on Ubuntu 14.04, 64-bit:
Install prerequisite libraries for building hl (there may be others I already have installed, like build-essential, etc)
sudo apt-get install libvorbis-dev libturbojpeg libsdl2-dev libopenal-dev libssl-dev
Clone and build the mbedtls library: (rev note: b5ba28)
cd ~/dev/
git clone https://github.com/ARMmbed/mbedtls.git
cd mbedtls
make CFLAGS='-fPIC'
Clone the hashlink repo: (rev note: eaa92b)
cd ~/dev/
git clone https://github.com/HaxeFoundation/hashlink.git
cd hashlink
In the # Linux section of the Makefile, ~line 67, add these flags:
CFLAGS += -I ../mbedtls/include
LIBFLAGS += -L../mbedtls/library
Now build with make
If everything works, you'll see two important output files, hl and libhl.so
Ok, at this point, it's easiest if you just build your project in the hashlink directory. For example:
# Still in the hashlink directory
haxe -cp /path/to/my/project -debug -main Main.hx -hl src/_main.c
Now run make hlc, and if everything works, hlc is the output executable (which depends on libhl.so):
cp libhl.so hlc /tmp/
cd /tmp/
./hlc
Prints:
Main.hx:7: Hello world!

Adding codegen flags to a Cargo build

On Macintosh, to allow some symbols to go unlinked, it is necessary to pass -C link-args='-Wl,-undefined,dynamic_lookup' to the Rust compiler. One needs to do this when building Postgres plugins, because some of the Postgres intrinsics are only compiled into the Postgres server, and not available for linking from shared libs.
At present, the project's process is as follows:
Build is run with cargo build -v.
Failing call to rustc is copied and -C link-args='-Wl,-undefined,dynamic_lookup' added to it.
Success!
This seems like a hard sell for automation. What options are available for adding codegen flags to Rust builds through cargo?
cargo provides rustc command which allows one to pass arbitrary compiler flags. The following should do it:
% cargo rustc -- -C link-args='-Wl,-undefined,dynamic_lookup'

How to cross compile a C++ file with LTIB

I have installed LTIB from freescale (by way of Congatec) and would like to compile a "hello, world" program. gcc gives me the native executable. How do I compile for my ARM processor?
You need to install a cross compiler for ARM. gcc on your machine is the native compiler, a cross compiler has a different prefix that determines the target architecture, operating system and libc. In your case, the target system has probably an ARM architecture, is running a Linux kernel with the GNU libc.
arm-none-linux-gnueabi-gcc -o hello hello.cpp
Run ./ltib -m shell. In this mode all of the standard tools are setup to allow ./configure and other setups to build for an ARM by default. From this shell gcc -o hello hello.cpp will create an executable for you. It will be setup for your rootfs, be it uCLibc or glibc. You can also take external open-source packages and run the ./configure - make cycle.
The documentation in the LTIB FAQ has much more information on using LTIB. LTIB typically installs tools in the /opt/freescale directory. If you are not using a custom compiler you will find the compiler under /opt/freescale and can use it directly. In this directory, the compiler will be named something like arm-none-linux-gnuabi-gcc. With the ./ltib -m shell, it is aliased to gcc as are many other standard commands for cross-building.
Look at the -march=name option in the gcc man page. The exact architecture you will specify depends on the version of ARM you need to compile for. v5 ARM example:
gcc -o ARM.exe -march=armv5 source.c
First move to the LTIB folder.
cd "ltib folder"
Then enter the ltib shell
./ltib -m shell
then compile using
gcc -static "program name" .c -o "program name".sabre
Then run the command
file "program name".sabre

Resources