authors: Vec<String>,
targets: Vec<Target>,
target_dir: Path,
- sources: Vec<SourceId>
+ sources: Vec<SourceId>,
+ build: Option<String>,
}
impl Show for Manifest {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Manifest({}, authors={}, targets={}, target_dir={})", self.summary, self.authors, self.targets, self.target_dir.display())
+ write!(f, "Manifest({}, authors={}, targets={}, target_dir={}, \
+ build={})",
+ self.summary, self.authors, self.targets,
+ self.target_dir.display(), self.build)
}
}
dependencies: Vec<SerializedDependency>,
authors: Vec<String>,
targets: Vec<Target>,
- target_dir: String
+ target_dir: String,
+ build: Option<String>,
}
impl<E, S: Encoder<E>> Encodable<S, E> for Manifest {
dependencies: self.summary.get_dependencies().iter().map(|d| SerializedDependency::from_dependency(d)).collect(),
authors: self.authors.clone(),
targets: self.targets.clone(),
- target_dir: self.target_dir.display().to_str()
+ target_dir: self.target_dir.display().to_str(),
+ build: self.build.clone(),
}.encode(s)
}
}
}
impl Manifest {
- pub fn new(summary: &Summary, targets: &[Target], target_dir: &Path, sources: Vec<SourceId>) -> Manifest {
+ pub fn new(summary: &Summary, targets: &[Target],
+ target_dir: &Path, sources: Vec<SourceId>,
+ build: Option<String>) -> Manifest {
Manifest {
summary: summary.clone(),
authors: Vec::new(),
targets: Vec::from_slice(targets),
target_dir: target_dir.clone(),
- sources: sources
+ sources: sources,
+ build: build,
}
}
pub fn get_source_ids<'a>(&'a self) -> &'a [SourceId] {
self.sources.as_slice()
}
+
+ pub fn get_build<'a>(&'a self) -> Option<&'a str> {
+ self.build.as_ref().map(|s| s.as_slice())
+ }
}
impl Target {
use std::path::Path;
use core::{Package,PackageSet,Target};
use util;
-use util::{CargoResult, ChainError, ProcessBuilder, internal, human};
+use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError};
type Args = Vec<String>;
fn compile_pkg(pkg: &Package, dest: &Path, deps_dir: &Path, primary: bool) -> CargoResult<()> {
debug!("compile_pkg; pkg={}; targets={}", pkg, pkg.get_targets());
+ match pkg.get_manifest().get_build() {
+ Some(cmd) => try!(compile_custom(pkg, cmd, dest, deps_dir, primary)),
+ None => {}
+ }
+
// compile
for target in pkg.get_targets().iter() {
// Only compile lib targets for dependencies
io::fs::mkdir_recursive(target, io::UserRWX).chain_error(|| internal("could not create target directory"))
}
+fn compile_custom(pkg: &Package, cmd: &str, dest: &Path, deps_dir: &Path,
+ _primary: bool) -> CargoResult<()> {
+ // FIXME: this needs to be smarter about splitting
+ let mut cmd = cmd.split(' ');
+ let mut p = util::process(cmd.next().unwrap())
+ .cwd(pkg.get_root())
+ .env("OUT_DIR", Some(dest.as_str().unwrap()))
+ .env("DEPS_DIR", Some(dest.join(deps_dir).as_str().unwrap()));
+ for arg in cmd {
+ p = p.arg(arg);
+ }
+ p.exec_with_output().map(|_| ()).map_err(|e| e.mark_human())
+}
+
fn rustc(root: &Path, target: &Target, dest: &Path, deps: &Path, verbose: bool) -> CargoResult<()> {
let crate_types = target.rustc_crate_types();
let mut args = Vec::new();
build_base_args(&mut args, target, crate_type, dest);
- build_deps_args(&mut args, deps);
+ build_deps_args(&mut args, dest, deps);
util::process("rustc")
.cwd(root.clone())
into.push(dest.display().to_str());
}
-fn build_deps_args(dst: &mut Args, deps: &Path) {
+fn build_deps_args(dst: &mut Args, deps: &Path, dest: &Path) {
dst.push("-L".to_str());
dst.push(deps.display().to_str());
+ dst.push("-L".to_str());
+ dst.push(dest.display().to_str());
}
fn topsort(deps: &PackageSet) -> CargoResult<PackageSet> {
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};
+use std::str;
use TomlError = toml::Error;
static PATH_SEP : &'static str = ":";
impl ProcessBuilder {
- pub fn args<T: Show>(mut self, arguments: &[T]) -> ProcessBuilder {
- self.args = arguments.iter().map(|a| a.to_str()).collect();
+ pub fn arg<T: Str>(mut self, arg: T) -> ProcessBuilder {
+ self.args.push(arg.as_slice().to_str());
+ self
+ }
+
+ pub fn args<T: Str>(mut self, arguments: &[T]) -> ProcessBuilder {
+ self.args = arguments.iter().map(|a| a.as_slice().to_str()).collect();
self
}
}
}
- fn build_command(&self) -> Command {
+ pub fn build_command(&self) -> Command {
let mut command = Command::new(self.program.as_slice());
command.args(self.args.as_slice()).cwd(&self.cwd);
command
}
fn debug_string(&self) -> String {
- format!("{} {}", self.program, self.args.connect(" "))
+ if self.args.len() == 0 {
+ self.program.to_str()
+ } else {
+ format!("{} {}", self.program, self.args.connect(" "))
+ }
}
fn build_env(&self) -> Vec<(String, String)> {
pub struct TomlProject {
pub name: String,
pub version: String,
- pub authors: Vec<String>
+ pub authors: Vec<String>,
+ build: Option<String>,
}
impl TomlProject {
}
Ok((Manifest::new(
- &Summary::new(&self.project.to_package_id(source_id.get_url()), deps.as_slice()),
+ &Summary::new(&self.project.to_package_id(source_id.get_url()),
+ deps.as_slice()),
targets.as_slice(),
&Path::new("target"),
- sources), nested_paths))
+ sources,
+ self.project.build.clone()),
+ nested_paths))
}
}
use std;
use std::io;
use std::io::fs;
-use std::io::process::{ProcessOutput,ProcessExit};
+use std::io::process::{ProcessOutput};
use std::os;
use std::path::{Path,BytesContainer};
use std::str;
.extra_path(cargo_dir())
}
- pub fn file<B: BytesContainer, S: Str>(mut self, path: B, body: S) -> ProjectBuilder {
+ pub fn file<B: BytesContainer, S: Str>(mut self, path: B, body: S) -> ProjectBuilder {
self.files.push(FileBuilder::new(self.root.join(path), body.as_slice()));
self
}
}
fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
- self.match_status(actual.status)
+ self.match_status(actual)
.and(self.match_stdout(actual))
.and(self.match_stderr(actual))
}
- fn match_status(&self, actual: ProcessExit) -> ham::MatchResult {
+ fn match_status(&self, actual: &ProcessOutput) -> ham::MatchResult {
match self.expect_exit_code {
None => ham::success(),
Some(code) => {
ham::expect(
- actual.matches_exit_status(code),
- format!("exited with {}", actual))
+ actual.status.matches_exit_status(code),
+ format!("exited with {}\n--- stdout\n{}\n--- stderr\n{}",
+ actual.status,
+ str::from_utf8(actual.output.as_slice()),
+ str::from_utf8(actual.error.as_slice())))
}
}
}
assert_that(p.cargo_process("cargo-compile"),
execs()
.with_status(101)
- .with_stderr(format!("src/foo.rs:1:1: 1:8 error: expected item but found `invalid`\nsrc/foo.rs:1 invalid rust code!\n ^~~~~~~\nCould not execute process `rustc src/foo.rs --crate-type bin --out-dir {} -L {}` (status=101)", target.display(), target.join("deps").display()).as_slice()));
+ .with_stderr(format!("\
+src/foo.rs:1:1: 1:8 error: expected item but found `invalid`
+src/foo.rs:1 invalid rust code!
+ ^~~~~~~
+Could not execute process \
+`rustc src/foo.rs --crate-type bin --out-dir {} -L {} -L {}` (status=101)",
+ target.display(),
+ target.display(),
+ target.join("deps").display()).as_slice()));
})
test!(cargo_compile_with_warnings_in_the_root_package {
})
// test!(compiling_project_with_invalid_manifest)
+
+test!(custom_build {
+ let mut build = project("builder");
+ build = build
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[bin]] name = "foo"
+ "#)
+ .file("src/foo.rs", r#"
+ fn main() { println!("Hello!"); }
+ "#);
+ assert_that(build.cargo_process("cargo-compile"),
+ execs().with_status(0));
+
+
+ let mut p = project("foo");
+ p = p
+ .file("Cargo.toml", format!(r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ build = "{}"
+
+ [[bin]] name = "foo"
+ "#, build.root().join("target/foo").display()))
+ .file("src/foo.rs", r#"
+ fn main() {}
+ "#);
+ assert_that(p.cargo_process("cargo-compile"),
+ execs().with_status(0)
+ .with_stdout(format!("Compiling foo v0.5.0 (file:{})\n",
+ p.root().display()))
+ .with_stderr(""));
+})
+
+test!(custom_build_failure {
+ let mut build = project("builder");
+ build = build
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[bin]] name = "foo"
+ "#)
+ .file("src/foo.rs", r#"
+ fn main() { fail!("nope") }
+ "#);
+ assert_that(build.cargo_process("cargo-compile"), execs().with_status(0));
+
+
+ let mut p = project("foo");
+ p = p
+ .file("Cargo.toml", format!(r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ build = "{}"
+
+ [[bin]] name = "foo"
+ "#, build.root().join("target/foo").display()))
+ .file("src/foo.rs", r#"
+ fn main() {}
+ "#);
+ assert_that(p.cargo_process("cargo-compile"),
+ execs().with_status(101).with_stderr(format!("\
+Could not execute process `{}` (status=101)
+--- stderr
+task '<main>' failed at 'nope', src/foo.rs:2
+", build.root().join("target/foo").display())));
+})
+
+test!(custom_build_env_vars {
+ let mut p = project("foo");
+ let mut build = project("builder");
+ build = build
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[bin]] name = "foo"
+ "#)
+ .file("src/foo.rs", format!(r#"
+ use std::os;
+ fn main() {{
+ assert_eq!(os::getenv("OUT_DIR").unwrap(), "{}".to_str());
+ assert_eq!(os::getenv("DEPS_DIR").unwrap(), "{}".to_str());
+ }}
+ "#,
+ p.root().join("target").display(),
+ p.root().join("target/deps").display()));
+ assert_that(build.cargo_process("cargo-compile"), execs().with_status(0));
+
+
+ p = p
+ .file("Cargo.toml", format!(r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ build = "{}"
+
+ [[bin]] name = "foo"
+ "#, build.root().join("target/foo").display()))
+ .file("src/foo.rs", r#"
+ fn main() {}
+ "#);
+ assert_that(p.cargo_process("cargo-compile"), execs().with_status(0));
+})
+
+test!(custom_build_in_dependency {
+ let mut p = project("foo");
+ let bar = p.root().join("bar");
+ let mut build = project("builder");
+ build = build
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[bin]] name = "foo"
+ "#)
+ .file("src/foo.rs", format!(r#"
+ use std::os;
+ fn main() {{
+ assert_eq!(os::getenv("OUT_DIR").unwrap(), "{}".to_str());
+ assert_eq!(os::getenv("DEPS_DIR").unwrap(), "{}".to_str());
+ }}
+ "#,
+ p.root().join("target/deps").display(),
+ p.root().join("target/deps").display()));
+ assert_that(build.cargo_process("cargo-compile"), execs().with_status(0));
+
+
+ p = p
+ .file(".cargo/config", format!(r#"
+ paths = ["{}"]
+ "#, bar.display()).as_slice())
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[bin]] name = "foo"
+ [dependencies.bar] version = "0.5.0"
+ "#)
+ .file("src/foo.rs", r#"
+ extern crate bar;
+ fn main() { bar::bar() }
+ "#)
+ .file("bar/Cargo.toml", format!(r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+ build = "{}"
+
+ [[lib]] name = "bar"
+ "#, build.root().join("target/foo").display()))
+ .file("bar/src/bar.rs", r#"
+ pub fn bar() {}
+ "#);
+ assert_that(p.cargo_process("cargo-compile"),
+ execs().with_status(0));
+})