rustc: Add knowledge of Windows subsystems.

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 #37499
This commit is contained in:
Alex Crichton 2016-10-31 09:36:30 -07:00
parent 074d30d030
commit 20c301330c
10 changed files with 127 additions and 4 deletions

View file

@ -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 {
@ -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.
@ -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 {

View file

@ -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> {
@ -294,6 +295,10 @@ fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) {
self.cmd.arg(arg);
}
fn subsystem(&mut self, subsystem: &str) {
self.cmd.arg(&format!("-Wl,--subsystem,{}", subsystem));
}
}
pub struct MsvcLinker<'a> {
@ -441,6 +446,30 @@ fn export_symbols(&mut self,
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,

View file

@ -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,
};
}
@ -1747,6 +1748,17 @@ 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,
@ -1754,7 +1766,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
metadata: metadata,
reachable: reachable_symbols,
no_builtins: no_builtins,
linker_info: linker_info
linker_info: linker_info,
windows_subsystem: windows_subsystem,
}
}

View file

@ -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
}

View file

@ -309,6 +309,9 @@ pub fn new() -> 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! (
@ -713,6 +716,12 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att
"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),

View file

@ -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() {}

View file

@ -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() {}

View file

@ -0,0 +1,5 @@
-include ../tools.mk
all:
$(RUSTC) windows.rs
$(RUSTC) console.rs

View file

@ -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() {}

View file

@ -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() {}