Merge branch 'master' of https://github.com/rust-lang/rust into e0389
This commit is contained in:
commit
1b06fe1ef5
2862 changed files with 33811 additions and 21923 deletions
1406
src/Cargo.lock
generated
1406
src/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -72,3 +72,4 @@ cargo = { path = "tools/cargo" }
|
|||
# RLS depends on `rustfmt` from crates.io, so we put this in a `[patch]` section
|
||||
# for crates.io
|
||||
rustfmt-nightly = { path = "tools/rustfmt" }
|
||||
clippy_lints = { path = "tools/clippy/clippy_lints" }
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,12 +92,12 @@ 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));
|
||||
let mut dylib_path = bootstrap::util::dylib_path();
|
||||
dylib_path.insert(0, PathBuf::from(libdir));
|
||||
dylib_path.insert(0, PathBuf::from(&libdir));
|
||||
|
||||
let mut cmd = Command::new(rustc);
|
||||
cmd.args(&args)
|
||||
|
|
@ -103,11 +105,12 @@ 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
|
||||
// actually downloaded, so we just always pass the `--sysroot` option.
|
||||
cmd.arg("--sysroot").arg(sysroot);
|
||||
cmd.arg("--sysroot").arg(&sysroot);
|
||||
|
||||
// When we build Rust dylibs they're all intended for intermediate
|
||||
// usage, so make sure we pass the -Cprefer-dynamic flag instead of
|
||||
|
|
@ -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
|
||||
|
|
@ -175,9 +179,6 @@ fn main() {
|
|||
if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
|
||||
cmd.arg("-C").arg(format!("codegen-units={}", s));
|
||||
}
|
||||
if env::var("RUSTC_THINLTO").is_ok() {
|
||||
cmd.arg("-Ccodegen-units=16").arg("-Zthinlto");
|
||||
}
|
||||
|
||||
// Emit save-analysis info.
|
||||
if env::var("RUSTC_SAVE_ANALYSIS") == Ok("api".to_string()) {
|
||||
|
|
@ -280,33 +281,56 @@ fn main() {
|
|||
|
||||
if verbose > 1 {
|
||||
eprintln!("rustc command: {:?}", cmd);
|
||||
eprintln!("sysroot: {:?}", sysroot);
|
||||
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -597,10 +597,8 @@ class RustBuild(object):
|
|||
self.cargo()))
|
||||
args = [self.cargo(), "build", "--manifest-path",
|
||||
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
|
||||
if self.verbose:
|
||||
for _ in range(1, self.verbose):
|
||||
args.append("--verbose")
|
||||
if self.verbose > 1:
|
||||
args.append("--verbose")
|
||||
if self.use_locked_deps:
|
||||
args.append("--locked")
|
||||
if self.use_vendored_sources:
|
||||
|
|
@ -614,20 +612,55 @@ class RustBuild(object):
|
|||
return config
|
||||
return default_build_triple()
|
||||
|
||||
def check_submodule(self, module, slow_submodules):
|
||||
if not slow_submodules:
|
||||
checked_out = subprocess.Popen(["git", "rev-parse", "HEAD"],
|
||||
cwd=os.path.join(self.rust_root, module),
|
||||
stdout=subprocess.PIPE)
|
||||
return checked_out
|
||||
else:
|
||||
return None
|
||||
|
||||
def update_submodule(self, module, checked_out, recorded_submodules):
|
||||
module_path = os.path.join(self.rust_root, module)
|
||||
|
||||
if checked_out != None:
|
||||
default_encoding = sys.getdefaultencoding()
|
||||
checked_out = checked_out.communicate()[0].decode(default_encoding).strip()
|
||||
if recorded_submodules[module] == checked_out:
|
||||
return
|
||||
|
||||
print("Updating submodule", module)
|
||||
|
||||
run(["git", "submodule", "-q", "sync", module],
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
run(["git", "submodule", "update",
|
||||
"--init", "--recursive", module],
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
run(["git", "reset", "-q", "--hard"],
|
||||
cwd=module_path, verbose=self.verbose)
|
||||
run(["git", "clean", "-qdfx"],
|
||||
cwd=module_path, verbose=self.verbose)
|
||||
|
||||
def update_submodules(self):
|
||||
"""Update submodules"""
|
||||
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
|
||||
self.get_toml('submodules') == "false":
|
||||
return
|
||||
print('Updating submodules')
|
||||
slow_submodules = self.get_toml('fast-submodules') == "false"
|
||||
start_time = time()
|
||||
if slow_submodules:
|
||||
print('Unconditionally updating all submodules')
|
||||
else:
|
||||
print('Updating only changed submodules')
|
||||
default_encoding = sys.getdefaultencoding()
|
||||
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root, verbose=self.verbose)
|
||||
submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
|
||||
["git", "config", "--file",
|
||||
os.path.join(self.rust_root, ".gitmodules"),
|
||||
"--get-regexp", "path"]
|
||||
).decode(default_encoding).splitlines()]
|
||||
filtered_submodules = []
|
||||
submodules_names = []
|
||||
for module in submodules:
|
||||
if module.endswith("llvm"):
|
||||
if self.get_toml('llvm-config'):
|
||||
|
|
@ -645,16 +678,19 @@ class RustBuild(object):
|
|||
config = self.get_toml('lld')
|
||||
if config is None or config == 'false':
|
||||
continue
|
||||
filtered_submodules.append(module)
|
||||
run(["git", "submodule", "update",
|
||||
"--init", "--recursive"] + filtered_submodules,
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
run(["git", "submodule", "-q", "foreach", "git",
|
||||
"reset", "-q", "--hard"],
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
run(["git", "submodule", "-q", "foreach", "git",
|
||||
"clean", "-qdfx"],
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
check = self.check_submodule(module, slow_submodules)
|
||||
filtered_submodules.append((module, check))
|
||||
submodules_names.append(module)
|
||||
recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names,
|
||||
cwd=self.rust_root, stdout=subprocess.PIPE)
|
||||
recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines()
|
||||
recorded_submodules = {}
|
||||
for data in recorded:
|
||||
data = data.split()
|
||||
recorded_submodules[data[3]] = data[2]
|
||||
for module in filtered_submodules:
|
||||
self.update_submodule(module[0], module[1], recorded_submodules)
|
||||
print("Submodules updated in %.2f seconds" % (time() - start_time))
|
||||
|
||||
def set_dev_environment(self):
|
||||
"""Set download URL for development environment"""
|
||||
|
|
@ -674,14 +710,16 @@ 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='store_true')
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||
|
||||
args = [a for a in sys.argv if a != '-h' and a != '--help']
|
||||
args, _ = parser.parse_known_args(args)
|
||||
|
||||
# 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
|
||||
|
||||
|
|
@ -691,10 +729,9 @@ def bootstrap(help_triggered):
|
|||
except (OSError, IOError):
|
||||
pass
|
||||
|
||||
if '\nverbose = 2' in build.config_toml:
|
||||
build.verbose = 2
|
||||
elif '\nverbose = 1' in build.config_toml:
|
||||
build.verbose = 1
|
||||
match = re.search(r'\nverbose = (\d+)', build.config_toml)
|
||||
if match is not None:
|
||||
build.verbose = max(build.verbose, int(match.group(1)))
|
||||
|
||||
build.use_vendored_sources = '\nvendor = true' in build.config_toml
|
||||
|
||||
|
|
@ -753,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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
@ -60,12 +70,6 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
|
|||
/// Run this rule for all hosts without cross compiling.
|
||||
const ONLY_HOSTS: bool = false;
|
||||
|
||||
/// Run this rule for all targets, but only with the native host.
|
||||
const ONLY_BUILD_TARGETS: bool = false;
|
||||
|
||||
/// Only run this step with the build triple as host and target.
|
||||
const ONLY_BUILD: bool = false;
|
||||
|
||||
/// Primary function to execute this rule. Can call `builder.ensure(...)`
|
||||
/// with other steps to run those.
|
||||
fn run(self, builder: &Builder) -> Self::Output;
|
||||
|
|
@ -101,8 +105,6 @@ pub struct RunConfig<'a> {
|
|||
struct StepDescription {
|
||||
default: bool,
|
||||
only_hosts: bool,
|
||||
only_build_targets: bool,
|
||||
only_build: bool,
|
||||
should_run: fn(ShouldRun) -> ShouldRun,
|
||||
make_run: fn(RunConfig),
|
||||
name: &'static str,
|
||||
|
|
@ -138,8 +140,6 @@ impl StepDescription {
|
|||
StepDescription {
|
||||
default: S::DEFAULT,
|
||||
only_hosts: S::ONLY_HOSTS,
|
||||
only_build_targets: S::ONLY_BUILD_TARGETS,
|
||||
only_build: S::ONLY_BUILD,
|
||||
should_run: S::should_run,
|
||||
make_run: S::make_run,
|
||||
name: unsafe { ::std::intrinsics::type_name::<S>() },
|
||||
|
|
@ -155,18 +155,12 @@ impl StepDescription {
|
|||
self.name, builder.config.exclude);
|
||||
}
|
||||
let build = builder.build;
|
||||
let hosts = if self.only_build_targets || self.only_build {
|
||||
build.build_triple()
|
||||
} else {
|
||||
&build.hosts
|
||||
};
|
||||
let hosts = &build.hosts;
|
||||
|
||||
// Determine the targets participating in this rule.
|
||||
let targets = if self.only_hosts {
|
||||
if build.config.run_host_only {
|
||||
&[]
|
||||
} else if self.only_build {
|
||||
build.build_triple()
|
||||
if !build.config.run_host_only {
|
||||
return; // don't run anything
|
||||
} else {
|
||||
&build.hosts
|
||||
}
|
||||
|
|
@ -324,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::Crate, test::CrateLibrustc, test::Rustdoc, test::Linkcheck, test::Cargotest,
|
||||
test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
|
||||
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),
|
||||
}
|
||||
|
|
@ -359,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;
|
||||
|
|
@ -375,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[..]),
|
||||
|
|
@ -387,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
|
||||
|
|
@ -562,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);
|
||||
}
|
||||
|
||||
|
|
@ -678,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
|
||||
|
|
@ -775,9 +793,11 @@ impl<'a> Builder<'a> {
|
|||
// be resolved because MinGW has the import library. The downside is we
|
||||
// don't get newer functions from Windows, but we don't use any of them
|
||||
// anyway.
|
||||
cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
|
||||
if mode != Mode::Tool {
|
||||
cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1");
|
||||
}
|
||||
|
||||
if self.is_very_verbose() {
|
||||
for _ in 1..self.verbosity {
|
||||
cargo.arg("-v");
|
||||
}
|
||||
|
||||
|
|
@ -792,17 +812,6 @@ impl<'a> Builder<'a> {
|
|||
if cmd != "bench" {
|
||||
cargo.arg("--release");
|
||||
}
|
||||
|
||||
if self.config.rust_codegen_units.is_none() &&
|
||||
self.build.is_rust_llvm(compiler.host) &&
|
||||
self.config.rust_thinlto {
|
||||
cargo.env("RUSTC_THINLTO", "1");
|
||||
} else if self.config.rust_codegen_units.is_none() {
|
||||
// Generally, if ThinLTO has been disabled for some reason, we
|
||||
// want to set the codegen units to 1. However, we shouldn't do
|
||||
// this if the option was specifically set by the user.
|
||||
cargo.env("RUSTC_CODEGEN_UNITS", "1");
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.locked_deps {
|
||||
|
|
@ -838,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");
|
||||
|
|
@ -854,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,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ pub fn find(build: &mut Build) {
|
|||
.collect::<HashSet<_>>();
|
||||
for target in targets.into_iter() {
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
|
||||
cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false)
|
||||
.target(&target).host(&build.build);
|
||||
if target.contains("msvc") {
|
||||
cfg.static_crt(true);
|
||||
|
|
@ -109,7 +109,7 @@ pub fn find(build: &mut Build) {
|
|||
let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
|
||||
for host in hosts.into_iter() {
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
|
||||
cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
|
||||
.target(&host).host(&build.build);
|
||||
let config = build.config.target_config.get(&host);
|
||||
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -86,7 +88,6 @@ pub struct Config {
|
|||
// rust codegen options
|
||||
pub rust_optimize: bool,
|
||||
pub rust_codegen_units: Option<u32>,
|
||||
pub rust_thinlto: bool,
|
||||
pub rust_debug_assertions: bool,
|
||||
pub rust_debuginfo: bool,
|
||||
pub rust_debuginfo_lines: bool,
|
||||
|
|
@ -122,11 +123,13 @@ 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>,
|
||||
pub prefix: Option<PathBuf>,
|
||||
pub sysconfdir: Option<PathBuf>,
|
||||
pub datadir: Option<PathBuf>,
|
||||
pub docdir: Option<PathBuf>,
|
||||
pub bindir: Option<PathBuf>,
|
||||
pub libdir: 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.
|
||||
|
|
@ -212,13 +219,13 @@ struct Build {
|
|||
struct Install {
|
||||
prefix: Option<String>,
|
||||
sysconfdir: Option<String>,
|
||||
datadir: Option<String>,
|
||||
docdir: Option<String>,
|
||||
bindir: Option<String>,
|
||||
libdir: Option<String>,
|
||||
mandir: Option<String>,
|
||||
|
||||
// standard paths, currently unused
|
||||
datadir: Option<String>,
|
||||
infodir: Option<String>,
|
||||
localstatedir: Option<String>,
|
||||
}
|
||||
|
|
@ -269,7 +276,6 @@ impl Default for StringOrBool {
|
|||
struct Rust {
|
||||
optimize: Option<bool>,
|
||||
codegen_units: Option<u32>,
|
||||
thinlto: Option<bool>,
|
||||
debug_assertions: Option<bool>,
|
||||
debuginfo: Option<bool>,
|
||||
debuginfo_lines: Option<bool>,
|
||||
|
|
@ -314,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;
|
||||
|
|
@ -327,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();
|
||||
|
|
@ -337,17 +341,40 @@ 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();
|
||||
config.run_host_only = !(flags.host.is_empty() && !flags.target.is_empty());
|
||||
|
||||
let toml = file.map(|file| {
|
||||
let mut f = t!(File::open(&file));
|
||||
|
|
@ -364,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);
|
||||
|
|
@ -403,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);
|
||||
|
|
@ -414,11 +437,13 @@ 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 {
|
||||
config.prefix = install.prefix.clone().map(PathBuf::from);
|
||||
config.sysconfdir = install.sysconfdir.clone().map(PathBuf::from);
|
||||
config.datadir = install.datadir.clone().map(PathBuf::from);
|
||||
config.docdir = install.docdir.clone().map(PathBuf::from);
|
||||
config.bindir = install.bindir.clone().map(PathBuf::from);
|
||||
config.libdir = install.libdir.clone().map(PathBuf::from);
|
||||
|
|
@ -427,7 +452,6 @@ impl Config {
|
|||
|
||||
// Store off these values as options because if they're not provided
|
||||
// we'll infer default values for them later
|
||||
let mut thinlto = None;
|
||||
let mut llvm_assertions = None;
|
||||
let mut debuginfo_lines = None;
|
||||
let mut debuginfo_only_std = None;
|
||||
|
|
@ -471,7 +495,6 @@ impl Config {
|
|||
optimize = rust.optimize;
|
||||
ignore_git = rust.ignore_git;
|
||||
debug_jemalloc = rust.debug_jemalloc;
|
||||
thinlto = rust.thinlto;
|
||||
set(&mut config.rust_optimize_tests, rust.optimize_tests);
|
||||
set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
|
||||
set(&mut config.codegen_tests, rust.codegen_tests);
|
||||
|
|
@ -509,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);
|
||||
|
|
@ -536,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);
|
||||
|
||||
|
|
@ -559,7 +572,6 @@ impl Config {
|
|||
"stable" | "beta" | "nightly" => true,
|
||||
_ => false,
|
||||
};
|
||||
config.rust_thinlto = thinlto.unwrap_or(true);
|
||||
config.rust_debuginfo_lines = debuginfo_lines.unwrap_or(default);
|
||||
config.rust_debuginfo_only_std = debuginfo_only_std.unwrap_or(default);
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -71,7 +72,6 @@ o("full-tools", None, "enable all tools")
|
|||
# Optimization and debugging options. These may be overridden by the release
|
||||
# channel, etc.
|
||||
o("optimize", "rust.optimize", "build optimized rust code")
|
||||
o("thinlto", "rust.thinlto", "build Rust with ThinLTO enabled")
|
||||
o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
|
||||
o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
|
||||
o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -29,21 +28,20 @@ use cache::{Interned, INTERNER};
|
|||
|
||||
/// Deserialized version of all flags for this compile.
|
||||
pub struct Flags {
|
||||
pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
|
||||
pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,22 +63,25 @@ 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");
|
||||
let docdir_default = PathBuf::from("share/doc/rust");
|
||||
let datadir_default = PathBuf::from("share");
|
||||
let docdir_default = datadir_default.join("doc/rust");
|
||||
let bindir_default = PathBuf::from("bin");
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = PathBuf::from("share/man");
|
||||
let mandir_default = datadir_default.join("man");
|
||||
let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
|
||||
let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let datadir = build.config.datadir.as_ref().unwrap_or(&datadir_default);
|
||||
let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
|
||||
let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
|
||||
let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);
|
||||
|
||||
let sysconfdir = prefix.join(sysconfdir);
|
||||
let datadir = prefix.join(datadir);
|
||||
let docdir = prefix.join(docdir);
|
||||
let bindir = prefix.join(bindir);
|
||||
let libdir = prefix.join(libdir);
|
||||
|
|
@ -88,6 +91,7 @@ fn install_sh(
|
|||
|
||||
let prefix = add_destdir(&prefix, &destdir);
|
||||
let sysconfdir = add_destdir(&sysconfdir, &destdir);
|
||||
let datadir = add_destdir(&datadir, &destdir);
|
||||
let docdir = add_destdir(&docdir, &destdir);
|
||||
let bindir = add_destdir(&bindir, &destdir);
|
||||
let libdir = add_destdir(&libdir, &destdir);
|
||||
|
|
@ -107,6 +111,7 @@ fn install_sh(
|
|||
.arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
|
||||
.arg(format!("--prefix={}", sanitize_sh(&prefix)))
|
||||
.arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
|
||||
.arg(format!("--datadir={}", sanitize_sh(&datadir)))
|
||||
.arg(format!("--docdir={}", sanitize_sh(&docdir)))
|
||||
.arg(format!("--bindir={}", sanitize_sh(&bindir)))
|
||||
.arg(format!("--libdir={}", sanitize_sh(&libdir)))
|
||||
|
|
@ -161,7 +166,6 @@ macro_rules! install {
|
|||
impl Step for $name {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_BUILD_TARGETS: bool = true;
|
||||
const ONLY_HOSTS: bool = $only_hosts;
|
||||
$(const $c: bool = true;)*
|
||||
|
||||
|
|
@ -174,7 +178,7 @@ macro_rules! install {
|
|||
run.builder.ensure($name {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
host: run.host,
|
||||
host: run.builder.build.build,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -208,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, {
|
||||
|
|
@ -216,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, {
|
||||
|
|
@ -226,10 +231,6 @@ install!((self, builder, _config),
|
|||
});
|
||||
install_analysis(builder, self.stage, self.target);
|
||||
};
|
||||
Src, "src", Self::should_build(_config) , only_hosts: true, {
|
||||
builder.ensure(dist::Src);
|
||||
install_src(builder, self.stage);
|
||||
}, ONLY_BUILD;
|
||||
Rustc, "src/librustc", true, only_hosts: true, {
|
||||
builder.ensure(dist::Rustc {
|
||||
compiler: builder.compiler(self.stage, self.target),
|
||||
|
|
@ -237,3 +238,32 @@ install!((self, builder, _config),
|
|||
install_rustc(builder, self.stage, self.target);
|
||||
};
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Src {
|
||||
pub stage: u32,
|
||||
}
|
||||
|
||||
impl Step for Src {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let config = &run.builder.config;
|
||||
let cond = config.extended &&
|
||||
config.tools.as_ref().map_or(true, |t| t.contains("src"));
|
||||
run.path("src").default_condition(cond)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Src {
|
||||
stage: run.builder.top_stage,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
builder.ensure(dist::Src);
|
||||
install_src(builder, self.stage);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,8 +113,9 @@
|
|||
//! More documentation can be found in each respective module below, and you can
|
||||
//! also check out the `src/bootstrap/README.md` file for more information.
|
||||
|
||||
//#![deny(warnings)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![deny(warnings)]
|
||||
#![feature(conservative_impl_trait, fs_read_write, core_intrinsics)]
|
||||
#![feature(slice_concat_ext)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate build_helper;
|
||||
|
|
@ -130,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;
|
||||
|
|
@ -137,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};
|
||||
|
||||
|
|
@ -197,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>,
|
||||
|
|
@ -253,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)]
|
||||
|
|
@ -304,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) => {
|
||||
|
|
@ -322,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,
|
||||
|
|
@ -352,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>] {
|
||||
|
|
@ -371,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();
|
||||
|
|
@ -510,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"))
|
||||
|
|
@ -575,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)
|
||||
}
|
||||
|
|
@ -589,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)
|
||||
}
|
||||
|
|
@ -597,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)
|
||||
}
|
||||
|
|
@ -605,10 +652,6 @@ impl Build {
|
|||
self.verbosity > 0
|
||||
}
|
||||
|
||||
pub fn is_very_verbose(&self) -> bool {
|
||||
self.verbosity > 1
|
||||
}
|
||||
|
||||
/// Prints a message if this build is configured in verbose mode.
|
||||
fn verbose(&self, msg: &str) {
|
||||
if self.is_verbose() {
|
||||
|
|
@ -616,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 {
|
||||
|
|
@ -702,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 {
|
||||
|
|
@ -923,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
|
||||
|
|
@ -971,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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -245,7 +245,7 @@ impl Step for Rls {
|
|||
let host = self.host;
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
builder.ensure(tool::Rls { compiler, target: self.host });
|
||||
builder.ensure(tool::Rls { compiler, target: self.host, extra_features: Vec::new() });
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
host,
|
||||
|
|
@ -291,7 +291,7 @@ impl Step for Rustfmt {
|
|||
let host = self.host;
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
builder.ensure(tool::Rustfmt { compiler, target: self.host });
|
||||
builder.ensure(tool::Rustfmt { compiler, target: self.host, extra_features: Vec::new() });
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
host,
|
||||
|
|
@ -339,7 +339,12 @@ impl Step for Miri {
|
|||
let host = self.host;
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
if let Some(miri) = builder.ensure(tool::Miri { compiler, target: self.host }) {
|
||||
let miri = builder.ensure(tool::Miri {
|
||||
compiler,
|
||||
target: self.host,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
if let Some(miri) = miri {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/miri/Cargo.toml"));
|
||||
|
||||
|
|
@ -391,7 +396,12 @@ impl Step for Clippy {
|
|||
let host = self.host;
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
if let Some(clippy) = builder.ensure(tool::Clippy { compiler, target: self.host }) {
|
||||
let clippy = builder.ensure(tool::Clippy {
|
||||
compiler,
|
||||
target: self.host,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
if let Some(clippy) = clippy {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml"));
|
||||
|
||||
|
|
@ -499,33 +509,27 @@ 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Tidy {
|
||||
host: Interned<String>,
|
||||
}
|
||||
pub struct Tidy;
|
||||
|
||||
impl Step for Tidy {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const ONLY_BUILD: bool = true;
|
||||
|
||||
/// Runs the `tidy` tool as compiled in `stage` by the `host` compiler.
|
||||
/// Runs the `tidy` tool.
|
||||
///
|
||||
/// This tool in `src/tools` checks up on various bits and pieces of style and
|
||||
/// otherwise just implements a few lint-like checks that are specific to the
|
||||
/// compiler itself.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let host = self.host;
|
||||
|
||||
let _folder = build.fold_output(|| "tidy");
|
||||
println!("tidy check ({})", host);
|
||||
let mut cmd = builder.tool_cmd(Tool::Tidy);
|
||||
cmd.arg(build.src.join("src"));
|
||||
cmd.arg(&build.initial_cargo);
|
||||
|
|
@ -535,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);
|
||||
}
|
||||
|
||||
|
|
@ -543,9 +550,7 @@ impl Step for Tidy {
|
|||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Tidy {
|
||||
host: run.builder.build.build,
|
||||
});
|
||||
run.builder.ensure(Tidy);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -755,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,
|
||||
|
|
@ -823,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 });
|
||||
}
|
||||
|
||||
|
|
@ -832,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
|
||||
|
|
@ -845,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));
|
||||
}
|
||||
|
||||
|
|
@ -919,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))
|
||||
|
|
@ -940,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("")
|
||||
|
|
@ -994,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1029,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()));
|
||||
|
|
@ -1048,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
|
||||
|
|
@ -1138,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");
|
||||
|
|
@ -1396,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.
|
||||
//
|
||||
|
|
@ -1432,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"));
|
||||
|
|
@ -1442,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
|
||||
|
|
@ -1461,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1509,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");
|
||||
}
|
||||
|
|
@ -1528,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);
|
||||
}
|
||||
|
|
@ -1575,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 });
|
||||
|
|
@ -1610,7 +1634,6 @@ pub struct Distcheck;
|
|||
|
||||
impl Step for Distcheck {
|
||||
type Output = ();
|
||||
const ONLY_BUILD: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("distcheck")
|
||||
|
|
@ -1624,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));
|
||||
|
|
@ -1648,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));
|
||||
|
|
@ -1676,7 +1699,6 @@ impl Step for Bootstrap {
|
|||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const ONLY_BUILD: bool = true;
|
||||
|
||||
/// Test the build system itself
|
||||
fn run(self, builder: &Builder) {
|
||||
|
|
@ -1684,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 doesn’t 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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use std::fs;
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, exit};
|
||||
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;
|
||||
|
|
@ -74,7 +75,7 @@ impl Step for CleanTools {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
struct ToolBuild {
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
|
|
@ -82,6 +83,7 @@ struct ToolBuild {
|
|||
path: &'static str,
|
||||
mode: Mode,
|
||||
is_ext_tool: bool,
|
||||
extra_features: Vec<String>,
|
||||
}
|
||||
|
||||
impl Step for ToolBuild {
|
||||
|
|
@ -110,11 +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);
|
||||
let is_expected = build.try_run(&mut cargo);
|
||||
cargo.arg("--features").arg(self.extra_features.join(" "));
|
||||
|
||||
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 {
|
||||
|
|
@ -131,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)
|
||||
}
|
||||
}
|
||||
|
|
@ -242,6 +318,7 @@ macro_rules! tool {
|
|||
mode: $mode,
|
||||
path: $path,
|
||||
is_ext_tool: false,
|
||||
extra_features: Vec::new(),
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
|
|
@ -291,6 +368,7 @@ impl Step for RemoteTestServer {
|
|||
mode: Mode::Libstd,
|
||||
path: "src/tools/remote-test-server",
|
||||
is_ext_tool: false,
|
||||
extra_features: Vec::new(),
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
|
|
@ -333,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,
|
||||
|
|
@ -347,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.
|
||||
|
|
@ -361,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
|
||||
|
|
@ -409,6 +492,7 @@ impl Step for Cargo {
|
|||
mode: Mode::Librustc,
|
||||
path: "src/tools/cargo",
|
||||
is_ext_tool: false,
|
||||
extra_features: Vec::new(),
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
|
|
@ -421,10 +505,11 @@ macro_rules! tool_extended {
|
|||
$tool_name:expr,
|
||||
$extra_deps:block;)+) => {
|
||||
$(
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct $name {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
pub extra_features: Vec<String>,
|
||||
}
|
||||
|
||||
impl Step for $name {
|
||||
|
|
@ -441,10 +526,12 @@ macro_rules! tool_extended {
|
|||
run.builder.ensure($name {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
fn run($sel, $builder: &Builder) -> Option<PathBuf> {
|
||||
#[allow(unused_mut)]
|
||||
fn run(mut $sel, $builder: &Builder) -> Option<PathBuf> {
|
||||
$extra_deps
|
||||
$builder.ensure(ToolBuild {
|
||||
compiler: $sel.compiler,
|
||||
|
|
@ -452,6 +539,7 @@ macro_rules! tool_extended {
|
|||
tool: $tool_name,
|
||||
mode: Mode::Librustc,
|
||||
path: $path,
|
||||
extra_features: $sel.extra_features,
|
||||
is_ext_tool: true,
|
||||
})
|
||||
}
|
||||
|
|
@ -472,6 +560,14 @@ tool_extended!((self, builder),
|
|||
};
|
||||
Miri, miri, "src/tools/miri", "miri", {};
|
||||
Rls, rls, "src/tools/rls", "rls", {
|
||||
let clippy = builder.ensure(Clippy {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
if clippy.is_some() {
|
||||
self.extra_features.push("clippy".to_owned());
|
||||
}
|
||||
builder.ensure(native::Openssl {
|
||||
target: self.target,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,3 @@ authors = ["The Rust Project Developers"]
|
|||
[lib]
|
||||
name = "build_helper"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
filetime = "0.1"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ ENV EM_CONFIG=/emsdk-portable/.emscripten
|
|||
|
||||
ENV TARGETS=asmjs-unknown-emscripten
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --enable-emscripten
|
||||
ENV RUST_CONFIGURE_ARGS --enable-emscripten --disable-optimize-tests
|
||||
|
||||
ENV SCRIPT python2.7 ../x.py test --target $TARGETS
|
||||
ENV SCRIPT python2.7 ../x.py test --target $TARGETS \
|
||||
src/test/run-pass \
|
||||
src/test/run-fail \
|
||||
src/libstd \
|
||||
src/liballoc \
|
||||
src/libcore
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -46,7 +48,6 @@ export RUST_RELEASE_CHANNEL=nightly
|
|||
if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL"
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp"
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-thinlto"
|
||||
|
||||
if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
|
||||
|
|
@ -73,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
|
||||
|
|
@ -91,11 +99,19 @@ make check-bootstrap
|
|||
travis_fold end check-bootstrap
|
||||
travis_time_finish
|
||||
|
||||
# Display the CPU and memory information. This helps us know why the CI timing
|
||||
# is fluctuating.
|
||||
travis_fold start log-system-info
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
system_profiler SPHardwareDataType || true
|
||||
sysctl hw || true
|
||||
ncpus=$(sysctl -n hw.ncpu)
|
||||
else
|
||||
cat /proc/cpuinfo || true
|
||||
cat /proc/meminfo || true
|
||||
ncpus=$(grep processor /proc/cpuinfo | wc -l)
|
||||
fi
|
||||
travis_fold end log-system-info
|
||||
|
||||
if [ ! -z "$SCRIPT" ]; then
|
||||
sh -x -c "$SCRIPT"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 98921e9de849acdaeaed08cfad6758bb89769b7d
|
||||
Subproject commit b889e1e30c5e9953834aa9fa6c982bb28df46ac9
|
||||
|
|
@ -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
|
||||
|
|
@ -5,3 +5,4 @@
|
|||
- [The `#[doc]` attribute](the-doc-attribute.md)
|
||||
- [Documentation tests](documentation-tests.md)
|
||||
- [Passes](passes.md)
|
||||
- [Unstable features](unstable-features.md)
|
||||
|
|
|
|||
|
|
@ -19,18 +19,38 @@ running `rustdoc --test foo.rs` will extract this example, and then run it as a
|
|||
Please note that by default, if no language is set for the block code, `rustdoc`
|
||||
assumes it is `Rust` code. So the following:
|
||||
|
||||
``````markdown
|
||||
```rust
|
||||
let x = 5;
|
||||
```
|
||||
``````
|
||||
|
||||
is strictly equivalent to:
|
||||
|
||||
``````markdown
|
||||
```
|
||||
let x = 5;
|
||||
```
|
||||
``````
|
||||
|
||||
There's some subtlety though! Read on for more details.
|
||||
|
||||
## Passing or failing a doctest
|
||||
|
||||
Like regular unit tests, regular doctests are considered to "pass"
|
||||
if they compile and run without panicking.
|
||||
So if you want to demonstrate that some computation gives a certain result,
|
||||
the `assert!` family of macros works the same as other Rust code:
|
||||
|
||||
```rust
|
||||
let foo = "foo";
|
||||
|
||||
assert_eq!(foo, "foo");
|
||||
```
|
||||
|
||||
This way, if the computation ever returns something different,
|
||||
the code panics and the doctest fails.
|
||||
|
||||
## Pre-processing examples
|
||||
|
||||
In the example above, you'll note something strange: there's no `main`
|
||||
|
|
|
|||
375
src/doc/rustdoc/src/unstable-features.md
Normal file
375
src/doc/rustdoc/src/unstable-features.md
Normal 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.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# `advanced_slice_patterns`
|
||||
|
||||
The tracking issue for this feature is: [#23121]
|
||||
|
||||
[#23121]: https://github.com/rust-lang/rust/issues/23121
|
||||
|
||||
See also [`slice_patterns`](language-features/slice-patterns.html).
|
||||
|
||||
------------------------
|
||||
|
||||
|
||||
The `advanced_slice_patterns` gate lets you use `..` to indicate any number of
|
||||
elements inside a pattern matching a slice. This wildcard can only be used once
|
||||
for a given array. If there's an identifier before the `..`, the result of the
|
||||
slice will be bound to that name. For example:
|
||||
|
||||
```rust
|
||||
#![feature(advanced_slice_patterns, slice_patterns)]
|
||||
|
||||
fn is_symmetric(list: &[u32]) -> bool {
|
||||
match list {
|
||||
&[] | &[_] => true,
|
||||
&[x, ref inside.., y] if x == y => is_symmetric(inside),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let sym = &[0, 1, 4, 2, 4, 1, 0];
|
||||
assert!(is_symmetric(sym));
|
||||
|
||||
let not_sym = &[0, 1, 7, 2, 4, 1, 0];
|
||||
assert!(!is_symmetric(not_sym));
|
||||
}
|
||||
```
|
||||
|
|
@ -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.
|
||||
|
|
@ -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() };
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# `inclusive_range_syntax`
|
||||
|
||||
The tracking issue for this feature is: [#28237]
|
||||
|
||||
[#28237]: https://github.com/rust-lang/rust/issues/28237
|
||||
|
||||
------------------------
|
||||
|
||||
To get a range that goes from 0 to 10 and includes the value 10, you
|
||||
can write `0..=10`:
|
||||
|
||||
```rust
|
||||
#![feature(inclusive_range_syntax)]
|
||||
|
||||
fn main() {
|
||||
for i in 0..=10 {
|
||||
println!("{}", i);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -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 => {},
|
||||
}
|
||||
}
|
||||
```
|
||||
18
src/doc/unstable-book/src/language-features/repr128.md
Normal file
18
src/doc/unstable-book/src/language-features/repr128.md
Normal 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),
|
||||
}
|
||||
```
|
||||
|
|
@ -4,25 +4,29 @@ The tracking issue for this feature is: [#23121]
|
|||
|
||||
[#23121]: https://github.com/rust-lang/rust/issues/23121
|
||||
|
||||
See also
|
||||
[`advanced_slice_patterns`](language-features/advanced-slice-patterns.html).
|
||||
|
||||
------------------------
|
||||
|
||||
|
||||
If you want to match against a slice or array, you can use `&` with the
|
||||
`slice_patterns` feature:
|
||||
The `slice_patterns` feature gate lets you use `..` to indicate any number of
|
||||
elements inside a pattern matching a slice. This wildcard can only be used once
|
||||
for a given array. If there's an pattern before the `..`, the subslice will be
|
||||
matched against that pattern. For example:
|
||||
|
||||
```rust
|
||||
#![feature(slice_patterns)]
|
||||
|
||||
fn main() {
|
||||
let v = vec!["match_this", "1"];
|
||||
|
||||
match &v[..] {
|
||||
&["match_this", second] => println!("The second element is {}", second),
|
||||
_ => {},
|
||||
fn is_symmetric(list: &[u32]) -> bool {
|
||||
match list {
|
||||
&[] | &[_] => true,
|
||||
&[x, ref inside.., y] if x == y => is_symmetric(inside),
|
||||
&[..] => false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
fn main() {
|
||||
let sym = &[0, 1, 4, 2, 4, 1, 0];
|
||||
assert!(is_symmetric(sym));
|
||||
|
||||
let not_sym = &[0, 1, 7, 2, 4, 1, 0];
|
||||
assert!(!is_symmetric(not_sym));
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
```
|
||||
|
|
@ -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");
|
||||
```
|
||||
|
|
@ -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");
|
||||
```
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ core = { path = "../libcore" }
|
|||
std_unicode = { path = "../libstd_unicode" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.3"
|
||||
rand = "0.4"
|
||||
|
||||
[[test]]
|
||||
name = "collectionstests"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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, Unsize};
|
||||
use core::mem;
|
||||
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,104 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// A pinned, heap allocated reference.
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
#[fundamental]
|
||||
pub struct PinBox<T: ?Sized> {
|
||||
inner: Box<T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T> PinBox<T> {
|
||||
/// Allocate memory on the heap, move the data into it and pin it.
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
pub fn new(data: T) -> PinBox<T> {
|
||||
PinBox { inner: Box::new(data) }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: ?Sized> PinBox<T> {
|
||||
/// Get a pinned reference to the data in this PinBox.
|
||||
pub fn as_pin<'a>(&'a mut self) -> Pin<'a, T> {
|
||||
unsafe { Pin::new_unchecked(&mut *self.inner) }
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the data inside this PinBox.
|
||||
///
|
||||
/// This function is unsafe. Users must guarantee that the data is never
|
||||
/// moved out of this reference.
|
||||
pub unsafe fn get_mut<'a>(this: &'a mut PinBox<T>) -> &'a mut T {
|
||||
&mut *this.inner
|
||||
}
|
||||
|
||||
/// Convert this PinBox into an unpinned Box.
|
||||
///
|
||||
/// This function is unsafe. Users must guarantee that the data is never
|
||||
/// moved out of the box.
|
||||
pub unsafe fn unpin(this: PinBox<T>) -> Box<T> {
|
||||
this.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: ?Sized> From<Box<T>> for PinBox<T> {
|
||||
fn from(boxed: Box<T>) -> PinBox<T> {
|
||||
PinBox { inner: boxed }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: Unpin + ?Sized> From<PinBox<T>> for Box<T> {
|
||||
fn from(pinned: PinBox<T>) -> Box<T> {
|
||||
pinned.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: ?Sized> Deref for PinBox<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&*self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: Unpin + ?Sized> DerefMut for PinBox<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut *self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: fmt::Display + ?Sized> fmt::Display for PinBox<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for PinBox<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: ?Sized> fmt::Pointer for PinBox<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// It's not possible to extract the inner Uniq directly from the Box,
|
||||
// instead we cast it to a *const which aliases the Unique
|
||||
let ptr: *const T = &*self.inner;
|
||||
fmt::Pointer::fmt(&ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "pin", issue = "49150")]
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<PinBox<U>> for PinBox<T> {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>>(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@
|
|||
//!
|
||||
//! * *nothing* ⇒ [`Display`]
|
||||
//! * `?` ⇒ [`Debug`]
|
||||
//! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers
|
||||
//! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers
|
||||
//! * `o` ⇒ [`Octal`](trait.Octal.html)
|
||||
//! * `x` ⇒ [`LowerHex`](trait.LowerHex.html)
|
||||
//! * `X` ⇒ [`UpperHex`](trait.UpperHex.html)
|
||||
|
|
@ -324,7 +326,7 @@
|
|||
//! sign := '+' | '-'
|
||||
//! width := count
|
||||
//! precision := count | '*'
|
||||
//! type := identifier | ''
|
||||
//! type := identifier | '?' | ''
|
||||
//! count := parameter | integer
|
||||
//! parameter := argument '$'
|
||||
//! ```
|
||||
|
|
@ -514,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};
|
||||
|
||||
|
|
@ -561,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
@ -228,14 +228,6 @@ unsafe impl Alloc for Heap {
|
|||
}
|
||||
}
|
||||
|
||||
/// An arbitrary non-null address to represent zero-size allocations.
|
||||
///
|
||||
/// This preserves the non-null invariant for types like `Box<T>`. The address
|
||||
/// may overlap with non-zero-size memory allocations.
|
||||
#[rustc_deprecated(since = "1.19.0", reason = "Use Unique/NonNull::empty() instead")]
|
||||
#[unstable(feature = "heap_api", issue = "27700")]
|
||||
pub const EMPTY: *mut () = 1 as *mut ();
|
||||
|
||||
/// The allocator for unique pointers.
|
||||
// This function must not unwind. If it does, MIR trans will fail.
|
||||
#[cfg(not(test))]
|
||||
|
|
|
|||
|
|
@ -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,27 +97,24 @@
|
|||
#![feature(fmt_internals)]
|
||||
#![feature(from_ref)]
|
||||
#![feature(fundamental)]
|
||||
#![feature(generic_param_attrs)]
|
||||
#![feature(i128_type)]
|
||||
#![feature(inclusive_range)]
|
||||
#![feature(iter_rfold)]
|
||||
#![cfg_attr(stage0, feature(generic_param_attrs))]
|
||||
#![cfg_attr(stage0, feature(i128_type))]
|
||||
#![feature(lang_items)]
|
||||
#![feature(needs_allocator)]
|
||||
#![feature(nonzero)]
|
||||
#![feature(offset_to)]
|
||||
#![feature(optin_builtin_traits)]
|
||||
#![feature(pattern)]
|
||||
#![feature(placement_in_syntax)]
|
||||
#![feature(placement_new_protocol)]
|
||||
#![feature(pin)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(slice_get_slice)]
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(slice_rsplit)]
|
||||
#![feature(specialization)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(trusted_len)]
|
||||
#![feature(try_reserve)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unicode)]
|
||||
#![feature(unsize)]
|
||||
|
|
@ -124,9 +122,10 @@
|
|||
#![feature(on_unimplemented)]
|
||||
#![feature(exact_chunks)]
|
||||
#![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
|
||||
|
||||
|
|
@ -144,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
|
||||
|
||||
|
|
@ -156,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;
|
||||
|
|
@ -177,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;
|
||||
|
|
@ -203,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> {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
impl<T> RangeArgument<T> for RangeInclusive<T> {
|
||||
fn start(&self) -> Bound<&T> {
|
||||
Included(&self.start)
|
||||
}
|
||||
fn end(&self) -> Bound<&T> {
|
||||
Included(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,15 @@
|
|||
// 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::*;
|
||||
|
||||
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
|
||||
/// a buffer of memory on the heap without having to worry about all the corner cases
|
||||
|
|
@ -84,7 +87,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
let elem_size = mem::size_of::<T>();
|
||||
|
||||
let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow");
|
||||
alloc_guard(alloc_size);
|
||||
alloc_guard(alloc_size).expect("capacity overflow");
|
||||
|
||||
// handles ZSTs and `cap = 0` alike
|
||||
let ptr = if alloc_size == 0 {
|
||||
|
|
@ -308,7 +311,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
let new_cap = 2 * self.cap;
|
||||
let new_size = new_cap * elem_size;
|
||||
let new_layout = Layout::from_size_align_unchecked(new_size, cur.align());
|
||||
alloc_guard(new_size);
|
||||
alloc_guard(new_size).expect("capacity overflow");
|
||||
let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8,
|
||||
cur,
|
||||
new_layout);
|
||||
|
|
@ -367,7 +370,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
// overflow and the alignment is sufficiently small.
|
||||
let new_cap = 2 * self.cap;
|
||||
let new_size = new_cap * elem_size;
|
||||
alloc_guard(new_size);
|
||||
alloc_guard(new_size).expect("capacity overflow");
|
||||
let ptr = self.ptr() as *mut _;
|
||||
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
||||
|
|
@ -403,7 +406,9 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
/// # Aborts
|
||||
///
|
||||
/// Aborts on OOM
|
||||
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||
pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize)
|
||||
-> Result<(), CollectionAllocErr> {
|
||||
|
||||
unsafe {
|
||||
// NOTE: we don't early branch on ZSTs here because we want this
|
||||
// to actually catch "asking for more than usize::MAX" in that case.
|
||||
|
|
@ -413,16 +418,15 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
// Don't actually need any more capacity.
|
||||
// Wrapping in case they gave a bad `used_cap`.
|
||||
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Nothing we can really do about these checks :(
|
||||
let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow");
|
||||
let new_layout = match Layout::array::<T>(new_cap) {
|
||||
Some(layout) => layout,
|
||||
None => panic!("capacity overflow"),
|
||||
};
|
||||
alloc_guard(new_layout.size());
|
||||
let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
|
||||
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
|
||||
|
||||
alloc_guard(new_layout.size())?;
|
||||
|
||||
let res = match self.current_layout() {
|
||||
Some(layout) => {
|
||||
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
||||
|
|
@ -430,26 +434,34 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
}
|
||||
None => self.a.alloc(new_layout),
|
||||
};
|
||||
let uniq = match res {
|
||||
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
|
||||
Err(e) => self.a.oom(e),
|
||||
};
|
||||
self.ptr = uniq;
|
||||
|
||||
self.ptr = Unique::new_unchecked(res? as *mut T);
|
||||
self.cap = new_cap;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||
match self.try_reserve_exact(used_cap, needed_extra_cap) {
|
||||
Err(CapacityOverflow) => panic!("capacity overflow"),
|
||||
Err(AllocErr(e)) => self.a.oom(e),
|
||||
Ok(()) => { /* yay */ }
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the buffer's new size given that it'll hold `used_cap +
|
||||
/// needed_extra_cap` elements. This logic is used in amortized reserve methods.
|
||||
/// Returns `(new_capacity, new_alloc_size)`.
|
||||
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize {
|
||||
fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize)
|
||||
-> Result<usize, CollectionAllocErr> {
|
||||
|
||||
// Nothing we can really do about these checks :(
|
||||
let required_cap = used_cap.checked_add(needed_extra_cap)
|
||||
.expect("capacity overflow");
|
||||
let required_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
|
||||
// Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
|
||||
let double_cap = self.cap * 2;
|
||||
// `double_cap` guarantees exponential growth.
|
||||
cmp::max(double_cap, required_cap)
|
||||
Ok(cmp::max(double_cap, required_cap))
|
||||
}
|
||||
|
||||
/// Ensures that the buffer contains at least enough space to hold
|
||||
|
|
@ -504,8 +516,9 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
/// # vector.push_all(&[1, 3, 5, 7, 9]);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||
unsafe {
|
||||
pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize)
|
||||
-> Result<(), CollectionAllocErr> {
|
||||
unsafe {
|
||||
// NOTE: we don't early branch on ZSTs here because we want this
|
||||
// to actually catch "asking for more than usize::MAX" in that case.
|
||||
// If we make it past the first branch then we are guaranteed to
|
||||
|
|
@ -514,17 +527,15 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
// Don't actually need any more capacity.
|
||||
// Wrapping in case they give a bad `used_cap`
|
||||
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
|
||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?;
|
||||
let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
|
||||
|
||||
// FIXME: may crash and burn on over-reserve
|
||||
alloc_guard(new_layout.size())?;
|
||||
|
||||
let new_layout = match Layout::array::<T>(new_cap) {
|
||||
Some(layout) => layout,
|
||||
None => panic!("capacity overflow"),
|
||||
};
|
||||
// FIXME: may crash and burn on over-reserve
|
||||
alloc_guard(new_layout.size());
|
||||
let res = match self.current_layout() {
|
||||
Some(layout) => {
|
||||
let old_ptr = self.ptr.as_ptr() as *mut u8;
|
||||
|
|
@ -532,15 +543,22 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
}
|
||||
None => self.a.alloc(new_layout),
|
||||
};
|
||||
let uniq = match res {
|
||||
Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
|
||||
Err(e) => self.a.oom(e),
|
||||
};
|
||||
self.ptr = uniq;
|
||||
|
||||
self.ptr = Unique::new_unchecked(res? as *mut T);
|
||||
self.cap = new_cap;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The same as try_reserve, but errors are lowered to a call to oom().
|
||||
pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) {
|
||||
match self.try_reserve(used_cap, needed_extra_cap) {
|
||||
Err(CapacityOverflow) => panic!("capacity overflow"),
|
||||
Err(AllocErr(e)) => self.a.oom(e),
|
||||
Ok(()) => { /* yay */ }
|
||||
}
|
||||
}
|
||||
/// Attempts to ensure that the buffer contains at least enough space to hold
|
||||
/// `used_cap + needed_extra_cap` elements. If it doesn't already have
|
||||
/// enough capacity, will reallocate in place enough space plus comfortable slack
|
||||
|
|
@ -576,7 +594,8 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
return false;
|
||||
}
|
||||
|
||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap);
|
||||
let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)
|
||||
.expect("capacity overflow");
|
||||
|
||||
// Here, `cap < used_cap + needed_extra_cap <= new_cap`
|
||||
// (regardless of whether `self.cap - used_cap` wrapped).
|
||||
|
|
@ -585,7 +604,7 @@ impl<T, A: Alloc> RawVec<T, A> {
|
|||
let ptr = self.ptr() as *mut _;
|
||||
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
|
||||
// FIXME: may crash and burn on over-reserve
|
||||
alloc_guard(new_layout.size());
|
||||
alloc_guard(new_layout.size()).expect("capacity overflow");
|
||||
match self.a.grow_in_place(ptr, old_layout, new_layout) {
|
||||
Ok(_) => {
|
||||
self.cap = new_cap;
|
||||
|
|
@ -709,14 +728,14 @@ unsafe impl<#[may_dangle] T, A: Alloc> Drop for RawVec<T, A> {
|
|||
// all 4GB in user-space. e.g. PAE or x32
|
||||
|
||||
#[inline]
|
||||
fn alloc_guard(alloc_size: usize) {
|
||||
if mem::size_of::<usize>() < 8 {
|
||||
assert!(alloc_size <= ::core::isize::MAX as usize,
|
||||
"capacity overflow");
|
||||
fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> {
|
||||
if mem::size_of::<usize>() < 8 && alloc_size > ::core::isize::MAX as usize {
|
||||
Err(CapacityOverflow)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -59,18 +59,18 @@
|
|||
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;
|
||||
use super::allocator::CollectionAllocErr;
|
||||
|
||||
/// A UTF-8 encoded, growable string.
|
||||
///
|
||||
|
|
@ -920,6 +920,79 @@ impl String {
|
|||
self.vec.reserve_exact(additional)
|
||||
}
|
||||
|
||||
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||
/// in the given `String`. The collection may reserve more space to avoid
|
||||
/// frequent reallocations. After calling `reserve`, capacity will be
|
||||
/// greater than or equal to `self.len() + additional`. Does nothing if
|
||||
/// capacity is already sufficient.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||
/// is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(try_reserve)]
|
||||
/// use std::collections::CollectionAllocErr;
|
||||
///
|
||||
/// fn process_data(data: &str) -> Result<String, CollectionAllocErr> {
|
||||
/// let mut output = String::new();
|
||||
///
|
||||
/// // Pre-reserve the memory, exiting if we can't
|
||||
/// output.try_reserve(data.len())?;
|
||||
///
|
||||
/// // Now we know this can't OOM in the middle of our complex work
|
||||
/// output.push_str(data);
|
||||
///
|
||||
/// Ok(output)
|
||||
/// }
|
||||
/// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?");
|
||||
/// ```
|
||||
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||
self.vec.try_reserve(additional)
|
||||
}
|
||||
|
||||
/// Tries to reserves the minimum capacity for exactly `additional` more elements to
|
||||
/// be inserted in the given `String`. After calling `reserve_exact`,
|
||||
/// capacity will be greater than or equal to `self.len() + additional`.
|
||||
/// Does nothing if the capacity is already sufficient.
|
||||
///
|
||||
/// Note that the allocator may give the collection more space than it
|
||||
/// requests. Therefore capacity can not be relied upon to be precisely
|
||||
/// minimal. Prefer `reserve` if future insertions are expected.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the capacity overflows, or the allocator reports a failure, then an error
|
||||
/// is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(try_reserve)]
|
||||
/// use std::collections::CollectionAllocErr;
|
||||
///
|
||||
/// fn process_data(data: &str) -> Result<String, CollectionAllocErr> {
|
||||
/// let mut output = String::new();
|
||||
///
|
||||
/// // Pre-reserve the memory, exiting if we can't
|
||||
/// output.try_reserve(data.len())?;
|
||||
///
|
||||
/// // Now we know this can't OOM in the middle of our complex work
|
||||
/// output.push_str(data);
|
||||
///
|
||||
/// Ok(output)
|
||||
/// }
|
||||
/// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?");
|
||||
/// ```
|
||||
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
|
||||
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
|
||||
self.vec.try_reserve_exact(additional)
|
||||
}
|
||||
|
||||
/// Shrinks the capacity of this `String` to match its length.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -941,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
|
||||
|
|
@ -1103,8 +1204,6 @@ impl String {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(string_retain)]
|
||||
///
|
||||
/// let mut s = String::from("f_o_ob_ar");
|
||||
///
|
||||
/// s.retain(|c| c != '_');
|
||||
|
|
@ -1112,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
|
||||
{
|
||||
|
|
@ -1384,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
|
||||
//
|
||||
|
|
@ -1418,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.
|
||||
///
|
||||
|
|
@ -1438,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() {
|
||||
|
|
@ -1502,7 +1600,6 @@ impl FromUtf8Error {
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(from_utf8_error_as_bytes)]
|
||||
/// // some invalid bytes, in a vector
|
||||
/// let bytes = vec![0, 159];
|
||||
///
|
||||
|
|
@ -1510,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[..]
|
||||
}
|
||||
|
|
@ -1876,7 +1973,7 @@ impl ops::Index<ops::RangeFull> for String {
|
|||
unsafe { str::from_utf8_unchecked(&self.vec) }
|
||||
}
|
||||
}
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::Index<ops::RangeInclusive<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
|
|
@ -1885,7 +1982,7 @@ impl ops::Index<ops::RangeInclusive<usize>> for String {
|
|||
Index::index(&**self, index)
|
||||
}
|
||||
}
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::Index<ops::RangeToInclusive<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
|
|
@ -1923,14 +2020,14 @@ impl ops::IndexMut<ops::RangeFull> for String {
|
|||
unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) }
|
||||
}
|
||||
}
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::IndexMut<ops::RangeInclusive<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
|
||||
IndexMut::index_mut(&mut **self, index)
|
||||
}
|
||||
}
|
||||
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::IndexMut<ops::RangeToInclusive<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue