Skip to content

Commit

Permalink
rustc: Add knowledge of Windows subsystems.
Browse files Browse the repository at this point in the history
This commit is an implementation of [RFC 1665] which adds support for the
`#![windows_subsystem]` attribute. This attribute allows specifying either the
"windows" or "console" subsystems on Windows to the linker.

[RFC 1665]: https://github.com/rust-lang/rfcs/blob/master/text/1665-windows-subsystem.md

Previously all Rust executables were compiled as the "console" subsystem which
meant that if you wanted a graphical application it would erroneously pop up a
console whenever opened. When compiling an application, however, this is
undesired behavior and the "windows" subsystem is used instead to have control
over user interactions.

This attribute is validated, but ignored on all non-Windows platforms.

cc rust-lang#37499
  • Loading branch information
alexcrichton committed Oct 31, 2016
1 parent 074d30d commit 20c3013
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 4 deletions.
12 changes: 10 additions & 2 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ fn link_natively(sess: &Session,
{
let mut linker = trans.linker_info.to_linker(&mut cmd, &sess);
link_args(&mut *linker, sess, crate_type, tmpdir,
objects, out_filename, outputs);
objects, out_filename, outputs, trans);
}
cmd.args(&sess.target.target.options.late_link_args);
for obj in &sess.target.target.options.post_link_objects {
Expand Down Expand Up @@ -711,7 +711,8 @@ fn link_args(cmd: &mut Linker,
tmpdir: &Path,
objects: &[PathBuf],
out_filename: &Path,
outputs: &OutputFilenames) {
outputs: &OutputFilenames,
trans: &CrateTranslation) {

// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
Expand All @@ -726,6 +727,13 @@ fn link_args(cmd: &mut Linker,
}
cmd.output_filename(out_filename);

if crate_type == config::CrateTypeExecutable &&
sess.target.target.options.is_like_windows {
if let Some(ref s) = trans.windows_subsystem {
cmd.subsystem(s);
}
}

// If we're building a dynamic library then some platforms need to make sure
// that all symbols are exported correctly from the dynamic library.
if crate_type != config::CrateTypeExecutable {
Expand Down
29 changes: 29 additions & 0 deletions src/librustc_trans/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub trait Linker {
fn whole_archives(&mut self);
fn no_whole_archives(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
fn subsystem(&mut self, subsystem: &str);
}

pub struct GnuLinker<'a> {
Expand Down Expand Up @@ -294,6 +295,10 @@ impl<'a> Linker for GnuLinker<'a> {

self.cmd.arg(arg);
}

fn subsystem(&mut self, subsystem: &str) {
self.cmd.arg(&format!("-Wl,--subsystem,{}", subsystem));
}
}

pub struct MsvcLinker<'a> {
Expand Down Expand Up @@ -441,6 +446,30 @@ impl<'a> Linker for MsvcLinker<'a> {
arg.push(path);
self.cmd.arg(&arg);
}

fn subsystem(&mut self, subsystem: &str) {
// Note that previous passes of the compiler validated this subsystem,
// so we just blindly pass it to the linker.
self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem));

// Windows has two subsystems we're interested in right now, the console
// and windows subsystems. These both implicitly have different entry
// points (starting symbols). The console entry point starts with
// `mainCRTStartup` and the windows entry point starts with
// `WinMainCRTStartup`. These entry points, defined in system libraries,
// will then later probe for either `main` or `WinMain`, respectively to
// start the application.
//
// In Rust we just always generate a `main` function so we want control
// to always start there, so we force the entry point on the windows
// subsystem to be `mainCRTStartup` to get everything booted up
// correctly.
//
// For more information see RFC #1665
if subsystem == "windows" {
self.cmd.arg("/ENTRY:mainCRTStartup");
}
}
}

fn exported_symbols(scx: &SharedCrateContext,
Expand Down
17 changes: 15 additions & 2 deletions src/librustc_trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
metadata: metadata,
reachable: vec![],
no_builtins: no_builtins,
linker_info: linker_info
linker_info: linker_info,
windows_subsystem: None,
};
}

Expand Down Expand Up @@ -1747,14 +1748,26 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

let linker_info = LinkerInfo::new(&shared_ccx, &reachable_symbols);

let subsystem = attr::first_attr_value_str_by_name(&krate.attrs,
"windows_subsystem");
let windows_subsystem = subsystem.map(|subsystem| {
if subsystem != "windows" && subsystem != "console" {
tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \
`windows` and `console` are allowed",
subsystem));
}
subsystem.to_string()
});

CrateTranslation {
modules: modules,
metadata_module: metadata_module,
link: link_meta,
metadata: metadata,
reachable: reachable_symbols,
no_builtins: no_builtins,
linker_info: linker_info
linker_info: linker_info,
windows_subsystem: windows_subsystem,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_trans/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ pub struct CrateTranslation {
pub metadata: Vec<u8>,
pub reachable: Vec<String>,
pub no_builtins: bool,
pub windows_subsystem: Option<String>,
pub linker_info: back::linker::LinkerInfo
}

Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ declare_features! (

// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
(active, field_init_shorthand, "1.14.0", Some(37340)),

// The #![windows_subsystem] attribute
(active, windows_subsystem, "1.14.0", Some(37499)),
);

declare_features! (
Expand Down Expand Up @@ -713,6 +716,12 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
"defining reflective traits is still evolving",
cfg_fn!(reflect))),

("windows_subsystem", Whitelisted, Gated(Stability::Unstable,
"windows_subsystem",
"the windows subsystem attribute \
id currently unstable",
cfg_fn!(windows_subsystem))),

// Crate level attributes
("crate_name", CrateLevel, Ungated),
("crate_type", CrateLevel, Ungated),
Expand Down
14 changes: 14 additions & 0 deletions src/test/compile-fail/windows-subsystem-gated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![windows_subsystem = "console"]
//~^ ERROR: the windows subsystem attribute is currently unstable

fn main() {}
15 changes: 15 additions & 0 deletions src/test/compile-fail/windows-subsystem-invalid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(windows_subsystem)]
#![windows_subsystem = "wrong"]
//~^ ERROR: invalid subsystem `wrong`, only `windows` and `console` are allowed

fn main() {}
5 changes: 5 additions & 0 deletions src/test/run-make/windows-subsystem/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-include ../tools.mk

all:
$(RUSTC) windows.rs
$(RUSTC) console.rs
15 changes: 15 additions & 0 deletions src/test/run-make/windows-subsystem/console.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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

fn main() {}

14 changes: 14 additions & 0 deletions src/test/run-make/windows-subsystem/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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

fn main() {}

0 comments on commit 20c3013

Please sign in to comment.