ALVR icon indicating copy to clipboard operation
ALVR copied to clipboard

Support cross compilation from Linux to Windows

Open rofferom opened this issue 1 month ago • 3 comments

Description

Based on #3100 and #3116, here is an dirty implementation of cross compilation for Windows (tested from a Debian 12).

I chose to base my work on v20 branch to make sure I have a stable environment, which allow me to excursively focus on the build system.

Also, the build.rs files don't seem to be very cross-compilation friendly. Since I don't have a good knowledge of alvr build system, I decided to implement an alternative codepath with a lot of code duplication, just to create a proof of concept that can be a base for discussion. I'm pretty sure I missed a lot of details, but at least I managed to have something working.

At the very end, I'm able to build with this simple command:

cargo xtask build-streamer --release windows

It requires some environment variables at first (more details bellow), but I managed to plug it in the existing command flow.

Known limitations

  • build.rs for Dashboard hasn't been updated. Which means that the executable doesn't have its icon correctly configured.
  • No ffmpeg build for now. So no x264 support
  • package command not patched

Environment requirements

  • Rust target x86_64-pc-windows-msvc installed
  • clang-cl as a toolchain since it is compilant with Windows C++ ABI
  • https://github.com/mstorsjo/msvc-wine to get the required MSVC/Windows SDK files

How to build (only tested on Debian 12)

Install required Windows components

Where: in msvc-wine folder.

VS_ROOT_DIR=<msvcinstallpath>

./vsdownload.py \
    --accept-license \
    --dest $VS_ROOT_DIR \
    --host-arch x64 \
    Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64 \
    Microsoft.Component.VC.Runtime.UCRTSDK \
    Microsoft.VisualStudio.Component.VC.Redist.14.Latest \
    Microsoft.VisualStudio.Component.Windows11SDK.26100

./install.sh $VS_ROOT_DIR

Note this may be improved, I discovered msvc-wine while working on this PR.

Setup build env

VS_ROOT_DIR=<msvcinstallpath>

export CXX=clang-cl-19
export AR=llvm-lib-19
export TARGET_CC=clang-cl-19
export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=lld-link-19

MSVC_ROOT=$VS_ROOT_DIR/vc/tools/msvc/14.44.35207
WINSDK_INCLUDE=$VS_ROOT_DIR/kits/10/include/10.0.26100.0
WINSDK_LIB=$VS_ROOT_DIR/kits/10/lib/10.0.26100.0

export INCLUDE="$MSVC_ROOT/include;$WINSDK_INCLUDE/shared;$WINSDK_INCLUDE/um;$WINSDK_INCLUDE/ucrt;$WINSDK_INCLUDE/winrt"
export LIB="$MSVC_ROOT/lib/x64;$WINSDK_LIB/ucrt/x64;$WINSDK_LIB/um/x64"

Build

cargo xtask build-streamer --release windows

Output

build
└── alvr_streamer_windows
    ├── ALVR Dashboard.exe
    ├── bin
    │   └── win64
    │       ├── alvr_server_openvr.pdb
    │       ├── driver_alvr_server.dll
    │       ├── openvr_api.dll
    │       └── vcruntime140_1.dll
    └── driver.vrdrivermanifest

rofferom avatar Nov 26 '25 17:11 rofferom

making use of std::env:consts::OS, which should point to the destination compilation target, not the host

I'm not familiar with this part of Rust, but actually this seems not to be true in build.rs. As it is an host tool, its target is the host OS, not the --target specified on cargo call. Which mean that cfg!(...) have an "unexpected" behavior. I attached a code example bellow.

I should have been more explicit about this part in the MR description. This is why I chose to create an alternate codepath to make it work, since I think some preliminary changes in the build system could be required at first. During my first try to add cross compilation support, I was triggering all the Linux build codepath, and I want tweaking all the conditional build tests, which lead to heavy changes.

You should rebase your branch because some changes were already merged.

I can open a new PR targeting master (since the name of my current branch is explicit about v20.14.1), but I suggest we can discuss about the correct strategy to remove code duplication. Then I'll be able to provide a PR targeting master, with a correct implementation.

build.rs behavior tests

build.rs example

fn main() {
    println!("cargo:warning=In build.rs");

    println!("cargo:warning=std::env:consts::OS={}", std::env::consts::OS);

    if cfg!(windows) {
        println!("cargo:warning=cfg!(windows)");
    } else if cfg!(target_os = "linux") {
        println!("cargo:warning=cfg!(target_os = \"linux\")");
    }
}

Linux build

$ cargo build
warning: [email protected]: In build.rs
warning: [email protected]: std::env:consts::OS=linux
warning: [email protected]: cfg!(target_os = "linux")

Linux -> Windows build

$ cargo build --target "x86_64-pc-windows-msvc"
   Compiling rusttest v0.1.0 (/mnt/kyber/rusttest)
warning: [email protected]: In build.rs
warning: [email protected]: std::env:consts::OS=linux
warning: [email protected]: cfg!(target_os = "linux")

rofferom avatar Nov 28 '25 08:11 rofferom

Ah I see, so env::var("CARGO_CFG_TARGET_OS") is the only good way to do this. I would say that interleaving the code is not that bad, as I see that significant portions of the code are duplicated. you can use many small if-else wherever you need instead.

zmerp avatar Nov 29 '25 01:11 zmerp

You'll have to rebase this on top of master so that we can actually do anything with it, tho I can do that for you if you think that's too much for you.

The-personified-devil avatar Dec 08 '25 17:12 The-personified-devil

It seems I also have to integrate #3051 since it adds an extra dependency, which is cross compiled too. I'll be busy the next week, I'm not sure I can provide a rebase quickly unfortunately.

rofferom avatar Dec 12 '25 10:12 rofferom