Merge pull request #1 from rust-lang/master

Merge upstream changes
This commit is contained in:
Joshua Barretto 2018-04-05 20:59:32 +01:00 committed by GitHub
commit 5e94d5498d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1465 changed files with 19236 additions and 12568 deletions

876
src/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -42,3 +42,7 @@ serde_json = "1.0.2"
toml = "0.4"
lazy_static = "0.2"
time = "0.1"
petgraph = "0.4.12"
[dev-dependencies]
pretty_assertions = "0.5"

View file

@ -31,9 +31,11 @@ extern crate bootstrap;
use std::env;
use std::ffi::OsString;
use std::str::FromStr;
use std::io;
use std::path::PathBuf;
use std::process::{Command, ExitStatus};
use std::process::Command;
use std::str::FromStr;
use std::time::Instant;
fn main() {
let mut args = env::args_os().skip(1).collect::<Vec<_>>();
@ -90,7 +92,7 @@ fn main() {
};
let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
let mut on_fail = env::var_os("RUSTC_ON_FAIL").map(|of| Command::new(of));
let on_fail = env::var_os("RUSTC_ON_FAIL").map(|of| Command::new(of));
let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc));
let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir));
@ -103,6 +105,7 @@ fn main() {
.arg(format!("stage{}", stage))
.env(bootstrap::util::dylib_path_var(),
env::join_paths(&dylib_path).unwrap());
let mut maybe_crate = None;
if let Some(target) = target {
// The stage0 compiler has a special sysroot distinct from what we
@ -134,6 +137,7 @@ fn main() {
.find(|a| &*a[0] == "--crate-name")
.unwrap();
let crate_name = &*crate_name[1];
maybe_crate = Some(crate_name);
// If we're compiling specifically the `panic_abort` crate then we pass
// the `-C panic=abort` option. Note that we do not do this for any
@ -281,31 +285,52 @@ fn main() {
eprintln!("libdir: {:?}", libdir);
}
// Actually run the compiler!
std::process::exit(if let Some(ref mut on_fail) = on_fail {
match cmd.status() {
Ok(s) if s.success() => 0,
_ => {
println!("\nDid not run successfully:\n{:?}\n-------------", cmd);
exec_cmd(on_fail).expect("could not run the backup command");
1
if let Some(mut on_fail) = on_fail {
let e = match cmd.status() {
Ok(s) if s.success() => std::process::exit(0),
e => e,
};
println!("\nDid not run successfully: {:?}\n{:?}\n-------------", e, cmd);
exec_cmd(&mut on_fail).expect("could not run the backup command");
std::process::exit(1);
}
if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() {
if let Some(krate) = maybe_crate {
let start = Instant::now();
let status = cmd
.status()
.expect(&format!("\n\n failed to run {:?}", cmd));
let dur = start.elapsed();
let is_test = args.iter().any(|a| a == "--test");
eprintln!("[RUSTC-TIMING] {} test:{} {}.{:03}",
krate.to_string_lossy(),
is_test,
dur.as_secs(),
dur.subsec_nanos() / 1_000_000);
match status.code() {
Some(i) => std::process::exit(i),
None => {
eprintln!("rustc exited with {}", status);
std::process::exit(0xfe);
}
}
}
} else {
std::process::exit(match exec_cmd(&mut cmd) {
Ok(s) => s.code().unwrap_or(0xfe),
Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
})
})
}
let code = exec_cmd(&mut cmd).expect(&format!("\n\n failed to run {:?}", cmd));
std::process::exit(code);
}
#[cfg(unix)]
fn exec_cmd(cmd: &mut Command) -> ::std::io::Result<ExitStatus> {
fn exec_cmd(cmd: &mut Command) -> io::Result<i32> {
use std::os::unix::process::CommandExt;
Err(cmd.exec())
}
#[cfg(not(unix))]
fn exec_cmd(cmd: &mut Command) -> ::std::io::Result<ExitStatus> {
cmd.status()
fn exec_cmd(cmd: &mut Command) -> io::Result<i32> {
cmd.status().map(|status| status.code().unwrap())
}

View file

@ -314,7 +314,7 @@ class RustBuild(object):
self.build_dir = os.path.join(os.getcwd(), "build")
self.clean = False
self.config_toml = ''
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
self.rust_root = ''
self.use_locked_deps = ''
self.use_vendored_sources = ''
self.verbose = False
@ -647,7 +647,7 @@ class RustBuild(object):
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
self.get_toml('submodules') == "false":
return
slow_submodules = self.get_toml('fast-submodule') == "false"
slow_submodules = self.get_toml('fast-submodules') == "false"
start_time = time()
if slow_submodules:
print('Unconditionally updating all submodules')
@ -710,6 +710,7 @@ def bootstrap(help_triggered):
parser = argparse.ArgumentParser(description='Build rust')
parser.add_argument('--config')
parser.add_argument('--build')
parser.add_argument('--src')
parser.add_argument('--clean', action='store_true')
parser.add_argument('-v', '--verbose', action='count', default=0)
@ -718,6 +719,7 @@ def bootstrap(help_triggered):
# Configure initial bootstrap
build = RustBuild()
build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..'))
build.verbose = args.verbose
build.clean = args.clean
@ -788,6 +790,7 @@ def bootstrap(help_triggered):
env["SRC"] = build.rust_root
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
env["BOOTSTRAP_PYTHON"] = sys.executable
env["BUILD_DIR"] = build.build_dir
run(args, env=env, verbose=build.verbose)

View file

@ -9,7 +9,7 @@
// except according to those terms.
use std::any::Any;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::env;
use std::fmt::Debug;
@ -18,6 +18,8 @@ use std::hash::Hash;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{Instant, Duration};
use std::collections::HashMap;
use compile;
use install;
@ -34,12 +36,20 @@ use native;
pub use Compiler;
use petgraph::Graph;
use petgraph::graph::NodeIndex;
pub struct Builder<'a> {
pub build: &'a Build,
pub top_stage: u32,
pub kind: Kind,
cache: Cache,
stack: RefCell<Vec<Box<Any>>>,
time_spent_on_dependencies: Cell<Duration>,
pub paths: Vec<PathBuf>,
graph_nodes: RefCell<HashMap<String, NodeIndex>>,
graph: RefCell<Graph<String, bool>>,
parent: Cell<Option<NodeIndex>>,
}
impl<'a> Deref for Builder<'a> {
@ -308,19 +318,24 @@ impl<'a> Builder<'a> {
test::UiFullDeps, test::RunPassFullDeps, test::RunFailFullDeps,
test::CompileFailFullDeps, test::IncrementalFullDeps, test::Rustdoc, test::Pretty,
test::RunPassPretty, test::RunFailPretty, test::RunPassValgrindPretty,
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty, test::RunMake,
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty,
test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck,
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
test::RunMakeFullDeps,
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
test::TheBook, test::UnstableBook,
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme),
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,
// Run run-make last, since these won't pass without make on Windows
test::RunMake),
Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
doc::Reference, doc::Rustdoc, doc::RustByExample, doc::CargoBook),
Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo,
dist::Rls, dist::Rustfmt, dist::Extended, dist::HashSign),
doc::Standalone, doc::Std, doc::Test, doc::WhitelistedRustc, doc::Rustc,
doc::ErrorIndex, doc::Nomicon, doc::Reference, doc::Rustdoc, doc::RustByExample,
doc::CargoBook),
Kind::Dist => describe!(dist::Docs, dist::RustcDocs, dist::Mingw, dist::Rustc,
dist::DebuggerScripts, dist::Std, dist::Analysis, dist::Src,
dist::PlainSourceTarball, dist::Cargo, dist::Rls, dist::Rustfmt, dist::Extended,
dist::HashSign),
Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
install::Rustfmt, install::Analysis, install::Src, install::Rustc),
}
@ -343,6 +358,11 @@ impl<'a> Builder<'a> {
kind,
cache: Cache::new(),
stack: RefCell::new(Vec::new()),
time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
paths: vec![],
graph_nodes: RefCell::new(HashMap::new()),
graph: RefCell::new(Graph::new()),
parent: Cell::new(None),
};
let builder = &builder;
@ -359,7 +379,7 @@ impl<'a> Builder<'a> {
Some(help)
}
pub fn run(build: &Build) {
pub fn new(build: &Build) -> Builder {
let (kind, paths) = match build.config.cmd {
Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
Subcommand::Check { ref paths } => (Kind::Check, &paths[..]),
@ -371,32 +391,40 @@ impl<'a> Builder<'a> {
Subcommand::Clean { .. } => panic!(),
};
if let Some(path) = paths.get(0) {
if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
return;
}
}
let builder = Builder {
build,
top_stage: build.config.stage.unwrap_or(2),
kind,
cache: Cache::new(),
stack: RefCell::new(Vec::new()),
time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
paths: paths.to_owned(),
graph_nodes: RefCell::new(HashMap::new()),
graph: RefCell::new(Graph::new()),
parent: Cell::new(None),
};
if kind == Kind::Dist {
assert!(!build.config.test_miri, "Do not distribute with miri enabled.\n\
assert!(!builder.config.test_miri, "Do not distribute with miri enabled.\n\
The distributed libraries would include all MIR (increasing binary size).
The distributed MIR would include validation statements.");
}
StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths);
builder
}
pub fn execute_cli(&self) -> Graph<String, bool> {
self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
self.graph.borrow().clone()
}
pub fn default_doc(&self, paths: Option<&[PathBuf]>) {
let paths = paths.unwrap_or(&[]);
StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths);
self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths);
}
fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
StepDescription::run(v, self, paths);
}
/// Obtain a compiler at a given stage and for a given host. Explicitly does
@ -546,7 +574,9 @@ impl<'a> Builder<'a> {
let mut extra_args = env::var(&format!("RUSTFLAGS_STAGE_{}", stage)).unwrap_or_default();
if stage != 0 {
let s = env::var("RUSTFLAGS_STAGE_NOT_0").unwrap_or_default();
extra_args.push_str(" ");
if !extra_args.is_empty() {
extra_args.push_str(" ");
}
extra_args.push_str(&s);
}
@ -662,6 +692,10 @@ impl<'a> Builder<'a> {
cargo.env("RUSTC_ON_FAIL", on_fail);
}
if self.config.print_step_timings {
cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
}
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
// Throughout the build Cargo can execute a number of build scripts
@ -813,12 +847,56 @@ impl<'a> Builder<'a> {
if let Some(out) = self.cache.get(&step) {
self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step));
{
let mut graph = self.graph.borrow_mut();
let parent = self.parent.get();
let us = *self.graph_nodes.borrow_mut()
.entry(format!("{:?}", step))
.or_insert_with(|| graph.add_node(format!("{:?}", step)));
if let Some(parent) = parent {
graph.add_edge(parent, us, false);
}
}
return out;
}
self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step));
stack.push(Box::new(step.clone()));
}
let out = step.clone().run(self);
let prev_parent = self.parent.get();
{
let mut graph = self.graph.borrow_mut();
let parent = self.parent.get();
let us = *self.graph_nodes.borrow_mut()
.entry(format!("{:?}", step))
.or_insert_with(|| graph.add_node(format!("{:?}", step)));
self.parent.set(Some(us));
if let Some(parent) = parent {
graph.add_edge(parent, us, true);
}
}
let (out, dur) = {
let start = Instant::now();
let zero = Duration::new(0, 0);
let parent = self.time_spent_on_dependencies.replace(zero);
let out = step.clone().run(self);
let dur = start.elapsed();
let deps = self.time_spent_on_dependencies.replace(parent + dur);
(out, dur - deps)
};
self.parent.set(prev_parent);
if self.build.config.print_step_timings && dur > Duration::from_millis(100) {
println!("[TIMING] {:?} -- {}.{:03}",
step,
dur.as_secs(),
dur.subsec_nanos() / 1_000_000);
}
{
let mut stack = self.stack.borrow_mut();
let cur_step = stack.pop().expect("step stack empty");
@ -829,3 +907,483 @@ impl<'a> Builder<'a> {
out
}
}
#[cfg(test)]
mod __test {
use config::Config;
use std::thread;
use super::*;
fn configure(host: &[&str], target: &[&str]) -> Config {
let mut config = Config::default_opts();
// don't save toolstates
config.save_toolstates = None;
config.run_host_only = true;
config.dry_run = true;
// try to avoid spurious failures in dist where we create/delete each others file
let dir = config.out.join("tmp-rustbuild-tests")
.join(&thread::current().name().unwrap_or("unknown").replace(":", "-"));
t!(fs::create_dir_all(&dir));
config.out = dir;
config.build = INTERNER.intern_str("A");
config.hosts = vec![config.build].clone().into_iter()
.chain(host.iter().map(|s| INTERNER.intern_str(s))).collect::<Vec<_>>();
config.targets = config.hosts.clone().into_iter()
.chain(target.iter().map(|s| INTERNER.intern_str(s))).collect::<Vec<_>>();
config
}
fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
v.into_iter().map(|(a, _)| a).collect::<Vec<_>>()
}
#[test]
fn dist_baseline() {
let build = Build::new(configure(&[], &[]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_targets() {
let build = Build::new(configure(&[], &["B"]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
dist::Docs { stage: 2, host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
dist::Mingw { host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_hosts() {
let build = Build::new(configure(&["B"], &[]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
dist::Docs { stage: 2, host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
dist::Mingw { host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_targets_and_hosts() {
let build = Build::new(configure(&["B"], &["C"]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
let c = INTERNER.intern_str("C");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
dist::Docs { stage: 2, host: b },
dist::Docs { stage: 2, host: c },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
dist::Mingw { host: b },
dist::Mingw { host: c },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: c,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_target_flag() {
let mut config = configure(&["B"], &["C"]);
config.run_host_only = false; // as-if --target=C was passed
let build = Build::new(config);
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
let c = INTERNER.intern_str("C");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
dist::Docs { stage: 2, host: b },
dist::Docs { stage: 2, host: c },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
dist::Mingw { host: b },
dist::Mingw { host: c },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: c,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[]);
}
#[test]
fn dist_with_same_targets_and_hosts() {
let build = Build::new(configure(&["B"], &["B"]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
dist::Docs { stage: 2, host: a },
dist::Docs { stage: 2, host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
dist::Mingw { host: a },
dist::Mingw { host: b },
]);
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]);
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
dist::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
assert_eq!(first(builder.cache.all::<compile::Std>()), &[
compile::Std {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Std {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Std {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
compile::Std {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
compile::Std {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
compile::Test {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<compile::Assemble>()), &[
compile::Assemble {
target_compiler: Compiler { host: a, stage: 0 },
},
compile::Assemble {
target_compiler: Compiler { host: a, stage: 1 },
},
compile::Assemble {
target_compiler: Compiler { host: a, stage: 2 },
},
compile::Assemble {
target_compiler: Compiler { host: b, stage: 2 },
},
]);
}
#[test]
fn build_default() {
let build = Build::new(configure(&["B"], &["C"]));
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
let c = INTERNER.intern_str("C");
assert!(!builder.cache.all::<compile::Std>().is_empty());
assert!(!builder.cache.all::<compile::Assemble>().is_empty());
assert_eq!(first(builder.cache.all::<compile::Rustc>()), &[
compile::Rustc {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: b, stage: 2 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 0 },
target: b,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
compile::Rustc {
compiler: Compiler { host: b, stage: 2 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
compile::Test {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 0 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: c,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: c,
},
]);
}
#[test]
fn build_with_target_flag() {
let mut config = configure(&["B"], &["C"]);
config.run_host_only = false;
let build = Build::new(config);
let mut builder = Builder::new(&build);
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
let a = INTERNER.intern_str("A");
let b = INTERNER.intern_str("B");
let c = INTERNER.intern_str("C");
assert!(!builder.cache.all::<compile::Std>().is_empty());
assert_eq!(first(builder.cache.all::<compile::Assemble>()), &[
compile::Assemble {
target_compiler: Compiler { host: a, stage: 0 },
},
compile::Assemble {
target_compiler: Compiler { host: a, stage: 1 },
},
compile::Assemble {
target_compiler: Compiler { host: b, stage: 1 },
},
compile::Assemble {
target_compiler: Compiler { host: a, stage: 2 },
},
compile::Assemble {
target_compiler: Compiler { host: b, stage: 2 },
},
]);
assert_eq!(first(builder.cache.all::<compile::Rustc>()), &[
compile::Rustc {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 0 },
target: b,
},
compile::Rustc {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
]);
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
compile::Test {
compiler: Compiler { host: a, stage: 0 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: a,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: a,
},
compile::Test {
compiler: Compiler { host: a, stage: 0 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 1 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: b,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: b,
},
compile::Test {
compiler: Compiler { host: a, stage: 2 },
target: c,
},
compile::Test {
compiler: Compiler { host: b, stage: 2 },
target: c,
},
]);
}
}

View file

@ -21,6 +21,7 @@ use std::mem;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::cmp::{PartialOrd, Ord, Ordering};
use builder::Step;
@ -154,6 +155,19 @@ impl AsRef<OsStr> for Interned<String> {
}
}
impl PartialOrd<Interned<String>> for Interned<String> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let l = INTERNER.strs.lock().unwrap();
l.get(*self).partial_cmp(l.get(*other))
}
}
impl Ord for Interned<String> {
fn cmp(&self, other: &Self) -> Ordering {
let l = INTERNER.strs.lock().unwrap();
l.get(*self).cmp(l.get(*other))
}
}
struct TyIntern<T> {
items: Vec<T>,
@ -264,4 +278,16 @@ impl Cache {
.expect("invalid type mapped");
stepcache.get(step).cloned()
}
#[cfg(test)]
pub fn all<S: Ord + Copy + Step>(&mut self) -> Vec<(S, S::Output)> {
let cache = self.0.get_mut();
let type_id = TypeId::of::<S>();
let mut v = cache.remove(&type_id)
.map(|b| b.downcast::<HashMap<S, S::Output>>().expect("correct type"))
.map(|m| m.into_iter().collect::<Vec<_>>())
.unwrap_or_default();
v.sort_by_key(|&(a, _)| a);
v
}
}

View file

@ -24,7 +24,7 @@ use Build;
use config::Config;
// The version number
pub const CFG_RELEASE_NUM: &str = "1.26.0";
pub const CFG_RELEASE_NUM: &str = "1.27.0";
pub struct GitInfo {
inner: Option<Info>,

View file

@ -40,19 +40,20 @@ impl Step for Std {
let target = self.target;
let compiler = builder.compiler(0, build.build);
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
println!("Checking std artifacts ({} -> {})", &compiler.host, target);
let out_dir = build.stage_out(compiler, Mode::Libstd);
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "check");
std_cargo(builder, &compiler, target, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
println!("Checking std artifacts ({} -> {})", &compiler.host, target);
run_cargo(build,
&mut cargo,
&libstd_stamp(build, compiler, target),
true);
let libdir = builder.sysroot_libdir(compiler, target);
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
add_to_sysroot(&build, &libdir, &libstd_stamp(build, compiler, target));
}
}
@ -86,21 +87,22 @@ impl Step for Rustc {
let compiler = builder.compiler(0, build.build);
let target = self.target;
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
println!("Checking compiler artifacts ({} -> {})", &compiler.host, target);
let stage_out = builder.stage_out(compiler, Mode::Librustc);
build.clear_if_dirty(&stage_out, &libstd_stamp(build, compiler, target));
build.clear_if_dirty(&stage_out, &libtest_stamp(build, compiler, target));
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check");
rustc_cargo(build, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
println!("Checking compiler artifacts ({} -> {})", &compiler.host, target);
run_cargo(build,
&mut cargo,
&librustc_stamp(build, compiler, target),
true);
let libdir = builder.sysroot_libdir(compiler, target);
add_to_sysroot(&libdir, &librustc_stamp(build, compiler, target));
add_to_sysroot(&build, &libdir, &librustc_stamp(build, compiler, target));
}
}
@ -128,18 +130,20 @@ impl Step for Test {
let target = self.target;
let compiler = builder.compiler(0, build.build);
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
println!("Checking test artifacts ({} -> {})", &compiler.host, target);
let out_dir = build.stage_out(compiler, Mode::Libtest);
build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "check");
test_cargo(build, &compiler, target, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
println!("Checking test artifacts ({} -> {})", &compiler.host, target);
run_cargo(build,
&mut cargo,
&libtest_stamp(build, compiler, target),
true);
let libdir = builder.sysroot_libdir(compiler, target);
add_to_sysroot(&libdir, &libtest_stamp(build, compiler, target));
add_to_sysroot(&build, &libdir, &libtest_stamp(build, compiler, target));
}
}

View file

@ -16,6 +16,7 @@
//! compiler. This module is also responsible for assembling the sysroot as it
//! goes along from the output of the previous stage.
use std::borrow::Cow;
use std::env;
use std::fs::{self, File};
use std::io::BufReader;
@ -29,7 +30,7 @@ use build_helper::{output, mtime, up_to_date};
use filetime::FileTime;
use serde_json;
use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv};
use util::{exe, libdir, is_dylib, CiEnv};
use {Build, Compiler, Mode};
use native;
use tool;
@ -37,7 +38,7 @@ use tool;
use cache::{INTERNER, Interned};
use builder::{Step, RunConfig, ShouldRun, Builder};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Std {
pub target: Interned<String>,
pub compiler: Compiler,
@ -76,7 +77,7 @@ impl Step for Std {
compiler: from,
target,
});
println!("Uplifting stage1 std ({} -> {})", from.host, target);
builder.info(&format!("Uplifting stage1 std ({} -> {})", from.host, target));
// Even if we're not building std this stage, the new sysroot must
// still contain the musl startup objects.
@ -93,19 +94,19 @@ impl Step for Std {
return;
}
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
&compiler.host, target);
if target.contains("musl") {
let libdir = builder.sysroot_libdir(compiler, target);
copy_musl_third_party_objects(build, target, &libdir);
}
let out_dir = build.stage_out(compiler, Mode::Libstd);
let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
std_cargo(builder, &compiler, target, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
build.info(&format!("Building stage{} std artifacts ({} -> {})", compiler.stage,
&compiler.host, target));
run_cargo(build,
&mut cargo,
&libstd_stamp(build, compiler, target),
@ -129,7 +130,7 @@ fn copy_musl_third_party_objects(build: &Build,
target: Interned<String>,
into: &Path) {
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
build.copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
}
}
@ -139,48 +140,58 @@ pub fn std_cargo(build: &Builder,
compiler: &Compiler,
target: Interned<String>,
cargo: &mut Command) {
let mut features = build.std_features();
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
}
// When doing a local rebuild we tell cargo that we're stage1 rather than
// stage0. This works fine if the local rust and being-built rust have the
// same view of what the default allocator is, but fails otherwise. Since
// we don't have a way to express an allocator preference yet, work
// around the issue in the case of a local rebuild with jemalloc disabled.
if compiler.stage == 0 && build.local_rebuild && !build.config.use_jemalloc {
features.push_str(" force_alloc_system");
}
if build.no_std(target) == Some(true) {
// for no-std targets we only compile a few no_std crates
cargo.arg("--features").arg("c mem")
.args(&["-p", "alloc"])
.args(&["-p", "compiler_builtins"])
.args(&["-p", "std_unicode"])
.arg("--manifest-path")
.arg(build.src.join("src/rustc/compiler_builtins_shim/Cargo.toml"));
} else {
let mut features = build.std_features();
if compiler.stage != 0 && build.config.sanitizers {
// This variable is used by the sanitizer runtime crates, e.g.
// rustc_lsan, to build the sanitizer runtime from C code
// When this variable is missing, those crates won't compile the C code,
// so we don't set this variable during stage0 where llvm-config is
// missing
// We also only build the runtimes when --enable-sanitizers (or its
// config.toml equivalent) is used
let llvm_config = build.ensure(native::Llvm {
target: build.config.build,
emscripten: false,
});
cargo.env("LLVM_CONFIG", llvm_config);
}
cargo.arg("--features").arg(features)
.arg("--manifest-path")
.arg(build.src.join("src/libstd/Cargo.toml"));
if let Some(target) = build.config.target_config.get(&target) {
if let Some(ref jemalloc) = target.jemalloc {
cargo.env("JEMALLOC_OVERRIDE", jemalloc);
// When doing a local rebuild we tell cargo that we're stage1 rather than
// stage0. This works fine if the local rust and being-built rust have the
// same view of what the default allocator is, but fails otherwise. Since
// we don't have a way to express an allocator preference yet, work
// around the issue in the case of a local rebuild with jemalloc disabled.
if compiler.stage == 0 && build.local_rebuild && !build.config.use_jemalloc {
features.push_str(" force_alloc_system");
}
}
if target.contains("musl") {
if let Some(p) = build.musl_root(target) {
cargo.env("MUSL_ROOT", p);
if compiler.stage != 0 && build.config.sanitizers {
// This variable is used by the sanitizer runtime crates, e.g.
// rustc_lsan, to build the sanitizer runtime from C code
// When this variable is missing, those crates won't compile the C code,
// so we don't set this variable during stage0 where llvm-config is
// missing
// We also only build the runtimes when --enable-sanitizers (or its
// config.toml equivalent) is used
let llvm_config = build.ensure(native::Llvm {
target: build.config.build,
emscripten: false,
});
cargo.env("LLVM_CONFIG", llvm_config);
}
cargo.arg("--features").arg(features)
.arg("--manifest-path")
.arg(build.src.join("src/libstd/Cargo.toml"));
if let Some(target) = build.config.target_config.get(&target) {
if let Some(ref jemalloc) = target.jemalloc {
cargo.env("JEMALLOC_OVERRIDE", jemalloc);
}
}
if target.contains("musl") {
if let Some(p) = build.musl_root(target) {
cargo.env("MUSL_ROOT", p);
}
}
}
}
@ -212,20 +223,20 @@ impl Step for StdLink {
let compiler = self.compiler;
let target_compiler = self.target_compiler;
let target = self.target;
println!("Copying stage{} std from stage{} ({} -> {} / {})",
build.info(&format!("Copying stage{} std from stage{} ({} -> {} / {})",
target_compiler.stage,
compiler.stage,
&compiler.host,
target_compiler.host,
target);
target));
let libdir = builder.sysroot_libdir(target_compiler, target);
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
add_to_sysroot(&build, &libdir, &libstd_stamp(build, compiler, target));
if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
// The sanitizers are only built in stage1 or above, so the dylibs will
// be missing in stage0 and causes panic. See the `std()` function above
// for reason why the sanitizers are not built in stage0.
copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir);
copy_apple_sanitizer_dylibs(&build, &build.native_dir(target), "osx", &libdir);
}
builder.ensure(tool::CleanTools {
@ -236,7 +247,7 @@ impl Step for StdLink {
}
}
fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
fn copy_apple_sanitizer_dylibs(build: &Build, native_dir: &Path, platform: &str, into: &Path) {
for &sanitizer in &["asan", "tsan"] {
let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
let mut src_path = native_dir.join(sanitizer);
@ -244,7 +255,7 @@ fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
src_path.push("lib");
src_path.push("darwin");
src_path.push(&filename);
copy(&src_path, &into.join(filename));
build.copy(&src_path, &into.join(filename));
}
}
@ -300,7 +311,7 @@ impl Step for StartupObjects {
.arg(src_file));
}
copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
build.copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
}
for obj in ["crt2.o", "dllcrt2.o"].iter() {
@ -308,15 +319,15 @@ impl Step for StartupObjects {
build.cc(target),
target,
obj);
copy(&src, &sysroot_dir.join(obj));
build.copy(&src, &sysroot_dir.join(obj));
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Test {
pub compiler: Compiler,
pub target: Interned<String>,
pub compiler: Compiler,
}
impl Step for Test {
@ -351,7 +362,7 @@ impl Step for Test {
compiler: builder.compiler(1, build.build),
target,
});
println!("Uplifting stage1 test ({} -> {})", &build.build, target);
build.info(&format!("Uplifting stage1 test ({} -> {})", &build.build, target));
builder.ensure(TestLink {
compiler: builder.compiler(1, build.build),
target_compiler: compiler,
@ -360,13 +371,14 @@ impl Step for Test {
return;
}
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
&compiler.host, target);
let out_dir = build.stage_out(compiler, Mode::Libtest);
let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
test_cargo(build, &compiler, target, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
build.info(&format!("Building stage{} test artifacts ({} -> {})", compiler.stage,
&compiler.host, target));
run_cargo(build,
&mut cargo,
&libtest_stamp(build, compiler, target),
@ -412,13 +424,13 @@ impl Step for TestLink {
let compiler = self.compiler;
let target_compiler = self.target_compiler;
let target = self.target;
println!("Copying stage{} test from stage{} ({} -> {} / {})",
build.info(&format!("Copying stage{} test from stage{} ({} -> {} / {})",
target_compiler.stage,
compiler.stage,
&compiler.host,
target_compiler.host,
target);
add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
target));
add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target),
&libtest_stamp(build, compiler, target));
builder.ensure(tool::CleanTools {
compiler: target_compiler,
@ -428,10 +440,10 @@ impl Step for TestLink {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
pub compiler: Compiler,
pub target: Interned<String>,
pub compiler: Compiler,
}
impl Step for Rustc {
@ -467,7 +479,7 @@ impl Step for Rustc {
compiler: builder.compiler(1, build.build),
target,
});
println!("Uplifting stage1 rustc ({} -> {})", &build.build, target);
build.info(&format!("Uplifting stage1 rustc ({} -> {})", &build.build, target));
builder.ensure(RustcLink {
compiler: builder.compiler(1, build.build),
target_compiler: compiler,
@ -481,17 +493,16 @@ impl Step for Rustc {
compiler: builder.compiler(self.compiler.stage, build.build),
target: build.build,
});
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
println!("Building stage{} compiler artifacts ({} -> {})",
compiler.stage, &compiler.host, target);
let stage_out = builder.stage_out(compiler, Mode::Librustc);
build.clear_if_dirty(&stage_out, &libstd_stamp(build, compiler, target));
build.clear_if_dirty(&stage_out, &libtest_stamp(build, compiler, target));
let cargo_out = builder.cargo_out(compiler, Mode::Librustc, target);
build.clear_if_dirty(&cargo_out, &libstd_stamp(build, compiler, target));
build.clear_if_dirty(&cargo_out, &libtest_stamp(build, compiler, target));
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
rustc_cargo(build, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
build.info(&format!("Building stage{} compiler artifacts ({} -> {})",
compiler.stage, &compiler.host, target));
run_cargo(build,
&mut cargo,
&librustc_stamp(build, compiler, target),
@ -568,13 +579,13 @@ impl Step for RustcLink {
let compiler = self.compiler;
let target_compiler = self.target_compiler;
let target = self.target;
println!("Copying stage{} rustc from stage{} ({} -> {} / {})",
build.info(&format!("Copying stage{} rustc from stage{} ({} -> {} / {})",
target_compiler.stage,
compiler.stage,
&compiler.host,
target_compiler.host,
target);
add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
target));
add_to_sysroot(&build, &builder.sysroot_libdir(target_compiler, target),
&librustc_stamp(build, compiler, target));
builder.ensure(tool::CleanTools {
compiler: target_compiler,
@ -634,8 +645,6 @@ impl Step for CodegenBackend {
.arg(build.src.join("src/librustc_trans/Cargo.toml"));
rustc_cargo_env(build, &mut cargo);
let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
match &*self.backend {
"llvm" | "emscripten" => {
// Build LLVM for our target. This will implicitly build the
@ -649,8 +658,8 @@ impl Step for CodegenBackend {
features.push_str(" emscripten");
}
println!("Building stage{} codegen artifacts ({} -> {}, {})",
compiler.stage, &compiler.host, target, self.backend);
build.info(&format!("Building stage{} codegen artifacts ({} -> {}, {})",
compiler.stage, &compiler.host, target, self.backend));
// Pass down configuration from the LLVM build into the build of
// librustc_llvm and librustc_trans.
@ -685,10 +694,15 @@ impl Step for CodegenBackend {
let tmp_stamp = build.cargo_out(compiler, Mode::Librustc, target)
.join(".tmp.stamp");
let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
let files = run_cargo(build,
cargo.arg("--features").arg(features),
&tmp_stamp,
false);
if builder.config.dry_run {
return;
}
let mut files = files.into_iter()
.filter(|f| {
let filename = f.file_name().unwrap().to_str().unwrap();
@ -732,6 +746,10 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
let dst = builder.sysroot_codegen_backends(target_compiler);
t!(fs::create_dir_all(&dst));
if builder.config.dry_run {
return;
}
for backend in builder.config.rust_codegen_backends.iter() {
let stamp = codegen_backend_stamp(build, compiler, target, *backend);
let mut dylib = String::new();
@ -747,7 +765,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
backend,
&filename[dot..])
};
copy(&file, &dst.join(target_filename));
build.copy(&file, &dst.join(target_filename));
}
}
@ -763,7 +781,7 @@ fn copy_lld_to_sysroot(builder: &Builder,
t!(fs::create_dir_all(&dst));
let exe = exe("lld", &target);
copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe));
builder.copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe));
}
/// Cargo's output path for the standard library in a given stage, compiled
@ -835,7 +853,7 @@ impl Step for Sysroot {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
pub struct Assemble {
/// The compiler which we will produce in this step. Assemble itself will
/// take care of ensuring that the necessary prerequisites to do so exist,
@ -915,7 +933,7 @@ impl Step for Assemble {
}
}
let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 {
let lld_install = if build.config.lld_enabled {
Some(builder.ensure(native::Lld {
target: target_compiler.host,
}))
@ -925,17 +943,17 @@ impl Step for Assemble {
let stage = target_compiler.stage;
let host = target_compiler.host;
println!("Assembling stage{} compiler ({})", stage, host);
build.info(&format!("Assembling stage{} compiler ({})", stage, host));
// Link in all dylibs to the libdir
let sysroot = builder.sysroot(target_compiler);
let sysroot_libdir = sysroot.join(libdir(&*host));
t!(fs::create_dir_all(&sysroot_libdir));
let src_libdir = builder.sysroot_libdir(build_compiler, host);
for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
for f in builder.read_dir(&src_libdir) {
let filename = f.file_name().into_string().unwrap();
if is_dylib(&filename) {
copy(&f.path(), &sysroot_libdir.join(&filename));
builder.copy(&f.path(), &sysroot_libdir.join(&filename));
}
}
@ -953,7 +971,7 @@ impl Step for Assemble {
t!(fs::create_dir_all(&bindir));
let compiler = builder.rustc(target_compiler);
let _ = fs::remove_file(&compiler);
copy(&rustc, &compiler);
builder.copy(&rustc, &compiler);
target_compiler
}
@ -963,10 +981,10 @@ impl Step for Assemble {
///
/// For a particular stage this will link the file listed in `stamp` into the
/// `sysroot_dst` provided.
pub fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
pub fn add_to_sysroot(build: &Build, sysroot_dst: &Path, stamp: &Path) {
t!(fs::create_dir_all(&sysroot_dst));
for path in read_stamp_file(stamp) {
copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
for path in build.read_stamp_file(stamp) {
build.copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
}
}
@ -996,24 +1014,10 @@ fn stderr_isatty() -> bool {
pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool)
-> Vec<PathBuf>
{
// Instruct Cargo to give us json messages on stdout, critically leaving
// stderr as piped so we can get those pretty colors.
cargo.arg("--message-format").arg("json")
.stdout(Stdio::piped());
if stderr_isatty() && build.ci_env == CiEnv::None {
// since we pass message-format=json to cargo, we need to tell the rustc
// wrapper to give us colored output if necessary. This is because we
// only want Cargo's JSON output, not rustcs.
cargo.env("RUSTC_COLOR", "1");
if build.config.dry_run {
return Vec::new();
}
build.verbose(&format!("running: {:?}", cargo));
let mut child = match cargo.spawn() {
Ok(child) => child,
Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
};
// `target_root_dir` looks like $dir/$target/release
let target_root_dir = stamp.parent().unwrap();
// `target_deps_dir` looks like $dir/$target/release/deps
@ -1028,46 +1032,33 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
// files we need to probe for later.
let mut deps = Vec::new();
let mut toplevel = Vec::new();
let stdout = BufReader::new(child.stdout.take().unwrap());
for line in stdout.lines() {
let line = t!(line);
let json: serde_json::Value = if line.starts_with("{") {
t!(serde_json::from_str(&line))
} else {
// If this was informational, just print it out and continue
println!("{}", line);
continue
let ok = stream_cargo(build, cargo, &mut |msg| {
let filenames = match msg {
CargoMessage::CompilerArtifact { filenames, .. } => filenames,
_ => return,
};
if json["reason"].as_str() != Some("compiler-artifact") {
if build.config.rustc_error_format.as_ref().map_or(false, |e| e == "json") {
// most likely not a cargo message, so let's send it out as well
println!("{}", line);
}
continue
}
for filename in json["filenames"].as_array().unwrap() {
let filename = filename.as_str().unwrap();
for filename in filenames {
// Skip files like executables
if !filename.ends_with(".rlib") &&
!filename.ends_with(".lib") &&
!is_dylib(&filename) &&
!(is_check && filename.ends_with(".rmeta")) {
continue
return;
}
let filename = Path::new(filename);
let filename = Path::new(&*filename);
// If this was an output file in the "host dir" we don't actually
// worry about it, it's not relevant for us.
if filename.starts_with(&host_root_dir) {
continue;
return;
}
// If this was output in the `deps` dir then this is a precise file
// name (hash included) so we start tracking it.
if filename.starts_with(&target_deps_dir) {
deps.push(filename.to_path_buf());
continue;
return;
}
// Otherwise this was a "top level artifact" which right now doesn't
@ -1088,15 +1079,10 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
toplevel.push((file_stem, extension, expected_len));
}
}
});
// Make sure Cargo actually succeeded after we read all of its stdout.
let status = t!(child.wait());
if !status.success() {
panic!("command did not execute successfully: {:?}\n\
expected success, got: {}",
cargo,
status);
if !ok {
panic!("cargo must succeed");
}
// Ok now we need to actually find all the files listed in `toplevel`. We've
@ -1155,7 +1141,7 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
let max = max.unwrap();
let max_path = max_path.unwrap();
if stamp_contents == new_contents && max <= stamp_mtime {
build.verbose(&format!("not updating {:?}; contents equal and {} <= {}",
build.verbose(&format!("not updating {:?}; contents equal and {:?} <= {:?}",
stamp, max, stamp_mtime));
return deps
}
@ -1167,3 +1153,66 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
t!(t!(File::create(stamp)).write_all(&new_contents));
deps
}
pub fn stream_cargo(
build: &Build,
cargo: &mut Command,
cb: &mut FnMut(CargoMessage),
) -> bool {
if build.config.dry_run {
return true;
}
// Instruct Cargo to give us json messages on stdout, critically leaving
// stderr as piped so we can get those pretty colors.
cargo.arg("--message-format").arg("json")
.stdout(Stdio::piped());
if stderr_isatty() && build.ci_env == CiEnv::None {
// since we pass message-format=json to cargo, we need to tell the rustc
// wrapper to give us colored output if necessary. This is because we
// only want Cargo's JSON output, not rustcs.
cargo.env("RUSTC_COLOR", "1");
}
build.verbose(&format!("running: {:?}", cargo));
let mut child = match cargo.spawn() {
Ok(child) => child,
Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
};
// Spawn Cargo slurping up its JSON output. We'll start building up the
// `deps` array of all files it generated along with a `toplevel` array of
// files we need to probe for later.
let stdout = BufReader::new(child.stdout.take().unwrap());
for line in stdout.lines() {
let line = t!(line);
match serde_json::from_str::<CargoMessage>(&line) {
Ok(msg) => cb(msg),
// If this was informational, just print it out and continue
Err(_) => println!("{}", line)
}
}
// Make sure Cargo actually succeeded after we read all of its stdout.
let status = t!(child.wait());
if !status.success() {
eprintln!("command did not execute successfully: {:?}\n\
expected success, got: {}",
cargo,
status);
}
status.success()
}
#[derive(Deserialize)]
#[serde(tag = "reason", rename_all = "kebab-case")]
pub enum CargoMessage<'a> {
CompilerArtifact {
package_id: Cow<'a, str>,
features: Vec<Cow<'a, str>>,
filenames: Vec<Cow<'a, str>>,
},
BuildScriptExecuted {
package_id: Cow<'a, str>,
}
}

View file

@ -15,7 +15,7 @@
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs::File;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process;
@ -45,6 +45,7 @@ pub struct Config {
pub ninja: bool,
pub verbose: usize,
pub submodules: bool,
pub fast_submodules: bool,
pub compiler_docs: bool,
pub docs: bool,
pub locked_deps: bool,
@ -68,6 +69,7 @@ pub struct Config {
pub jobs: Option<u32>,
pub cmd: Subcommand,
pub incremental: bool,
pub dry_run: bool,
// llvm codegen options
pub llvm_enabled: bool,
@ -121,6 +123,7 @@ pub struct Config {
pub quiet_tests: bool,
pub test_miri: bool,
pub save_toolstates: Option<PathBuf>,
pub print_step_timings: bool,
// Fallback musl-root for all targets
pub musl_root: Option<PathBuf>,
@ -141,6 +144,7 @@ pub struct Config {
// These are either the stage0 downloaded binaries or the locally installed ones.
pub initial_cargo: PathBuf,
pub initial_rustc: PathBuf,
pub out: PathBuf,
}
/// Per-target configuration stored in the global configuration structure.
@ -157,6 +161,7 @@ pub struct Target {
pub crt_static: Option<bool>,
pub musl_root: Option<PathBuf>,
pub qemu_rootfs: Option<PathBuf>,
pub no_std: bool,
}
/// Structure of the `config.toml` file that configuration is read from.
@ -190,6 +195,7 @@ struct Build {
compiler_docs: Option<bool>,
docs: Option<bool>,
submodules: Option<bool>,
fast_submodules: Option<bool>,
gdb: Option<String>,
locked_deps: Option<bool>,
vendor: Option<bool>,
@ -204,6 +210,7 @@ struct Build {
openssl_static: Option<bool>,
configure_args: Option<Vec<String>>,
local_rebuild: Option<bool>,
print_step_timings: Option<bool>,
}
/// TOML representation of various global install decisions.
@ -313,11 +320,8 @@ struct TomlTarget {
}
impl Config {
pub fn parse(args: &[String]) -> Config {
let flags = Flags::parse(&args);
let file = flags.config.clone();
pub fn default_opts() -> Config {
let mut config = Config::default();
config.exclude = flags.exclude;
config.llvm_enabled = true;
config.llvm_optimize = true;
config.llvm_version_check = true;
@ -326,6 +330,7 @@ impl Config {
config.rust_optimize = true;
config.rust_optimize_tests = true;
config.submodules = true;
config.fast_submodules = true;
config.docs = true;
config.rust_rpath = true;
config.channel = "dev".to_string();
@ -336,15 +341,38 @@ impl Config {
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
config.rust_codegen_backends_dir = "codegen-backends".to_owned();
// set by bootstrap.py
config.src = env::var_os("SRC").map(PathBuf::from).expect("'SRC' to be set");
config.build = INTERNER.intern_str(&env::var("BUILD").expect("'BUILD' to be set"));
config.out = env::var_os("BUILD_DIR").map(PathBuf::from).expect("'BUILD_DIR' set");
let stage0_root = config.out.join(&config.build).join("stage0/bin");
config.initial_rustc = stage0_root.join(exe("rustc", &config.build));
config.initial_cargo = stage0_root.join(exe("cargo", &config.build));
config
}
pub fn parse(args: &[String]) -> Config {
let flags = Flags::parse(&args);
let file = flags.config.clone();
let mut config = Config::default_opts();
config.exclude = flags.exclude;
config.rustc_error_format = flags.rustc_error_format;
config.on_fail = flags.on_fail;
config.stage = flags.stage;
config.src = flags.src;
config.jobs = flags.jobs;
config.cmd = flags.cmd;
config.incremental = flags.incremental;
config.dry_run = flags.dry_run;
config.keep_stage = flags.keep_stage;
if config.dry_run {
let dir = config.out.join("tmp-dry-run");
t!(fs::create_dir_all(&dir));
config.out = dir;
}
// If --target was specified but --host wasn't specified, don't run any host-only tests.
config.run_host_only = !(flags.host.is_empty() && !flags.target.is_empty());
@ -363,12 +391,7 @@ impl Config {
}).unwrap_or_else(|| TomlConfig::default());
let build = toml.build.clone().unwrap_or(Build::default());
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
set(&mut config.build, flags.build);
if config.build.is_empty() {
// set by bootstrap.py
config.build = INTERNER.intern_str(&env::var("BUILD").unwrap());
}
// set by bootstrap.py
config.hosts.push(config.build.clone());
for host in build.host.iter() {
let host = INTERNER.intern_str(host);
@ -402,6 +425,7 @@ impl Config {
set(&mut config.compiler_docs, build.compiler_docs);
set(&mut config.docs, build.docs);
set(&mut config.submodules, build.submodules);
set(&mut config.fast_submodules, build.fast_submodules);
set(&mut config.locked_deps, build.locked_deps);
set(&mut config.vendor, build.vendor);
set(&mut config.full_bootstrap, build.full_bootstrap);
@ -413,6 +437,7 @@ impl Config {
set(&mut config.openssl_static, build.openssl_static);
set(&mut config.configure_args, build.configure_args);
set(&mut config.local_rebuild, build.local_rebuild);
set(&mut config.print_step_timings, build.print_step_timings);
config.verbose = cmp::max(config.verbose, flags.verbose);
if let Some(ref install) = toml.install {
@ -507,13 +532,13 @@ impl Config {
let mut target = Target::default();
if let Some(ref s) = cfg.llvm_config {
target.llvm_config = Some(env::current_dir().unwrap().join(s));
target.llvm_config = Some(config.src.join(s));
}
if let Some(ref s) = cfg.jemalloc {
target.jemalloc = Some(env::current_dir().unwrap().join(s));
target.jemalloc = Some(config.src.join(s));
}
if let Some(ref s) = cfg.android_ndk {
target.ndk = Some(env::current_dir().unwrap().join(s));
target.ndk = Some(config.src.join(s));
}
target.cc = cfg.cc.clone().map(PathBuf::from);
target.cxx = cfg.cxx.clone().map(PathBuf::from);
@ -534,22 +559,12 @@ impl Config {
set(&mut config.rust_dist_src, t.src_tarball);
}
let cwd = t!(env::current_dir());
let out = cwd.join("build");
let stage0_root = out.join(&config.build).join("stage0/bin");
config.initial_rustc = match build.rustc {
Some(s) => PathBuf::from(s),
None => stage0_root.join(exe("rustc", &config.build)),
};
config.initial_cargo = match build.cargo {
Some(s) => PathBuf::from(s),
None => stage0_root.join(exe("cargo", &config.build)),
};
// Now that we've reached the end of our configuration, infer the
// default values for all options that we haven't otherwise stored yet.
set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
set(&mut config.initial_rustc, build.cargo.map(PathBuf::from));
let default = false;
config.llvm_assertions = llvm_assertions.unwrap_or(default);

View file

@ -44,6 +44,7 @@ o("debug", "rust.debug", "debug mode; disables optimization unless `--enable-opt
o("docs", "build.docs", "build standard library documentation")
o("compiler-docs", "build.compiler-docs", "build compiler documentation")
o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
o("experimental-parallel-queries", "rust.experimental-parallel-queries", "build rustc with experimental parallelization")
o("test-miri", "rust.test-miri", "run miri's test suite")
o("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
o("quiet-tests", "rust.quiet-tests", "enable quieter output when running tests")

View file

@ -20,7 +20,7 @@
use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::io::{Read, Write};
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
@ -28,7 +28,7 @@ use build_helper::output;
use {Build, Compiler, Mode};
use channel;
use util::{cp_r, libdir, is_dylib, cp_filtered, copy, replace_in_file, exe};
use util::{libdir, is_dylib, exe};
use builder::{Builder, RunConfig, ShouldRun, Step};
use compile;
use native;
@ -61,7 +61,7 @@ fn rust_installer(builder: &Builder) -> Command {
builder.tool_cmd(Tool::RustInstaller)
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Docs {
pub stage: u32,
pub host: Interned<String>,
@ -89,9 +89,9 @@ impl Step for Docs {
let name = pkgname(build, "rust-docs");
println!("Dist docs ({})", host);
build.info(&format!("Dist docs ({})", host));
if !build.config.docs {
println!("\tskipping - docs disabled");
build.info(&format!("\tskipping - docs disabled"));
return distdir(build).join(format!("{}-{}.tar.gz", name, host));
}
@ -102,8 +102,8 @@ impl Step for Docs {
let dst = image.join("share/doc/rust/html");
t!(fs::create_dir_all(&dst));
let src = build.out.join(host).join("doc");
cp_r(&src, &dst);
let src = build.doc_out(host);
build.cp_r(&src, &dst);
let mut cmd = rust_installer(builder);
cmd.arg("generate")
@ -118,16 +118,71 @@ impl Step for Docs {
.arg("--legacy-manifest-dirs=rustlib,cargo")
.arg("--bulk-dirs=share/doc/rust/html");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
build.remove_dir(&image);
// As part of this step, *also* copy the docs directory to a directory which
// buildbot typically uploads.
if host == build.build {
let dst = distdir(build).join("doc").join(build.rust_package_vers());
t!(fs::create_dir_all(&dst));
cp_r(&src, &dst);
distdir(build).join(format!("{}-{}.tar.gz", name, host))
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct RustcDocs {
pub stage: u32,
pub host: Interned<String>,
}
impl Step for RustcDocs {
type Output = PathBuf;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustc")
}
fn make_run(run: RunConfig) {
run.builder.ensure(RustcDocs {
stage: run.builder.top_stage,
host: run.target,
});
}
/// Builds the `rustc-docs` installer component.
fn run(self, builder: &Builder) -> PathBuf {
let build = builder.build;
let host = self.host;
let name = pkgname(build, "rustc-docs");
build.info(&format!("Dist compiler docs ({})", host));
if !build.config.compiler_docs {
build.info(&format!("\tskipping - compiler docs disabled"));
return distdir(build).join(format!("{}-{}.tar.gz", name, host));
}
builder.default_doc(None);
let image = tmpdir(build).join(format!("{}-{}-image", name, host));
let _ = fs::remove_dir_all(&image);
let dst = image.join("share/doc/rust/html");
t!(fs::create_dir_all(&dst));
let src = build.compiler_doc_out(host);
build.cp_r(&src, &dst);
let mut cmd = rust_installer(builder);
cmd.arg("generate")
.arg("--product-name=Rustc-Documentation")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=Rustc-documentation-is-installed.")
.arg("--image-dir").arg(&image)
.arg("--work-dir").arg(&tmpdir(build))
.arg("--output-dir").arg(&distdir(build))
.arg(format!("--package-name={}-{}", name, host))
.arg("--component-name=rustc-docs")
.arg("--legacy-manifest-dirs=rustlib,cargo")
.arg("--bulk-dirs=share/doc/rust/html");
build.run(&mut cmd);
build.remove_dir(&image);
distdir(build).join(format!("{}-{}.tar.gz", name, host))
}
}
@ -237,37 +292,31 @@ fn make_win_dist(
let rustc_dlls = find_files(&rustc_dlls, &bin_path);
let target_libs = find_files(&target_libs, &lib_path);
fn copy_to_folder(src: &Path, dest_folder: &Path) {
let file_name = src.file_name().unwrap();
let dest = dest_folder.join(file_name);
copy(src, &dest);
}
//Copy runtime dlls next to rustc.exe
// Copy runtime dlls next to rustc.exe
let dist_bin_dir = rust_root.join("bin/");
fs::create_dir_all(&dist_bin_dir).expect("creating dist_bin_dir failed");
for src in rustc_dlls {
copy_to_folder(&src, &dist_bin_dir);
build.copy_to_folder(&src, &dist_bin_dir);
}
//Copy platform tools to platform-specific bin directory
let target_bin_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("bin");
fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed");
for src in target_tools {
copy_to_folder(&src, &target_bin_dir);
build.copy_to_folder(&src, &target_bin_dir);
}
//Copy platform libs to platform-specific lib directory
let target_lib_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("lib");
fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed");
for src in target_libs {
copy_to_folder(&src, &target_lib_dir);
build.copy_to_folder(&src, &target_lib_dir);
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Mingw {
host: Interned<String>,
pub host: Interned<String>,
}
impl Step for Mingw {
@ -294,7 +343,7 @@ impl Step for Mingw {
return None;
}
println!("Dist mingw ({})", host);
build.info(&format!("Dist mingw ({})", host));
let name = pkgname(build, "rust-mingw");
let image = tmpdir(build).join(format!("{}-{}-image", name, host));
let _ = fs::remove_dir_all(&image);
@ -323,7 +372,7 @@ impl Step for Mingw {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Rustc {
pub compiler: Compiler,
}
@ -349,7 +398,7 @@ impl Step for Rustc {
let compiler = self.compiler;
let host = self.compiler.host;
println!("Dist rustc stage{} ({})", compiler.stage, compiler.host);
build.info(&format!("Dist rustc stage{} ({})", compiler.stage, compiler.host));
let name = pkgname(build, "rustc");
let image = tmpdir(build).join(format!("{}-{}-image", name, host));
let _ = fs::remove_dir_all(&image);
@ -362,7 +411,7 @@ impl Step for Rustc {
// Prepare the overlay which is part of the tarball but won't actually be
// installed
let cp = |file: &str| {
install(&build.src.join(file), &overlay, 0o644);
build.install(&build.src.join(file), &overlay, 0o644);
};
cp("COPYRIGHT");
cp("LICENSE-APACHE");
@ -370,9 +419,9 @@ impl Step for Rustc {
cp("README.md");
// tiny morsel of metadata is used by rust-packaging
let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
build.create(&overlay.join("version"), &version);
if let Some(sha) = build.rust_sha() {
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
build.create(&overlay.join("git-commit-hash"), &sha);
}
// On MinGW we've got a few runtime DLL dependencies that we need to
@ -390,7 +439,7 @@ impl Step for Rustc {
let dst = image.join("share/doc");
t!(fs::create_dir_all(&dst));
cp_r(&build.src.join("src/etc/third-party"), &dst);
build.cp_r(&build.src.join("src/etc/third-party"), &dst);
}
// Finally, wrap everything up in a nice tarball!
@ -407,8 +456,8 @@ impl Step for Rustc {
.arg("--component-name=rustc")
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
t!(fs::remove_dir_all(&overlay));
build.remove_dir(&image);
build.remove_dir(&overlay);
return distdir(build).join(format!("{}-{}.tar.gz", name, host));
@ -420,17 +469,17 @@ impl Step for Rustc {
// Copy rustc/rustdoc binaries
t!(fs::create_dir_all(image.join("bin")));
cp_r(&src.join("bin"), &image.join("bin"));
build.cp_r(&src.join("bin"), &image.join("bin"));
install(&builder.rustdoc(compiler.host), &image.join("bin"), 0o755);
build.install(&builder.rustdoc(compiler.host), &image.join("bin"), 0o755);
// Copy runtime DLLs needed by the compiler
if libdir != "bin" {
for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) {
for entry in build.read_dir(&src.join(libdir)) {
let name = entry.file_name();
if let Some(s) = name.to_str() {
if is_dylib(s) {
install(&entry.path(), &image.join(libdir), 0o644);
build.install(&entry.path(), &image.join(libdir), 0o644);
}
}
}
@ -441,7 +490,7 @@ impl Step for Rustc {
let backends_rel = backends_src.strip_prefix(&src).unwrap();
let backends_dst = image.join(&backends_rel);
t!(fs::create_dir_all(&backends_dst));
cp_r(&backends_src, &backends_dst);
build.cp_r(&backends_src, &backends_dst);
// Copy over lld if it's there
if builder.config.lld_enabled {
@ -456,7 +505,7 @@ impl Step for Rustc {
.join("bin")
.join(&exe);
t!(fs::create_dir_all(&dst.parent().unwrap()));
copy(&src, &dst);
build.copy(&src, &dst);
}
// Man pages
@ -466,13 +515,12 @@ impl Step for Rustc {
let month_year = t!(time::strftime("%B %Y", &time::now()));
// don't use our `bootstrap::util::{copy, cp_r}`, because those try
// to hardlink, and we don't want to edit the source templates
for entry_result in t!(fs::read_dir(man_src)) {
let file_entry = t!(entry_result);
for file_entry in build.read_dir(&man_src) {
let page_src = file_entry.path();
let page_dst = man_dst.join(file_entry.file_name());
t!(fs::copy(&page_src, &page_dst));
// template in month/year and version number
replace_in_file(&page_dst,
build.replace_in_file(&page_dst,
&[("<INSERT DATE HERE>", &month_year),
("<INSERT VERSION HERE>", channel::CFG_RELEASE_NUM)]);
}
@ -485,7 +533,7 @@ impl Step for Rustc {
// Misc license info
let cp = |file: &str| {
install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
build.install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
};
cp("COPYRIGHT");
cp("LICENSE-APACHE");
@ -523,11 +571,11 @@ impl Step for DebuggerScripts {
let dst = sysroot.join("lib/rustlib/etc");
t!(fs::create_dir_all(&dst));
let cp_debugger_script = |file: &str| {
install(&build.src.join("src/etc/").join(file), &dst, 0o644);
build.install(&build.src.join("src/etc/").join(file), &dst, 0o644);
};
if host.contains("windows-msvc") {
// windbg debugger scripts
install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"),
build.install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"),
0o755);
cp_debugger_script("natvis/intrinsic.natvis");
@ -537,14 +585,14 @@ impl Step for DebuggerScripts {
cp_debugger_script("debugger_pretty_printers_common.py");
// gdb debugger scripts
install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"),
build.install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"),
0o755);
cp_debugger_script("gdb_load_rust_pretty_printers.py");
cp_debugger_script("gdb_rust_pretty_printing.py");
// lldb debugger scripts
install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"),
build.install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"),
0o755);
cp_debugger_script("lldb_rust_formatters.py");
@ -552,7 +600,7 @@ impl Step for DebuggerScripts {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Std {
pub compiler: Compiler,
pub target: Interned<String>,
@ -579,12 +627,12 @@ impl Step for Std {
let target = self.target;
let name = pkgname(build, "rust-std");
println!("Dist std stage{} ({} -> {})", compiler.stage, &compiler.host, target);
build.info(&format!("Dist std stage{} ({} -> {})", compiler.stage, &compiler.host, target));
// The only true set of target libraries came from the build triple, so
// let's reduce redundant work by only producing archives from that host.
if compiler.host != build.build {
println!("\tskipping, not a build host");
build.info(&format!("\tskipping, not a build host"));
return distdir(build).join(format!("{}-{}.tar.gz", name, target));
}
@ -594,7 +642,12 @@ impl Step for Std {
if build.hosts.iter().any(|t| t == target) {
builder.ensure(compile::Rustc { compiler, target });
} else {
builder.ensure(compile::Test { compiler, target });
if build.no_std(target) == Some(true) {
// the `test` doesn't compile for no-std targets
builder.ensure(compile::Std { compiler, target });
} else {
builder.ensure(compile::Test { compiler, target });
}
}
let image = tmpdir(build).join(format!("{}-{}-image", name, target));
@ -604,7 +657,7 @@ impl Step for Std {
t!(fs::create_dir_all(&dst));
let mut src = builder.sysroot_libdir(compiler, target).to_path_buf();
src.pop(); // Remove the trailing /lib folder from the sysroot_libdir
cp_filtered(&src, &dst, &|path| {
build.cp_filtered(&src, &dst, &|path| {
let name = path.file_name().and_then(|s| s.to_str());
name != Some(build.config.rust_codegen_backends_dir.as_str()) &&
name != Some("bin")
@ -623,7 +676,7 @@ impl Step for Std {
.arg(format!("--component-name=rust-std-{}", target))
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
build.remove_dir(&image);
distdir(build).join(format!("{}-{}.tar.gz", name, target))
}
}
@ -656,11 +709,11 @@ impl Step for Analysis {
let compiler = self.compiler;
let target = self.target;
assert!(build.config.extended);
println!("Dist analysis");
build.info(&format!("Dist analysis"));
let name = pkgname(build, "rust-analysis");
if &compiler.host != build.build {
println!("\tskipping, not a build host");
build.info(&format!("\tskipping, not a build host"));
return distdir(build).join(format!("{}-{}.tar.gz", name, target));
}
@ -682,8 +735,8 @@ impl Step for Analysis {
let image_src = src.join("save-analysis");
let dst = image.join("lib/rustlib").join(target).join("analysis");
t!(fs::create_dir_all(&dst));
println!("image_src: {:?}, dst: {:?}", image_src, dst);
cp_r(&image_src, &dst);
build.info(&format!("image_src: {:?}, dst: {:?}", image_src, dst));
build.cp_r(&image_src, &dst);
let mut cmd = rust_installer(builder);
cmd.arg("generate")
@ -697,7 +750,7 @@ impl Step for Analysis {
.arg(format!("--component-name=rust-analysis-{}", target))
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
build.remove_dir(&image);
distdir(build).join(format!("{}-{}.tar.gz", name, target))
}
}
@ -741,11 +794,11 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di
for item in src_dirs {
let dst = &dst_dir.join(item);
t!(fs::create_dir_all(dst));
cp_filtered(&build.src.join(item), dst, &|path| filter_fn(exclude_dirs, item, path));
build.cp_filtered(&build.src.join(item), dst, &|path| filter_fn(exclude_dirs, item, path));
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src;
impl Step for Src {
@ -765,7 +818,7 @@ impl Step for Src {
/// Creates the `rust-src` installer component
fn run(self, builder: &Builder) -> PathBuf {
let build = builder.build;
println!("Dist src");
build.info(&format!("Dist src"));
let name = pkgname(build, "rust-src");
let image = tmpdir(build).join(format!("{}-image", name));
@ -815,7 +868,7 @@ impl Step for Src {
copy_src_dirs(build, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src);
for file in src_files.iter() {
copy(&build.src.join(file), &dst_src.join(file));
build.copy(&build.src.join(file), &dst_src.join(file));
}
// Create source tarball in rust-installer format
@ -832,14 +885,14 @@ impl Step for Src {
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
build.remove_dir(&image);
distdir(build).join(&format!("{}.tar.gz", name))
}
}
const CARGO_VENDOR_VERSION: &str = "0.1.4";
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct PlainSourceTarball;
impl Step for PlainSourceTarball {
@ -860,7 +913,7 @@ impl Step for PlainSourceTarball {
/// Creates the plain source tarball
fn run(self, builder: &Builder) -> PathBuf {
let build = builder.build;
println!("Create plain source tarball");
build.info(&format!("Create plain source tarball"));
// Make sure that the root folder of tarball has the correct name
let plain_name = format!("{}-src", pkgname(build, "rustc"));
@ -888,13 +941,13 @@ impl Step for PlainSourceTarball {
// Copy the files normally
for item in &src_files {
copy(&build.src.join(item), &plain_dst_src.join(item));
build.copy(&build.src.join(item), &plain_dst_src.join(item));
}
// Create the version file
write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes());
build.create(&plain_dst_src.join("version"), &build.rust_version());
if let Some(sha) = build.rust_sha() {
write_file(&plain_dst_src.join("git-commit-hash"), sha.as_bytes());
build.create(&plain_dst_src.join("git-commit-hash"), &sha);
}
// If we're building from git sources, we need to vendor a complete distribution.
@ -935,9 +988,9 @@ impl Step for PlainSourceTarball {
tarball.set_extension(""); // strip .gz
tarball.set_extension(""); // strip .tar
if let Some(dir) = tarball.parent() {
t!(fs::create_dir_all(dir));
build.create_dir(&dir);
}
println!("running installer");
build.info(&format!("running installer"));
let mut cmd = rust_installer(builder);
cmd.arg("tarball")
.arg("--input").arg(&plain_name)
@ -949,26 +1002,6 @@ impl Step for PlainSourceTarball {
}
}
fn install(src: &Path, dstdir: &Path, perms: u32) {
let dst = dstdir.join(src.file_name().unwrap());
t!(fs::create_dir_all(dstdir));
drop(fs::remove_file(&dst));
{
let mut s = t!(fs::File::open(&src));
let mut d = t!(fs::File::create(&dst));
io::copy(&mut s, &mut d).expect("failed to copy");
}
chmod(&dst, perms);
}
#[cfg(unix)]
fn chmod(path: &Path, perms: u32) {
use std::os::unix::fs::*;
t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
}
#[cfg(windows)]
fn chmod(_path: &Path, _perms: u32) {}
// We have to run a few shell scripts, which choke quite a bit on both `\`
// characters and on `C:\` paths, so normalize both of them away.
pub fn sanitize_sh(path: &Path) -> String {
@ -988,12 +1021,7 @@ pub fn sanitize_sh(path: &Path) -> String {
}
}
fn write_file(path: &Path, data: &[u8]) {
let mut vf = t!(fs::File::create(path));
t!(vf.write_all(data));
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Cargo {
pub stage: u32,
pub target: Interned<String>,
@ -1019,7 +1047,7 @@ impl Step for Cargo {
let stage = self.stage;
let target = self.target;
println!("Dist cargo stage{} ({})", stage, target);
build.info(&format!("Dist cargo stage{} ({})", stage, target));
let src = build.src.join("src/tools/cargo");
let etc = src.join("src/etc");
let release_num = build.release_num("cargo");
@ -1029,38 +1057,38 @@ impl Step for Cargo {
let tmp = tmpdir(build);
let image = tmp.join("cargo-image");
drop(fs::remove_dir_all(&image));
t!(fs::create_dir_all(&image));
build.create_dir(&image);
// Prepare the image directory
t!(fs::create_dir_all(image.join("share/zsh/site-functions")));
t!(fs::create_dir_all(image.join("etc/bash_completion.d")));
build.create_dir(&image.join("share/zsh/site-functions"));
build.create_dir(&image.join("etc/bash_completion.d"));
let cargo = builder.ensure(tool::Cargo {
compiler: builder.compiler(stage, build.build),
target
});
install(&cargo, &image.join("bin"), 0o755);
build.install(&cargo, &image.join("bin"), 0o755);
for man in t!(etc.join("man").read_dir()) {
let man = t!(man);
install(&man.path(), &image.join("share/man/man1"), 0o644);
build.install(&man.path(), &image.join("share/man/man1"), 0o644);
}
install(&etc.join("_cargo"), &image.join("share/zsh/site-functions"), 0o644);
copy(&etc.join("cargo.bashcomp.sh"),
build.install(&etc.join("_cargo"), &image.join("share/zsh/site-functions"), 0o644);
build.copy(&etc.join("cargo.bashcomp.sh"),
&image.join("etc/bash_completion.d/cargo"));
let doc = image.join("share/doc/cargo");
install(&src.join("README.md"), &doc, 0o644);
install(&src.join("LICENSE-MIT"), &doc, 0o644);
install(&src.join("LICENSE-APACHE"), &doc, 0o644);
install(&src.join("LICENSE-THIRD-PARTY"), &doc, 0o644);
build.install(&src.join("README.md"), &doc, 0o644);
build.install(&src.join("LICENSE-MIT"), &doc, 0o644);
build.install(&src.join("LICENSE-APACHE"), &doc, 0o644);
build.install(&src.join("LICENSE-THIRD-PARTY"), &doc, 0o644);
// Prepare the overlay
let overlay = tmp.join("cargo-overlay");
drop(fs::remove_dir_all(&overlay));
t!(fs::create_dir_all(&overlay));
install(&src.join("README.md"), &overlay, 0o644);
install(&src.join("LICENSE-MIT"), &overlay, 0o644);
install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
install(&src.join("LICENSE-THIRD-PARTY"), &overlay, 0o644);
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
build.create_dir(&overlay);
build.install(&src.join("README.md"), &overlay, 0o644);
build.install(&src.join("LICENSE-MIT"), &overlay, 0o644);
build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
build.install(&src.join("LICENSE-THIRD-PARTY"), &overlay, 0o644);
build.create(&overlay.join("version"), &version);
// Generate the installer tarball
let mut cmd = rust_installer(builder);
@ -1080,7 +1108,7 @@ impl Step for Cargo {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Rls {
pub stage: u32,
pub target: Interned<String>,
@ -1107,7 +1135,7 @@ impl Step for Rls {
let target = self.target;
assert!(build.config.extended);
println!("Dist RLS stage{} ({})", stage, target);
build.info(&format!("Dist RLS stage{} ({})", stage, target));
let src = build.src.join("src/tools/rls");
let release_num = build.release_num("rls");
let name = pkgname(build, "rls");
@ -1126,20 +1154,20 @@ impl Step for Rls {
target, extra_features: Vec::new()
}).or_else(|| { println!("Unable to build RLS, skipping dist"); None })?;
install(&rls, &image.join("bin"), 0o755);
build.install(&rls, &image.join("bin"), 0o755);
let doc = image.join("share/doc/rls");
install(&src.join("README.md"), &doc, 0o644);
install(&src.join("LICENSE-MIT"), &doc, 0o644);
install(&src.join("LICENSE-APACHE"), &doc, 0o644);
build.install(&src.join("README.md"), &doc, 0o644);
build.install(&src.join("LICENSE-MIT"), &doc, 0o644);
build.install(&src.join("LICENSE-APACHE"), &doc, 0o644);
// Prepare the overlay
let overlay = tmp.join("rls-overlay");
drop(fs::remove_dir_all(&overlay));
t!(fs::create_dir_all(&overlay));
install(&src.join("README.md"), &overlay, 0o644);
install(&src.join("LICENSE-MIT"), &overlay, 0o644);
install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
build.install(&src.join("README.md"), &overlay, 0o644);
build.install(&src.join("LICENSE-MIT"), &overlay, 0o644);
build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
build.create(&overlay.join("version"), &version);
// Generate the installer tarball
let mut cmd = rust_installer(builder);
@ -1161,7 +1189,7 @@ impl Step for Rls {
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Rustfmt {
pub stage: u32,
pub target: Interned<String>,
@ -1186,9 +1214,8 @@ impl Step for Rustfmt {
let build = builder.build;
let stage = self.stage;
let target = self.target;
assert!(build.config.extended);
println!("Dist Rustfmt stage{} ({})", stage, target);
build.info(&format!("Dist Rustfmt stage{} ({})", stage, target));
let src = build.src.join("src/tools/rustfmt");
let release_num = build.release_num("rustfmt");
let name = pkgname(build, "rustfmt");
@ -1197,7 +1224,7 @@ impl Step for Rustfmt {
let tmp = tmpdir(build);
let image = tmp.join("rustfmt-image");
drop(fs::remove_dir_all(&image));
t!(fs::create_dir_all(&image));
build.create_dir(&image);
// Prepare the image directory
let rustfmt = builder.ensure(tool::Rustfmt {
@ -1209,21 +1236,21 @@ impl Step for Rustfmt {
target, extra_features: Vec::new()
}).or_else(|| { println!("Unable to build Cargofmt, skipping dist"); None })?;
install(&rustfmt, &image.join("bin"), 0o755);
install(&cargofmt, &image.join("bin"), 0o755);
build.install(&rustfmt, &image.join("bin"), 0o755);
build.install(&cargofmt, &image.join("bin"), 0o755);
let doc = image.join("share/doc/rustfmt");
install(&src.join("README.md"), &doc, 0o644);
install(&src.join("LICENSE-MIT"), &doc, 0o644);
install(&src.join("LICENSE-APACHE"), &doc, 0o644);
build.install(&src.join("README.md"), &doc, 0o644);
build.install(&src.join("LICENSE-MIT"), &doc, 0o644);
build.install(&src.join("LICENSE-APACHE"), &doc, 0o644);
// Prepare the overlay
let overlay = tmp.join("rustfmt-overlay");
drop(fs::remove_dir_all(&overlay));
t!(fs::create_dir_all(&overlay));
install(&src.join("README.md"), &overlay, 0o644);
install(&src.join("LICENSE-MIT"), &overlay, 0o644);
install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
build.create_dir(&overlay);
build.install(&src.join("README.md"), &overlay, 0o644);
build.install(&src.join("LICENSE-MIT"), &overlay, 0o644);
build.install(&src.join("LICENSE-APACHE"), &overlay, 0o644);
build.create(&overlay.join("version"), &version);
// Generate the installer tarball
let mut cmd = rust_installer(builder);
@ -1244,7 +1271,7 @@ impl Step for Rustfmt {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Extended {
stage: u32,
host: Interned<String>,
@ -1275,7 +1302,7 @@ impl Step for Extended {
let stage = self.stage;
let target = self.target;
println!("Dist extended stage{} ({})", stage, target);
build.info(&format!("Dist extended stage{} ({})", stage, target));
let rustc_installer = builder.ensure(Rustc {
compiler: builder.compiler(stage, target),
@ -1301,15 +1328,15 @@ impl Step for Extended {
let work = tmp.join("work");
let _ = fs::remove_dir_all(&overlay);
install(&build.src.join("COPYRIGHT"), &overlay, 0o644);
install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644);
install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
build.install(&build.src.join("COPYRIGHT"), &overlay, 0o644);
build.install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644);
build.install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
build.create(&overlay.join("version"), &version);
if let Some(sha) = build.rust_sha() {
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
build.create(&overlay.join("git-commit-hash"), &sha);
}
install(&etc.join("README.md"), &overlay, 0o644);
build.install(&etc.join("README.md"), &overlay, 0o644);
// When rust-std package split from rustc, we needed to ensure that during
// upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
@ -1348,11 +1375,11 @@ impl Step for Extended {
build.run(&mut cmd);
let mut license = String::new();
t!(t!(File::open(build.src.join("COPYRIGHT"))).read_to_string(&mut license));
license += &build.read(&build.src.join("COPYRIGHT"));
license += &build.read(&build.src.join("LICENSE-APACHE"));
license += &build.read(&build.src.join("LICENSE-MIT"));
license.push_str("\n");
t!(t!(File::open(build.src.join("LICENSE-APACHE"))).read_to_string(&mut license));
license.push_str("\n");
t!(t!(File::open(build.src.join("LICENSE-MIT"))).read_to_string(&mut license));
let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
let mut rtf = rtf.to_string();
@ -1409,10 +1436,10 @@ impl Step for Extended {
};
let prepare = |name: &str| {
t!(fs::create_dir_all(pkg.join(name)));
cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)),
build.create_dir(&pkg.join(name));
build.cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)),
&pkg.join(name));
install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755);
build.install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755);
pkgbuild(name);
};
prepare("rustc");
@ -1426,12 +1453,12 @@ impl Step for Extended {
}
// create an 'uninstall' package
install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755);
build.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755);
pkgbuild("uninstall");
t!(fs::create_dir_all(pkg.join("res")));
t!(t!(File::create(pkg.join("res/LICENSE.txt"))).write_all(license.as_bytes()));
install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
build.create_dir(&pkg.join("res"));
build.create(&pkg.join("res/LICENSE.txt"), &license);
build.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
let mut cmd = Command::new("productbuild");
cmd.arg("--distribution").arg(xform(&etc.join("pkg/Distribution.xml")))
.arg("--resources").arg(pkg.join("res"))
@ -1447,7 +1474,7 @@ impl Step for Extended {
let _ = fs::remove_dir_all(&exe);
let prepare = |name: &str| {
t!(fs::create_dir_all(exe.join(name)));
build.create_dir(&exe.join(name));
let dir = if name == "rust-std" || name == "rust-analysis" {
format!("{}-{}", name, target)
} else if name == "rls" {
@ -1455,10 +1482,10 @@ impl Step for Extended {
} else {
name.to_string()
};
cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target))
build.cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target))
.join(dir),
&exe.join(name));
t!(fs::remove_file(exe.join(name).join("manifest.in")));
build.remove(&exe.join(name).join("manifest.in"));
};
prepare("rustc");
prepare("cargo");
@ -1472,11 +1499,11 @@ impl Step for Extended {
prepare("rust-mingw");
}
install(&xform(&etc.join("exe/rust.iss")), &exe, 0o644);
install(&etc.join("exe/modpath.iss"), &exe, 0o644);
install(&etc.join("exe/upgrade.iss"), &exe, 0o644);
install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644);
t!(t!(File::create(exe.join("LICENSE.txt"))).write_all(license.as_bytes()));
build.install(&xform(&etc.join("exe/rust.iss")), &exe, 0o644);
build.install(&etc.join("exe/modpath.iss"), &exe, 0o644);
build.install(&etc.join("exe/upgrade.iss"), &exe, 0o644);
build.install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644);
build.create(&exe.join("LICENSE.txt"), &license);
// Generate exe installer
let mut cmd = Command::new("iscc");
@ -1487,7 +1514,7 @@ impl Step for Extended {
}
add_env(build, &mut cmd, target);
build.run(&mut cmd);
install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)),
build.install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)),
&distdir(build),
0o755);
@ -1611,9 +1638,9 @@ impl Step for Extended {
candle("GccGroup.wxs".as_ref());
}
t!(t!(File::create(exe.join("LICENSE.rtf"))).write_all(rtf.as_bytes()));
install(&etc.join("gfx/banner.bmp"), &exe, 0o644);
install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644);
build.create(&exe.join("LICENSE.rtf"), &rtf);
build.install(&etc.join("gfx/banner.bmp"), &exe, 0o644);
build.install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644);
let filename = format!("{}-{}.msi", pkgname(build, "rust"), target);
let mut cmd = Command::new(&light);
@ -1643,7 +1670,9 @@ impl Step for Extended {
build.run(&mut cmd);
t!(fs::rename(exe.join(&filename), distdir(build).join(&filename)));
if !build.config.dry_run {
t!(fs::rename(exe.join(&filename), distdir(build).join(&filename)));
}
}
}
}
@ -1677,7 +1706,7 @@ fn add_env(build: &Build, cmd: &mut Command, target: Interned<String>) {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct HashSign;
impl Step for HashSign {
@ -1695,6 +1724,9 @@ impl Step for HashSign {
fn run(self, builder: &Builder) {
let build = builder.build;
let mut cmd = builder.tool_cmd(Tool::BuildManifest);
if build.config.dry_run {
return;
}
let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n")
});
@ -1718,7 +1750,7 @@ impl Step for HashSign {
cmd.arg(build.package_vers(&build.release_num("rustfmt")));
cmd.arg(addr);
t!(fs::create_dir_all(distdir(build)));
build.create_dir(&distdir(build));
let mut child = t!(cmd.stdin(Stdio::piped()).spawn());
t!(child.stdin.take().unwrap().write_all(pass.as_bytes()));

View file

@ -17,19 +17,21 @@
//! Everything here is basically just a shim around calling either `rustbook` or
//! `rustdoc`.
use std::collections::HashSet;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{PathBuf, Path};
use Mode;
use {Build, Mode};
use build_helper::up_to_date;
use util::{cp_r, symlink_dir};
use util::symlink_dir;
use builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
use tool::Tool;
use compile;
use cache::{INTERNER, Interned};
use config::Config;
macro_rules! book {
($($name:ident, $path:expr, $book_name:expr;)+) => {
@ -167,7 +169,7 @@ impl Step for CargoBook {
let out = out.join(name);
println!("Cargo Book ({}) - {}", target, name);
build.info(&format!("Cargo Book ({}) - {}", target, name));
let _ = fs::remove_dir_all(&out);
@ -209,12 +211,13 @@ impl Step for RustbookSrc {
let src = src.join(name);
let index = out.join("index.html");
let rustbook = builder.tool_exe(Tool::Rustbook);
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
return
}
println!("Rustbook ({}) - {}", target, name);
build.info(&format!("Rustbook ({}) - {}", target, name));
let _ = fs::remove_dir_all(&out);
build.run(builder.tool_cmd(Tool::Rustbook)
build.run(rustbook_cmd
.arg("build")
.arg(&src)
.arg("-d")
@ -280,11 +283,11 @@ impl Step for TheBook {
// build the index page
let index = format!("{}/index.md", name);
println!("Documenting book index ({})", target);
build.info(&format!("Documenting book index ({})", target));
invoke_rustdoc(builder, compiler, target, &index);
// build the redirect pages
println!("Documenting book redirect pages ({})", target);
build.info(&format!("Documenting book redirect pages ({})", target));
for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
let file = t!(file);
let path = file.path();
@ -357,7 +360,7 @@ impl Step for Standalone {
let build = builder.build;
let target = self.target;
let compiler = self.compiler;
println!("Documenting standalone ({})", target);
build.info(&format!("Documenting standalone ({})", target));
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
@ -369,7 +372,7 @@ impl Step for Standalone {
let version_input = build.src.join("src/doc/version_info.html.template");
let version_info = out.join("version_info.html");
if !up_to_date(&version_input, &version_info) {
if !build.config.dry_run && !up_to_date(&version_input, &version_info) {
let mut info = String::new();
t!(t!(File::open(&version_input)).read_to_string(&mut info));
let info = info.replace("VERSION", &build.rust_release())
@ -393,7 +396,7 @@ impl Step for Standalone {
up_to_date(&favicon, &html) &&
up_to_date(&full_toc, &html) &&
up_to_date(&version_info, &html) &&
up_to_date(&rustdoc, &html) {
(build.config.dry_run || up_to_date(&rustdoc, &html)) {
continue
}
@ -448,7 +451,7 @@ impl Step for Std {
let build = builder.build;
let stage = self.stage;
let target = self.target;
println!("Documenting stage{} std ({})", stage, target);
build.info(&format!("Documenting stage{} std ({})", stage, target));
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
let compiler = builder.compiler(stage, build.build);
@ -478,28 +481,24 @@ impl Step for Std {
// will also directly handle merging.
let my_out = build.crate_doc_out(target);
build.clear_if_dirty(&my_out, &rustdoc);
t!(symlink_dir_force(&my_out, &out_dir));
t!(symlink_dir_force(&build.config, &my_out, &out_dir));
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
compile::std_cargo(builder, &compiler, target, &mut cargo);
// We don't want to build docs for internal std dependencies unless
// in compiler-docs mode. When not in that mode, we whitelist the crates
// for which docs must be built.
if !build.config.compiler_docs {
cargo.arg("--no-deps");
for krate in &["alloc", "core", "std", "std_unicode"] {
cargo.arg("-p").arg(krate);
// Create all crate output directories first to make sure rustdoc uses
// relative links.
// FIXME: Cargo should probably do this itself.
t!(fs::create_dir_all(out_dir.join(krate)));
}
// Keep a whitelist so we do not build internal stdlib crates, these will be
// build by the rustc step later if enabled.
cargo.arg("--no-deps");
for krate in &["alloc", "core", "std", "std_unicode"] {
cargo.arg("-p").arg(krate);
// Create all crate output directories first to make sure rustdoc uses
// relative links.
// FIXME: Cargo should probably do this itself.
t!(fs::create_dir_all(out_dir.join(krate)));
}
build.run(&mut cargo);
cp_r(&my_out, &out);
build.cp_r(&my_out, &out);
}
}
@ -533,7 +532,7 @@ impl Step for Test {
let build = builder.build;
let stage = self.stage;
let target = self.target;
println!("Documenting stage{} test ({})", stage, target);
build.info(&format!("Documenting stage{} test ({})", stage, target));
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
let compiler = builder.compiler(stage, build.build);
@ -554,12 +553,87 @@ impl Step for Test {
// See docs in std above for why we symlink
let my_out = build.crate_doc_out(target);
build.clear_if_dirty(&my_out, &rustdoc);
t!(symlink_dir_force(&my_out, &out_dir));
t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc");
compile::test_cargo(build, &compiler, target, &mut cargo);
build.run(&mut cargo);
cp_r(&my_out, &out);
build.cp_r(&my_out, &out);
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct WhitelistedRustc {
stage: u32,
target: Interned<String>,
}
impl Step for WhitelistedRustc {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let builder = run.builder;
run.krate("rustc-main").default_condition(builder.build.config.docs)
}
fn make_run(run: RunConfig) {
run.builder.ensure(WhitelistedRustc {
stage: run.builder.top_stage,
target: run.target,
});
}
/// Generate whitelisted compiler crate documentation.
///
/// This will generate all documentation for crates that are whitelisted
/// to be included in the standard documentation. This documentation is
/// included in the standard Rust documentation, so we should always
/// document it and symlink to merge with the rest of the std and test
/// documentation. We don't build other compiler documentation
/// here as we want to be able to keep it separate from the standard
/// documentation. This is largely just a wrapper around `cargo doc`.
fn run(self, builder: &Builder) {
let build = builder.build;
let stage = self.stage;
let target = self.target;
build.info(&format!("Documenting stage{} whitelisted compiler ({})", stage, target));
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
let compiler = builder.compiler(stage, build.build);
let rustdoc = builder.rustdoc(compiler.host);
let compiler = if build.force_use_stage1(compiler, target) {
builder.compiler(1, compiler.host)
} else {
compiler
};
// Build libstd docs so that we generate relative links
builder.ensure(Std { stage, target });
builder.ensure(compile::Rustc { compiler, target });
let out_dir = build.stage_out(compiler, Mode::Librustc)
.join(target).join("doc");
// See docs in std above for why we symlink
let my_out = build.crate_doc_out(target);
build.clear_if_dirty(&my_out, &rustdoc);
t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
compile::rustc_cargo(build, &mut cargo);
// We don't want to build docs for internal compiler dependencies in this
// step (there is another step for that). Therefore, we whitelist the crates
// for which docs must be built.
cargo.arg("--no-deps");
for krate in &["proc_macro"] {
cargo.arg("-p").arg(krate);
}
build.run(&mut cargo);
build.cp_r(&my_out, &out);
}
}
@ -586,16 +660,18 @@ impl Step for Rustc {
});
}
/// Generate all compiler documentation.
/// Generate compiler documentation.
///
/// This will generate all documentation for the compiler libraries and their
/// dependencies. This is largely just a wrapper around `cargo doc`.
/// This will generate all documentation for compiler and dependencies.
/// Compiler documentation is distributed separately, so we make sure
/// we do not merge it with the other documentation from std, test and
/// proc_macros. This is largely just a wrapper around `cargo doc`.
fn run(self, builder: &Builder) {
let build = builder.build;
let stage = self.stage;
let target = self.target;
println!("Documenting stage{} compiler ({})", stage, target);
let out = build.doc_out(target);
build.info(&format!("Documenting stage{} compiler ({})", stage, target));
let out = build.compiler_doc_out(target);
t!(fs::create_dir_all(&out));
let compiler = builder.compiler(stage, build.build);
let rustdoc = builder.rustdoc(compiler.host);
@ -605,36 +681,57 @@ impl Step for Rustc {
compiler
};
if !build.config.compiler_docs {
build.info(&format!("\tskipping - compiler docs disabled"));
return;
}
// Build libstd docs so that we generate relative links
builder.ensure(Std { stage, target });
builder.ensure(compile::Rustc { compiler, target });
let out_dir = build.stage_out(compiler, Mode::Librustc)
.join(target).join("doc");
// See docs in std above for why we symlink
let my_out = build.crate_doc_out(target);
build.clear_if_dirty(&my_out, &rustdoc);
t!(symlink_dir_force(&my_out, &out_dir));
// We do not symlink to the same shared folder that already contains std library
// documentation from previous steps as we do not want to include that.
build.clear_if_dirty(&out, &rustdoc);
t!(symlink_dir_force(&builder.config, &out, &out_dir));
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
cargo.env("RUSTDOCFLAGS", "--document-private-items");
compile::rustc_cargo(build, &mut cargo);
if build.config.compiler_docs {
// src/rustc/Cargo.toml contains a bin crate called rustc which
// would otherwise overwrite the docs for the real rustc lib crate.
cargo.arg("-p").arg("rustc_driver");
} else {
// Like with libstd above if compiler docs aren't enabled then we're not
// documenting internal dependencies, so we have a whitelist.
cargo.arg("--no-deps");
for krate in &["proc_macro"] {
cargo.arg("-p").arg(krate);
}
// Only include compiler crates, no dependencies of those, such as `libc`.
cargo.arg("--no-deps");
// Find dependencies for top level crates.
let mut compiler_crates = HashSet::new();
for root_crate in &["rustc", "rustc_driver"] {
let interned_root_crate = INTERNER.intern_str(root_crate);
find_compiler_crates(&build, &interned_root_crate, &mut compiler_crates);
}
for krate in &compiler_crates {
cargo.arg("-p").arg(krate);
}
build.run(&mut cargo);
cp_r(&my_out, &out);
}
}
fn find_compiler_crates(
build: &Build,
name: &Interned<String>,
crates: &mut HashSet<Interned<String>>
) {
// Add current crate.
crates.insert(*name);
// Look for dependencies.
for dep in build.crates.get(name).unwrap().deps.iter() {
if build.crates.get(dep).unwrap().is_local(build) {
find_compiler_crates(build, dep, crates);
}
}
}
@ -665,7 +762,7 @@ impl Step for ErrorIndex {
let build = builder.build;
let target = self.target;
println!("Documenting error index ({})", target);
build.info(&format!("Documenting error index ({})", target));
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
let mut index = builder.tool_cmd(Tool::ErrorIndex);
@ -710,10 +807,10 @@ impl Step for UnstableBookGen {
target,
});
println!("Generating unstable book md files ({})", target);
build.info(&format!("Generating unstable book md files ({})", target));
let out = build.md_doc_out(target).join("unstable-book");
t!(fs::create_dir_all(&out));
t!(fs::remove_dir_all(&out));
build.create_dir(&out);
build.remove_dir(&out);
let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
cmd.arg(build.src.join("src"));
cmd.arg(out);
@ -722,7 +819,10 @@ impl Step for UnstableBookGen {
}
}
fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
if config.dry_run {
return Ok(());
}
if let Ok(m) = fs::symlink_metadata(dst) {
if m.file_type().is_dir() {
try!(fs::remove_dir_all(dst));
@ -735,5 +835,5 @@ fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
}
}
symlink_dir(src, dst)
symlink_dir(config, src, dst)
}

View file

@ -13,7 +13,6 @@
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;
@ -33,17 +32,16 @@ pub struct Flags {
pub on_fail: Option<String>,
pub stage: Option<u32>,
pub keep_stage: Option<u32>,
pub build: Option<Interned<String>>,
pub host: Vec<Interned<String>>,
pub target: Vec<Interned<String>>,
pub config: Option<PathBuf>,
pub src: PathBuf,
pub jobs: Option<u32>,
pub cmd: Subcommand,
pub incremental: bool,
pub exclude: Vec<PathBuf>,
pub rustc_error_format: Option<String>,
pub dry_run: bool,
}
pub enum Subcommand {
@ -114,6 +112,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
opts.optmulti("", "target", "target targets to build", "TARGET");
opts.optmulti("", "exclude", "build paths to exclude", "PATH");
opts.optopt("", "on-fail", "command to run on failure", "CMD");
opts.optflag("", "dry-run", "dry run; don't build anything");
opts.optopt("", "stage", "stage to build", "N");
opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
@ -278,10 +277,6 @@ Arguments:
_ => { }
};
// Get any optional paths which occur after the subcommand
let cwd = t!(env::current_dir());
let src = matches.opt_str("src").map(PathBuf::from)
.or_else(|| env::var_os("SRC").map(PathBuf::from))
.unwrap_or(cwd.clone());
let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
@ -371,10 +366,10 @@ Arguments:
Flags {
verbose: matches.opt_count("verbose"),
stage,
dry_run: matches.opt_present("dry-run"),
on_fail: matches.opt_str("on-fail"),
rustc_error_format: matches.opt_str("error-format"),
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)),
host: split(matches.opt_strs("host"))
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
target: split(matches.opt_strs("target"))
@ -385,7 +380,6 @@ Arguments:
incremental: matches.opt_present("incremental"),
exclude: split(matches.opt_strs("exclude"))
.into_iter().map(|p| p.into()).collect::<Vec<_>>(),
src,
}
}
}

View file

@ -63,7 +63,7 @@ fn install_sh(
host: Option<Interned<String>>
) {
let build = builder.build;
println!("Install {} stage{} ({:?})", package, stage, host);
build.info(&format!("Install {} stage{} ({:?})", package, stage, host));
let prefix_default = PathBuf::from("/usr/local");
let sysconfdir_default = PathBuf::from("/etc");
@ -212,7 +212,7 @@ install!((self, builder, _config),
Self::should_install(builder) {
install_rls(builder, self.stage, self.target);
} else {
println!("skipping Install RLS stage{} ({})", self.stage, self.target);
builder.info(&format!("skipping Install RLS stage{} ({})", self.stage, self.target));
}
};
Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
@ -220,7 +220,8 @@ install!((self, builder, _config),
Self::should_install(builder) {
install_rustfmt(builder, self.stage, self.target);
} else {
println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target);
builder.info(
&format!("skipping Install Rustfmt stage{} ({})", self.stage, self.target));
}
};
Analysis, "analysis", Self::should_build(_config), only_hosts: false, {

View file

@ -114,7 +114,7 @@
//! also check out the `src/bootstrap/README.md` file for more information.
#![deny(warnings)]
#![feature(core_intrinsics)]
#![feature(conservative_impl_trait, fs_read_write, core_intrinsics)]
#![feature(slice_concat_ext)]
#[macro_use]
@ -131,6 +131,11 @@ extern crate getopts;
extern crate num_cpus;
extern crate toml;
extern crate time;
extern crate petgraph;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[cfg(unix)]
extern crate libc;
@ -138,13 +143,15 @@ extern crate libc;
use std::cell::{RefCell, Cell};
use std::collections::{HashSet, HashMap};
use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::fs::{self, OpenOptions, File};
use std::io::{self, Seek, SeekFrom, Write, Read};
use std::path::{PathBuf, Path};
use std::process::{self, Command};
use std::slice;
use std::str;
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
use filetime::FileTime;
use util::{exe, libdir, OutputFolder, CiEnv};
@ -198,7 +205,7 @@ use toolstate::ToolState;
/// Each compiler has a `stage` that it is associated with and a `host` that
/// corresponds to the platform the compiler runs on. This structure is used as
/// a parameter to many methods below.
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
#[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)]
pub struct Compiler {
stage: u32,
host: Interned<String>,
@ -254,6 +261,10 @@ pub struct Build {
ci_env: CiEnv,
delayed_failures: RefCell<Vec<String>>,
prerelease_version: Cell<Option<u32>>,
tool_artifacts: RefCell<HashMap<
Interned<String>,
HashMap<String, (&'static str, PathBuf, Vec<String>)>
>>,
}
#[derive(Debug)]
@ -305,9 +316,8 @@ impl Build {
///
/// By default all build output will be placed in the current directory.
pub fn new(config: Config) -> Build {
let cwd = t!(env::current_dir());
let src = config.src.clone();
let out = cwd.join("build");
let out = config.out.clone();
let is_sudo = match env::var_os("SUDO_USER") {
Some(sudo_user) => {
@ -323,7 +333,7 @@ impl Build {
let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls"));
let rustfmt_info = channel::GitInfo::new(&config, &src.join("src/tools/rustfmt"));
Build {
let mut build = Build {
initial_rustc: config.initial_rustc.clone(),
initial_cargo: config.initial_cargo.clone(),
local_rebuild: config.local_rebuild,
@ -353,7 +363,31 @@ impl Build {
ci_env: CiEnv::current(),
delayed_failures: RefCell::new(Vec::new()),
prerelease_version: Cell::new(None),
tool_artifacts: Default::default(),
};
build.verbose("finding compilers");
cc_detect::find(&mut build);
build.verbose("running sanity check");
sanity::check(&mut build);
// If local-rust is the same major.minor as the current version, then force a
// local-rebuild
let local_version_verbose = output(
Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
let local_release = local_version_verbose
.lines().filter(|x| x.starts_with("release:"))
.next().unwrap().trim_left_matches("release:").trim();
let my_version = channel::CFG_RELEASE_NUM;
if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
build.verbose(&format!("auto-detected local-rebuild {}", local_release));
build.local_rebuild = true;
}
build.verbose("learning about cargo");
metadata::build(&mut build);
build
}
pub fn build_triple(&self) -> &[Interned<String>] {
@ -372,25 +406,28 @@ impl Build {
return clean::clean(self, all);
}
self.verbose("finding compilers");
cc_detect::find(self);
self.verbose("running sanity check");
sanity::check(self);
// If local-rust is the same major.minor as the current version, then force a local-rebuild
let local_version_verbose = output(
Command::new(&self.initial_rustc).arg("--version").arg("--verbose"));
let local_release = local_version_verbose
.lines().filter(|x| x.starts_with("release:"))
.next().unwrap().trim_left_matches("release:").trim();
let my_version = channel::CFG_RELEASE_NUM;
if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
self.verbose(&format!("auto-detected local-rebuild {}", local_release));
self.local_rebuild = true;
{
let builder = builder::Builder::new(&self);
if let Some(path) = builder.paths.get(0) {
if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
return;
}
}
}
self.verbose("learning about cargo");
metadata::build(self);
builder::Builder::run(&self);
if !self.config.dry_run {
{
self.config.dry_run = true;
let builder = builder::Builder::new(&self);
builder.execute_cli();
}
self.config.dry_run = false;
let builder = builder::Builder::new(&self);
builder.execute_cli();
} else {
let builder = builder::Builder::new(&self);
let _ = builder.execute_cli();
}
// Check for postponed failures from `test --no-fail-fast`.
let failures = self.delayed_failures.borrow();
@ -511,6 +548,11 @@ impl Build {
self.out.join(&*target).join("doc")
}
/// Output directory for all documentation for a target
fn compiler_doc_out(&self, target: Interned<String>) -> PathBuf {
self.out.join(&*target).join("compiler-doc")
}
/// Output directory for some generated md crate documentation for a target (temporary)
fn md_doc_out(&self, target: Interned<String>) -> Interned<PathBuf> {
INTERNER.intern_path(self.out.join(&*target).join("md-doc"))
@ -576,12 +618,14 @@ impl Build {
/// Runs a command, printing out nice contextual information if it fails.
fn run(&self, cmd: &mut Command) {
if self.config.dry_run { return; }
self.verbose(&format!("running: {:?}", cmd));
run_silent(cmd)
}
/// Runs a command, printing out nice contextual information if it fails.
fn run_quiet(&self, cmd: &mut Command) {
if self.config.dry_run { return; }
self.verbose(&format!("running: {:?}", cmd));
run_suppressed(cmd)
}
@ -590,6 +634,7 @@ impl Build {
/// Exits if the command failed to execute at all, otherwise returns its
/// `status.success()`.
fn try_run(&self, cmd: &mut Command) -> bool {
if self.config.dry_run { return true; }
self.verbose(&format!("running: {:?}", cmd));
try_run_silent(cmd)
}
@ -598,6 +643,7 @@ impl Build {
/// Exits if the command failed to execute at all, otherwise returns its
/// `status.success()`.
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
if self.config.dry_run { return true; }
self.verbose(&format!("running: {:?}", cmd));
try_run_suppressed(cmd)
}
@ -613,6 +659,11 @@ impl Build {
}
}
fn info(&self, msg: &str) {
if self.config.dry_run { return; }
println!("{}", msg);
}
/// Returns the number of parallel jobs that have been configured for this
/// build.
fn jobs(&self) -> u32 {
@ -699,6 +750,12 @@ impl Build {
.map(|p| &**p)
}
/// Returns true if this is a no-std `target`, if defined
fn no_std(&self, target: Interned<String>) -> Option<bool> {
self.config.target_config.get(&target)
.map(|t| t.no_std)
}
/// Returns whether the target will be tested using the `remote-test-client`
/// and `remote-test-server` binaries.
fn remote_tested(&self, target: Interned<String>) -> bool {
@ -920,7 +977,7 @@ impl Build {
pub fn fold_output<D, F>(&self, name: F) -> Option<OutputFolder>
where D: Into<String>, F: FnOnce() -> D
{
if self.ci_env == CiEnv::Travis {
if !self.config.dry_run && self.ci_env == CiEnv::Travis {
Some(OutputFolder::new(name().into()))
} else {
None
@ -968,8 +1025,173 @@ impl Build {
}
ret
}
fn read_stamp_file(&self, stamp: &Path) -> Vec<PathBuf> {
if self.config.dry_run {
return Vec::new();
}
let mut paths = Vec::new();
let mut contents = Vec::new();
t!(t!(File::open(stamp)).read_to_end(&mut contents));
// This is the method we use for extracting paths from the stamp file passed to us. See
// run_cargo for more information (in compile.rs).
for part in contents.split(|b| *b == 0) {
if part.is_empty() {
continue
}
let path = PathBuf::from(t!(str::from_utf8(part)));
paths.push(path);
}
paths
}
/// Copies a file from `src` to `dst`
pub fn copy(&self, src: &Path, dst: &Path) {
if self.config.dry_run { return; }
let _ = fs::remove_file(&dst);
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
// windows), but if that fails just fall back to a slow `copy` operation.
if let Ok(()) = fs::hard_link(src, dst) {
return
}
if let Err(e) = fs::copy(src, dst) {
panic!("failed to copy `{}` to `{}`: {}", src.display(),
dst.display(), e)
}
let metadata = t!(src.metadata());
t!(fs::set_permissions(dst, metadata.permissions()));
let atime = FileTime::from_last_access_time(&metadata);
let mtime = FileTime::from_last_modification_time(&metadata);
t!(filetime::set_file_times(dst, atime, mtime));
}
/// Search-and-replaces within a file. (Not maximally efficiently: allocates a
/// new string for each replacement.)
pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) {
if self.config.dry_run { return; }
let mut contents = String::new();
let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
t!(file.read_to_string(&mut contents));
for &(target, replacement) in replacements {
contents = contents.replace(target, replacement);
}
t!(file.seek(SeekFrom::Start(0)));
t!(file.set_len(0));
t!(file.write_all(contents.as_bytes()));
}
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
/// when this function is called.
pub fn cp_r(&self, src: &Path, dst: &Path) {
if self.config.dry_run { return; }
for f in t!(fs::read_dir(src)) {
let f = t!(f);
let path = f.path();
let name = path.file_name().unwrap();
let dst = dst.join(name);
if t!(f.file_type()).is_dir() {
t!(fs::create_dir_all(&dst));
self.cp_r(&path, &dst);
} else {
let _ = fs::remove_file(&dst);
self.copy(&path, &dst);
}
}
}
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
/// when this function is called. Unwanted files or directories can be skipped
/// by returning `false` from the filter function.
pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) {
// Immediately recurse with an empty relative path
self.recurse_(src, dst, Path::new(""), filter)
}
// Inner function does the actual work
fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) {
for f in self.read_dir(src) {
let path = f.path();
let name = path.file_name().unwrap();
let dst = dst.join(name);
let relative = relative.join(name);
// Only copy file or directory if the filter function returns true
if filter(&relative) {
if t!(f.file_type()).is_dir() {
let _ = fs::remove_dir_all(&dst);
self.create_dir(&dst);
self.recurse_(&path, &dst, &relative, filter);
} else {
let _ = fs::remove_file(&dst);
self.copy(&path, &dst);
}
}
}
}
fn copy_to_folder(&self, src: &Path, dest_folder: &Path) {
let file_name = src.file_name().unwrap();
let dest = dest_folder.join(file_name);
self.copy(src, &dest);
}
fn install(&self, src: &Path, dstdir: &Path, perms: u32) {
if self.config.dry_run { return; }
let dst = dstdir.join(src.file_name().unwrap());
t!(fs::create_dir_all(dstdir));
drop(fs::remove_file(&dst));
{
let mut s = t!(fs::File::open(&src));
let mut d = t!(fs::File::create(&dst));
io::copy(&mut s, &mut d).expect("failed to copy");
}
chmod(&dst, perms);
}
fn create(&self, path: &Path, s: &str) {
if self.config.dry_run { return; }
t!(fs::write(path, s));
}
fn read(&self, path: &Path) -> String {
if self.config.dry_run { return String::new(); }
t!(fs::read_string(path))
}
fn create_dir(&self, dir: &Path) {
if self.config.dry_run { return; }
t!(fs::create_dir_all(dir))
}
fn remove_dir(&self, dir: &Path) {
if self.config.dry_run { return; }
t!(fs::remove_dir_all(dir))
}
fn read_dir(&self, dir: &Path) -> impl Iterator<Item=fs::DirEntry> {
let iter = match fs::read_dir(dir) {
Ok(v) => v,
Err(_) if self.config.dry_run => return vec![].into_iter(),
Err(err) => panic!("could not read dir {:?}: {:?}", dir, err),
};
iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
}
fn remove(&self, f: &Path) {
if self.config.dry_run { return; }
fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
}
}
#[cfg(unix)]
fn chmod(path: &Path, perms: u32) {
use std::os::unix::fs::*;
t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
}
#[cfg(windows)]
fn chmod(_path: &Path, _perms: u32) {}
impl<'a> Compiler {
pub fn with_stage(mut self, stage: u32) -> Compiler {
self.stage = stage;

View file

@ -85,5 +85,12 @@ check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu:
check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu:
$(Q)$(BOOTSTRAP) test --target x86_64-unknown-linux-musl
TESTS_IN_2 := src/test/run-pass src/test/compile-fail src/test/run-pass-fulldeps
appveyor-subset-1:
$(Q)$(BOOTSTRAP) test $(TESTS_IN_2:%=--exclude %)
appveyor-subset-2:
$(Q)$(BOOTSTRAP) test $(TESTS_IN_2)
.PHONY: dist

View file

@ -106,8 +106,8 @@ impl Step for Llvm {
let _folder = build.fold_output(|| "llvm");
let descriptor = if emscripten { "Emscripten " } else { "" };
println!("Building {}LLVM for {}", descriptor, target);
let _time = util::timeit();
build.info(&format!("Building {}LLVM for {}", descriptor, target));
let _time = util::timeit(&build);
t!(fs::create_dir_all(&out_dir));
// http://llvm.org/docs/CMake.html
@ -217,6 +217,11 @@ impl Step for Llvm {
// libraries here, e.g. we just want a few components and a few
// tools. Figure out how to filter them down and only build the right
// tools and libs on all platforms.
if builder.config.dry_run {
return build_llvm_config;
}
cfg.build();
t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
@ -230,6 +235,10 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) {
return
}
if build.config.dry_run {
return;
}
let mut cmd = Command::new(llvm_config);
let version = output(cmd.arg("--version"));
let mut parts = version.split('.').take(2)
@ -336,6 +345,9 @@ impl Step for Lld {
/// Compile LLVM for `target`.
fn run(self, builder: &Builder) -> PathBuf {
if builder.config.dry_run {
return PathBuf::from("lld-out-dir-test-gen");
}
let target = self.target;
let build = builder.build;
@ -351,8 +363,8 @@ impl Step for Lld {
}
let _folder = build.fold_output(|| "lld");
println!("Building LLD for {}", target);
let _time = util::timeit();
build.info(&format!("Building LLD for {}", target));
let _time = util::timeit(&build);
t!(fs::create_dir_all(&out_dir));
let mut cfg = cmake::Config::new(build.src.join("src/tools/lld"));
@ -389,6 +401,9 @@ impl Step for TestHelpers {
/// Compiles the `rust_test_helpers.c` library which we used in various
/// `run-pass` test suites for ABI testing.
fn run(self, builder: &Builder) {
if builder.config.dry_run {
return;
}
let build = builder.build;
let target = self.target;
let dst = build.test_helpers_out(target);
@ -398,7 +413,7 @@ impl Step for TestHelpers {
}
let _folder = build.fold_output(|| "build_test_helpers");
println!("Building test helpers");
build.info(&format!("Building test helpers"));
t!(fs::create_dir_all(&dst));
let mut cfg = cc::Build::new();
@ -441,6 +456,9 @@ impl Step for Openssl {
}
fn run(self, builder: &Builder) {
if builder.config.dry_run {
return;
}
let build = builder.build;
let target = self.target;
let out = match build.openssl_dir(target) {
@ -591,11 +609,11 @@ impl Step for Openssl {
configure.arg("no-asm");
}
configure.current_dir(&obj);
println!("Configuring openssl for {}", target);
build.info(&format!("Configuring openssl for {}", target));
build.run_quiet(&mut configure);
println!("Building openssl for {}", target);
build.info(&format!("Building openssl for {}", target));
build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
println!("Installing openssl for {}", target);
build.info(&format!("Installing openssl for {}", target));
build.run_quiet(Command::new("make").arg("install").arg("-j1").current_dir(&obj));
let mut f = t!(File::create(&stamp));

View file

@ -169,6 +169,19 @@ pub fn check(build: &mut Build) {
panic!("the iOS target is only supported on macOS");
}
if target.contains("-none-") {
if build.no_std(*target).is_none() {
let target = build.config.target_config.entry(target.clone())
.or_insert(Default::default());
target.no_std = true;
}
if build.no_std(*target) == Some(false) {
panic!("All the *-none-* targets are no-std targets")
}
}
// Make sure musl-root is valid
if target.contains("musl") {
// If this is a native target (host is also musl) and no musl-root is given,

View file

@ -109,11 +109,11 @@ impl Step for Linkcheck {
let build = builder.build;
let host = self.host;
println!("Linkcheck ({})", host);
build.info(&format!("Linkcheck ({})", host));
builder.default_doc(None);
let _time = util::timeit();
let _time = util::timeit(&build);
try_run(build, builder.tool_cmd(Tool::Linkchecker)
.arg(build.out.join(host).join("doc")));
}
@ -164,7 +164,7 @@ impl Step for Cargotest {
let out_dir = build.out.join("ct");
t!(fs::create_dir_all(&out_dir));
let _time = util::timeit();
let _time = util::timeit(&build);
let mut cmd = builder.tool_cmd(Tool::CargoTest);
try_run(build, cmd.arg(&build.initial_cargo)
.arg(&out_dir)
@ -509,7 +509,7 @@ impl Step for RustdocJS {
});
builder.run(&mut command);
} else {
println!("No nodejs found, skipping \"src/test/rustdoc-js\" tests");
builder.info(&format!("No nodejs found, skipping \"src/test/rustdoc-js\" tests"));
}
}
}
@ -530,8 +530,6 @@ impl Step for Tidy {
fn run(self, builder: &Builder) {
let build = builder.build;
let _folder = build.fold_output(|| "tidy");
println!("tidy check");
let mut cmd = builder.tool_cmd(Tool::Tidy);
cmd.arg(build.src.join("src"));
cmd.arg(&build.initial_cargo);
@ -541,6 +539,9 @@ impl Step for Tidy {
if build.config.quiet_tests {
cmd.arg("--quiet");
}
let _folder = build.fold_output(|| "tidy");
builder.info(&format!("tidy check"));
try_run(build, &mut cmd);
}
@ -759,12 +760,18 @@ test!(RunFailFullDepsPretty {
host: true
});
host_test!(RunMake {
default_test!(RunMake {
path: "src/test/run-make",
mode: "run-make",
suite: "run-make"
});
host_test!(RunMakeFullDeps {
path: "src/test/run-make-fulldeps",
mode: "run-make",
suite: "run-make-fulldeps"
});
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Compiletest {
compiler: Compiler,
@ -827,8 +834,7 @@ impl Step for Compiletest {
// FIXME: Does pretty need librustc compiled? Note that there are
// fulldeps test suites with mode = pretty as well.
mode == "pretty" ||
mode == "rustdoc" ||
mode == "run-make" {
mode == "rustdoc" {
builder.ensure(compile::Rustc { compiler, target });
}
@ -836,9 +842,6 @@ impl Step for Compiletest {
builder.ensure(native::TestHelpers { target });
builder.ensure(RemoteCopyLibs { compiler, target });
let _folder = build.fold_output(|| format!("test_{}", suite));
println!("Check compiletest suite={} mode={} ({} -> {})",
suite, mode, &compiler.host, target);
let mut cmd = builder.tool_cmd(Tool::Compiletest);
// compiletest currently has... a lot of arguments, so let's just pass all
@ -849,7 +852,7 @@ impl Step for Compiletest {
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
// Avoid depending on rustdoc when we don't need it.
if mode == "rustdoc" || mode == "run-make" {
if mode == "rustdoc" || (mode == "run-make" && suite.ends_with("fulldeps")) {
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
}
@ -923,15 +926,17 @@ impl Step for Compiletest {
target: build.config.build,
emscripten: false,
});
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
cmd.arg("--llvm-version").arg(llvm_version);
if !build.config.dry_run {
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
cmd.arg("--llvm-version").arg(llvm_version);
}
if !build.is_rust_llvm(target) {
cmd.arg("--system-llvm");
}
// Only pass correct values for these flags for the `run-make` suite as it
// requires that a C++ compiler was configured which isn't always the case.
if suite == "run-make" {
if !build.config.dry_run && suite == "run-make-fulldeps" {
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
cmd.arg("--cc").arg(build.cc(target))
@ -944,12 +949,13 @@ impl Step for Compiletest {
}
}
}
if suite == "run-make" && !build.config.llvm_enabled {
println!("Ignoring run-make test suite as they generally don't work without LLVM");
if suite == "run-make-fulldeps" && !build.config.llvm_enabled {
builder.info(
&format!("Ignoring run-make test suite as they generally don't work without LLVM"));
return;
}
if suite != "run-make" {
if suite != "run-make-fulldeps" {
cmd.arg("--cc").arg("")
.arg("--cxx").arg("")
.arg("--cflags").arg("")
@ -998,7 +1004,10 @@ impl Step for Compiletest {
build.ci_env.force_coloring_in_ci(&mut cmd);
let _time = util::timeit();
let _folder = build.fold_output(|| format!("test_{}", suite));
builder.info(&format!("Check compiletest suite={} mode={} ({} -> {})",
suite, mode, &compiler.host, target));
let _time = util::timeit(&build);
try_run(build, &mut cmd);
}
}
@ -1033,9 +1042,10 @@ impl Step for DocTest {
// Do a breadth-first traversal of the `src/doc` directory and just run
// tests for all files that end in `*.md`
let mut stack = vec![build.src.join(self.path)];
let _time = util::timeit();
let _time = util::timeit(&build);
let _folder = build.fold_output(|| format!("test_{}", self.name));
let mut files = Vec::new();
while let Some(p) = stack.pop() {
if p.is_dir() {
stack.extend(t!(p.read_dir()).map(|p| t!(p).path()));
@ -1052,7 +1062,13 @@ impl Step for DocTest {
continue;
}
let test_result = markdown_test(builder, compiler, &p);
files.push(p);
}
files.sort();
for file in files {
let test_result = markdown_test(builder, compiler, &file);
if self.is_ext_doc {
let toolstate = if test_result {
ToolState::TestPass
@ -1142,34 +1158,39 @@ impl Step for ErrorIndex {
builder.ensure(compile::Std { compiler, target: compiler.host });
let _folder = build.fold_output(|| "test_error_index");
println!("Testing error-index stage{}", compiler.stage);
let dir = testdir(build, compiler.host);
t!(fs::create_dir_all(&dir));
let output = dir.join("error-index.md");
let _time = util::timeit();
build.run(builder.tool_cmd(Tool::ErrorIndex)
.arg("markdown")
.arg(&output)
.env("CFG_BUILD", &build.build)
.env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir()));
let mut tool = builder.tool_cmd(Tool::ErrorIndex);
tool.arg("markdown")
.arg(&output)
.env("CFG_BUILD", &build.build)
.env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir());
let _folder = build.fold_output(|| "test_error_index");
build.info(&format!("Testing error-index stage{}", compiler.stage));
let _time = util::timeit(&build);
build.run(&mut tool);
markdown_test(builder, compiler, &output);
}
}
fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) -> bool {
let build = builder.build;
let mut file = t!(File::open(markdown));
let mut contents = String::new();
t!(file.read_to_string(&mut contents));
if !contents.contains("```") {
return true;
match File::open(markdown) {
Ok(mut file) => {
let mut contents = String::new();
t!(file.read_to_string(&mut contents));
if !contents.contains("```") {
return true;
}
}
Err(_) => {},
}
println!("doc tests for: {}", markdown.display());
build.info(&format!("doc tests for: {}", markdown.display()));
let mut cmd = builder.rustdoc_cmd(compiler.host);
build.add_rust_test_threads(&mut cmd);
cmd.arg("--test");
@ -1400,11 +1421,6 @@ impl Step for Crate {
}
_ => panic!("can only test libraries"),
};
let _folder = build.fold_output(|| {
format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, krate)
});
println!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage,
&compiler.host, target);
// Build up the base `cargo test` command.
//
@ -1436,8 +1452,6 @@ impl Step for Crate {
cargo.arg("--quiet");
}
let _time = util::timeit();
if target.contains("emscripten") {
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
build.config.nodejs.as_ref().expect("nodejs not configured"));
@ -1446,8 +1460,8 @@ impl Step for Crate {
// The javascript shim implements the syscall interface so that test
// output can be correctly reported.
if !build.config.wasm_syscall {
println!("Libstd was built without `wasm_syscall` feature enabled: \
test output may not be visible.");
build.info(&format!("Libstd was built without `wasm_syscall` feature enabled: \
test output may not be visible."));
}
// On the wasm32-unknown-unknown target we're using LTO which is
@ -1465,6 +1479,13 @@ impl Step for Crate {
format!("{} run",
builder.tool_exe(Tool::RemoteTestClient).display()));
}
let _folder = build.fold_output(|| {
format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, krate)
});
build.info(&format!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage,
&compiler.host, target));
let _time = util::timeit(&build);
try_run(build, &mut cargo);
}
}
@ -1513,12 +1534,6 @@ impl Step for CrateRustdoc {
target,
test_kind.subcommand(),
"src/tools/rustdoc");
let _folder = build.fold_output(|| {
format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage)
});
println!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage,
&compiler.host, target);
if test_kind.subcommand() == "test" && !build.fail_fast {
cargo.arg("--no-fail-fast");
}
@ -1532,7 +1547,12 @@ impl Step for CrateRustdoc {
cargo.arg("--quiet");
}
let _time = util::timeit();
let _folder = build.fold_output(|| {
format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage)
});
build.info(&format!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage,
&compiler.host, target));
let _time = util::timeit(&build);
try_run(build, &mut cargo);
}
@ -1579,7 +1599,7 @@ impl Step for RemoteCopyLibs {
builder.ensure(compile::Test { compiler, target });
println!("REMOTE copy libs to emulator ({})", target);
build.info(&format!("REMOTE copy libs to emulator ({})", target));
t!(fs::create_dir_all(build.out.join("tmp")));
let server = builder.ensure(tool::RemoteTestServer { compiler, target });
@ -1627,7 +1647,7 @@ impl Step for Distcheck {
fn run(self, builder: &Builder) {
let build = builder.build;
println!("Distcheck");
build.info(&format!("Distcheck"));
let dir = build.out.join("tmp").join("distcheck");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
@ -1651,7 +1671,7 @@ impl Step for Distcheck {
.current_dir(&dir));
// Now make sure that rust-src has all of libstd's dependencies
println!("Distcheck rust-src");
build.info(&format!("Distcheck rust-src"));
let dir = build.out.join("tmp").join("distcheck-src");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
@ -1686,9 +1706,16 @@ impl Step for Bootstrap {
let mut cmd = Command::new(&build.initial_cargo);
cmd.arg("test")
.current_dir(build.src.join("src/bootstrap"))
.env("RUSTFLAGS", "-Cdebuginfo=2")
.env("CARGO_TARGET_DIR", build.out.join("bootstrap"))
.env("RUSTC_BOOTSTRAP", "1")
.env("RUSTC", &build.initial_rustc);
if let Some(flags) = option_env!("RUSTFLAGS") {
// Use the same rustc flags for testing as for "normal" compilation,
// so that Cargo doesnt recompile the entire dependency graph every time:
// https://github.com/rust-lang/rust/issues/49215
cmd.env("RUSTFLAGS", flags);
}
if !build.fail_fast {
cmd.arg("--no-fail-fast");
}

View file

@ -17,7 +17,7 @@ use std::slice::SliceConcatExt;
use Mode;
use Compiler;
use builder::{Step, RunConfig, ShouldRun, Builder};
use util::{copy, exe, add_lib_path};
use util::{exe, add_lib_path};
use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
use native;
use channel::GitInfo;
@ -112,12 +112,85 @@ impl Step for ToolBuild {
Mode::Tool => panic!("unexpected Mode::Tool for tool build")
}
let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
cargo.arg("--features").arg(self.extra_features.join(" "));
let is_expected = build.try_run(&mut cargo);
let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
build.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
let mut duplicates = Vec::new();
let is_expected = compile::stream_cargo(build, &mut cargo, &mut |msg| {
// Only care about big things like the RLS/Cargo for now
if tool != "rls" && tool != "cargo" {
return
}
let (id, features, filenames) = match msg {
compile::CargoMessage::CompilerArtifact {
package_id,
features,
filenames
} => {
(package_id, features, filenames)
}
_ => return,
};
let features = features.iter().map(|s| s.to_string()).collect::<Vec<_>>();
for path in filenames {
let val = (tool, PathBuf::from(&*path), features.clone());
// we're only interested in deduplicating rlibs for now
if val.1.extension().and_then(|s| s.to_str()) != Some("rlib") {
continue
}
// Don't worry about libs that turn out to be host dependencies
// or build scripts, we only care about target dependencies that
// are in `deps`.
if let Some(maybe_target) = val.1
.parent() // chop off file name
.and_then(|p| p.parent()) // chop off `deps`
.and_then(|p| p.parent()) // chop off `release`
.and_then(|p| p.file_name())
.and_then(|p| p.to_str())
{
if maybe_target != &*target {
continue
}
}
let mut artifacts = build.tool_artifacts.borrow_mut();
let prev_artifacts = artifacts
.entry(target)
.or_insert_with(Default::default);
if let Some(prev) = prev_artifacts.get(&*id) {
if prev.1 != val.1 {
duplicates.push((
id.to_string(),
val,
prev.clone(),
));
}
return
}
prev_artifacts.insert(id.to_string(), val);
}
});
if is_expected && duplicates.len() != 0 {
println!("duplicate artfacts found when compiling a tool, this \
typically means that something was recompiled because \
a transitive dependency has different features activated \
than in a previous build:\n");
for (id, cur, prev) in duplicates {
println!(" {}", id);
println!(" `{}` enabled features {:?} at {:?}",
cur.0, cur.2, cur.1);
println!(" `{}` enabled features {:?} at {:?}",
prev.0, prev.2, prev.1);
}
println!("");
panic!("tools should not compile multiple copies of the same crate");
}
build.save_toolstate(tool, if is_expected {
ToolState::TestFail
} else {
@ -134,7 +207,7 @@ impl Step for ToolBuild {
let cargo_out = build.cargo_out(compiler, Mode::Tool, target)
.join(exe(tool, &compiler.host));
let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host));
copy(&cargo_out, &bin);
build.copy(&cargo_out, &bin);
Some(bin)
}
}
@ -338,9 +411,10 @@ impl Step for Rustdoc {
};
builder.ensure(compile::Rustc { compiler: build_compiler, target });
let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host);
builder.ensure(compile::Rustc {
compiler: build_compiler,
target: builder.build.build,
});
let mut cargo = prepare_tool_cargo(builder,
build_compiler,
@ -352,7 +426,11 @@ impl Step for Rustdoc {
cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
.env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
build.info(&format!("Building rustdoc for stage{} ({})",
target_compiler.stage, target_compiler.host));
build.run(&mut cargo);
// Cargo adds a number of paths to the dylib search path on windows, which results in
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
// rustdoc a different name.
@ -366,7 +444,7 @@ impl Step for Rustdoc {
t!(fs::create_dir_all(&bindir));
let bin_rustdoc = bindir.join(exe("rustdoc", &*target_compiler.host));
let _ = fs::remove_file(&bin_rustdoc);
copy(&tool_rustdoc, &bin_rustdoc);
build.copy(&tool_rustdoc, &bin_rustdoc);
bin_rustdoc
} else {
tool_rustdoc

View file

@ -15,13 +15,14 @@
use std::env;
use std::str;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Read, Write, Seek, SeekFrom};
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, Instant};
use filetime::{self, FileTime};
use config::Config;
use Build;
/// Returns the `name` as the filename of a static library for `target`.
pub fn staticlib(name: &str, target: &str) -> String {
@ -32,102 +33,6 @@ pub fn staticlib(name: &str, target: &str) -> String {
}
}
/// Copies a file from `src` to `dst`
pub fn copy(src: &Path, dst: &Path) {
let _ = fs::remove_file(&dst);
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
// windows), but if that fails just fall back to a slow `copy` operation.
if let Ok(()) = fs::hard_link(src, dst) {
return
}
if let Err(e) = fs::copy(src, dst) {
panic!("failed to copy `{}` to `{}`: {}", src.display(),
dst.display(), e)
}
let metadata = t!(src.metadata());
t!(fs::set_permissions(dst, metadata.permissions()));
let atime = FileTime::from_last_access_time(&metadata);
let mtime = FileTime::from_last_modification_time(&metadata);
t!(filetime::set_file_times(dst, atime, mtime));
}
/// Search-and-replaces within a file. (Not maximally efficiently: allocates a
/// new string for each replacement.)
pub fn replace_in_file(path: &Path, replacements: &[(&str, &str)]) {
let mut contents = String::new();
let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
t!(file.read_to_string(&mut contents));
for &(target, replacement) in replacements {
contents = contents.replace(target, replacement);
}
t!(file.seek(SeekFrom::Start(0)));
t!(file.set_len(0));
t!(file.write_all(contents.as_bytes()));
}
pub fn read_stamp_file(stamp: &Path) -> Vec<PathBuf> {
let mut paths = Vec::new();
let mut contents = Vec::new();
t!(t!(File::open(stamp)).read_to_end(&mut contents));
// This is the method we use for extracting paths from the stamp file passed to us. See
// run_cargo for more information (in compile.rs).
for part in contents.split(|b| *b == 0) {
if part.is_empty() {
continue
}
let path = PathBuf::from(t!(str::from_utf8(part)));
paths.push(path);
}
paths
}
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
/// when this function is called.
pub fn cp_r(src: &Path, dst: &Path) {
for f in t!(fs::read_dir(src)) {
let f = t!(f);
let path = f.path();
let name = path.file_name().unwrap();
let dst = dst.join(name);
if t!(f.file_type()).is_dir() {
t!(fs::create_dir_all(&dst));
cp_r(&path, &dst);
} else {
let _ = fs::remove_file(&dst);
copy(&path, &dst);
}
}
}
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
/// when this function is called. Unwanted files or directories can be skipped
/// by returning `false` from the filter function.
pub fn cp_filtered(src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) {
// Inner function does the actual work
fn recurse(src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) {
for f in t!(fs::read_dir(src)) {
let f = t!(f);
let path = f.path();
let name = path.file_name().unwrap();
let dst = dst.join(name);
let relative = relative.join(name);
// Only copy file or directory if the filter function returns true
if filter(&relative) {
if t!(f.file_type()).is_dir() {
let _ = fs::remove_dir_all(&dst);
t!(fs::create_dir(&dst));
recurse(&path, &dst, &relative, filter);
} else {
let _ = fs::remove_file(&dst);
copy(&path, &dst);
}
}
}
}
// Immediately recurse with an empty relative path
recurse(src, dst, Path::new(""), filter)
}
/// Given an executable called `name`, return the filename for the
/// executable for a particular target.
pub fn exe(name: &str, target: &str) -> String {
@ -196,25 +101,28 @@ pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf {
buf
}
pub struct TimeIt(Instant);
pub struct TimeIt(bool, Instant);
/// Returns an RAII structure that prints out how long it took to drop.
pub fn timeit() -> TimeIt {
TimeIt(Instant::now())
pub fn timeit(build: &Build) -> TimeIt {
TimeIt(build.config.dry_run, Instant::now())
}
impl Drop for TimeIt {
fn drop(&mut self) {
let time = self.0.elapsed();
println!("\tfinished in {}.{:03}",
time.as_secs(),
time.subsec_nanos() / 1_000_000);
let time = self.1.elapsed();
if !self.0 {
println!("\tfinished in {}.{:03}",
time.as_secs(),
time.subsec_nanos() / 1_000_000);
}
}
}
/// Symlinks two directories, using junctions on Windows and normal symlinks on
/// Unix.
pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
if config.dry_run { return Ok(()); }
let _ = fs::remove_dir(dest);
return symlink_dir_inner(src, dest);
@ -288,6 +196,7 @@ pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
nOutBufferSize: DWORD,
lpBytesReturned: LPDWORD,
lpOverlapped: LPOVERLAPPED) -> BOOL;
fn CloseHandle(hObject: HANDLE) -> BOOL;
}
fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
@ -341,11 +250,13 @@ pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
&mut ret,
ptr::null_mut());
if res == 0 {
let out = if res == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
};
CloseHandle(h);
out
}
}
}

View file

@ -6,6 +6,3 @@ authors = ["The Rust Project Developers"]
[lib]
name = "build_helper"
path = "lib.rs"
[dependencies]
filetime = "0.1"

View file

@ -10,14 +10,11 @@
#![deny(warnings)]
extern crate filetime;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::{fs, env};
use filetime::FileTime;
use std::time::{SystemTime, UNIX_EPOCH};
/// A helper macro to `unwrap` a result except also print out details like:
///
@ -137,10 +134,8 @@ pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
}
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
pub fn mtime(path: &Path) -> FileTime {
fs::metadata(path).map(|f| {
FileTime::from_last_modification_time(&f)
}).unwrap_or(FileTime::zero())
pub fn mtime(path: &Path) -> SystemTime {
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
}
/// Returns whether `dst` is up to date given that the file or files in `src`
@ -157,9 +152,9 @@ pub fn up_to_date(src: &Path, dst: &Path) -> bool {
Err(e) => panic!("source {:?} failed to get metadata: {}", src, e),
};
if meta.is_dir() {
dir_up_to_date(src, &threshold)
dir_up_to_date(src, threshold)
} else {
FileTime::from_last_modification_time(&meta) <= threshold
meta.modified().unwrap_or(UNIX_EPOCH) <= threshold
}
}
@ -226,13 +221,13 @@ pub fn sanitizer_lib_boilerplate(sanitizer_name: &str) -> Result<NativeLibBoiler
search_path)
}
fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool {
fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
let meta = t!(e.metadata());
if meta.is_dir() {
dir_up_to_date(&e.path(), threshold)
} else {
FileTime::from_last_modification_time(&meta) < *threshold
meta.modified().unwrap_or(UNIX_EPOCH) < threshold
}
})
}

View file

@ -32,5 +32,5 @@ ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnueabi-gcc \
ENV HOSTS=aarch64-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -26,7 +26,8 @@ ENV RUST_CONFIGURE_ARGS \
--armv7-linux-androideabi-ndk=/android/ndk/arm-14 \
--i686-linux-android-ndk=/android/ndk/x86-14 \
--aarch64-linux-android-ndk=/android/ndk/arm64-21 \
--x86_64-linux-android-ndk=/android/ndk/x86_64-21
--x86_64-linux-android-ndk=/android/ndk/x86_64-21 \
--disable-docs
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS

View file

@ -32,5 +32,5 @@ ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \
ENV HOSTS=arm-unknown-linux-gnueabi
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -32,5 +32,5 @@ ENV CC_arm_unknown_linux_gnueabihf=arm-unknown-linux-gnueabihf-gcc \
ENV HOSTS=arm-unknown-linux-gnueabihf
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -32,5 +32,5 @@ ENV CC_armv7_unknown_linux_gnueabihf=armv7-unknown-linux-gnueabihf-gcc \
ENV HOSTS=armv7-unknown-linux-gnueabihf
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -32,7 +32,8 @@ RUN sh /scripts/sccache.sh
ENV RUST_CONFIGURE_ARGS \
--musl-root-i586=/musl-i586 \
--musl-root-i686=/musl-i686 \
--enable-extended
--enable-extended \
--disable-docs
# Newer binutils broke things on some vms/distros (i.e., linking against
# unknown relocs disabled by the following flag), so we need to go out of our

View file

@ -29,5 +29,5 @@ ENV \
ENV HOSTS=i686-unknown-freebsd
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -17,6 +17,23 @@ GCC=4.8.5
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.bz2 | tar xjf -
cd gcc-$GCC
# FIXME(#49246): Remove the `sed` below.
#
# On 2018 March 21st, two Travis builders' cache for Docker are suddenly invalidated. Normally this
# is fine, because we just need to rebuild the Docker image. However, it reveals a network issue:
# downloading from `ftp://gcc.gnu.org/` from Travis (using passive mode) often leads to "Connection
# timed out" error, and even when the download completed, the file is usually corrupted. This causes
# nothing to be landed that day.
#
# We observed that the `gcc-4.8.5.tar.bz2` above can be downloaded successfully, so as a stability
# improvement we try to download from the HTTPS mirror instead. Turns out this uncovered the third
# bug: the host `gcc.gnu.org` and `cygwin.com` share the same IP, and the TLS certificate of the
# latter host is presented to `wget`! Therefore, we choose to download from the insecure HTTP server
# instead here.
#
sed -i'' 's|ftp://gcc\.gnu\.org/|http://gcc.gnu.org/|g' ./contrib/download_prerequisites
./contrib/download_prerequisites
mkdir ../gcc-build
cd ../gcc-build

View file

@ -12,7 +12,7 @@
set -ex
source shared.sh
curl https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz | tar xzf -
curl -L https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz | tar xzf -
cd git-2.10.0
make configure

View file

@ -22,5 +22,5 @@ RUN sh /scripts/sccache.sh
ENV HOSTS=mips-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -21,5 +21,5 @@ RUN sh /scripts/sccache.sh
ENV HOSTS=mips64-unknown-linux-gnuabi64
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -22,5 +22,5 @@ RUN sh /scripts/sccache.sh
ENV HOSTS=mips64el-unknown-linux-gnuabi64
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -21,5 +21,5 @@ RUN sh /scripts/sccache.sh
ENV HOSTS=mipsel-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -34,7 +34,7 @@ ENV \
ENV HOSTS=powerpc-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
# FIXME(#36150) this will fail the bootstrap. Probably means something bad is

View file

@ -35,5 +35,5 @@ ENV \
ENV HOSTS=powerpc64-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -32,5 +32,5 @@ ENV \
ENV HOSTS=powerpc64le-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -34,5 +34,5 @@ ENV \
ENV HOSTS=s390x-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -20,7 +20,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
bzip2 \
patch \
libssl-dev \
pkg-config
pkg-config \
gcc-arm-none-eabi \
libnewlib-arm-none-eabi
WORKDIR /build
@ -66,6 +68,14 @@ RUN env \
bash musl.sh mipsel && \
rm -rf /build/*
# FIXME(mozilla/sccache#235) this shouldn't be necessary but is currently
# necessary to disambiguate the mips compiler with the mipsel compiler. We want
# to give these two wrapper scripts (currently identical ones) different hashes
# to ensure that sccache understands that they're different compilers.
RUN \
echo "# a" >> /usr/local/mips-linux-musl/bin/mips-openwrt-linux-musl-wrapper.sh && \
echo "# b" >> /usr/local/mipsel-linux-musl/bin/mipsel-openwrt-linux-musl-wrapper.sh
ENV TARGETS=asmjs-unknown-emscripten
ENV TARGETS=$TARGETS,wasm32-unknown-emscripten
ENV TARGETS=$TARGETS,x86_64-rumprun-netbsd
@ -78,6 +88,10 @@ ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
ENV TARGETS=$TARGETS,x86_64-unknown-redox
ENV TARGETS=$TARGETS,thumbv6m-none-eabi
ENV TARGETS=$TARGETS,thumbv7m-none-eabi
ENV TARGETS=$TARGETS,thumbv7em-none-eabi
ENV TARGETS=$TARGETS,thumbv7em-none-eabihf
# FIXME: remove armv5te vars after https://github.com/alexcrichton/cc-rs/issues/271
# get fixed and cc update
@ -95,7 +109,8 @@ ENV RUST_CONFIGURE_ARGS \
--musl-root-aarch64=/musl-aarch64 \
--musl-root-mips=/musl-mips \
--musl-root-mipsel=/musl-mipsel \
--enable-emscripten
--enable-emscripten \
--disable-docs
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS

View file

@ -55,5 +55,5 @@ ENV TARGETS=$TARGETS,x86_64-sun-solaris
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
ENV TARGETS=$TARGETS,x86_64-unknown-cloudabi
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS

View file

@ -29,5 +29,5 @@ ENV \
ENV HOSTS=x86_64-unknown-freebsd
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -84,7 +84,8 @@ ENV HOSTS=x86_64-unknown-linux-gnu
ENV RUST_CONFIGURE_ARGS \
--enable-full-tools \
--enable-sanitizers \
--enable-profiler
--enable-profiler \
--enable-compiler-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
# This is the only builder which will create source tarballs

View file

@ -17,6 +17,23 @@ GCC=4.8.5
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.bz2 | tar xjf -
cd gcc-$GCC
# FIXME(#49246): Remove the `sed` below.
#
# On 2018 March 21st, two Travis builders' cache for Docker are suddenly invalidated. Normally this
# is fine, because we just need to rebuild the Docker image. However, it reveals a network issue:
# downloading from `ftp://gcc.gnu.org/` from Travis (using passive mode) often leads to "Connection
# timed out" error, and even when the download completed, the file is usually corrupted. This causes
# nothing to be landed that day.
#
# We observed that the `gcc-4.8.5.tar.bz2` above can be downloaded successfully, so as a stability
# improvement we try to download from the HTTPS mirror instead. Turns out this uncovered the third
# bug: the host `gcc.gnu.org` and `cygwin.com` share the same IP, and the TLS certificate of the
# latter host is presented to `wget`! Therefore, we choose to download from the insecure HTTP server
# instead here.
#
sed -i'' 's|ftp://gcc\.gnu\.org/|http://gcc.gnu.org/|g' ./contrib/download_prerequisites
./contrib/download_prerequisites
mkdir ../gcc-build
cd ../gcc-build

View file

@ -12,7 +12,7 @@
set -ex
source shared.sh
curl https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz | tar xzf -
curl -L https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz | tar xzf -
cd git-2.10.0
make configure

View file

@ -31,7 +31,8 @@ RUN sh /scripts/sccache.sh
ENV RUST_CONFIGURE_ARGS \
--musl-root-x86_64=/musl-x86_64 \
--enable-extended
--enable-extended \
--disable-docs
# Newer binutils broke things on some vms/distros (i.e., linking against
# unknown relocs disabled by the following flag), so we need to go out of our

View file

@ -33,5 +33,5 @@ ENV \
ENV HOSTS=x86_64-unknown-netbsd
ENV RUST_CONFIGURE_ARGS --enable-extended
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS

View file

@ -27,6 +27,21 @@ travis_fold start build_docker
travis_time_start
if [ -f "$docker_dir/$image/Dockerfile" ]; then
if [ "$CI" != "" ]; then
cksum=$(find $docker_dir/$image $docker_dir/scripts -type f | \
sort | \
xargs cat | \
sha512sum | \
awk '{print $1}')
s3url="s3://$SCCACHE_BUCKET/docker/$cksum"
url="https://s3-us-west-1.amazonaws.com/$SCCACHE_BUCKET/docker/$cksum"
echo "Attempting to download $s3url"
set +e
loaded_images=$(curl $url | docker load | sed 's/.* sha/sha/')
set -e
echo "Downloaded containers:\n$loaded_images"
fi
dockerfile="$docker_dir/$image/Dockerfile"
if [ -x /usr/bin/cygpath ]; then
context="`cygpath -w $docker_dir`"
@ -40,6 +55,23 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
-t rust-ci \
-f "$dockerfile" \
"$context"
if [ "$s3url" != "" ]; then
digest=$(docker inspect rust-ci --format '{{.Id}}')
echo "Built container $digest"
if ! grep -q "$digest" <(echo "$loaded_images"); then
echo "Uploading finished image to $s3url"
set +e
docker history -q rust-ci | \
grep -v missing | \
xargs docker save | \
gzip | \
aws s3 cp - $s3url
set -e
else
echo "Looks like docker image is the same as before, not uploading"
fi
fi
elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
if [ -n "$TRAVIS_OS_NAME" ]; then
echo Cannot run disabled images on travis!
@ -72,8 +104,6 @@ if [ "$SCCACHE_BUCKET" != "" ]; then
args="$args --env SCCACHE_REGION"
args="$args --env AWS_ACCESS_KEY_ID"
args="$args --env AWS_SECRET_ACCESS_KEY"
args="$args --env SCCACHE_ERROR_LOG=/tmp/sccache/sccache.log"
args="$args --volume $objdir/tmp:/tmp/sccache"
else
mkdir -p $HOME/.cache/sccache
args="$args --env SCCACHE_DIR=/sccache --volume $HOME/.cache/sccache:/sccache"

View file

@ -13,6 +13,6 @@
set -ex
curl -fo /usr/local/bin/sccache \
https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-05-12-sccache-x86_64-unknown-linux-musl
https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2018-04-02-sccache-x86_64-unknown-linux-musl
chmod +x /usr/local/bin/sccache

View file

@ -26,6 +26,7 @@ ENV RUST_CONFIGURE_ARGS \
--set rust.lld
ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
src/test/run-make \
src/test/ui \
src/test/run-pass \
src/test/compile-fail \

View file

@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
ENV PARALLEL_CHECK 1
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
--enable-debug \

View file

@ -1,22 +0,0 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
file \
curl \
ca-certificates \
python2.7 \
git \
cmake \
sudo \
gdb \
xz-utils
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
ENV RUSTFLAGS -Zincremental=/tmp/rust-incr-cache
ENV RUST_CHECK_TARGET check
ENV CARGO_INCREMENTAL 0

View file

@ -17,6 +17,7 @@ ci_dir=$(cd $(dirname $0) && pwd)
. "$ci_dir/shared.sh"
travis_fold start init_repo
travis_time_start
REPO_DIR="$1"
CACHE_DIR="$2"
@ -42,54 +43,39 @@ if grep -q RUST_RELEASE_CHANNEL=beta src/ci/run.sh; then
git fetch origin --unshallow beta master
fi
travis_fold start update_cache
travis_time_start
function fetch_submodule {
local module=$1
local cached="download-${module//\//-}.tar.gz"
retry sh -c "rm -f $cached && \
curl -sSL -o $cached $2"
mkdir $module
touch "$module/.git"
tar -C $module --strip-components=1 -xf $cached
rm $cached
}
# Update the cache (a pristine copy of the rust source master)
retry sh -c "rm -rf $cache_src_dir && mkdir -p $cache_src_dir && \
git clone --depth 1 https://github.com/rust-lang/rust.git $cache_src_dir"
if [ -d $cache_src_dir/src/llvm ]; then
(cd $cache_src_dir && git rm src/llvm)
fi
if [ -d $cache_src_dir/src/llvm-emscripten ]; then
(cd $cache_src_dir && git rm src/llvm-emscripten)
fi
retry sh -c "cd $cache_src_dir && \
git submodule deinit -f . && git submodule sync && git submodule update --init"
travis_fold end update_cache
travis_time_finish
travis_fold start update_submodules
travis_time_start
# Update the submodules of the repo we're in, using the pristine repo as
# a cache for any object files
# No, `git submodule foreach` won't work:
# http://stackoverflow.com/questions/12641469/list-submodules-in-a-git-repository
included="src/llvm src/llvm-emscripten src/doc/book src/doc/rust-by-example"
modules="$(git config --file .gitmodules --get-regexp '\.path$' | cut -d' ' -f2)"
for module in $modules; do
if [ "$module" = src/llvm ] || [ "$module" = src/llvm-emscripten ]; then
modules=($modules)
use_git=""
urls="$(git config --file .gitmodules --get-regexp '\.url$' | cut -d' ' -f2)"
urls=($urls)
for i in ${!modules[@]}; do
module=${modules[$i]}
if [[ " $included " = *" $module "* ]]; then
commit="$(git ls-tree HEAD $module | awk '{print $3}')"
git rm $module
retry sh -c "rm -f $commit.tar.gz && \
curl -sSL -O https://github.com/rust-lang/llvm/archive/$commit.tar.gz"
tar -C src/ -xf "$commit.tar.gz"
rm "$commit.tar.gz"
mv "src/llvm-$commit" $module
url=${urls[$i]}
url=${url/\.git/}
fetch_submodule $module "$url/archive/$commit.tar.gz" &
continue
else
use_git="$use_git $module"
fi
if [ ! -e "$cache_src_dir/$module/.git" ]; then
echo "WARNING: $module not found in pristine repo"
retry sh -c "git submodule deinit -f $module && \
git submodule update --init --recursive $module"
continue
fi
retry sh -c "git submodule deinit -f $module && \
git submodule update --init --recursive --reference $cache_src_dir/$module $module"
done
travis_fold end update_submodules
travis_time_finish
retry sh -c "git submodule deinit -f $use_git && \
git submodule sync && \
git submodule update -j 16 --init --recursive $use_git"
wait
travis_fold end init_repo
travis_time_finish

View file

@ -25,6 +25,8 @@ source "$ci_dir/shared.sh"
if [ "$TRAVIS" == "true" ] && [ "$TRAVIS_BRANCH" != "auto" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-quiet-tests"
else
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings"
fi
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache"
@ -72,6 +74,13 @@ fi
# sccache server at the start of the build, but no need to worry if this fails.
SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true
if [ "$PARALLEL_CHECK" != "" ]; then
$SRC/configure --enable-experimental-parallel-queries
python2.7 ../x.py check
rm -f config.toml
rm -rf build
fi
travis_fold start configure
travis_time_start
$SRC/configure $RUST_CONFIGURE_ARGS

@ -1 +1 @@
Subproject commit 98921e9de849acdaeaed08cfad6758bb89769b7d
Subproject commit b889e1e30c5e9953834aa9fa6c982bb28df46ac9

View file

@ -6,55 +6,72 @@ nav {
}
</style>
This page is an overview of the documentation included with your Rust install.
Other unofficial documentation may exist elsewhere; for example, the [Rust
Learning] project collects documentation from the community, and [Docs.rs]
builds documentation for individual Rust packages.
Welcome to an overview of the documentation provided by the Rust project.
All of these projects are managed by the Docs Team; there are other
unofficial documentation resources as well!
# API Documentation
Many of these resources take the form of "books"; we collectively call these
"The Rust Bookshelf." Some are large, some are small.
Rust provides a standard library with a number of features; [we host its
documentation here][api].
## Learn Rust
# Extended Error Documentation
If you'd like to learn Rust, this is the spot for you! All of these resources
assume that you have programmed before, but not in any specific language:
### The Rust Programming Language
Affectionately nicknamed "the book," [The Rust Programming
Language](book/index.html) will give you an overview of the language from
first principles. You'll build a few projects along the way, and by the end,
you'll have a solid grasp of the language.
### Rust By Example
If reading multiple hundreds of pages about a language isn't your style, then
[Rust By Example](rust-by-example/index.html) has you covered. While the book talks about code with
a lot of words, RBE shows off a bunch of code, and keeps the talking to a
minimum. It also includes exercises!
## Use Rust
Once you've gotten familliar with the language, these resources can help you
when you're actually using it day-to-day.
### The Standard Library
Rust's standard library has [extensive API documentation](std/index.html),
with explanations of how to use various things, as well as example code for
accomplishing various tasks.
### The Cargo Book
[The Cargo Book](cargo/index.html) is a guide to Cargo, Rust's build tool and dependency manager.
### The Rustdoc Book
[The Rustdoc Book](rustdoc/index.html) describes our documentation tool, `rustdoc`.
### Extended Error Listing
Many of Rust's errors come with error codes, and you can request extended
diagnostics from the compiler on those errors. We also [have the text of those
extended errors on the web][err], if you prefer to read them that way.
diagnostics from the compiler on those errors. You can also [read them
here](error-index.html), if you prefer to read them that way.
# The Rust Bookshelf
## Master Rust
Rust provides a number of book-length sets of documentation, collectively
nicknamed 'The Rust Bookshelf.'
Once you're quite familiar with the language, you may find these advanced
resources useful.
* [The Rust Programming Language][book] teaches you how to program in Rust.
* [Rust By Example][rbe] teaches you how to program in Rust using editable examples.
* [The Cargo Book][cargo-book] is a guide to Cargo, Rust's build tool and dependency manager.
* [The Unstable Book][unstable-book] has documentation for unstable features.
* [The Rustonomicon][nomicon] is your guidebook to the dark arts of unsafe Rust.
* [The Reference][ref] is not a formal spec, but is more detailed and comprehensive than the book.
* [The Rustdoc Book][rustdoc-book] describes our documentation tool, `rustdoc`.
### The Reference
Initially, documentation lands in the Unstable Book, and then, as part of the
stabilization process, is moved into the Book, Nomicon, or Reference.
[The Reference](reference/index.html) is not a formal spec, but is more detailed and
comprehensive than the book.
Another few words about the reference: it is guaranteed to be accurate, but not
complete. We have a policy that features must have documentation to be stabilized,
but we did not always have this policy, and so there are some stable things that
are not yet in the reference. We're working on back-filling things that landed
before this policy was put into place. That work is being tracked
[here][refchecklist].
### The Rustonomicon
[Rust Learning]: https://github.com/ctjhoa/rust-learning
[Docs.rs]: https://docs.rs/
[api]: std/index.html
[ref]: reference/index.html
[refchecklist]: https://github.com/rust-lang-nursery/reference/issues/9
[err]: error-index.html
[book]: book/index.html
[rbe]: rust-by-example/index.html
[nomicon]: nomicon/index.html
[unstable-book]: unstable-book/index.html
[rustdoc-book]: rustdoc/index.html
[cargo-book]: cargo/index.html
[The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of unsafe
Rust. It's also sometimes called "the 'nomicon."
### The Unstable Book
[The Unstable Book](unstable-book/index.html) has documentation for unstable features.

@ -1 +1 @@
Subproject commit ad5ddd62c098d5b424151beda574ae7df2154df1
Subproject commit 6a8f0a27e9a58c55c89d07bc43a176fdae5e051c

@ -1 +1 @@
Subproject commit 254df654a9b75abf6ca08806535dbe1fad41be3f
Subproject commit 76296346e97c3702974d3398fdb94af9e10111a2

@ -1 +1 @@
Subproject commit ebb28c95b2ea68b96eddb9e71aff4d32eacc74f0
Subproject commit d5ec87eabe5733cc2348c7dada89fc67c086f391

View file

@ -5,3 +5,4 @@
- [The `#[doc]` attribute](the-doc-attribute.md)
- [Documentation tests](documentation-tests.md)
- [Passes](passes.md)
- [Unstable features](unstable-features.md)

View file

@ -0,0 +1,375 @@
# Unstable features
Rustdoc is under active developement, and like the Rust compiler, some features are only available
on the nightly releases. Some of these are new and need some more testing before they're able to get
released to the world at large, and some of them are tied to features in the Rust compiler that are
themselves unstable. Several features here require a matching `#![feature(...)]` attribute to
enable, and thus are more fully documented in the [Unstable Book]. Those sections will link over
there as necessary.
[Unstable Book]: ../unstable-book/index.html
## Nightly-gated functionality
These features just require a nightly build to operate. Unlike the other features on this page,
these don't need to be "turned on" with a command-line flag or a `#![feature(...)]` attribute in
your crate. This can give them some subtle fallback modes when used on a stable release, so be
careful!
### Error numbers for `compile-fail` doctests
As detailed in [the chapter on documentation tests][doctest-attributes], you can add a
`compile_fail` attribute to a doctest to state that the test should fail to compile. However, on
nightly, you can optionally add an error number to state that a doctest should emit a specific error
number:
[doctest-attributes]: documentation-tests.html#attributes
``````markdown
```compile_fail,E0044
extern { fn some_func<T>(x: T); }
```
``````
This is used by the error index to ensure that the samples that correspond to a given error number
properly emit that error code. However, these error codes aren't guaranteed to be the only thing
that a piece of code emits from version to version, so this is unlikely to be stabilized in the
future.
Attempting to use these error numbers on stable will result in the code sample being interpreted as
plain text.
### Linking to items by type
As designed in [RFC 1946], Rustdoc can parse paths to items when you use them as links. To resolve
these type names, it uses the items currently in-scope, either by declaration or by `use` statement.
For modules, the "active scope" depends on whether the documentation is written outside the module
(as `///` comments on the `mod` statement) or inside the module (at `//!` comments inside the file
or block). For all other items, it uses the enclosing module's scope.
[RFC 1946]: https://github.com/rust-lang/rfcs/pull/1946
For example, in the following code:
```rust
/// Does the thing.
pub fn do_the_thing(_: SomeType) {
println!("Let's do the thing!");
}
/// Token you use to [`do_the_thing`].
pub struct SomeType;
```
The link to ``[`do_the_thing`]`` in `SomeType`'s docs will properly link to the page for `fn
do_the_thing`. Note that here, rustdoc will insert the link target for you, but manually writing the
target out also works:
```rust
pub mod some_module {
/// Token you use to do the thing.
pub struct SomeStruct;
}
/// Does the thing. Requires one [`SomeStruct`] for the thing to work.
///
/// [`SomeStruct`]: some_module::SomeStruct
pub fn do_the_thing(_: some_module::SomeStruct) {
println!("Let's do the thing!");
}
```
For more details, check out [the RFC][RFC 1946], and see [the tracking issue][43466] for more
information about what parts of the feature are available.
[43466]: https://github.com/rust-lang/rust/issues/43466
## Extensions to the `#[doc]` attribute
These features operate by extending the `#[doc]` attribute, and thus can be caught by the compiler
and enabled with a `#![feature(...)]` attribute in your crate.
### Documenting platform-/feature-specific information
Because of the way Rustdoc documents a crate, the documentation it creates is specific to the target
rustc compiles for. Anything that's specific to any other target is dropped via `#[cfg]` attribute
processing early in the compilation process. However, Rustdoc has a trick up its sleeve to handle
platform-specific code if it *does* receive it.
Because Rustdoc doesn't need to fully compile a crate to binary, it replaces function bodies with
`loop {}` to prevent having to process more than necessary. This means that any code within a
function that requires platform-specific pieces is ignored. Combined with a special attribute,
`#[doc(cfg(...))]`, you can tell Rustdoc exactly which platform something is supposed to run on,
ensuring that doctests are only run on the appropriate platforms.
The `#[doc(cfg(...))]` attribute has another effect: When Rustdoc renders documentation for that
item, it will be accompanied by a banner explaining that the item is only available on certain
platforms.
As mentioned earlier, getting the items to Rustdoc requires some extra preparation. The standard
library adds a `--cfg dox` flag to every Rustdoc command, but the same thing can be accomplished by
adding a feature to your Cargo.toml and adding `--feature dox` (or whatever you choose to name the
feature) to your `cargo doc` calls.
Either way, once you create an environment for the documentation, you can start to augment your
`#[cfg]` attributes to allow both the target platform *and* the documentation configuration to leave
the item in. For example, `#[cfg(any(windows, feature = "dox"))]` will preserve the item either on
Windows or during the documentation process. Then, adding a new attribute `#[doc(cfg(windows))]`
will tell Rustdoc that the item is supposed to be used on Windows. For example:
```rust
#![feature(doc_cfg)]
/// Token struct that can only be used on Windows.
#[cfg(any(windows, feature = "dox"))]
#[doc(cfg(windows))]
pub struct WindowsToken;
/// Token struct that can only be used on Unix.
#[cfg(any(unix, feature = "dox"))]
#[doc(cfg(unix))]
pub struct UnixToken;
```
In this sample, the tokens will only appear on their respective platforms, but they will both appear
in documentation.
`#[doc(cfg(...))]` was introduced to be used by the standard library and currently requires the
`#![feature(doc_cfg)]` feature gate. For more information, see [its chapter in the Unstable
Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg].
[unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html
[issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781
### Adding your trait to the "Important Traits" dialog
Rustdoc keeps a list of a few traits that are believed to be "fundamental" to a given type when
implemented on it. These traits are intended to be the primary interface for their types, and are
often the only thing available to be documented on their types. For this reason, Rustdoc will track
when a given type implements one of these traits and call special attention to it when a function
returns one of these types. This is the "Important Traits" dialog, visible as a circle-i button next
to the function, which, when clicked, shows the dialog.
In the standard library, the traits that qualify for inclusion are `Iterator`, `io::Read`, and
`io::Write`. However, rather than being implemented as a hard-coded list, these traits have a
special marker attribute on them: `#[doc(spotlight)]`. This means that you could apply this
attribute to your own trait to include it in the "Important Traits" dialog in documentation.
The `#[doc(spotlight)]` attribute currently requires the `#![feature(doc_spotlight)]` feature gate.
For more information, see [its chapter in the Unstable Book][unstable-spotlight] and [its tracking
issue][issue-spotlight].
[unstable-spotlight]: ../unstable-book/language-features/doc-spotlight.html
[issue-spotlight]: https://github.com/rust-lang/rust/issues/45040
### Exclude certain dependencies from documentation
The standard library uses several dependencies which, in turn, use several types and traits from the
standard library. In addition, there are several compiler-internal crates that are not considered to
be part of the official standard library, and thus would be a distraction to include in
documentation. It's not enough to exclude their crate documentation, since information about trait
implementations appears on the pages for both the type and the trait, which can be in different
crates!
To prevent internal types from being included in documentation, the standard library adds an
attribute to their `extern crate` declarations: `#[doc(masked)]`. This causes Rustdoc to "mask out"
types from these crates when building lists of trait implementations.
The `#[doc(masked)]` attribute is intended to be used internally, and requires the
`#![feature(doc_masked)]` feature gate. For more information, see [its chapter in the Unstable
Book][unstable-masked] and [its tracking issue][issue-masked].
[unstable-masked]: ../unstable-book/language-features/doc-masked.html
[issue-masked]: https://github.com/rust-lang/rust/issues/44027
### Include external files as API documentation
As designed in [RFC 1990], Rustdoc can read an external file to use as a type's documentation. This
is useful if certain documentation is so long that it would break the flow of reading the source.
Instead of writing it all inline, writing `#[doc(include = "sometype.md")]` (where `sometype.md` is
a file adjacent to the `lib.rs` for the crate) will ask Rustdoc to instead read that file and use it
as if it were written inline.
[RFC 1990]: https://github.com/rust-lang/rfcs/pull/1990
`#[doc(include = "...")]` currently requires the `#![feature(external_doc)]` feature gate. For more
information, see [its chapter in the Unstable Book][unstable-include] and [its tracking
issue][issue-include].
[unstable-include]: ../unstable-book/language-features/external-doc.html
[issue-include]: https://github.com/rust-lang/rust/issues/44732
## Unstable command-line arguments
These features are enabled by passing a command-line flag to Rustdoc, but the flags in question are
themselves marked as unstable. To use any of these options, pass `-Z unstable-options` as well as
the flag in question to Rustdoc on the command-line. To do this from Cargo, you can either use the
`RUSTDOCFLAGS` environment variable or the `cargo rustdoc` command.
### `--markdown-before-content`: include rendered Markdown before the content
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --markdown-before-content extra.md
$ rustdoc README.md -Z unstable-options --markdown-before-content extra.md
```
Just like `--html-before-content`, this allows you to insert extra content inside the `<body>` tag
but before the other content `rustdoc` would normally produce in the rendered documentation.
However, instead of directly inserting the file verbatim, `rustdoc` will pass the files through a
Markdown renderer before inserting the result into the file.
### `--markdown-after-content`: include rendered Markdown after the content
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --markdown-after-content extra.md
$ rustdoc README.md -Z unstable-options --markdown-after-content extra.md
```
Just like `--html-after-content`, this allows you to insert extra content before the `</body>` tag
but after the other content `rustdoc` would normally produce in the rendered documentation.
However, instead of directly inserting the file verbatim, `rustdoc` will pass the files through a
Markdown renderer before inserting the result into the file.
### `--playground-url`: control the location of the playground
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --playground-url https://play.rust-lang.org/
```
When rendering a crate's docs, this flag gives the base URL of the Rust Playground, to use for
generating `Run` buttons. Unlike `--markdown-playground-url`, this argument works for standalone
Markdown files *and* Rust crates. This works the same way as adding `#![doc(html_playground_url =
"url")]` to your crate root, as mentioned in [the chapter about the `#[doc]`
attribute][doc-playground]. Please be aware that the official Rust Playground at
https://play.rust-lang.org does not have every crate available, so if your examples require your
crate, make sure the playground you provide has your crate available.
[doc-playground]: the-doc-attribute.html#html_playground_url
If both `--playground-url` and `--markdown-playground-url` are present when rendering a standalone
Markdown file, the URL given to `--markdown-playground-url` will take precedence. If both
`--playground-url` and `#![doc(html_playground_url = "url")]` are present when rendering crate docs,
the attribute will take precedence.
### `--crate-version`: control the crate version
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --crate-version 1.3.37
```
When `rustdoc` receives this flag, it will print an extra "Version (version)" into the sidebar of
the crate root's docs. You can use this flag to differentiate between different versions of your
library's documentation.
### `--linker`: control the linker used for documentation tests
Using this flag looks like this:
```bash
$ rustdoc --test src/lib.rs -Z unstable-options --linker foo
$ rustdoc --test README.md -Z unstable-options --linker foo
```
When `rustdoc` runs your documentation tests, it needs to compile and link the tests as executables
before running them. This flag can be used to change the linker used on these executables. It's
equivalent to passing `-C linker=foo` to `rustc`.
### `--sort-modules-by-appearance`: control how items on module pages are sorted
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --sort-modules-by-appearance
```
Ordinarily, when `rustdoc` prints items in module pages, it will sort them alphabetically (taking
some consideration for their stability, and names that end in a number). Giving this flag to
`rustdoc` will disable this sorting and instead make it print the items in the order they appear in
the source.
### `--themes`: provide additional themes
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --themes theme.css
```
Giving this flag to `rustdoc` will make it copy your theme into the generated crate docs and enable
it in the theme selector. Note that `rustdoc` will reject your theme file if it doesn't style
everything the "light" theme does. See `--theme-checker` below for details.
### `--theme-checker`: verify theme CSS for validity
Using this flag looks like this:
```bash
$ rustdoc -Z unstable-options --theme-checker theme.css
```
Before including your theme in crate docs, `rustdoc` will compare all the CSS rules it contains
against the "light" theme included by default. Using this flag will allow you to see which rules are
missing if `rustdoc` rejects your theme.
### `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --resource-suffix suf
```
When rendering docs, `rustdoc` creates several CSS and JavaScript files as part of the output. Since
all these files are linked from every page, changing where they are can be cumbersome if you need to
specially cache them. This flag will rename all these files in the output to include the suffix in
the filename. For example, `light.css` would become `light-suf.css` with the above command.
### `--display-warnings`: display warnings when documenting or running documentation tests
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --display-warnings
$ rustdoc --test src/lib.rs -Z unstable-options --display-warnings
```
The intent behind this flag is to allow the user to see warnings that occur within their library or
their documentation tests, which are usually suppressed. However, [due to a
bug][issue-display-warnings], this flag doesn't 100% work as intended. See the linked issue for
details.
[issue-display-warnings]: https://github.com/rust-lang/rust/issues/41574
### `--edition`: control the edition of docs and doctests
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options --edition 2018
$ rustdoc --test src/lib.rs -Z unstable-options --edition 2018
```
This flag allows rustdoc to treat your rust code as the given edition. It will compile doctests with
the given edition as well. As with `rustc`, the default edition that `rustdoc` will use is `2015`
(the first edition).
### `-Z force-unstable-if-unmarked`
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z force-unstable-if-unmarked
```
This is an internal flag intended for the standard library and compiler that applies an
`#[unstable]` attribute to any dependent crate that doesn't have another stability attribute. This
allows `rustdoc` to be able to generate documentation for the compiler crates and the standard
library, as an equivalent command-line argument is provided to `rustc` when building those crates.

View file

@ -1,66 +0,0 @@
# `conservative_impl_trait`
The tracking issue for this feature is: [#34511]
[#34511]: https://github.com/rust-lang/rust/issues/34511
------------------------
The `conservative_impl_trait` feature allows a conservative form of abstract
return types.
Abstract return types allow a function to hide a concrete return type behind a
trait interface similar to trait objects, while still generating the same
statically dispatched code as with concrete types.
## Examples
```rust
#![feature(conservative_impl_trait)]
fn even_iter() -> impl Iterator<Item=u32> {
(0..).map(|n| n * 2)
}
fn main() {
let first_four_even_numbers = even_iter().take(4).collect::<Vec<_>>();
assert_eq!(first_four_even_numbers, vec![0, 2, 4, 6]);
}
```
## Background
In today's Rust, you can write function signatures like:
````rust,ignore
fn consume_iter_static<I: Iterator<Item=u8>>(iter: I) { }
fn consume_iter_dynamic(iter: Box<Iterator<Item=u8>>) { }
````
In both cases, the function does not depend on the exact type of the argument.
The type held is "abstract", and is assumed only to satisfy a trait bound.
* In the `_static` version using generics, each use of the function is
specialized to a concrete, statically-known type, giving static dispatch,
inline layout, and other performance wins.
* In the `_dynamic` version using trait objects, the concrete argument type is
only known at runtime using a vtable.
On the other hand, while you can write:
````rust,ignore
fn produce_iter_dynamic() -> Box<Iterator<Item=u8>> { }
````
...but you _cannot_ write something like:
````rust,ignore
fn produce_iter_static() -> Iterator<Item=u8> { }
````
That is, in today's Rust, abstract return types can only be written using trait
objects, which can be a significant performance penalty. This RFC proposes
"unboxed abstract types" as a way of achieving signatures like
`produce_iter_static`. Like generics, unboxed abstract types guarantee static
dispatch and inline data layout.

View file

@ -36,11 +36,11 @@ fn main() {
return "foo"
};
match generator.resume() {
match unsafe { generator.resume() } {
GeneratorState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
match generator.resume() {
match unsafe { generator.resume() } {
GeneratorState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
@ -69,9 +69,9 @@ fn main() {
};
println!("1");
generator.resume();
unsafe { generator.resume() };
println!("3");
generator.resume();
unsafe { generator.resume() };
println!("5");
}
```
@ -92,7 +92,7 @@ The `Generator` trait in `std::ops` currently looks like:
pub trait Generator {
type Yield;
type Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
}
```
@ -175,8 +175,8 @@ fn main() {
return ret
};
generator.resume();
generator.resume();
unsafe { generator.resume() };
unsafe { generator.resume() };
}
```
@ -200,7 +200,7 @@ fn main() {
type Yield = i32;
type Return = &'static str;
fn resume(&mut self) -> GeneratorState<i32, &'static str> {
unsafe fn resume(&mut self) -> GeneratorState<i32, &'static str> {
use std::mem;
match mem::replace(self, __Generator::Done) {
__Generator::Start(s) => {
@ -223,8 +223,8 @@ fn main() {
__Generator::Start(ret)
};
generator.resume();
generator.resume();
unsafe { generator.resume() };
unsafe { generator.resume() };
}
```

View file

@ -1,25 +0,0 @@
# `i128_type`
The tracking issue for this feature is: [#35118]
[#35118]: https://github.com/rust-lang/rust/issues/35118
------------------------
The `i128_type` feature adds support for 128 bit signed and unsigned integer
types.
```rust
#![feature(i128_type)]
fn main() {
assert_eq!(1u128 + 1u128, 2u128);
assert_eq!(u128::min_value(), 0);
assert_eq!(u128::max_value(), 340282366920938463463374607431768211455);
assert_eq!(1i128 - 2i128, -1i128);
assert_eq!(i128::min_value(), -170141183460469231731687303715884105728);
assert_eq!(i128::max_value(), 170141183460469231731687303715884105727);
}
```

View file

@ -1,58 +0,0 @@
# `match_default_bindings`
The tracking issue for this feature is: [#42640]
[#42640]: https://github.com/rust-lang/rust/issues/42640
------------------------
Match default bindings (also called "default binding modes in match") improves ergonomics for
pattern-matching on references by introducing automatic dereferencing (and a corresponding shift
in binding modes) for large classes of patterns that would otherwise not compile.
For example, under match default bindings,
```rust
#![feature(match_default_bindings)]
fn main() {
let x: &Option<_> = &Some(0);
match x {
Some(y) => {
println!("y={}", *y);
},
None => {},
}
}
```
compiles and is equivalent to either of the below:
```rust
fn main() {
let x: &Option<_> = &Some(0);
match *x {
Some(ref y) => {
println!("y={}", *y);
},
None => {},
}
}
```
or
```rust
fn main() {
let x: &Option<_> = &Some(0);
match x {
&Some(ref y) => {
println!("y={}", *y);
},
&None => {},
}
}
```

View file

@ -0,0 +1,18 @@
# `repr128`
The tracking issue for this feature is: [#35118]
[#35118]: https://github.com/rust-lang/rust/issues/35118
------------------------
The `repr128` feature adds support for `#[repr(u128)]` on `enum`s.
```rust
#![feature(repr128)]
#[repr(u128)]
enum Foo {
Bar(u64),
}
```

View file

@ -1,32 +0,0 @@
# `universal_impl_trait`
The tracking issue for this feature is: [#34511].
[#34511]: https://github.com/rust-lang/rust/issues/34511
--------------------
The `universal_impl_trait` feature extends the [`conservative_impl_trait`]
feature allowing the `impl Trait` syntax in arguments (universal
quantification).
[`conservative_impl_trait`]: ./language-features/conservative-impl-trait.html
## Examples
```rust
#![feature(universal_impl_trait)]
use std::ops::Not;
fn any_zero(values: impl IntoIterator<Item = i32>) -> bool {
for val in values { if val == 0 { return true; } }
false
}
fn main() {
let test1 = -5..;
let test2 = vec![1, 8, 42, -87, 60];
assert!(any_zero(test1));
assert!(bool::not(any_zero(test2)));
}
```

View file

@ -1,22 +0,0 @@
# `splice`
The tracking issue for this feature is: [#44643]
[#44643]: https://github.com/rust-lang/rust/issues/44643
------------------------
The `splice()` method on `String` allows you to replace a range
of values in a string with another range of values.
A simple example:
```rust
#![feature(splice)]
let mut s = String::from("α is alpha, β is beta");
let beta_offset = s.find('β').unwrap_or(s.len());
// Replace the range up until the β from the string
s.splice(..beta_offset, "Α is capital alpha; ");
assert_eq!(s, "Α is capital alpha; β is beta");
```

View file

@ -1,23 +0,0 @@
# `string_retain`
The tracking issue for this feature is: [#43874]
[#43874]: https://github.com/rust-lang/rust/issues/43874
------------------------
Retains only the characters specified by the predicate.
In other words, remove all characters `c` such that `f(c)` returns `false`.
This method operates in place and preserves the order of the retained
characters.
```rust
#![feature(string_retain)]
let mut s = String::from("f_o_ob_ar");
s.retain(|c| c != '_');
assert_eq!(s, "foobar");
```

View file

@ -63,6 +63,11 @@ done
shift $((OPTIND - 1))
# use gnu version of tool if available (for bsd)
if command -v "g${GREPPER}"; then
GREPPER="g${GREPPER}"
fi
LOG=$(mktemp -t cgrep.XXXXXX)
trap "rm -f $LOG" EXIT

View file

@ -1400,7 +1400,6 @@ nonblock_expr
| BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); }
| YIELD { $$ = mk_node("ExprYield", 0); }
| YIELD expr { $$ = mk_node("ExprYield", 1, $2); }
| nonblock_expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); }
| nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); }
| nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
| nonblock_expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
@ -1463,7 +1462,6 @@ expr
| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); }
| YIELD { $$ = mk_node("ExprYield", 0); }
| YIELD expr { $$ = mk_node("ExprYield", 1, $2); }
| expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); }
| expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); }
| expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
| expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); }
@ -1527,7 +1525,6 @@ expr_nostruct
| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); }
| YIELD { $$ = mk_node("ExprYield", 0); }
| YIELD expr { $$ = mk_node("ExprYield", 1, $2); }
| expr_nostruct LARROW expr_nostruct { $$ = mk_node("ExprInPlace", 2, $1, $3); }
| expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); }
| expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); }
| expr_nostruct SHREQ expr_nostruct { $$ = mk_node("ExprAssignShr", 2, $1, $3); }

View file

@ -21,6 +21,7 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
use core::borrow;
use core::fmt;
use core::cmp::Ordering;
use core::heap::{Alloc, Layout};
use core::intrinsics::abort;
use core::mem::{self, align_of_val, size_of_val, uninitialized};
use core::ops::Deref;
@ -31,7 +32,7 @@ use core::hash::{Hash, Hasher};
use core::{isize, usize};
use core::convert::From;
use heap::{Heap, Alloc, Layout, box_free};
use heap::{Heap, box_free};
use boxed::Box;
use string::String;
use vec::Vec;

View file

@ -10,9 +10,10 @@
#![deny(warnings)]
#![feature(i128_type)]
#![cfg_attr(stage0, feature(i128_type))]
#![feature(rand)]
#![feature(repr_simd)]
#![feature(slice_sort_by_cached_key)]
#![feature(test)]
extern crate rand;

View file

@ -284,6 +284,17 @@ macro_rules! sort_expensive {
}
}
macro_rules! sort_lexicographic {
($f:ident, $name:ident, $gen:expr, $len:expr) => {
#[bench]
fn $name(b: &mut Bencher) {
let v = $gen($len);
b.iter(|| v.clone().$f(|x| x.to_string()));
b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
}
}
}
sort!(sort, sort_small_ascending, gen_ascending, 10);
sort!(sort, sort_small_descending, gen_descending, 10);
sort!(sort, sort_small_random, gen_random, 10);
@ -312,6 +323,10 @@ sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000);
sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000);
sort_lexicographic!(sort_by_key, sort_by_key_lexicographic, gen_random, 10000);
sort_lexicographic!(sort_unstable_by_key, sort_unstable_by_key_lexicographic, gen_random, 10000);
sort_lexicographic!(sort_by_cached_key, sort_by_cached_key_lexicographic, gen_random, 10000);
macro_rules! reverse {
($name:ident, $ty:ty, $f:expr) => {
#[bench]

View file

@ -155,7 +155,7 @@
#![allow(missing_docs)]
#![stable(feature = "rust1", since = "1.0.0")]
use core::ops::{Deref, DerefMut, Place, Placer, InPlace};
use core::ops::{Deref, DerefMut};
use core::iter::{FromIterator, FusedIterator};
use core::mem::{swap, size_of};
use core::ptr;
@ -509,6 +509,31 @@ impl<T: Ord> BinaryHeap<T> {
self.data.shrink_to_fit();
}
/// Discards capacity with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::collections::BinaryHeap;
/// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
///
/// assert!(heap.capacity() >= 100);
/// heap.shrink_to(10);
/// assert!(heap.capacity() >= 10);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.data.shrink_to(min_capacity)
}
/// Removes the greatest item from the binary heap and returns it, or `None` if it
/// is empty.
///
@ -1170,67 +1195,3 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap<T> {
self.extend(iter.into_iter().cloned());
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
pub struct BinaryHeapPlace<'a, T: 'a>
where T: Clone + Ord {
heap: *mut BinaryHeap<T>,
place: vec::PlaceBack<'a, T>,
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T: Clone + Ord + fmt::Debug> fmt::Debug for BinaryHeapPlace<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("BinaryHeapPlace")
.field(&self.place)
.finish()
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T: 'a> Placer<T> for &'a mut BinaryHeap<T>
where T: Clone + Ord {
type Place = BinaryHeapPlace<'a, T>;
fn make_place(self) -> Self::Place {
let ptr = self as *mut BinaryHeap<T>;
let place = Placer::make_place(self.data.place_back());
BinaryHeapPlace {
heap: ptr,
place,
}
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
unsafe impl<'a, T> Place<T> for BinaryHeapPlace<'a, T>
where T: Clone + Ord {
fn pointer(&mut self) -> *mut T {
self.place.pointer()
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> InPlace<T> for BinaryHeapPlace<'a, T>
where T: Clone + Ord {
type Owner = &'a T;
unsafe fn finalize(self) -> &'a T {
self.place.finalize();
let heap: &mut BinaryHeap<T> = &mut *self.heap;
let len = heap.len();
let i = heap.sift_up(0, len - 1);
heap.data.get_unchecked(i)
}
}

View file

@ -59,6 +59,7 @@ pub trait ToOwned {
/// let vv: Vec<i32> = v.to_owned();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "cloning is often expensive and is not expected to have side effects"]
fn to_owned(&self) -> Self::Owned;
/// Uses borrowed data to replace owned data, usually by cloning.

View file

@ -55,54 +55,21 @@
#![stable(feature = "rust1", since = "1.0.0")]
use heap::{Heap, Layout, Alloc};
use raw_vec::RawVec;
use core::any::Any;
use core::borrow;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{self, Hash, Hasher};
use core::hash::{Hash, Hasher};
use core::iter::FusedIterator;
use core::marker::{self, Unpin, Unsize};
use core::marker::{Unpin, Unsize};
use core::mem::{self, Pin};
use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState};
use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer};
use core::ptr::{self, NonNull, Unique};
use core::convert::From;
use str::from_boxed_utf8_unchecked;
/// A value that represents the heap. This is the default place that the `box`
/// keyword allocates into when no place is supplied.
///
/// The following two examples are equivalent:
///
/// ```
/// #![feature(box_heap)]
///
/// #![feature(box_syntax, placement_in_syntax)]
/// use std::boxed::HEAP;
///
/// fn main() {
/// let foo: Box<i32> = in HEAP { 5 };
/// let foo = box 5;
/// }
/// ```
#[unstable(feature = "box_heap",
reason = "may be renamed; uncertain about custom allocator design",
issue = "27779")]
pub const HEAP: ExchangeHeapSingleton = ExchangeHeapSingleton { _force_singleton: () };
/// This the singleton type used solely for `boxed::HEAP`.
#[unstable(feature = "box_heap",
reason = "may be renamed; uncertain about custom allocator design",
issue = "27779")]
#[allow(missing_debug_implementations)]
#[derive(Copy, Clone)]
pub struct ExchangeHeapSingleton {
_force_singleton: (),
}
/// A pointer type for heap allocation.
///
/// See the [module-level documentation](../../std/boxed/index.html) for more.
@ -111,121 +78,6 @@ pub struct ExchangeHeapSingleton {
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Box<T: ?Sized>(Unique<T>);
/// `IntermediateBox` represents uninitialized backing storage for `Box`.
///
/// FIXME (pnkfelix): Ideally we would just reuse `Box<T>` instead of
/// introducing a separate `IntermediateBox<T>`; but then you hit
/// issues when you e.g. attempt to destructure an instance of `Box`,
/// since it is a lang item and so it gets special handling by the
/// compiler. Easier just to make this parallel type for now.
///
/// FIXME (pnkfelix): Currently the `box` protocol only supports
/// creating instances of sized types. This IntermediateBox is
/// designed to be forward-compatible with a future protocol that
/// supports creating instances of unsized types; that is why the type
/// parameter has the `?Sized` generalization marker, and is also why
/// this carries an explicit size. However, it probably does not need
/// to carry the explicit alignment; that is just a work-around for
/// the fact that the `align_of` intrinsic currently requires the
/// input type to be Sized (which I do not think is strictly
/// necessary).
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
#[allow(missing_debug_implementations)]
pub struct IntermediateBox<T: ?Sized> {
ptr: *mut u8,
layout: Layout,
marker: marker::PhantomData<*mut T>,
}
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
unsafe impl<T> Place<T> for IntermediateBox<T> {
fn pointer(&mut self) -> *mut T {
self.ptr as *mut T
}
}
unsafe fn finalize<T>(b: IntermediateBox<T>) -> Box<T> {
let p = b.ptr as *mut T;
mem::forget(b);
Box::from_raw(p)
}
fn make_place<T>() -> IntermediateBox<T> {
let layout = Layout::new::<T>();
let p = if layout.size() == 0 {
mem::align_of::<T>() as *mut u8
} else {
unsafe {
Heap.alloc(layout.clone()).unwrap_or_else(|err| {
Heap.oom(err)
})
}
};
IntermediateBox {
ptr: p,
layout,
marker: marker::PhantomData,
}
}
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
impl<T> BoxPlace<T> for IntermediateBox<T> {
fn make_place() -> IntermediateBox<T> {
make_place()
}
}
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
impl<T> InPlace<T> for IntermediateBox<T> {
type Owner = Box<T>;
unsafe fn finalize(self) -> Box<T> {
finalize(self)
}
}
#[unstable(feature = "placement_new_protocol", issue = "27779")]
impl<T> Boxed for Box<T> {
type Data = T;
type Place = IntermediateBox<T>;
unsafe fn finalize(b: IntermediateBox<T>) -> Box<T> {
finalize(b)
}
}
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
impl<T> Placer<T> for ExchangeHeapSingleton {
type Place = IntermediateBox<T>;
fn make_place(self) -> IntermediateBox<T> {
make_place()
}
}
#[unstable(feature = "placement_in",
reason = "placement box design is still being worked out.",
issue = "27779")]
impl<T: ?Sized> Drop for IntermediateBox<T> {
fn drop(&mut self) {
if self.layout.size() > 0 {
unsafe {
Heap.dealloc(self.ptr, self.layout.clone())
}
}
}
}
impl<T> Box<T> {
/// Allocates memory on the heap and then places `x` into it.
///
@ -508,7 +360,7 @@ impl<T: ?Sized + Eq> Eq for Box<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized + Hash> Hash for Box<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
@ -892,7 +744,7 @@ impl<T> Generator for Box<T>
{
type Yield = T::Yield;
type Return = T::Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
unsafe fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
(**self).resume()
}
}

View file

@ -13,12 +13,12 @@ use core::fmt::Debug;
use core::hash::{Hash, Hasher};
use core::iter::{FromIterator, Peekable, FusedIterator};
use core::marker::PhantomData;
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::Index;
use core::ops::RangeBounds;
use core::{fmt, intrinsics, mem, ptr};
use borrow::Borrow;
use Bound::{Excluded, Included, Unbounded};
use range::RangeArgument;
use super::node::{self, Handle, NodeRef, marker};
use super::search;
@ -576,6 +576,33 @@ impl<K: Ord, V> BTreeMap<K, V> {
}
}
/// Returns the key-value pair corresponding to the supplied key.
///
/// The supplied key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
///
/// # Examples
///
/// ```
/// #![feature(map_get_key_value)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// assert_eq!(map.get_key_value(&1), Some((&1, &"a")));
/// assert_eq!(map.get_key_value(&2), None);
/// ```
#[unstable(feature = "map_get_key_value", issue = "49347")]
pub fn get_key_value<Q: ?Sized>(&self, k: &Q) -> Option<(&K, &V)>
where K: Borrow<Q>,
Q: Ord
{
match search::search_tree(self.root.as_ref(), k) {
Found(handle) => Some(handle.into_kv()),
GoDown(_) => None,
}
}
/// Returns `true` if the map contains a value for the specified key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
@ -777,7 +804,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
///
/// ```
/// use std::collections::BTreeMap;
/// use std::collections::Bound::Included;
/// use std::ops::Bound::Included;
///
/// let mut map = BTreeMap::new();
/// map.insert(3, "a");
@ -790,7 +817,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// ```
#[stable(feature = "btree_range", since = "1.17.0")]
pub fn range<T: ?Sized, R>(&self, range: R) -> Range<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
where T: Ord, K: Borrow<T>, R: RangeBounds<T>
{
let root1 = self.root.as_ref();
let root2 = self.root.as_ref();
@ -830,7 +857,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// ```
#[stable(feature = "btree_range", since = "1.17.0")]
pub fn range_mut<T: ?Sized, R>(&mut self, range: R) -> RangeMut<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
where T: Ord, K: Borrow<T>, R: RangeBounds<T>
{
let root1 = self.root.as_mut();
let root2 = unsafe { ptr::read(&root1) };
@ -1785,7 +1812,7 @@ fn last_leaf_edge<BorrowType, K, V>
}
}
fn range_search<BorrowType, K, V, Q: ?Sized, R: RangeArgument<Q>>(
fn range_search<BorrowType, K, V, Q: ?Sized, R: RangeBounds<Q>>(
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
root2: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
range: R

View file

@ -41,14 +41,14 @@
// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges.
// This implies that even an empty internal node has at least one edge.
use core::heap::{Alloc, Layout};
use core::marker::PhantomData;
use core::mem;
use core::nonzero::NonZero;
use core::ptr::{self, Unique};
use core::ptr::{self, Unique, NonNull};
use core::slice;
use boxed::Box;
use heap::{Heap, Alloc, Layout};
use heap::Heap;
const B: usize = 6;
pub const MIN_LEN: usize = B - 1;
@ -149,14 +149,12 @@ impl<K, V> BoxedNode<K, V> {
}
}
unsafe fn from_ptr(ptr: NonZero<*const LeafNode<K, V>>) -> Self {
BoxedNode { ptr: Unique::new_unchecked(ptr.get() as *mut LeafNode<K, V>) }
unsafe fn from_ptr(ptr: NonNull<LeafNode<K, V>>) -> Self {
BoxedNode { ptr: Unique::from(ptr) }
}
fn as_ptr(&self) -> NonZero<*const LeafNode<K, V>> {
unsafe {
NonZero::from(self.ptr.as_ref())
}
fn as_ptr(&self) -> NonNull<LeafNode<K, V>> {
NonNull::from(self.ptr)
}
}
@ -276,7 +274,7 @@ impl<K, V> Root<K, V> {
/// `NodeRef` could be pointing to either type of node.
pub struct NodeRef<BorrowType, K, V, Type> {
height: usize,
node: NonZero<*const LeafNode<K, V>>,
node: NonNull<LeafNode<K, V>>,
// This is null unless the borrow type is `Mut`
root: *const Root<K, V>,
_marker: PhantomData<(BorrowType, Type)>
@ -302,7 +300,7 @@ unsafe impl<K: Send, V: Send, Type> Send
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
fn as_internal(&self) -> &InternalNode<K, V> {
unsafe {
&*(self.node.get() as *const InternalNode<K, V>)
&*(self.node.as_ptr() as *mut InternalNode<K, V>)
}
}
}
@ -310,7 +308,7 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
fn as_internal_mut(&mut self) -> &mut InternalNode<K, V> {
unsafe {
&mut *(self.node.get() as *mut InternalNode<K, V>)
&mut *(self.node.as_ptr() as *mut InternalNode<K, V>)
}
}
}
@ -352,7 +350,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
fn as_leaf(&self) -> &LeafNode<K, V> {
unsafe {
&*self.node.get()
self.node.as_ref()
}
}
@ -382,7 +380,8 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
>,
Self
> {
if let Some(non_zero) = NonZero::new(self.as_leaf().parent as *const LeafNode<K, V>) {
let parent_as_leaf = self.as_leaf().parent as *const LeafNode<K, V>;
if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) {
Ok(Handle {
node: NodeRef {
height: self.height + 1,
@ -498,7 +497,7 @@ impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
fn as_leaf_mut(&mut self) -> &mut LeafNode<K, V> {
unsafe {
&mut *(self.node.get() as *mut LeafNode<K, V>)
self.node.as_mut()
}
}
@ -1241,12 +1240,12 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
}
Heap.dealloc(
right_node.node.get() as *mut u8,
right_node.node.as_ptr() as *mut u8,
Layout::new::<InternalNode<K, V>>(),
);
} else {
Heap.dealloc(
right_node.node.get() as *mut u8,
right_node.node.as_ptr() as *mut u8,
Layout::new::<LeafNode<K, V>>(),
);
}

View file

@ -16,12 +16,11 @@ use core::cmp::{min, max};
use core::fmt::Debug;
use core::fmt;
use core::iter::{Peekable, FromIterator, FusedIterator};
use core::ops::{BitOr, BitAnd, BitXor, Sub};
use core::ops::{BitOr, BitAnd, BitXor, Sub, RangeBounds};
use borrow::Borrow;
use btree_map::{BTreeMap, Keys};
use super::Recover;
use range::RangeArgument;
// FIXME(conventions): implement bounded iterators
@ -240,7 +239,7 @@ impl<T: Ord> BTreeSet<T> {
///
/// ```
/// use std::collections::BTreeSet;
/// use std::collections::Bound::Included;
/// use std::ops::Bound::Included;
///
/// let mut set = BTreeSet::new();
/// set.insert(3);
@ -253,7 +252,7 @@ impl<T: Ord> BTreeSet<T> {
/// ```
#[stable(feature = "btree_range", since = "1.17.0")]
pub fn range<K: ?Sized, R>(&self, range: R) -> Range<T>
where K: Ord, T: Borrow<K>, R: RangeArgument<K>
where K: Ord, T: Borrow<K>, R: RangeBounds<K>
{
Range { iter: self.map.range(range) }
}

View file

@ -326,7 +326,7 @@
//! sign := '+' | '-'
//! width := count
//! precision := count | '*'
//! type := identifier | ''
//! type := identifier | '?' | ''
//! count := parameter | integer
//! parameter := argument '$'
//! ```
@ -516,17 +516,17 @@ pub use core::fmt::rt;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{Formatter, Result, Write};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{Octal, Binary};
pub use core::fmt::{Binary, Octal};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{Display, Debug};
pub use core::fmt::{Debug, Display};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{LowerHex, UpperHex, Pointer};
pub use core::fmt::{LowerHex, Pointer, UpperHex};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{LowerExp, UpperExp};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::Error;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{ArgumentV1, Arguments, write};
pub use core::fmt::{write, ArgumentV1, Arguments};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
@ -563,7 +563,8 @@ use string;
pub fn format(args: Arguments) -> string::String {
let capacity = args.estimated_capacity();
let mut output = string::String::with_capacity(capacity);
output.write_fmt(args)
.expect("a formatting trait implementation returned an error");
output
.write_fmt(args)
.expect("a formatting trait implementation returned an error");
output
}

View file

@ -19,7 +19,7 @@ use core::intrinsics::{min_align_of_val, size_of_val};
use core::mem::{self, ManuallyDrop};
use core::usize;
pub use allocator::*;
pub use core::heap::*;
#[doc(hidden)]
pub mod __core {
pub use core::*;

View file

@ -76,11 +76,11 @@
#![deny(missing_debug_implementations)]
#![cfg_attr(test, allow(deprecated))] // rand
#![cfg_attr(test, feature(placement_in))]
#![cfg_attr(not(test), feature(core_float))]
#![cfg_attr(not(test), feature(exact_size_is_empty))]
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(rand, test))]
#![feature(allocator_api)]
#![feature(allow_internal_unstable)]
#![feature(ascii_ctype)]
#![feature(box_into_raw_non_null)]
@ -88,6 +88,7 @@
#![feature(box_syntax)]
#![feature(cfg_target_has_atomic)]
#![feature(coerce_unsized)]
#![feature(collections_range)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
@ -96,9 +97,8 @@
#![feature(fmt_internals)]
#![feature(from_ref)]
#![feature(fundamental)]
#![feature(generic_param_attrs)]
#![feature(i128_type)]
#![feature(iter_rfold)]
#![cfg_attr(stage0, feature(generic_param_attrs))]
#![cfg_attr(stage0, feature(i128_type))]
#![feature(lang_items)]
#![feature(needs_allocator)]
#![feature(nonzero)]
@ -106,8 +106,6 @@
#![feature(optin_builtin_traits)]
#![feature(pattern)]
#![feature(pin)]
#![feature(placement_in_syntax)]
#![feature(placement_new_protocol)]
#![feature(ptr_internals)]
#![feature(rustc_attrs)]
#![feature(slice_get_slice)]
@ -126,8 +124,8 @@
#![feature(pointer_methods)]
#![feature(inclusive_range_fields)]
#![cfg_attr(not(test), feature(fn_traits, placement_new_protocol, swap_with_slice, i128))]
#![cfg_attr(test, feature(test, box_heap))]
#![cfg_attr(not(test), feature(fn_traits, swap_with_slice, i128))]
#![cfg_attr(test, feature(test))]
// Allow testing this library
@ -145,9 +143,9 @@ extern crate std_unicode;
#[macro_use]
mod macros;
// Allocator trait and helper struct definitions
pub mod allocator;
#[rustc_deprecated(since = "1.27.0", reason = "use the heap module in core, alloc, or std instead")]
#[unstable(feature = "allocator_api", issue = "32838")]
pub use core::heap as allocator;
// Heaps provided for low-level allocation strategies
@ -157,13 +155,12 @@ pub mod heap;
// Need to conditionally define the mod from `boxed.rs` to avoid
// duplicating the lang-items when building in test cfg; but also need
// to allow code to have `use boxed::HEAP;`
// and `use boxed::Box;` declarations.
// to allow code to have `use boxed::Box;` declarations.
#[cfg(not(test))]
pub mod boxed;
#[cfg(test)]
mod boxed {
pub use std::boxed::{Box, IntermediateBox, HEAP};
pub use std::boxed::Box;
}
#[cfg(test)]
mod boxed_test;
@ -178,7 +175,6 @@ mod btree;
pub mod borrow;
pub mod fmt;
pub mod linked_list;
pub mod range;
pub mod slice;
pub mod str;
pub mod string;
@ -204,57 +200,6 @@ mod std {
pub use core::ops; // RangeFull
}
/// An endpoint of a range of keys.
///
/// # Examples
///
/// `Bound`s are range endpoints:
///
/// ```
/// #![feature(collections_range)]
///
/// use std::collections::range::RangeArgument;
/// use std::collections::Bound::*;
///
/// assert_eq!((..100).start(), Unbounded);
/// assert_eq!((1..12).start(), Included(&1));
/// assert_eq!((1..12).end(), Excluded(&12));
/// ```
///
/// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`].
/// Note that in most cases, it's better to use range syntax (`1..5`) instead.
///
/// ```
/// use std::collections::BTreeMap;
/// use std::collections::Bound::{Excluded, Included, Unbounded};
///
/// let mut map = BTreeMap::new();
/// map.insert(3, "a");
/// map.insert(5, "b");
/// map.insert(8, "c");
///
/// for (key, value) in map.range((Excluded(3), Included(8))) {
/// println!("{}: {}", key, value);
/// }
///
/// assert_eq!(Some((&3, &"a")), map.range((Unbounded, Included(5))).next());
/// ```
///
/// [`BTreeMap::range`]: btree_map/struct.BTreeMap.html#method.range
#[stable(feature = "collections_bound", since = "1.17.0")]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Bound<T> {
/// An inclusive bound.
#[stable(feature = "collections_bound", since = "1.17.0")]
Included(#[stable(feature = "collections_bound", since = "1.17.0")] T),
/// An exclusive bound.
#[stable(feature = "collections_bound", since = "1.17.0")]
Excluded(#[stable(feature = "collections_bound", since = "1.17.0")] T),
/// An infinite endpoint. Indicates that there is no bound in this direction.
#[stable(feature = "collections_bound", since = "1.17.0")]
Unbounded,
}
/// An intermediate trait for specialization of `Extend`.
#[doc(hidden)]
trait SpecExtend<I: IntoIterator> {

View file

@ -28,10 +28,9 @@ use core::hash::{Hasher, Hash};
use core::iter::{FromIterator, FusedIterator};
use core::marker::PhantomData;
use core::mem;
use core::ops::{BoxPlace, InPlace, Place, Placer};
use core::ptr::{self, NonNull};
use core::ptr::NonNull;
use boxed::{Box, IntermediateBox};
use boxed::Box;
use super::SpecExtend;
/// A doubly-linked list with owned nodes.
@ -786,62 +785,6 @@ impl<T> LinkedList<T> {
old_len: old_len,
}
}
/// Returns a place for insertion at the front of the list.
///
/// Using this method with placement syntax is equivalent to
/// [`push_front`](#method.push_front), but may be more efficient.
///
/// # Examples
///
/// ```
/// #![feature(collection_placement)]
/// #![feature(placement_in_syntax)]
///
/// use std::collections::LinkedList;
///
/// let mut list = LinkedList::new();
/// list.front_place() <- 2;
/// list.front_place() <- 4;
/// assert!(list.iter().eq(&[4, 2]));
/// ```
#[unstable(feature = "collection_placement",
reason = "method name and placement protocol are subject to change",
issue = "30172")]
pub fn front_place(&mut self) -> FrontPlace<T> {
FrontPlace {
list: self,
node: IntermediateBox::make_place(),
}
}
/// Returns a place for insertion at the back of the list.
///
/// Using this method with placement syntax is equivalent to [`push_back`](#method.push_back),
/// but may be more efficient.
///
/// # Examples
///
/// ```
/// #![feature(collection_placement)]
/// #![feature(placement_in_syntax)]
///
/// use std::collections::LinkedList;
///
/// let mut list = LinkedList::new();
/// list.back_place() <- 2;
/// list.back_place() <- 4;
/// assert!(list.iter().eq(&[2, 4]));
/// ```
#[unstable(feature = "collection_placement",
reason = "method name and placement protocol are subject to change",
issue = "30172")]
pub fn back_place(&mut self) -> BackPlace<T> {
BackPlace {
list: self,
node: IntermediateBox::make_place(),
}
}
}
#[stable(feature = "rust1", since = "1.0.0")]
@ -1242,123 +1185,6 @@ impl<T: Hash> Hash for LinkedList<T> {
}
}
unsafe fn finalize<T>(node: IntermediateBox<Node<T>>) -> Box<Node<T>> {
let mut node = node.finalize();
ptr::write(&mut node.next, None);
ptr::write(&mut node.prev, None);
node
}
/// A place for insertion at the front of a `LinkedList`.
///
/// See [`LinkedList::front_place`](struct.LinkedList.html#method.front_place) for details.
#[must_use = "places do nothing unless written to with `<-` syntax"]
#[unstable(feature = "collection_placement",
reason = "struct name and placement protocol are subject to change",
issue = "30172")]
pub struct FrontPlace<'a, T: 'a> {
list: &'a mut LinkedList<T>,
node: IntermediateBox<Node<T>>,
}
#[unstable(feature = "collection_placement",
reason = "struct name and placement protocol are subject to change",
issue = "30172")]
impl<'a, T: 'a + fmt::Debug> fmt::Debug for FrontPlace<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("FrontPlace")
.field(&self.list)
.finish()
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> Placer<T> for FrontPlace<'a, T> {
type Place = Self;
fn make_place(self) -> Self {
self
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
unsafe impl<'a, T> Place<T> for FrontPlace<'a, T> {
fn pointer(&mut self) -> *mut T {
unsafe { &mut (*self.node.pointer()).element }
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> InPlace<T> for FrontPlace<'a, T> {
type Owner = ();
unsafe fn finalize(self) {
let FrontPlace { list, node } = self;
list.push_front_node(finalize(node));
}
}
/// A place for insertion at the back of a `LinkedList`.
///
/// See [`LinkedList::back_place`](struct.LinkedList.html#method.back_place) for details.
#[must_use = "places do nothing unless written to with `<-` syntax"]
#[unstable(feature = "collection_placement",
reason = "struct name and placement protocol are subject to change",
issue = "30172")]
pub struct BackPlace<'a, T: 'a> {
list: &'a mut LinkedList<T>,
node: IntermediateBox<Node<T>>,
}
#[unstable(feature = "collection_placement",
reason = "struct name and placement protocol are subject to change",
issue = "30172")]
impl<'a, T: 'a + fmt::Debug> fmt::Debug for BackPlace<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("BackPlace")
.field(&self.list)
.finish()
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> Placer<T> for BackPlace<'a, T> {
type Place = Self;
fn make_place(self) -> Self {
self
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
unsafe impl<'a, T> Place<T> for BackPlace<'a, T> {
fn pointer(&mut self) -> *mut T {
unsafe { &mut (*self.node.pointer()).element }
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> InPlace<T> for BackPlace<'a, T> {
type Owner = ();
unsafe fn finalize(self) {
let BackPlace { list, node } = self;
list.push_back_node(finalize(node));
}
}
// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters.
#[allow(dead_code)]
fn assert_covariance() {

View file

@ -1,152 +0,0 @@
// Copyright 2015 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.
#![unstable(feature = "collections_range",
reason = "waiting for dust to settle on inclusive ranges",
issue = "30877")]
//! Range syntax.
use core::ops::{RangeFull, Range, RangeTo, RangeFrom, RangeInclusive, RangeToInclusive};
use Bound::{self, Excluded, Included, Unbounded};
/// `RangeArgument` is implemented by Rust's built-in range types, produced
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
pub trait RangeArgument<T: ?Sized> {
/// Start index bound.
///
/// Returns the start value as a `Bound`.
///
/// # Examples
///
/// ```
/// #![feature(alloc)]
/// #![feature(collections_range)]
///
/// extern crate alloc;
///
/// # fn main() {
/// use alloc::range::RangeArgument;
/// use alloc::Bound::*;
///
/// assert_eq!((..10).start(), Unbounded);
/// assert_eq!((3..10).start(), Included(&3));
/// # }
/// ```
fn start(&self) -> Bound<&T>;
/// End index bound.
///
/// Returns the end value as a `Bound`.
///
/// # Examples
///
/// ```
/// #![feature(alloc)]
/// #![feature(collections_range)]
///
/// extern crate alloc;
///
/// # fn main() {
/// use alloc::range::RangeArgument;
/// use alloc::Bound::*;
///
/// assert_eq!((3..).end(), Unbounded);
/// assert_eq!((3..10).end(), Excluded(&10));
/// # }
/// ```
fn end(&self) -> Bound<&T>;
}
// FIXME add inclusive ranges to RangeArgument
impl<T: ?Sized> RangeArgument<T> for RangeFull {
fn start(&self) -> Bound<&T> {
Unbounded
}
fn end(&self) -> Bound<&T> {
Unbounded
}
}
impl<T> RangeArgument<T> for RangeFrom<T> {
fn start(&self) -> Bound<&T> {
Included(&self.start)
}
fn end(&self) -> Bound<&T> {
Unbounded
}
}
impl<T> RangeArgument<T> for RangeTo<T> {
fn start(&self) -> Bound<&T> {
Unbounded
}
fn end(&self) -> Bound<&T> {
Excluded(&self.end)
}
}
impl<T> RangeArgument<T> for Range<T> {
fn start(&self) -> Bound<&T> {
Included(&self.start)
}
fn end(&self) -> Bound<&T> {
Excluded(&self.end)
}
}
#[stable(feature = "inclusive_range", since = "1.26.0")]
impl<T> RangeArgument<T> for RangeInclusive<T> {
fn start(&self) -> Bound<&T> {
Included(&self.start)
}
fn end(&self) -> Bound<&T> {
Included(&self.end)
}
}
#[stable(feature = "inclusive_range", since = "1.26.0")]
impl<T> RangeArgument<T> for RangeToInclusive<T> {
fn start(&self) -> Bound<&T> {
Unbounded
}
fn end(&self) -> Bound<&T> {
Included(&self.end)
}
}
impl<T> RangeArgument<T> for (Bound<T>, Bound<T>) {
fn start(&self) -> Bound<&T> {
match *self {
(Included(ref start), _) => Included(start),
(Excluded(ref start), _) => Excluded(start),
(Unbounded, _) => Unbounded,
}
}
fn end(&self) -> Bound<&T> {
match *self {
(_, Included(ref end)) => Included(end),
(_, Excluded(ref end)) => Excluded(end),
(_, Unbounded) => Unbounded,
}
}
}
impl<'a, T: ?Sized + 'a> RangeArgument<T> for (Bound<&'a T>, Bound<&'a T>) {
fn start(&self) -> Bound<&T> {
self.0
}
fn end(&self) -> Bound<&T> {
self.1
}
}

View file

@ -9,11 +9,12 @@
// except according to those terms.
use core::cmp;
use core::heap::{Alloc, Layout};
use core::mem;
use core::ops::Drop;
use core::ptr::{self, Unique};
use core::slice;
use heap::{Alloc, Layout, Heap};
use heap::Heap;
use super::boxed::Box;
use super::allocator::CollectionAllocErr;
use super::allocator::CollectionAllocErr::*;

View file

@ -250,6 +250,7 @@ use core::cell::Cell;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::heap::{Alloc, Layout};
use core::intrinsics::abort;
use core::marker;
use core::marker::{Unsize, PhantomData};
@ -259,7 +260,7 @@ use core::ops::CoerceUnsized;
use core::ptr::{self, NonNull};
use core::convert::From;
use heap::{Heap, Alloc, Layout, box_free};
use heap::{Heap, box_free};
use string::String;
use vec::Vec;

View file

@ -102,6 +102,7 @@ use core::mem::size_of;
use core::mem;
use core::ptr;
use core::slice as core_slice;
use core::{u8, u16, u32};
use borrow::{Borrow, BorrowMut, ToOwned};
use boxed::Box;
@ -1302,7 +1303,8 @@ impl<T> [T] {
/// Sorts the slice with a key extraction function.
///
/// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case.
/// This sort is stable (i.e. does not reorder equal elements) and `O(m n log(m n))`
/// worst-case, where the key function is `O(m)`.
///
/// When applicable, unstable sorting is preferred because it is generally faster than stable
/// sorting and it doesn't allocate auxiliary memory.
@ -1328,12 +1330,82 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_sort_by_key", since = "1.7.0")]
#[inline]
pub fn sort_by_key<B, F>(&mut self, mut f: F)
where F: FnMut(&T) -> B, B: Ord
pub fn sort_by_key<K, F>(&mut self, mut f: F)
where F: FnMut(&T) -> K, K: Ord
{
merge_sort(self, |a, b| f(a).lt(&f(b)));
}
/// Sorts the slice with a key extraction function.
///
/// During sorting, the key function is called only once per element.
///
/// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)`
/// worst-case, where the key function is `O(m)`.
///
/// For simple key functions (e.g. functions that are property accesses or
/// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be
/// faster.
///
/// # Current implementation
///
/// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
/// which combines the fast average case of randomized quicksort with the fast worst case of
/// heapsort, while achieving linear time on slices with certain patterns. It uses some
/// randomization to avoid degenerate cases, but with a fixed seed to always provide
/// deterministic behavior.
///
/// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the
/// length of the slice.
///
/// # Examples
///
/// ```
/// #![feature(slice_sort_by_cached_key)]
/// let mut v = [-5i32, 4, 32, -3, 2];
///
/// v.sort_by_cached_key(|k| k.to_string());
/// assert!(v == [-3, -5, 2, 32, 4]);
/// ```
///
/// [pdqsort]: https://github.com/orlp/pdqsort
#[unstable(feature = "slice_sort_by_cached_key", issue = "34447")]
#[inline]
pub fn sort_by_cached_key<K, F>(&mut self, f: F)
where F: FnMut(&T) -> K, K: Ord
{
// Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
macro_rules! sort_by_key {
($t:ty, $slice:ident, $f:ident) => ({
let mut indices: Vec<_> =
$slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect();
// The elements of `indices` are unique, as they are indexed, so any sort will be
// stable with respect to the original slice. We use `sort_unstable` here because
// it requires less memory allocation.
indices.sort_unstable();
for i in 0..$slice.len() {
let mut index = indices[i].1;
while (index as usize) < i {
index = indices[index as usize].1;
}
indices[i].1 = index;
$slice.swap(i, index as usize);
}
})
}
let sz_u8 = mem::size_of::<(K, u8)>();
let sz_u16 = mem::size_of::<(K, u16)>();
let sz_u32 = mem::size_of::<(K, u32)>();
let sz_usize = mem::size_of::<(K, usize)>();
let len = self.len();
if sz_u8 < sz_u16 && len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) }
if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) }
if sz_u32 < sz_usize && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) }
sort_by_key!(usize, self, f)
}
/// Sorts the slice, but may not preserve the order of equal elements.
///
/// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
@ -1410,7 +1482,7 @@ impl<T> [T] {
/// elements.
///
/// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
/// and `O(n log n)` worst-case.
/// and `O(m n log(m n))` worst-case, where the key function is `O(m)`.
///
/// # Current implementation
///
@ -1420,9 +1492,6 @@ impl<T> [T] {
/// randomization to avoid degenerate cases, but with a fixed seed to always provide
/// deterministic behavior.
///
/// It is typically faster than stable sorting, except in a few special cases, e.g. when the
/// slice consists of several concatenated sorted sequences.
///
/// # Examples
///
/// ```
@ -1435,9 +1504,8 @@ impl<T> [T] {
/// [pdqsort]: https://github.com/orlp/pdqsort
#[stable(feature = "sort_unstable", since = "1.20.0")]
#[inline]
pub fn sort_unstable_by_key<B, F>(&mut self, f: F)
where F: FnMut(&T) -> B,
B: Ord
pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
where F: FnMut(&T) -> K, K: Ord
{
core_slice::SliceExt::sort_unstable_by_key(self, f);
}

View file

@ -2122,6 +2122,48 @@ impl str {
unsafe { String::from_utf8_unchecked(buf) }
}
/// Returns true if this `str` is entirely whitespace, and false otherwise.
///
/// 'Whitespace' is defined according to the terms of the Unicode Derived Core
/// Property `White_Space`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// assert!(" \t ".is_whitespace());
///
/// // a non-breaking space
/// assert!("\u{A0}".is_whitespace());
///
/// assert!(!" 越".is_whitespace());
/// ```
#[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")]
#[inline]
pub fn is_whitespace(&self) -> bool {
UnicodeStr::is_whitespace(self)
}
/// Returns true if this `str` is entirely alphanumeric, and false otherwise.
///
/// 'Alphanumeric'-ness is defined in terms of the Unicode General Categories
/// 'Nd', 'Nl', 'No' and the Derived Core Property 'Alphabetic'.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// assert!("٣7৬Kو藏".is_alphanumeric());
/// assert!(!"¾①".is_alphanumeric());
/// ```
#[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")]
#[inline]
pub fn is_alphanumeric(&self) -> bool {
UnicodeStr::is_alphanumeric(self)
}
/// Checks if all characters in this string are within the ASCII range.
///
/// # Examples

View file

@ -59,15 +59,14 @@
use core::fmt;
use core::hash;
use core::iter::{FromIterator, FusedIterator};
use core::ops::{self, Add, AddAssign, Index, IndexMut};
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds};
use core::ptr;
use core::str::pattern::Pattern;
use std_unicode::lossy;
use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER};
use borrow::{Cow, ToOwned};
use range::RangeArgument;
use Bound::{Excluded, Included, Unbounded};
use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars};
use vec::Vec;
use boxed::Box;
@ -1015,6 +1014,34 @@ impl String {
self.vec.shrink_to_fit()
}
/// Shrinks the capacity of this `String` with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// let mut s = String::from("foo");
///
/// s.reserve(100);
/// assert!(s.capacity() >= 100);
///
/// s.shrink_to(10);
/// assert!(s.capacity() >= 10);
/// s.shrink_to(0);
/// assert!(s.capacity() >= 3);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.vec.shrink_to(min_capacity)
}
/// Appends the given [`char`] to the end of this `String`.
///
/// [`char`]: ../../std/primitive.char.html
@ -1177,8 +1204,6 @@ impl String {
/// # Examples
///
/// ```
/// #![feature(string_retain)]
///
/// let mut s = String::from("f_o_ob_ar");
///
/// s.retain(|c| c != '_');
@ -1186,7 +1211,7 @@ impl String {
/// assert_eq!(s, "foobar");
/// ```
#[inline]
#[unstable(feature = "string_retain", issue = "43874")]
#[stable(feature = "string_retain", since = "1.26.0")]
pub fn retain<F>(&mut self, mut f: F)
where F: FnMut(char) -> bool
{
@ -1458,7 +1483,7 @@ impl String {
/// ```
#[stable(feature = "drain", since = "1.6.0")]
pub fn drain<R>(&mut self, range: R) -> Drain
where R: RangeArgument<usize>
where R: RangeBounds<usize>
{
// Memory safety
//
@ -1492,7 +1517,7 @@ impl String {
}
}
/// Creates a splicing iterator that removes the specified range in the string,
/// Removes the specified range in the string,
/// and replaces it with the given string.
/// The given string doesn't need to be the same length as the range.
///
@ -1512,21 +1537,20 @@ impl String {
/// Basic usage:
///
/// ```
/// #![feature(splice)]
/// let mut s = String::from("α is alpha, β is beta");
/// let beta_offset = s.find('β').unwrap_or(s.len());
///
/// // Replace the range up until the β from the string
/// s.splice(..beta_offset, "Α is capital alpha; ");
/// s.replace_range(..beta_offset, "Α is capital alpha; ");
/// assert_eq!(s, "Α is capital alpha; β is beta");
/// ```
#[unstable(feature = "splice", reason = "recently added", issue = "44643")]
pub fn splice<R>(&mut self, range: R, replace_with: &str)
where R: RangeArgument<usize>
#[stable(feature = "splice", since = "1.27.0")]
pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
where R: RangeBounds<usize>
{
// Memory safety
//
// The String version of Splice does not have the memory safety issues
// Replace_range does not have the memory safety issues of a vector Splice.
// of the vector version. The data is just plain bytes.
match range.start() {
@ -1576,7 +1600,6 @@ impl FromUtf8Error {
/// Basic usage:
///
/// ```
/// #![feature(from_utf8_error_as_bytes)]
/// // some invalid bytes, in a vector
/// let bytes = vec![0, 159];
///
@ -1584,7 +1607,7 @@ impl FromUtf8Error {
///
/// assert_eq!(&[0, 159], value.unwrap_err().as_bytes());
/// ```
#[unstable(feature = "from_utf8_error_as_bytes", reason = "recently added", issue = "40895")]
#[stable(feature = "from_utf8_error_as_bytes", since = "1.26.0")]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..]
}

View file

@ -278,26 +278,6 @@ fn test_extend_specialization() {
assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
}
#[test]
fn test_placement() {
let mut a = BinaryHeap::new();
&mut a <- 2;
&mut a <- 4;
&mut a <- 3;
assert_eq!(a.peek(), Some(&4));
assert_eq!(a.len(), 3);
&mut a <- 1;
assert_eq!(a.into_sorted_vec(), vec![1, 2, 3, 4]);
}
#[test]
fn test_placement_panic() {
let mut heap = BinaryHeap::from(vec![1, 2, 3]);
fn mkpanic() -> usize { panic!() }
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { &mut heap <- mkpanic(); }));
assert_eq!(heap.len(), 3);
}
#[allow(dead_code)]
fn assert_covariance() {
fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {

View file

@ -9,8 +9,8 @@
// except according to those terms.
use std::collections::BTreeMap;
use std::collections::Bound::{self, Excluded, Included, Unbounded};
use std::collections::btree_map::Entry::{Occupied, Vacant};
use std::ops::Bound::{self, Excluded, Included, Unbounded};
use std::rc::Rc;
use std::iter::FromIterator;

View file

@ -15,14 +15,13 @@
#![feature(attr_literals)]
#![feature(box_syntax)]
#![cfg_attr(stage0, feature(inclusive_range_syntax))]
#![feature(collection_placement)]
#![feature(const_fn)]
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(iterator_step_by)]
#![feature(pattern)]
#![feature(placement_in_syntax)]
#![feature(rand)]
#![feature(slice_sort_by_cached_key)]
#![feature(splice)]
#![feature(str_escape)]
#![feature(string_retain)]

View file

@ -425,6 +425,14 @@ fn test_sort() {
v.sort_by(|a, b| b.cmp(a));
assert!(v.windows(2).all(|w| w[0] >= w[1]));
// Sort in lexicographic order.
let mut v1 = orig.clone();
let mut v2 = orig.clone();
v1.sort_by_key(|x| x.to_string());
v2.sort_by_cached_key(|x| x.to_string());
assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string()));
assert!(v1 == v2);
// Sort with many pre-sorted runs.
let mut v = orig.clone();
v.sort();
@ -477,7 +485,7 @@ fn test_sort_stability() {
// the second item represents which occurrence of that
// number this element is, i.e. the second elements
// will occur in sorted order.
let mut v: Vec<_> = (0..len)
let mut orig: Vec<_> = (0..len)
.map(|_| {
let n = thread_rng().gen::<usize>() % 10;
counts[n] += 1;
@ -485,16 +493,21 @@ fn test_sort_stability() {
})
.collect();
// only sort on the first element, so an unstable sort
let mut v = orig.clone();
// Only sort on the first element, so an unstable sort
// may mix up the counts.
v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
// this comparison includes the count (the second item
// This comparison includes the count (the second item
// of the tuple), so elements with equal first items
// will need to be ordered with increasing
// counts... i.e. exactly asserting that this sort is
// stable.
assert!(v.windows(2).all(|w| w[0] <= w[1]));
let mut v = orig.clone();
v.sort_by_cached_key(|&(x, _)| x);
assert!(v.windows(2).all(|w| w[0] <= w[1]));
}
}
}
@ -1351,7 +1364,7 @@ fn test_copy_from_slice_dst_shorter() {
const MAX_LEN: usize = 80;
static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [
// FIXME #5244: AtomicUsize is not Copy.
// FIXME(RFC 1109): AtomicUsize is not Copy.
AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),

View file

@ -443,53 +443,53 @@ fn test_drain() {
}
#[test]
fn test_splice() {
fn test_replace_range() {
let mut s = "Hello, world!".to_owned();
s.splice(7..12, "世界");
s.replace_range(7..12, "世界");
assert_eq!(s, "Hello, 世界!");
}
#[test]
#[should_panic]
fn test_splice_char_boundary() {
fn test_replace_range_char_boundary() {
let mut s = "Hello, 世界!".to_owned();
s.splice(..8, "");
s.replace_range(..8, "");
}
#[test]
fn test_splice_inclusive_range() {
fn test_replace_range_inclusive_range() {
let mut v = String::from("12345");
v.splice(2..=3, "789");
v.replace_range(2..=3, "789");
assert_eq!(v, "127895");
v.splice(1..=2, "A");
v.replace_range(1..=2, "A");
assert_eq!(v, "1A895");
}
#[test]
#[should_panic]
fn test_splice_out_of_bounds() {
fn test_replace_range_out_of_bounds() {
let mut s = String::from("12345");
s.splice(5..6, "789");
s.replace_range(5..6, "789");
}
#[test]
#[should_panic]
fn test_splice_inclusive_out_of_bounds() {
fn test_replace_range_inclusive_out_of_bounds() {
let mut s = String::from("12345");
s.splice(5..=5, "789");
s.replace_range(5..=5, "789");
}
#[test]
fn test_splice_empty() {
fn test_replace_range_empty() {
let mut s = String::from("12345");
s.splice(1..2, "");
s.replace_range(1..2, "");
assert_eq!(s, "1345");
}
#[test]
fn test_splice_unbounded() {
fn test_replace_range_unbounded() {
let mut s = String::from("12345");
s.splice(.., "");
s.replace_range(.., "");
assert_eq!(s, "");
}

View file

@ -10,7 +10,7 @@
use std::borrow::Cow;
use std::mem::size_of;
use std::{usize, isize, panic};
use std::{usize, isize};
use std::vec::{Drain, IntoIter};
use std::collections::CollectionAllocErr::*;
@ -753,24 +753,6 @@ fn assert_covariance() {
}
}
#[test]
fn test_placement() {
let mut vec = vec![1];
assert_eq!(vec.place_back() <- 2, &2);
assert_eq!(vec.len(), 2);
assert_eq!(vec.place_back() <- 3, &3);
assert_eq!(vec.len(), 3);
assert_eq!(&vec, &[1, 2, 3]);
}
#[test]
fn test_placement_panic() {
let mut vec = vec![1, 2, 3];
fn mkpanic() -> usize { panic!() }
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { vec.place_back() <- mkpanic(); }));
assert_eq!(vec.len(), 3);
}
#[test]
fn from_into_inner() {
let vec = vec![1, 2, 3];

View file

@ -1004,28 +1004,6 @@ fn test_is_empty() {
assert!(v.into_iter().is_empty());
}
#[test]
fn test_placement_in() {
let mut buf: VecDeque<isize> = VecDeque::new();
buf.place_back() <- 1;
buf.place_back() <- 2;
assert_eq!(buf, [1,2]);
buf.place_front() <- 3;
buf.place_front() <- 4;
assert_eq!(buf, [4,3,1,2]);
{
let ptr_head = buf.place_front() <- 5;
assert_eq!(*ptr_head, 5);
}
{
let ptr_tail = buf.place_back() <- 6;
assert_eq!(*ptr_tail, 6);
}
assert_eq!(buf, [5,4,3,1,2,6]);
}
#[test]
fn test_reserve_exact_2() {
// This is all the same as test_reserve

View file

@ -66,7 +66,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
use core::cmp::Ordering;
use core::cmp::{self, Ordering};
use core::fmt;
use core::hash::{self, Hash};
use core::intrinsics::{arith_offset, assume};
@ -75,7 +75,8 @@ use core::marker::PhantomData;
use core::mem;
#[cfg(not(test))]
use core::num::Float;
use core::ops::{InPlace, Index, IndexMut, Place, Placer};
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{Index, IndexMut, RangeBounds};
use core::ops;
use core::ptr;
use core::ptr::NonNull;
@ -85,9 +86,7 @@ use borrow::ToOwned;
use borrow::Cow;
use boxed::Box;
use raw_vec::RawVec;
use super::range::RangeArgument;
use super::allocator::CollectionAllocErr;
use Bound::{Excluded, Included, Unbounded};
/// A contiguous growable array type, written `Vec<T>` but pronounced 'vector'.
///
@ -334,9 +333,10 @@ impl<T> Vec<T> {
/// The vector will be able to hold exactly `capacity` elements without
/// reallocating. If `capacity` is 0, the vector will not allocate.
///
/// It is important to note that this function does not specify the *length*
/// of the returned vector, but only the *capacity*. For an explanation of
/// the difference between length and capacity, see *[Capacity and reallocation]*.
/// It is important to note that although the returned vector has the
/// *capacity* specified, the vector will have a zero *length*. For an
/// explanation of the difference between length and capacity, see
/// *[Capacity and reallocation]*.
///
/// [Capacity and reallocation]: #capacity-and-reallocation
///
@ -586,6 +586,31 @@ impl<T> Vec<T> {
self.buf.shrink_to_fit(self.len);
}
/// Shrinks the capacity of the vector with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// let mut vec = Vec::with_capacity(10);
/// vec.extend([1, 2, 3].iter().cloned());
/// assert_eq!(vec.capacity(), 10);
/// vec.shrink_to(4);
/// assert!(vec.capacity() >= 4);
/// vec.shrink_to(0);
/// assert!(vec.capacity() >= 3);
/// ```
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.buf.shrink_to_fit(cmp::max(self.len, min_capacity));
}
/// Converts the vector into [`Box<[T]>`][owned slice].
///
/// Note that this will drop any excess capacity.
@ -1040,29 +1065,6 @@ impl<T> Vec<T> {
}
}
/// Returns a place for insertion at the back of the `Vec`.
///
/// Using this method with placement syntax is equivalent to [`push`](#method.push),
/// but may be more efficient.
///
/// # Examples
///
/// ```
/// #![feature(collection_placement)]
/// #![feature(placement_in_syntax)]
///
/// let mut vec = vec![1, 2];
/// vec.place_back() <- 3;
/// vec.place_back() <- 4;
/// assert_eq!(&vec, &[1, 2, 3, 4]);
/// ```
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
pub fn place_back(&mut self) -> PlaceBack<T> {
PlaceBack { vec: self }
}
/// Removes the last element from a vector and returns it, or [`None`] if it
/// is empty.
///
@ -1150,7 +1152,7 @@ impl<T> Vec<T> {
/// ```
#[stable(feature = "drain", since = "1.6.0")]
pub fn drain<R>(&mut self, range: R) -> Drain<T>
where R: RangeArgument<usize>
where R: RangeBounds<usize>
{
// Memory safety
//
@ -1281,6 +1283,49 @@ impl<T> Vec<T> {
}
other
}
/// Resizes the `Vec` in-place so that `len` is equal to `new_len`.
///
/// If `new_len` is greater than `len`, the `Vec` is extended by the
/// difference, with each additional slot filled with the result of
/// calling the closure `f`. The return values from `f` will end up
/// in the `Vec` in the order they have been generated.
///
/// If `new_len` is less than `len`, the `Vec` is simply truncated.
///
/// This method uses a closure to create new values on every push. If
/// you'd rather [`Clone`] a given value, use [`resize`]. If you want
/// to use the [`Default`] trait to generate values, you can pass
/// [`Default::default()`] as the second argument..
///
/// # Examples
///
/// ```
/// #![feature(vec_resize_with)]
///
/// let mut vec = vec![1, 2, 3];
/// vec.resize_with(5, Default::default);
/// assert_eq!(vec, [1, 2, 3, 0, 0]);
///
/// let mut vec = vec![];
/// let mut p = 1;
/// vec.resize_with(4, || { p *= 2; p });
/// assert_eq!(vec, [2, 4, 8, 16]);
/// ```
///
/// [`resize`]: #method.resize
/// [`Clone`]: ../../std/clone/trait.Clone.html
#[unstable(feature = "vec_resize_with", issue = "41758")]
pub fn resize_with<F>(&mut self, new_len: usize, f: F)
where F: FnMut() -> T
{
let len = self.len();
if new_len > len {
self.extend_with(new_len - len, ExtendFunc(f));
} else {
self.truncate(new_len);
}
}
}
impl<T: Clone> Vec<T> {
@ -1291,8 +1336,8 @@ impl<T: Clone> Vec<T> {
/// If `new_len` is less than `len`, the `Vec` is simply truncated.
///
/// This method requires [`Clone`] to be able clone the passed value. If
/// you'd rather create a value with [`Default`] instead, see
/// [`resize_default`].
/// you need more flexibility (or want to rely on [`Default`] instead of
/// [`Clone`]), use [`resize_with`].
///
/// # Examples
///
@ -1308,7 +1353,7 @@ impl<T: Clone> Vec<T> {
///
/// [`Clone`]: ../../std/clone/trait.Clone.html
/// [`Default`]: ../../std/default/trait.Default.html
/// [`resize_default`]: #method.resize_default
/// [`resize_with`]: #method.resize_with
#[stable(feature = "vec_resize", since = "1.5.0")]
pub fn resize(&mut self, new_len: usize, value: T) {
let len = self.len();
@ -1387,24 +1432,31 @@ impl<T: Default> Vec<T> {
// This code generalises `extend_with_{element,default}`.
trait ExtendWith<T> {
fn next(&self) -> T;
fn next(&mut self) -> T;
fn last(self) -> T;
}
struct ExtendElement<T>(T);
impl<T: Clone> ExtendWith<T> for ExtendElement<T> {
fn next(&self) -> T { self.0.clone() }
fn next(&mut self) -> T { self.0.clone() }
fn last(self) -> T { self.0 }
}
struct ExtendDefault;
impl<T: Default> ExtendWith<T> for ExtendDefault {
fn next(&self) -> T { Default::default() }
fn next(&mut self) -> T { Default::default() }
fn last(self) -> T { Default::default() }
}
struct ExtendFunc<F>(F);
impl<T, F: FnMut() -> T> ExtendWith<T> for ExtendFunc<F> {
fn next(&mut self) -> T { (self.0)() }
fn last(mut self) -> T { (self.0)() }
}
impl<T> Vec<T> {
/// Extend the vector by `n` values, using the given generator.
fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, value: E) {
fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) {
self.reserve(n);
unsafe {
@ -1542,40 +1594,69 @@ impl SpecFromElem for u8 {
}
}
macro_rules! impl_spec_from_elem {
($t: ty, $is_zero: expr) => {
impl SpecFromElem for $t {
#[inline]
fn from_elem(elem: $t, n: usize) -> Vec<$t> {
if $is_zero(elem) {
return Vec {
buf: RawVec::with_capacity_zeroed(n),
len: n,
}
}
let mut v = Vec::with_capacity(n);
v.extend_with(n, ExtendElement(elem));
v
impl<T: Clone + IsZero> SpecFromElem for T {
#[inline]
fn from_elem(elem: T, n: usize) -> Vec<T> {
if elem.is_zero() {
return Vec {
buf: RawVec::with_capacity_zeroed(n),
len: n,
}
}
};
let mut v = Vec::with_capacity(n);
v.extend_with(n, ExtendElement(elem));
v
}
}
impl_spec_from_elem!(i8, |x| x == 0);
impl_spec_from_elem!(i16, |x| x == 0);
impl_spec_from_elem!(i32, |x| x == 0);
impl_spec_from_elem!(i64, |x| x == 0);
impl_spec_from_elem!(i128, |x| x == 0);
impl_spec_from_elem!(isize, |x| x == 0);
unsafe trait IsZero {
/// Whether this value is zero
fn is_zero(&self) -> bool;
}
impl_spec_from_elem!(u16, |x| x == 0);
impl_spec_from_elem!(u32, |x| x == 0);
impl_spec_from_elem!(u64, |x| x == 0);
impl_spec_from_elem!(u128, |x| x == 0);
impl_spec_from_elem!(usize, |x| x == 0);
macro_rules! impl_is_zero {
($t: ty, $is_zero: expr) => {
unsafe impl IsZero for $t {
#[inline]
fn is_zero(&self) -> bool {
$is_zero(*self)
}
}
}
}
impl_is_zero!(i8, |x| x == 0);
impl_is_zero!(i16, |x| x == 0);
impl_is_zero!(i32, |x| x == 0);
impl_is_zero!(i64, |x| x == 0);
impl_is_zero!(i128, |x| x == 0);
impl_is_zero!(isize, |x| x == 0);
impl_is_zero!(u16, |x| x == 0);
impl_is_zero!(u32, |x| x == 0);
impl_is_zero!(u64, |x| x == 0);
impl_is_zero!(u128, |x| x == 0);
impl_is_zero!(usize, |x| x == 0);
impl_is_zero!(char, |x| x == '\0');
impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
unsafe impl<T: ?Sized> IsZero for *const T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}
unsafe impl<T: ?Sized> IsZero for *mut T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}
impl_spec_from_elem!(f32, |x: f32| x == 0. && x.is_sign_positive());
impl_spec_from_elem!(f64, |x: f64| x == 0. && x.is_sign_positive());
////////////////////////////////////////////////////////////////////////////////
// Common trait implementations for Vec
@ -1924,7 +2005,7 @@ impl<T> Vec<T> {
#[inline]
#[stable(feature = "vec_splice", since = "1.21.0")]
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<I::IntoIter>
where R: RangeArgument<usize>, I: IntoIterator<Item=T>
where R: RangeBounds<usize>, I: IntoIterator<Item=T>
{
Splice {
drain: self.drain(range),
@ -2467,57 +2548,6 @@ impl<'a, T> ExactSizeIterator for Drain<'a, T> {
#[stable(feature = "fused", since = "1.26.0")]
impl<'a, T> FusedIterator for Drain<'a, T> {}
/// A place for insertion at the back of a `Vec`.
///
/// See [`Vec::place_back`](struct.Vec.html#method.place_back) for details.
#[must_use = "places do nothing unless written to with `<-` syntax"]
#[unstable(feature = "collection_placement",
reason = "struct name and placement protocol are subject to change",
issue = "30172")]
#[derive(Debug)]
pub struct PlaceBack<'a, T: 'a> {
vec: &'a mut Vec<T>,
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> Placer<T> for PlaceBack<'a, T> {
type Place = PlaceBack<'a, T>;
fn make_place(self) -> Self {
// This will panic or abort if we would allocate > isize::MAX bytes
// or if the length increment would overflow for zero-sized types.
if self.vec.len == self.vec.buf.cap() {
self.vec.buf.double();
}
self
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
unsafe impl<'a, T> Place<T> for PlaceBack<'a, T> {
fn pointer(&mut self) -> *mut T {
unsafe { self.vec.as_mut_ptr().offset(self.vec.len as isize) }
}
}
#[unstable(feature = "collection_placement",
reason = "placement protocol is subject to change",
issue = "30172")]
impl<'a, T> InPlace<T> for PlaceBack<'a, T> {
type Owner = &'a mut T;
unsafe fn finalize(mut self) -> &'a mut T {
let ptr = self.pointer();
self.vec.len += 1;
&mut *ptr
}
}
/// A splicing iterator for `Vec`.
///
/// This struct is created by the [`splice()`] method on [`Vec`]. See its

Some files were not shown because too many files have changed in this diff Show more