user2601064
user2601064

Reputation: 271

Can I conditionally compile my Rust program for a Windows subsystem?

I have a Rust program that I want to compile for the "windows" subsystem when I'm building it for distribution. Currently I am using this in my main.rs:

#![feature(windows_subsystem)]
#![windows_subsystem = "windows"]

This works, but when I run the tests on a Windows machine, the Windows subsystem does not have access to the console so I cannot see the output. I need to comment out the above lines of code in order to see the result of my tests.

Is there a way to conditionally compile which subsystem I'm running on in order to get the tests to work?

Upvotes: 4

Views: 2520

Answers (2)

zrneely
zrneely

Reputation: 1942

I prefer to take this approach to solving the problem of "seeing debug output when run from a console, but don't spawn a console window when running normally" for desktop apps.

First, I always add the #![windows_subsystem = "windows"] attribute.

Then I take a dependency on the windows crate. In Cargo.toml:

windows = { version = "{put in the current version here}", features = ["Win32_System_Console"] }

I use that crate to call AttachConsole at the start of my main function, passing the argument ATTACH_PARENT_PROCESS. For cross-platform development, you can do this in a function that is a no-op on non-Windows platforms. Typically I just ignore the result code of that function.

The end result is that when the program is run from a terminal, it attaches to the parent terminal and STDOUT/STDERR make it there. When run without a terminal, it opens with no console.

The downside of this approach is the extra dependency and its build time, but I've found that the windows crate does a pretty good job of using cargo features to limit the amount of code you need to build. The dependency build is cached as well so it's a one-time cost per machine.

Full example code:

#![windows_subsystem = "windows"]

#[cfg(windows)]
fn attach_parent_console() {
    use windows::Win32::System::Console::*;
    let _ = unsafe { AttachConsole(ATTACH_PARENT_PROCESS) };
}

#[cfg(not(windows))]
fn attach_parent_console() {
    // no-op
}

fn main() {
    attach_parent_console();
    println!("Hello world!");
}

Upvotes: 0

Shepmaster
Shepmaster

Reputation: 432059

As described in The Rust Programming Language, specifically the chapter on conditional compilation:

You can also set another attribute based on a cfg variable with cfg_attr:

#[cfg_attr(a, b)]

Will be the same as #[b] if a is set by cfg attribute, and nothing otherwise.

In this case, it should be something like

#![cfg_attr(my_feature_name_i_made_up, windows_subsystem = "windows")]

Upvotes: 9

Related Questions