When cross compiling from Linux to Windows, how do I add an icon to the exe file?

If I was on Windows, doing this step

https://stackoverflow.com/questions/30291757/attaching-an-icon-resource-to-a-rust-application

Works flawlessly to get an icon to the *.exe file.

However if I tried this on (Arch) Linux and wanted to compile this for Windows and I ran cargo build --release --target x86_64-pc-windows-gnu

While it does compile without any issues and it does work fine on Windows, it fails to include an image to the *.exe file.

Is there a way to solve this?

MinGW seems to have a windres tool that functions the same as rc when using the MSVC toolchain. Something like windres resources.rc -O coff -o res.lib it seems and then the same println!("cargo:rustc-link-lib=./res"); as for MSVC. (On linux it may be called something like x86_64-w64-mingw32-windres instead of plain windres)

Isn't WindowsResource::set_toolkit_path the thing you need (combined with set_windres_path, if necessary)?

I could give that a shot but why wouldn't my previous code even work though?

Edit: it doesn't seem like it is executing #[cfg(windows)]?

extern crate winapi;
extern crate winres;
fn main() {
  if cfg!(target_os = "windows") {
    let mut res = winres::WindowsResource::new();
    res.set_language(winapi::um::winnt::MAKELANGID(
        winapi::um::winnt::LANG_ENGLISH,
        winapi::um::winnt::SUBLANG_ENGLISH_US
    ));
    res.compile().unwrap();
  }
}

This is what you gave me but like where is it linking to my icon though?

Nowhere, since you're not calling set_icon. And it won't do anything on Linux, since you explicitly stopped it with if cfg!. Also note that it is not the code I gave you - it is in the documentation to another function (the function I've linked to has no examples, but should be trivial to call just using the signature).

I think I get what you mean now mate.

But what is this bit though?


Anyways I tried doing the following:

In my main.rs it is just a simple "hello world" program

In my build.rs file, I have this:

use std::io;
use winres::WindowsResource;

fn main() -> io::Result<()> {
        WindowsResource::new()
            // This path can be absolute, or relative to your crate root.
            .set_icon("assets/icon.ico")
            .compile()?;
    Ok(())
}

And in my Cargo.toml file I have this:

[package]
name = "testing"
version = "0.1.0"
edition = "2018"

[build-dependencies]
winres = "0.1"

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

[dependencies]

Running cargo build --release --target x86_64-pc-windows-gnu resulted in this error:

test@test-virtualbox ~/D/p/testing (master) [101]> cargo build --release --target x86_64-pc-windows-gnu
   Compiling serde v1.0.127
   Compiling toml v0.5.8
   Compiling winres v0.1.11
   Compiling testing v0.1.0 (/home/test/Desktop/programming/testing)
error: failed to run custom build command for `testing v0.1.0 (/home/test/Desktop/programming/testing)`

Caused by:
  process didn't exit successfully: `/home/test/Desktop/programming/testing/target/release/build/testing-eda5b7592106f7de/build-script-build` (exit status: 1)
  --- stdout
  package.metadata does not exist

  --- stderr
  Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

Not too sure now.

Well, winres by default looks for windres.exe, but on Linux you'll likely have windres.

...which seems to be exactly because winres wasn't able to find the program to execute, since you didn't use neither set_toolkit_path nor set_windres_path, which seem to be both necessary.

Oh makes sense mate.

So am I supposed to call set_windres_path() in the build.rs file and like how I use it etc?

You are supposed to tell winres what it should to do. That means:

  • call set_toolkit_path with the path where windres is;
  • call set_windres_path with exact name of executable,
  • call all necessary methods to describe the action to be taken (in your case, set_icon).

Thanks for clearing it up.

Where is it normally located?

I can call my executable different to the project name, right?

Not sure, but you can always consult the documentation for the toolkit you're using for cross-compilation. Or just use which windres, but it won't work if the program name is different, as was suggested before.

With exact name of windres executable, not yours.

So if I am importing the winres library, it will store the windres file somewhere within the project folder? Sorry mate I am confused where windres comes from in the first place?

From some package installed in the system, like the one you use to cross-compile in the first place (probably even the same one).

Am I supposed to give the full path of the executable including the name of the executable or just the name of the executable?

I have done this now inside the build.rs file

use std::io;
use winres::WindowsResource;

fn main() -> io::Result<()> {
        WindowsResource::new()
            // This path can be absolute, or relative to your crate root.
            .set_toolkit_path("/usr/bin")
            .set_windres_path("x86_64-w64-mingw32-windres")
            .set_icon("assets/icon.ico")
            .compile()?;
    Ok(())
}

I get this error now:

test@test-virtualbox ~/D/p/testing (master) [101]> cargo build --release --target x86_64-pc-windows-gnu
   Compiling testing v0.1.0 (/home/test/Desktop/programming/testing)
error: failed to run custom build command for `testing v0.1.0 (/home/test/Desktop/programming/testing)`

Caused by:
  process didn't exit successfully: `/home/test/Desktop/programming/testing/target/release/build/testing-eda5b7592106f7de/build-script-build` (exit status: 1)
  --- stdout
  package.metadata does not exist

  --- stderr
  Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

I am not too sure what the problem is over here?

Do this work when you compile on Windows? Intuitively, I'd expect this to be set_icon("./assets/icon.ico").

Yes.

I did exactly this:

on Windows and it worked without any drama.

Just did that now, still getting errors:

test@test-virtualbox ~/D/p/testing (master)> cargo build --release --target x86_64-pc-windows-gnu
   Compiling testing v0.1.0 (/home/test/Desktop/programming/testing)
error: failed to run custom build command for `testing v0.1.0 (/home/test/Desktop/programming/testing)`

Caused by:
  process didn't exit successfully: `/home/test/Desktop/programming/testing/target/release/build/testing-eda5b7592106f7de/build-script-build` (exit status: 1)
  --- stdout
  package.metadata does not exist

  --- stderr
  Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

Not too sure why

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.