diff --git a/CHANGELOG.md b/CHANGELOG.md index e407414..67a29b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.3.8] - 2017-05-30 + +### Changed + +- Improved the error message when `--target foo.json` is used. + ## [v0.3.7] - 2017-05-13 ### Changed @@ -272,7 +278,8 @@ stage = 1 - Initial release -[Unreleased]: https://github.com/japaric/xargo/compare/v0.3.7...HEAD +[Unreleased]: https://github.com/japaric/xargo/compare/v0.3.8...HEAD +[v0.3.8]: https://github.com/japaric/xargo/compare/v0.3.7...v0.3.8 [v0.3.7]: https://github.com/japaric/xargo/compare/v0.3.6...v0.3.7 [v0.3.6]: https://github.com/japaric/xargo/compare/v0.3.5...v0.3.6 [v0.3.5]: https://github.com/japaric/xargo/compare/v0.3.4...v0.3.5 diff --git a/Cargo.lock b/Cargo.lock index 1093904..a1608a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "xargo" -version = "0.3.7" +version = "0.3.8" dependencies = [ "error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 6e504a4..816950a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["cli", "cross", "compilation", "std"] license = "MIT OR Apache-2.0" name = "xargo" repository = "https://github.com/japaric/xargo" -version = "0.3.7" +version = "0.3.8" [dependencies] error-chain = "0.7.2" diff --git a/README.md b/README.md index 25f6a79..90e83c9 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ But we also have [binary releases] for the three major OSes. ## Usage +### `no_std` + `xargo` has the exact same CLI as `cargo`. ``` @@ -65,6 +67,8 @@ need a bigger subset of the standard crates, specify the dependencies in a ``` $ cat Xargo.toml +# Alternatively you can use [build.dependencies] +# the syntax is the same as Cargo.toml's; you don't need to specify path or git [target.thumbv6m-none-eabi.dependencies] collections = {} @@ -78,6 +82,8 @@ $ xargo build --target thumbv6m-none-eabi Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs ``` +### `std` + You can compile a customized `std` crate as well, just specify which Cargo features to enable. @@ -133,9 +139,11 @@ $ xargo build --target thumbv6m-none-eabi -v Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs ``` -Oh, and if you want to use `xargo` to compile `std` using a "dev" `rustc`, you -can use the `XARGO_RUST_SRC` environment variable to tell `xargo` where the Rust -source is. +### Dev channel + +Oh, and if you want to use `xargo` to compile `std` using a "dev" `rustc`, a +rust compiled from source, you can use the `XARGO_RUST_SRC` environment variable +to tell `xargo` where the Rust source is. ``` # The source of the `core` crate must be in `$XARGO_RUST_SRC/libcore` @@ -144,6 +152,166 @@ $ export XARGO_RUST_SRC=/path/to/rust/src $ xargo build --target msp430-none-elf ``` +**NOTE** This also works with the nightly channel but it's not recommended as +the Rust source may diverge from what your compiler is able to compile as it may +make use of newer features that your compiler doesn't understand. + +### Compiling the sysroot with custom rustc flags + +Xargo uses the same custom rustc flags that apply to the target Cargo project. +So you can use either the `RUSTFLAGS` env variable or a `.cargo/config` +configuration file to specify custom rustc flags. + +``` +# build the sysroot with debug information +$ RUSTFLAGS='-g' xargo build --target x86_64-unknown-linux-gnu + +# Alternatively +$ edit .cargo/config && cat $_ +[build] +rustflags = ["-g"] + +# Then you can omit RUSTFLAGS +$ xargo build --target x86_64-unknown-linux-gnu +``` + +### Compiling the sysroot for a custom target + +At some point you may want to develop a program for a target that's not +officially supported by rustc. Xargo's got your back! It supports custom targets +via target specifications files, which are not really documented anywhere other +than in the [compiler source code][spec-docs]. Luckily you don't need to write +a specification file from scratch; you can start from an existing one. + +[spec-docs]: https://github.com/rust-lang/rust/blob/256e497fe63bf4b13f7c0b58fa17360ca849c54d/src/librustc_back/target/mod.rs#L228-L409 + +For example, let's say that you want to cross compile a program for a PowerPC +Linux systems that uses uclibc instead of glibc. There's a similarly looking +target in the list of targets supported by the compiler -- see `rustc --print +target-list` -- and that is `powerpc-unknown-linux-gnu`. So you can start by +dumping the specification of that target into a file: + +``` console +$ rustc -Z unstable-options --print target-spec-json --target powerpc-unknown-linux-gnu | tee powerpc-unknown-linux-uclibc.json +``` + +``` js +{ + "arch": "powerpc", + "data-layout": "E-m:e-p:32:32-i64:64-n32", + "dynamic-linking": true, + "env": "gnu", + "executables": true, + "has-elf-tls": true, + "has-rpath": true, + "is-builtin": true, + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "powerpc-unknown-linux-gnu", + "max-atomic-width": 32, + "os": "linux", + "position-independent-executables": true, + "pre-link-args": { + "gcc": [ + "-Wl,--as-needed", + "-Wl,-z,noexecstack", + "-m32" + ] + }, + "target-endian": "big", + "target-family": "unix", + "target-pointer-width": "32", + "vendor": "unknown" +} +``` + +One of the things you'll definitively want to do is drop the `is-builtin` field +as that's reserved for targets that are defined in the compiler itself. Apart +from that the only modification you would have to in this case is change the +`env` field from `gnu` (glibc) to `uclibc`. + +``` diff + "arch": "powerpc", + "data-layout": "E-m:e-p:32:32-i64:64-n32", + "dynamic-linking": true, +- "env": "gnu", ++ "env": "uclibc", + "executables": true, + "has-elf-tls": true, + "has-rpath": true, +- "is-builtin": true, + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "powerpc-unknown-linux-gnu", +``` + +Once you have your target specification file you only have to call Xargo with +the right target triple; make sure that the specification file is the same +folder from where you invoke Xargo because that's where rustc expects it to be. + +``` console +$ ls powerpc-unknown-linux-uclibc.json +powerpc-unknown-linux-uclibc.json + +$ xargo build --target powerpc-unknown-linux-uclibc +``` + +Your build may fail because if rustc doesn't support your target then it's +likely that the standard library doesn't support it either. In that case you +will have to modify the source of the standard library. Xargo helps with that +too because you can make a copy of the original source -- see `rustc --print +sysroot`, modify it and then point Xargo to it using the `XARGO_RUST_SRC` env +variable. + +### Multi-stage builds + +Some standard crates have implicit dependencies between them. For example, the +`test` crate implicitly depends on the `std`. Implicit here means that the test +crate Cargo.toml [doesn't list std as its dependency][test]. To compile a +sysroot that contains such crates you can perform the build in stages by +specifying which crates belong to each stage in the Xargo.toml file: + +[test]: https://github.com/rust-lang/rust/blob/1.17.0/src/libtest/Cargo.toml + +``` toml +[dependencies.std] +stage = 0 + +[dependencies.test] +stage = 1 +``` + +This will compile an intermediate sysroot, the stage 0 sysroot, containing the +`std` crate, and then it will compile the `test` crate against that intermediate +sysroot. The final sysroot, the stage 1 sysroot, will contain both the `std` and +`test` crates, and their dependencies. + +### Creating a sysroot with custom crates + +Xargo lets you create a sysroot with custom crates. You can virtually put any +crate in the sysroot. However, this feature is mainly used to create [alternative +`std` facades][steed], and to replace the `test` crate with [one that supports +`no_std` targets][utest]. To specify the contents of the sysroot simply list the +dependencies in the Xargo.toml file as you would do with Cargo.toml: + +[steed]: https://github.com/japaric/steed +[utest]: https://github.com/japaric/utest + +``` toml +[dependencies] +collections = {} +rand = {} + +[dependencies.compiler_builtins] +features = ["mem"] +git = "https://github.com/rust-lang-nursery/compiler-builtins" +stage = 1 + +[dependencies.std] +git = "https://github.com/japaric/steed" +stage = 2 +``` + ## Caveats / gotchas - Xargo won't build a sysroot when used with stable or beta Rust. This is @@ -161,11 +329,14 @@ $ xargo build --target msp430-none-elf `rust/src/jemalloc` directory. `chmod -R +x rust/src/jemalloc` should do the trick. -- When using compiler plugins (e.g. `serde_derive`) the target triple must - be provided even when compiling for the host platform due to the way - cargo handles compiler plugins. i.e. use `xargo build --target - x86_64-unknown-linux-gnu` instead of just `xargo build`. (Surprisingly, these - commands are NOT the same to Cargo) +- When using compiler plugins (e.g. `serde_derive`) the target triple must be + provided even when compiling for the host platform due to the way cargo + handles compiler plugins. I.e., use `xargo build --target + x86_64-unknown-linux-gnu` instead of just `xargo + build`. (Surprisingly, these commands are NOT the same to Cargo.) You can + determine your host's target triple with `rustc -vV`. On *nix, the following + rune will extract the triple: + `rustc -vV | egrep '^host: ' | sed 's/^host: //'`. ## License diff --git a/build.rs b/build.rs index be876d6..fa5c998 100644 --- a/build.rs +++ b/build.rs @@ -8,7 +8,8 @@ use std::process::Command; struct Some {} impl From for Some - where E: Error +where + E: Error, { fn from(_: E) -> Some { Some {} @@ -32,7 +33,8 @@ fn commit_info() -> String { } fn commit_hash() -> Result { - let output = Command::new("git").args(&["rev-parse", "--short", "HEAD"]) + let output = Command::new("git") + .args(&["rev-parse", "--short", "HEAD"]) .output()?; if output.status.success() { diff --git a/src/cargo.rs b/src/cargo.rs index 89fb8b2..39e5f59 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -18,7 +18,8 @@ pub struct Rustflags { impl Rustflags { pub fn hash(&self, hasher: &mut H) - where H: Hasher + where + H: Hasher, { let mut flags = self.flags.iter(); @@ -26,7 +27,8 @@ impl Rustflags { if flag == "-C" { if let Some(next) = flags.next() { if next.starts_with("link-arg=") || - next.starts_with("link-args=") { + next.starts_with("link-args=") + { // don't hash linker arguments } else { flag.hash(hasher); @@ -74,9 +76,10 @@ impl Rustdocflags { } } -pub fn rustdocflags(config: Option<&Config>, - target: &str) - -> Result { +pub fn rustdocflags( + config: Option<&Config>, + target: &str, +) -> Result { flags(config, target, "rustdocflags").map(|fs| Rustdocflags { flags: fs }) } @@ -84,25 +87,30 @@ pub fn rustdocflags(config: Option<&Config>, /// Returns the flags for `tool` (e.g. rustflags) /// /// This looks into the environment and into `.cargo/config` -fn flags(config: Option<&Config>, - target: &str, - tool: &str) - -> Result> { +fn flags( + config: Option<&Config>, + target: &str, + tool: &str, +) -> Result> { if let Some(t) = env::var_os(tool.to_uppercase()) { - return Ok(t.to_string_lossy() - .split_whitespace() - .map(|w| w.to_owned()) - .collect()); + return Ok( + t.to_string_lossy() + .split_whitespace() + .map(|w| w.to_owned()) + .collect(), + ); } if let Some(config) = config.as_ref() { let mut build = false; - if let Some(array) = config.table + if let Some(array) = config + .table .lookup(&format!("target.{}.{}", target, tool)) .or_else(|| { build = true; config.table.lookup(&format!("build.{}", tool)) - }) { + }) + { let mut flags = vec![]; let mut error = false; @@ -121,14 +129,18 @@ fn flags(config: Option<&Config>, if error { if build { - Err(format!(".cargo/config: build.{} must be an array \ + Err(format!( + ".cargo/config: build.{} must be an array \ of strings", - tool))? + tool + ))? } else { - Err(format!(".cargo/config: target.{}.{} must be an \ + Err(format!( + ".cargo/config: target.{}.{} must be an \ array of strings", - target, - tool))? + target, + tool + ))? } } else { Ok(flags) @@ -142,7 +154,9 @@ fn flags(config: Option<&Config>, } pub fn run(args: &Args, verbose: bool) -> Result { - Command::new("cargo").args(args.all()).run_and_get_status(verbose) + Command::new("cargo").args(args.all()).run_and_get_status( + verbose, + ) } pub struct Config { @@ -152,10 +166,11 @@ pub struct Config { impl Config { pub fn target(&self) -> Result> { if let Some(v) = self.table.lookup("build.target") { - Ok(Some(v.as_str() - .ok_or_else(|| { - format!(".cargo/config: build.target must be a string") - })?)) + Ok(Some( + v.as_str().ok_or_else( + || format!(".cargo/config: build.target must be a string"), + )?, + )) } else { Ok(None) } @@ -163,11 +178,14 @@ impl Config { } pub fn config() -> Result> { - let cd = - env::current_dir().chain_err(|| "couldn't get the current directory")?; + let cd = env::current_dir().chain_err( + || "couldn't get the current directory", + )?; if let Some(p) = util::search(&cd, ".cargo/config") { - Ok(Some(Config { table: util::parse(&p.join(".cargo/config"))? })) + Ok(Some( + Config { table: util::parse(&p.join(".cargo/config"))? }, + )) } else { Ok(None) } @@ -179,7 +197,8 @@ pub struct Profile<'t> { impl<'t> Profile<'t> { pub fn hash(&self, hasher: &mut H) - where H: Hasher + where + H: Hasher, { let mut v = self.table.clone(); @@ -218,7 +237,9 @@ pub struct Toml { impl Toml { /// `profile.release` part of `Cargo.toml` pub fn profile(&self) -> Option { - self.table.lookup("profile.release").map(|t| Profile { table: t }) + self.table.lookup("profile.release").map( + |t| Profile { table: t }, + ) } } @@ -237,10 +258,13 @@ impl Root { } pub fn root() -> Result> { - let cd = - env::current_dir().chain_err(|| "couldn't get the current directory")?; + let cd = env::current_dir().chain_err( + || "couldn't get the current directory", + )?; - Ok(util::search(&cd, "Cargo.toml").map(|p| Root { path: p.to_owned() })) + Ok( + util::search(&cd, "Cargo.toml").map(|p| Root { path: p.to_owned() }), + ) } #[derive(Clone, Copy, PartialEq)] diff --git a/src/cli.rs b/src/cli.rs index 17b349a..96a4a0b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -22,7 +22,9 @@ impl Args { } pub fn verbose(&self) -> bool { - self.all.iter().any(|a| a == "--verbose" || a == "-v" || a == "-vv") + self.all.iter().any(|a| { + a == "--verbose" || a == "-v" || a == "-vv" + }) } pub fn version(&self) -> bool { diff --git a/src/extensions.rs b/src/extensions.rs index 74f8a5f..b5375f4 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -18,9 +18,11 @@ impl CommandExt for Command { if status.success() { Ok(()) } else { - Err(format!("`{:?}` failed with exit code: {:?}", - self, - status.code()))? + Err(format!( + "`{:?}` failed with exit code: {:?}", + self, + status.code() + ))? } } @@ -30,8 +32,9 @@ impl CommandExt for Command { writeln!(io::stderr(), "+ {:?}", self).ok(); } - self.status() - .chain_err(|| format!("couldn't execute `{:?}`", self)) + self.status().chain_err( + || format!("couldn't execute `{:?}`", self), + ) } /// Runs the command to completion and returns its stdout @@ -40,18 +43,22 @@ impl CommandExt for Command { writeln!(io::stderr(), "+ {:?}", self).ok(); } - let out = self.output() - .chain_err(|| format!("couldn't execute `{:?}`", self))?; + let out = self.output().chain_err( + || format!("couldn't execute `{:?}`", self), + )?; if out.status.success() { - Ok(String::from_utf8(out.stdout).chain_err(|| { - format!("`{:?}` output was not UTF-8", - self) - })?) + Ok( + String::from_utf8(out.stdout).chain_err(|| { + format!("`{:?}` output was not UTF-8", self) + })?, + ) } else { - Err(format!("`{:?}` failed with exit code: {:?}", - self, - out.status.code()))? + Err(format!( + "`{:?}` failed with exit code: {:?}", + self, + out.status.code() + ))? } } } diff --git a/src/flock.rs b/src/flock.rs index 8ec32f5..501e6be 100644 --- a/src/flock.rs +++ b/src/flock.rs @@ -57,58 +57,72 @@ impl Filesystem { } pub fn join(&self, other: T) -> Filesystem - where T: AsRef + where + T: AsRef, { Filesystem::new(self.path.join(other)) } pub fn open_ro

(&self, path: P, msg: &str) -> io::Result - where P: AsRef + where + P: AsRef, { - self.open(path.as_ref(), - OpenOptions::new().read(true), - State::Shared, - msg) + self.open( + path.as_ref(), + OpenOptions::new().read(true), + State::Shared, + msg, + ) } pub fn open_rw

(&self, path: P, msg: &str) -> io::Result - where P: AsRef + where + P: AsRef, { - self.open(path.as_ref(), - OpenOptions::new().read(true).write(true).create(true), - State::Exclusive, - msg) - } - - fn open(&self, - path: &Path, - opts: &OpenOptions, - state: State, - msg: &str) - -> io::Result { + self.open( + path.as_ref(), + OpenOptions::new().read(true).write(true).create(true), + State::Exclusive, + msg, + ) + } + + fn open( + &self, + path: &Path, + opts: &OpenOptions, + state: State, + msg: &str, + ) -> io::Result { let path = self.path.join(path); - let f = opts.open(&path) - .or_else(|e| if e.kind() == io::ErrorKind::NotFound && - state == State::Exclusive { + let f = opts.open(&path).or_else( + |e| if e.kind() == io::ErrorKind::NotFound && + state == State::Exclusive + { create_dir_all(path.parent().unwrap())?; opts.open(&path) } else { Err(e) - })?; + }, + )?; match state { State::Exclusive => { - acquire(msg, - &path, - &|| f.try_lock_exclusive(), - &|| f.lock_exclusive())?; + acquire( + msg, + &path, + &|| f.try_lock_exclusive(), + &|| f.lock_exclusive(), + )?; } State::Shared => { - acquire(msg, - &path, - &|| f.try_lock_shared(), - &|| f.lock_shared())?; + acquire( + msg, + &path, + &|| f.try_lock_shared(), + &|| f.lock_shared(), + )?; } } @@ -129,11 +143,12 @@ impl Drop for FileLock { } } -fn acquire(msg: &str, - path: &Path, - try: &Fn() -> io::Result<()>, - block: &Fn() -> io::Result<()>) - -> io::Result<()> { +fn acquire( + msg: &str, + path: &Path, + try: &Fn() -> io::Result<()>, + block: &Fn() -> io::Result<()>, +) -> io::Result<()> { #[cfg(all(target_os = "linux", not(target_env = "musl")))] fn is_on_nfs_mount(path: &Path) -> bool { use std::ffi::CString; @@ -175,10 +190,12 @@ fn acquire(msg: &str, } } - writeln!(io::stderr(), - "{:>12} waiting for file lock on {}", - "Blocking", - msg) + writeln!( + io::stderr(), + "{:>12} waiting for file lock on {}", + "Blocking", + msg + ) .ok(); block() diff --git a/src/main.rs b/src/main.rs index aacb1dc..dea90d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,8 @@ pub enum CompilationMode { impl CompilationMode { fn hash(&self, hasher: &mut H) -> Result<()> - where H: Hasher + where + H: Hasher, { match *self { CompilationMode::Cross(ref target) => target.hash(hasher)?, @@ -90,8 +91,10 @@ pub fn main() { writeln!(stderr, "{:?}", backtrace).ok(); } } else { - writeln!(stderr, - "note: run with `RUST_BACKTRACE=1` for a backtrace") + writeln!( + stderr, + "note: run with `RUST_BACKTRACE=1` for a backtrace" + ) .ok(); } @@ -116,9 +119,11 @@ fn run() -> Result { return cargo::run(&args, verbose); } } else if args.version() { - writeln!(io::stderr(), - concat!("xargo ", env!("CARGO_PKG_VERSION"), "{}"), - include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))) + writeln!( + io::stderr(), + concat!("xargo ", env!("CARGO_PKG_VERSION"), "{}"), + include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) + ) .ok(); return cargo::run(&args, verbose); @@ -147,17 +152,24 @@ fn run() -> Result { }; let cmode = if let Some(triple) = args.target() { - if triple == meta.host { + if Path::new(triple).is_file() { + bail!( + "Xargo doesn't support files as an argument to --target. \ + Use `--target foo` instead of `--target foo.json`." + ) + } else if triple == meta.host { Some(CompilationMode::Native(meta.host.clone())) } else { - Target::new(triple, &cd, verbose)?.map(CompilationMode::Cross) + Target::new(triple, &cd, verbose)?.map( + CompilationMode::Cross, + ) } } else { if let Some(ref config) = config { if let Some(triple) = config.target()? { - Target::new(triple, &cd, verbose) - ? - .map(CompilationMode::Cross) + Target::new(triple, &cd, verbose)?.map( + CompilationMode::Cross, + ) } else { Some(CompilationMode::Native(meta.host.clone())) } @@ -170,21 +182,25 @@ fn run() -> Result { let home = xargo::home(&cmode)?; let rustflags = cargo::rustflags(config.as_ref(), cmode.triple())?; - sysroot::update(&cmode, - &home, - &root, - &rustflags, - &meta, - &src, - &sysroot, - verbose)?; - return xargo::run(&args, - &cmode, - rustflags, - &home, - &meta, - config.as_ref(), - verbose); + sysroot::update( + &cmode, + &home, + &root, + &rustflags, + &meta, + &src, + &sysroot, + verbose, + )?; + return xargo::run( + &args, + &cmode, + rustflags, + &home, + &meta, + config.as_ref(), + verbose, + ); } } diff --git a/src/rustc.rs b/src/rustc.rs index 74e23e2..d1c3613 100644 --- a/src/rustc.rs +++ b/src/rustc.rs @@ -16,9 +16,9 @@ use extensions::CommandExt; use {util, rustc}; fn command() -> Command { - env::var_os("RUSTC") - .map(Command::new) - .unwrap_or_else(|| Command::new("rustc")) + env::var_os("RUSTC").map(Command::new).unwrap_or_else(|| { + Command::new("rustc") + }) } /// `rustc --print target-list` @@ -89,8 +89,10 @@ impl Sysroot { } } - Err("`rust-src` component not found. Run `rustup component add \ - rust-src`.")? + Err( + "`rust-src` component not found. Run `rustup component add \ + rust-src`.", + )? } } @@ -101,10 +103,11 @@ pub enum Target { } impl Target { - pub fn new(triple: &str, - cd: &CurrentDirectory, - verbose: bool) - -> Result> { + pub fn new( + triple: &str, + cd: &CurrentDirectory, + verbose: bool, + ) -> Result> { let triple = triple.to_owned(); if rustc::targets(verbose)?.iter().any(|t| t == &triple) { @@ -145,7 +148,8 @@ impl Target { } pub fn hash(&self, hasher: &mut H) -> Result<()> - where H: Hasher + where + H: Hasher, { if let Target::Custom { ref json, .. } = *self { // Here we roundtrip to/from JSON to get the same hash when some diff --git a/src/sysroot.rs b/src/sysroot.rs index 0b21611..2f65bb7 100644 --- a/src/sysroot.rs +++ b/src/sysroot.rs @@ -27,14 +27,15 @@ fn profile() -> &'static str { "release" } -fn build(cmode: &CompilationMode, - blueprint: Blueprint, - ctoml: &cargo::Toml, - home: &Home, - rustflags: &Rustflags, - hash: u64, - verbose: bool) - -> Result<()> { +fn build( + cmode: &CompilationMode, + blueprint: Blueprint, + ctoml: &cargo::Toml, + home: &Home, + rustflags: &Rustflags, + hash: u64, + verbose: bool, +) -> Result<()> { const TOML: &'static str = r#" [package] authors = ["The Rust Project Developers"] @@ -43,13 +44,15 @@ version = "0.0.0" "#; let rustlib = home.lock_rw(cmode.triple())?; - rustlib.remove_siblings() - .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?; + rustlib.remove_siblings().chain_err(|| { + format!("couldn't clear {}", rustlib.path().display()) + })?; let dst = rustlib.parent().join("lib"); util::mkdir(&dst)?; for (_, stage) in blueprint.stages { - let td = TempDir::new("xargo") - .chain_err(|| "couldn't create a temporary directory")?; + let td = TempDir::new("xargo").chain_err( + || "couldn't create a temporary directory", + )?; let td = td.path(); let mut stoml = TOML.to_owned(); @@ -68,7 +71,9 @@ version = "0.0.0" let cargo = || { let mut cmd = Command::new("cargo"); - cmd.env("RUSTFLAGS", rustflags.for_xargo(home)); + let mut flags = rustflags.for_xargo(home); + flags.push_str(" -Z force-unstable-if-unmarked"); + cmd.env("RUSTFLAGS", flags); cmd.env_remove("CARGO_TARGET_DIR"); cmd.arg("build"); @@ -96,11 +101,13 @@ version = "0.0.0" } // Copy artifacts to Xargo sysroot - util::cp_r(&td.join("target") - .join(cmode.triple()) - .join(profile()) - .join("deps"), - &dst)?; + util::cp_r( + &td.join("target") + .join(cmode.triple()) + .join(profile()) + .join("deps"), + &dst, + )?; } @@ -131,12 +138,13 @@ fn old_hash(cmode: &CompilationMode, home: &Home) -> Result> { /// - The target specification file, is any /// - `[profile.release]` in `Cargo.toml` /// - `rustc` commit hash -fn hash(cmode: &CompilationMode, - blueprint: &Blueprint, - rustflags: &Rustflags, - ctoml: &cargo::Toml, - meta: &VersionMeta) - -> Result { +fn hash( + cmode: &CompilationMode, + blueprint: &Blueprint, + rustflags: &Rustflags, + ctoml: &cargo::Toml, + meta: &VersionMeta, +) -> Result { let mut hasher = DefaultHasher::new(); blueprint.hash(&mut hasher); @@ -156,15 +164,16 @@ fn hash(cmode: &CompilationMode, Ok(hasher.finish()) } -pub fn update(cmode: &CompilationMode, - home: &Home, - root: &Root, - rustflags: &Rustflags, - meta: &VersionMeta, - src: &Src, - sysroot: &Sysroot, - verbose: bool) - -> Result<()> { +pub fn update( + cmode: &CompilationMode, + home: &Home, + root: &Root, + rustflags: &Rustflags, + meta: &VersionMeta, + src: &Src, + sysroot: &Sysroot, + verbose: bool, +) -> Result<()> { let ctoml = cargo::toml(root)?; let xtoml = xargo::toml(root)?; @@ -192,15 +201,17 @@ pub fn update(cmode: &CompilationMode, } } - lock.remove_siblings() - .chain_err(|| format!("couldn't clear {}", lock.path().display()))?; + lock.remove_siblings().chain_err(|| { + format!("couldn't clear {}", lock.path().display()) + })?; let dst = lock.parent().join("lib"); util::mkdir(&dst)?; - util::cp_r(&sysroot.path() - .join("lib/rustlib") - .join(&meta.host) - .join("lib"), - &dst)?; + util::cp_r( + &sysroot.path().join("lib/rustlib").join(&meta.host).join( + "lib", + ), + &dst, + )?; util::write(&hfile, hash)?; @@ -225,32 +236,36 @@ impl Blueprint { Blueprint { stages: BTreeMap::new() } } - fn from(toml: Option<&xargo::Toml>, - target: &str, - root: &Root, - src: &Src) - -> Result { - let deps = match (toml.and_then(|t| t.dependencies()), - toml.and_then(|t| t.target_dependencies(target))) { + fn from( + toml: Option<&xargo::Toml>, + target: &str, + root: &Root, + src: &Src, + ) -> Result { + let deps = match ( + toml.and_then(|t| t.dependencies()), + toml.and_then(|t| t.target_dependencies(target)), + ) { (Some(value), Some(tvalue)) => { - let mut deps = value.as_table() - .cloned() - .ok_or_else(|| { - format!("Xargo.toml: `dependencies` must be a table") - })?; - - let more_deps = tvalue.as_table() - .ok_or_else(|| { - format!("Xargo.toml: `target.{}.dependencies` must be \ + let mut deps = value.as_table().cloned().ok_or_else( + || format!("Xargo.toml: `dependencies` must be a table"), + )?; + + let more_deps = tvalue.as_table().ok_or_else(|| { + format!( + "Xargo.toml: `target.{}.dependencies` must be \ a table", - target) - })?; + target + ) + })?; for (k, v) in more_deps { if deps.insert(k.to_owned(), v.clone()).is_some() { - Err(format!("found duplicate dependency name {}, \ + Err(format!( + "found duplicate dependency name {}, \ but all dependencies must have a \ unique name", - k))? + k + ))? } } @@ -261,9 +276,11 @@ impl Blueprint { if let Some(table) = value.as_table() { table.clone() } else { - Err(format!("Xargo.toml: target.{}.dependencies must be \ + Err(format!( + "Xargo.toml: target.{}.dependencies must be \ a table", - target))? + target + ))? } } (None, None) => { @@ -279,30 +296,40 @@ impl Blueprint { for (k, v) in deps { if let Value::Table(mut map) = v { let stage = if let Some(value) = map.remove("stage") { - value.as_integer() - .ok_or_else(|| { - format!("dependencies.{}.stage must be an integer", - k) + value.as_integer().ok_or_else(|| { + format!( + "dependencies.{}.stage must be an integer", + k + ) })? } else { 0 }; if let Some(path) = map.get_mut("path") { - let p = PathBuf::from(path.as_str() - .ok_or_else(|| { - format!("dependencies.{}.path must be a string", k) - })?); + let p = PathBuf::from( + path.as_str().ok_or_else(|| { + format!( + "dependencies.{}.path must be a string", + k + ) + })?, + ); if !p.is_absolute() { - *path = Value::String(root.path() - .join(&p) - .canonicalize() - .chain_err(|| { - format!("couldn't canonicalize {}", p.display()) - })? - .display() - .to_string()); + *path = Value::String( + root.path() + .join(&p) + .canonicalize() + .chain_err(|| { + format!( + "couldn't canonicalize {}", + p.display() + ) + })? + .display() + .to_string(), + ); } } @@ -317,10 +344,12 @@ impl Blueprint { blueprint.push(stage, k, map); } else { - Err(format!("Xargo.toml: target.{}.dependencies.{} must be \ + Err(format!( + "Xargo.toml: target.{}.dependencies.{} must be \ a table", - target, - k))? + target, + k + ))? } } @@ -340,7 +369,8 @@ impl Blueprint { } fn hash(&self, hasher: &mut H) - where H: Hasher + where + H: Hasher, { for stage in self.stages.values() { for (k, v) in stage.toml.iter() { diff --git a/src/util.rs b/src/util.rs index a8eb69d..0be65f6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -10,43 +10,45 @@ use errors::*; pub fn cp_r(src: &Path, dst: &Path) -> Result<()> { for e in WalkDir::new(src) { - // This is only an error when there's some sort of intermittent IO error during iteration. + // This is only an error when there's some sort of intermittent IO error + // during iteration. // see https://doc.rust-lang.org/std/fs/struct.ReadDir.html - let e = e.chain_err(||{ - format!("intermittent IO error while iterating directory `{}`", src.display()) + let e = e.chain_err(|| { + format!( + "intermittent IO error while iterating directory `{}`", + src.display() + ) })?; let src_file = e.path(); - let relative_path = src_file - .strip_prefix(src) - .chain_err(|| { - format!("Could not retrieve relative path of child directory or file `{}` with regards to parent directory `{}`", + let relative_path = src_file.strip_prefix(src).chain_err(|| { + format!( + "Could not retrieve relative path of child directory or \ + file `{}` with regards to parent directory `{}`", src_file.display(), - src.display()) - })?; + src.display() + ) + })?; let dst_file = dst.join(relative_path); - let metadata = e.metadata() - .chain_err(|| { - format!("Could not retrieve metadata of `{}`", - e.path().display()) - })?; + let metadata = e.metadata().chain_err(|| { + format!("Could not retrieve metadata of `{}`", e.path().display()) + })?; if metadata.is_dir() { // ensure the destination directory exists - fs::create_dir_all(&dst_file) - .chain_err(|| { - format!("Could not create directory `{}`", - dst_file.display()) - })?; + fs::create_dir_all(&dst_file).chain_err(|| { + format!("Could not create directory `{}`", dst_file.display()) + })?; } else { // else copy the file - fs::copy(&src_file, &dst_file) - .chain_err(|| { - format!("copying files from `{}` to `{}` failed", - src_file.display(), - dst_file.display()) - })?; + fs::copy(&src_file, &dst_file).chain_err(|| { + format!( + "copying files from `{}` to `{}` failed", + src_file.display(), + dst_file.display() + ) + })?; }; } @@ -54,21 +56,26 @@ pub fn cp_r(src: &Path, dst: &Path) -> Result<()> { } pub fn mkdir(path: &Path) -> Result<()> { - fs::create_dir(path) - .chain_err(|| format!("couldn't create directory {}", path.display())) + fs::create_dir(path).chain_err(|| { + format!("couldn't create directory {}", path.display()) + }) } /// Parses `path` as TOML pub fn parse(path: &Path) -> Result { - Ok(Value::Table(Parser::new(&read(path)?).parse() - .ok_or_else(|| format!("{} is not valid TOML", path.display()))?)) + Ok(Value::Table( + Parser::new(&read(path)?).parse().ok_or_else(|| { + format!("{} is not valid TOML", path.display()) + })?, + )) } pub fn read(path: &Path) -> Result { let mut s = String::new(); let p = path.display(); - File::open(path).chain_err(|| format!("couldn't open {}", p))? + File::open(path) + .chain_err(|| format!("couldn't open {}", p))? .read_to_string(&mut s) .chain_err(|| format!("couldn't read {}", p))?; diff --git a/src/xargo.rs b/src/xargo.rs index f50bf5d..7765fe3 100644 --- a/src/xargo.rs +++ b/src/xargo.rs @@ -13,20 +13,23 @@ use extensions::CommandExt; use flock::{FileLock, Filesystem}; use {cargo, util}; -pub fn run(args: &Args, - cmode: &CompilationMode, - rustflags: Rustflags, - home: &Home, - meta: &VersionMeta, - config: Option<&Config>, - verbose: bool) - -> Result { +pub fn run( + args: &Args, + cmode: &CompilationMode, + rustflags: Rustflags, + home: &Home, + meta: &VersionMeta, + config: Option<&Config>, + verbose: bool, +) -> Result { let mut cmd = Command::new("cargo"); cmd.args(args.all()); if args.subcommand() == Some(Subcommand::Doc) { - cmd.env("RUSTDOCFLAGS", - cargo::rustdocflags(config, cmode.triple())?.for_xargo(home)); + cmd.env( + "RUSTDOCFLAGS", + cargo::rustdocflags(config, cmode.triple())?.for_xargo(home), + ); } cmd.env("RUSTFLAGS", rustflags.for_xargo(home)); @@ -50,9 +53,7 @@ impl Home { } fn path(&self, triple: &str) -> Filesystem { - self.path - .join("lib/rustlib") - .join(triple) + self.path.join("lib/rustlib").join(triple) } pub fn lock_ro(&self, triple: &str) -> Result { @@ -102,7 +103,9 @@ impl Toml { /// Returns the `target.{}.dependencies` part of `Xargo.toml` pub fn target_dependencies(&self, target: &str) -> Option<&Value> { - self.table.lookup(&format!("target.{}.dependencies", target)) + self.table.lookup( + &format!("target.{}.dependencies", target), + ) } } diff --git a/tests/smoke.rs b/tests/smoke.rs index 60a220c..242daad 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -36,9 +36,13 @@ fn home() -> Result { if let Some(h) = env::var_os("XARGO_HOME") { Ok(PathBuf::from(h)) } else { - Ok(env::home_dir() - .ok_or_else(|| "couldn't find your home directory. Is $HOME set?")? - .join(".xargo")) + Ok( + env::home_dir() + .ok_or_else( + || "couldn't find your home directory. Is $HOME set?", + )? + .join(".xargo"), + ) } } @@ -46,28 +50,27 @@ fn cleanup(target: &str) -> Result<()> { let p = home()?.join("lib/rustlib").join(target); if p.exists() { - fs::remove_dir_all(&p) - .chain_err(|| format!("couldn't clean sysroot for {}", target)) + fs::remove_dir_all(&p).chain_err(|| { + format!("couldn't clean sysroot for {}", target) + }) } else { Ok(()) } } fn exists(krate: &str, target: &str) -> Result { - let p = home() - ? - .join("lib/rustlib") - .join(target) - .join("lib"); - - for e in - fs::read_dir(&p).chain_err(|| { - format!("couldn't read the directory {}", p.display()) - })? { + let p = home()?.join("lib/rustlib").join(target).join("lib"); + + for e in fs::read_dir(&p).chain_err(|| { + format!("couldn't read the directory {}", p.display()) + })? + { let e = e.chain_err(|| { - format!("error reading the contents of the directory {}", - p.display()) - })?; + format!( + "error reading the contents of the directory {}", + p.display() + ) + })?; if e.file_name().to_string_lossy().contains(krate) { return Ok(true); @@ -90,8 +93,8 @@ fn mkdir(path: &Path) -> Result<()> { fn sysroot_was_built(stderr: &str, target: &str) -> bool { stderr.lines().filter(|l| l.starts_with("+")).any(|l| { l.contains("cargo") && l.contains("build") && - l.contains("--target") && l.contains(target) && - l.contains("-p") && l.contains("core") + l.contains("--target") && l.contains(target) && + l.contains("-p") && l.contains("core") }) } @@ -114,8 +117,9 @@ fn write(path: &Path, append: bool, contents: &str) -> Result<()> { } fn xargo() -> Result { - let mut p = env::current_exe() - .chain_err(|| "couldn't get path to current executable")?; + let mut p = env::current_exe().chain_err( + || "couldn't get path to current executable", + )?; p.pop(); p.pop(); p.push("xargo"); @@ -129,31 +133,38 @@ trait CommandExt { impl CommandExt for Command { fn run(&mut self) -> Result<()> { - let status = self.status() - .chain_err(|| format!("couldn't execute `{:?}`", self))?; + let status = self.status().chain_err( + || format!("couldn't execute `{:?}`", self), + )?; if status.success() { Ok(()) } else { - Err(format!("`{:?}` failed with exit code: {:?}", - self, - status.code()))? + Err(format!( + "`{:?}` failed with exit code: {:?}", + self, + status.code() + ))? } } fn run_and_get_stderr(&mut self) -> Result { - let out = self.output() - .chain_err(|| format!("couldn't execute `{:?}`", self))?; + let out = self.output().chain_err( + || format!("couldn't execute `{:?}`", self), + )?; if out.status.success() { - Ok(String::from_utf8(out.stderr).chain_err(|| { - format!("`{:?}` output was not UTF-8", - self) - })?) + Ok( + String::from_utf8(out.stderr).chain_err(|| { + format!("`{:?}` output was not UTF-8", self) + })?, + ) } else { - Err(format!("`{:?}` failed with exit code: {:?}", - self, - out.status.code()))? + Err(format!( + "`{:?}` failed with exit code: {:?}", + self, + out.status.code() + ))? } } } @@ -178,9 +189,9 @@ impl Project { } "#; - let td = - TempDir::new("xargo") - .chain_err(|| "couldn't create a temporary directory")?; + let td = TempDir::new("xargo").chain_err( + || "couldn't create a temporary directory", + )?; xargo()? .args(&["init", "--lib", "--vcs", "none", "--name", name]) @@ -191,16 +202,12 @@ impl Project { write(&td.path().join(format!("{}.json", name)), false, JSON)?; - Ok(Project { - name: name, - td: td, - }) + Ok(Project { name: name, td: td }) } /// Calls `xargo build` fn build(&self, target: &str) -> Result<()> { - xargo() - ? + xargo()? .args(&["build", "--target", target]) .current_dir(self.td.path()) .run() @@ -234,8 +241,7 @@ impl Project { /// Calls `xargo doc` fn doc(&self, target: &str) -> Result<()> { - xargo() - ? + xargo()? .args(&["doc", "--target", target]) .current_dir(self.td.path()) .run() @@ -257,8 +263,9 @@ fn hcleanup(triple: &str) -> Result<()> { let p = home()?.join("HOST/lib/rustlib").join(triple); if p.exists() { - fs::remove_dir_all(&p) - .chain_err(|| format!("couldn't clean sysroot for {}", triple)) + fs::remove_dir_all(&p).chain_err(|| { + format!("couldn't clean sysroot for {}", triple) + }) } else { Ok(()) } @@ -277,9 +284,9 @@ impl HProject { let guard = ONCE.lock(); - let td = - TempDir::new("xargo") - .chain_err(|| "couldn't create a temporary directory")?; + let td = TempDir::new("xargo").chain_err( + || "couldn't create a temporary directory", + )?; xargo()? .args(&["init", "--lib", "--vcs", "none", "--name", "host"]) @@ -287,9 +294,11 @@ impl HProject { .run()?; if test { - write(&td.path().join("src/lib.rs"), - false, - "#![feature(alloc_system)]\nextern crate alloc_system;")?; + write( + &td.path().join("src/lib.rs"), + false, + "#![feature(alloc_system)]\nextern crate alloc_system;", + )?; } else { write(&td.path().join("src/lib.rs"), false, "#![no_std]")?; } @@ -350,9 +359,11 @@ fn target_dependencies() { const TARGET: &'static str = "__target_dependencies"; let project = Project::new(TARGET)?; - project.xargo_toml(r#" + project.xargo_toml( + r#" [target.__target_dependencies.dependencies.alloc] -"#)?; +"#, + )?; project.build(TARGET)?; assert!(exists("core", TARGET)?); assert!(exists("alloc", TARGET)?); @@ -371,9 +382,11 @@ fn dependencies() { const TARGET: &'static str = "__dependencies"; let project = Project::new(TARGET)?; - project.xargo_toml(r#" + project.xargo_toml( + r#" [dependencies.alloc] -"#)?; +"#, + )?; project.build(TARGET)?; assert!(exists("core", TARGET)?); assert!(exists("alloc", TARGET)?); @@ -432,10 +445,12 @@ fn build_target() { const TARGET: &'static str = "__build_target"; let project = Project::new(TARGET)?; - project.config(r#" + project.config( + r#" [build] target = "__build_target" -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(None)?; @@ -455,10 +470,12 @@ fn override_build_target() { const TARGET: &'static str = "__override_build_target"; let project = Project::new(TARGET)?; - project.config(r#" + project.config( + r#" [build] target = "BAD" -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -482,10 +499,12 @@ fn lto_changed() { assert!(sysroot_was_built(&stderr, TARGET)); - project.cargo_toml(r#" + project.cargo_toml( + r#" [profile.release] lto = true -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -509,10 +528,12 @@ fn rustflags_changed() { assert!(sysroot_was_built(&stderr, TARGET)); - project.config(r#" + project.config( + r#" [build] rustflags = ["--cfg", "xargo"] -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -533,16 +554,19 @@ fn rustflags() { let project = Project::new(TARGET)?; - project.config(r#" + project.config( + r#" [build] rustflags = ["--cfg", "xargo"] -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; - assert!(stderr.lines() - .filter(|l| !l.starts_with("+") && l.contains("rustc")) - .all(|l| l.contains("--cfg") && l.contains("xargo"))); + assert!(stderr + .lines() + .filter(|l| !l.starts_with("+") && l.contains("rustc")) + .all(|l| l.contains("--cfg") && l.contains("xargo"))); Ok(()) } @@ -560,16 +584,19 @@ fn panic_abort() { let project = Project::new(TARGET)?; - project.cargo_toml(r#" + project.cargo_toml( + r#" [profile.release] panic = "abort" -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; - assert!(stderr.lines() - .filter(|l| !l.starts_with("+") && l.contains("--release")) - .all(|l| l.contains("-C") && l.contains("panic=abort"))); + assert!(stderr + .lines() + .filter(|l| !l.starts_with("+") && l.contains("--release")) + .all(|l| l.contains("-C") && l.contains("panic=abort"))); Ok(()) } @@ -590,10 +617,12 @@ fn link_arg() { assert!(sysroot_was_built(&stderr, TARGET)); - project.config(r#" + project.config( + r#" [target.__link_arg] rustflags = ["-C", "link-arg=-lfoo"] -"#)?; +"#, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -631,9 +660,11 @@ fn specification_changed() { assert!(sysroot_was_built(&stderr, TARGET)); - write(&project.td.path().join("__specification_changed.json"), - false, - JSON)?; + write( + &project.td.path().join("__specification_changed.json"), + false, + JSON, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -671,9 +702,11 @@ fn unchanged_specification() { assert!(sysroot_was_built(&stderr, TARGET)); - write(&project.td.path().join("__unchanged_specification.json"), - false, - JSON)?; + write( + &project.td.path().join("__unchanged_specification.json"), + false, + JSON, + )?; let stderr = project.build_and_get_stderr(Some(TARGET))?; @@ -737,13 +770,15 @@ fn test() { fn run() -> Result<()> { let project = HProject::new(true)?; - project.xargo_toml(" + project.xargo_toml( + " [dependencies.std] features = [\"panic_unwind\"] [dependencies.test] stage = 1 -")?; +", + )?; xargo()?.arg("test").current_dir(project.td.path()).run()?;