Compile for windows from linux when have build.rs

Hello !

I'm trying to compile my software for windows from linux.

The concerned source code, contains a windows particularity :

[target.'cfg(windows)'.build-dependencies]
windres = "*"

build.rs

#[cfg(target_os = "windows")]
use windres::Build;

fn main() {
    #[cfg(target_os = "windows")]
    {
        Build::new().compile("trsync.rc").unwrap();
    }
}

Which define an icon with rsync.rc :

id ICON "icon.ico"
my-icon-name ICON "icon.ico"

When I build for windows with :

cross build --target x86_64-pc-windows-gnu --bin trsync_manager_systray --release --features windows

File trsync_manager_systray.exe is generated without error. But, when executed on Windows, give the error :

Unable to create tray item : 'Error setting icon from resource: 1812 at /cargo/registry/src/github.com-1ecc6299db9ec823/tray-item-0.7.0/src/api/windows/funcs.rs#36'

I have two interrogations. First, How cross build --target x86_64-pc-windows-gnu [...] can compile without errors ? Source code need some dependencies (dependencies are they used from my linux and made compatible for windows ?). Second, cross build [...]ignore the build.rs file ? Which explains Error setting icon from resource error ?

Thanks in advance ! I'm a beginner about cross compilations.

It is a mistake to use #[cfg(target_os = …)] in build.rs. This is because #[cfg] refers to the environment in which the build.rs's executable will run, which will be the host (compiler) operating system. It is not the actual compilation target system.

Build scripts have to use Cargo's environmental variables, like CARGO_CFG_TARGET_OS to get actual OS the program is being compiled for.

3 Likes

The windres documentation is actually wrong here. I've done something like this to run only when compiling for Windows targets:

use windres::Build;

fn main() {
    if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
        Build::new().compile("trsync.rc").unwrap();
        println!("cargo:rerun-if-changed=icon.ico");
    } else {
        println!("cargo:rerun-if-changed=build.rs");
    }
}

Just a note: printing cargo:rerun-if-changed=build.rs isn't problematic, so doing it unconditionally is fine and probably better than gating it on "I didn't rerun-if-changed anything else."

1 Like

Thanks all for your time ! I updated build.rs file as indicated, but windres curiously not found :

Cargo.toml

[package]
name = "trsync_manager_systray"
version = "0.1.0"
edition = "2021"

[dependencies]
trsync_manager = {path = "../manager"}
env_logger = "0.9.0"
log = "0.4.13"
tray-item = "0.7.0"
rust-ini = "0.18.0"
dirs = "4.0.0"
crossbeam-channel = "0.5"
tiny_http = "0.11"
uuid = {version = "1.0.0", features = ["v4"]}
whoami = "1.2.1"
windres = "0.2"

[target.'cfg(unix)'.dependencies]
gtk = "0.15.4"

build.rs

use windres::Build;

fn main() {
    if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
        Build::new().compile("trsync.rc").unwrap();
        println!("cargo:rerun-if-changed=icon.ico");
    }
    println!("cargo:rerun-if-changed=build.rs");
}

When execute cross build --target x86_64-pc-windows-gnu --bin trsync_manager_systray --release --features windows :

   Compiling trsync_manager_systray v0.1.0 (/project/systray)
error[E0432]: unresolved import `windres`
  --> systray/build.rs:13:5
   |
13 | use windres::Build;
   |     ^^^^^^^ use of undeclared crate or module `windres`

For more information about this error, try `rustc --explain E0432`.
error: could not compile `trsync_manager_systray` due to previous error

It's likely if windres dependencies was not seen :thinking:

Dependencies of build.rs must be placed under [build-dependencies], this was correct in the first post, the error was only with cfg.

2 Likes

Curiously, even with :

Cargo.toml

[build-dependencies]
windres = "*"

The line use windres::Build; produce following :

error[E0432]: unresolved import `windres::Build`
 --> systray/build.rs:1:5
  |
1 | use windres::Build;
  |     ^^^^^^^^^^^^^^ no `Build` in the root

I thought about target_os conditional in windres lib but I don't find :thinking:

You probably want to use not windres = "*", but windres = "0.2.2", to be sure that the version pulled in is the one you want. windres 0.2.2 does have Build in the root, unconditionally.

1 Like

With explicit "0.2.2" version in Cargo.toml, error is the same. I tried with a fresh project including only Cargo.toml, empty main.rs and the build.rs and same error.

If I take a look on source file /home/myusername/.cargo/registry/src/github.com-1ecc6299db9ec823/windres-0.2.2/src/lib.rs the Build struct is in :

#[derive(Clone, Debug)]
/// A builder for compiling Windows resources.
pub struct Build {
    [...]
}

I don't see cfg or target_os condition ... I don't understand.

Looked int o the source myself - I think I see the problem. The whole crate is annotated as #![cfg(target_os = "windows")], therefore when compiling for Unix, it is treated as empty. Seems that cross-compilation is not supported in it, really.

2 Likes

Well seen ! I will try to fork and adjust.

Thanks for your time !