$(BIN_TARGETS): target/%: src/bin/%.rs $(HAMMER) $(TOML) $(LIBCARGO)
$(RUSTC) $(RUSTC_FLAGS) $(DEPS) -Ltarget --out-dir target $<
-test:
- echo "testing"
+# === Tests
+
+TEST_SRC = $(wildcard tests/*.rs)
+TEST_DEPS = $(DEPS) -L libs/hamcrest-rust/target
+
+tests/tests: $(BIN_TARGETS) $(HAMCREST) $(TEST_SRC)
+ $(RUSTC) --test --crate-type=lib $(TEST_DEPS) -Ltarget --out-dir tests tests/tests.rs
+
+test-integration: tests/tests
+ tests/tests
+
+test: test-integration
clean:
rm -rf target
+ rm -f tests/tests
distclean: clean
cd libs/hamcrest-rust && make clean
cd libs/rust-toml && make clean
# Setup phony tasks
-.PHONY: all clean distclean test libcargo
+.PHONY: all clean distclean test test-integration libcargo
# Disable unnecessary built-in rules
.SUFFIXES:
+
use serialize::{Decoder,Decodable};
use serialize::json::Encoder;
use toml::from_toml;
-use cargo::{Manifest,LibTarget,ExecTarget,Project};
+use cargo::{Manifest,LibTarget,ExecTarget,Project,CargoResult,CargoError,ToCargoError};
use std::path::Path;
#[deriving(Decodable,Encodable,Eq,Clone,Ord)]
}
fn main() {
+ match execute() {
+ Err(e) => {
+ println!("{}", e.message);
+ // TODO: Exit with error code
+ },
+ _ => return
+ }
+}
+
+fn execute() -> CargoResult<()> {
let mut decoder = FlagDecoder::new::<ReadManifestFlags>(std::os::args().tail());
let flags: ReadManifestFlags = Decodable::decode(&mut decoder);
if decoder.error.is_some() {
- fail!("Error: {}", decoder.error.unwrap());
+ return Err(CargoError::new(decoder.error.unwrap(), 1));
}
- let root = toml::parse_from_file(flags.manifest_path).unwrap();
+ let manifest_path = flags.manifest_path;
+ let root = try!(toml::parse_from_file(manifest_path.clone()).to_cargo_error(format!("Couldn't parse Toml file: {}", manifest_path), 1));
let toml_manifest = from_toml::<SerializedManifest>(root.clone());
let (lib, bin) = normalize(&toml_manifest.lib, &toml_manifest.bin);
let manifest = Manifest{
- root: Path::new(flags.manifest_path).dirname_str().unwrap().to_owned(),
+ root: try!(Path::new(manifest_path.clone()).dirname_str().to_cargo_error(format!("Could not get dirname from {}", manifest_path), 1)).to_owned(),
project: toml_manifest.project,
lib: lib,
bin: bin
let encoded: ~str = Encoder::str_encode(&manifest);
println!("{}", encoded);
+
+ Ok(())
}
fn normalize(lib: &Option<~[SerializedLibTarget]>, bin: &Option<~[SerializedExecTarget]>) -> (~[LibTarget], ~[ExecTarget]) {
use serialize::json;
use serialize::Decodable;
use std::path::Path;
-use cargo::Manifest;
+use cargo::{Manifest,CargoResult,ToCargoError};
/**
cargo-rustc -- ...args
*/
fn main() {
+ match execute() {
+ Err(e) => {
+ println!("{}", e.message);
+ // TODO: Exit with error code
+ },
+ _ => return
+ }
+}
+
+fn execute() -> CargoResult<()> {
let mut reader = io::stdin();
- let input = reader.read_to_str().unwrap();
+ let input = try!(reader.read_to_str().to_cargo_error(~"Cannot read stdin to a string", 1));
- let json = json::from_str(input).unwrap();
+ let json = try!(json::from_str(input).to_cargo_error(format!("Cannot parse json: {}", input), 1));
let mut decoder = json::Decoder::new(json);
let manifest: Manifest = Decodable::decode(&mut decoder);
config.program = "rustc";
config.args = args.as_slice();
- let mut p = Process::configure(config).unwrap();
+ let mut p = try!(Process::configure(config).to_cargo_error(format!("Could not start process: rustc {}", args.as_slice()), 1));
let status = p.wait();
if status != std::io::process::ExitStatus(0) {
fail!("Failed to execute")
}
+
+ Ok(())
}
fn join(path: &Path, part: ~str) -> ~str {
- path.join(part).as_str().unwrap().to_owned()
+ format!("{}", path.join(part).display())
}
extern crate serialize;
use serialize::{Decoder};
+use std::fmt;
+use std::fmt::{Show,Formatter};
#[deriving(Decodable,Encodable,Eq,Clone,Ord)]
pub struct Manifest {
version: ~str,
authors: ~[~str]
}
+
+pub type CargoResult<T> = Result<T, CargoError>;
+
+pub struct CargoError {
+ message: ~str,
+ exit_code: uint
+}
+
+impl CargoError {
+ pub fn new(message: ~str, exit_code: uint) -> CargoError {
+ CargoError { message: message, exit_code: exit_code }
+ }
+}
+
+impl Show for CargoError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f.buf, "{}", self.message)
+ }
+}
+
+pub trait ToCargoError<T> {
+ fn to_cargo_error(self, message: ~str, exit_code: uint) -> Result<T, CargoError>;
+}
+
+impl<T,U> ToCargoError<T> for Result<T,U> {
+ fn to_cargo_error(self, message: ~str, exit_code: uint) -> Result<T, CargoError> {
+ match self {
+ Err(_) => Err(CargoError{ message: message, exit_code: exit_code }),
+ Ok(val) => Ok(val)
+ }
+ }
+}
+
+impl<T> ToCargoError<T> for Option<T> {
+ fn to_cargo_error(self, message: ~str, exit_code: uint) -> CargoResult<T> {
+ match self {
+ None => Err(CargoError{ message: message, exit_code: exit_code }),
+ Some(val) => Ok(val)
+ }
+ }
+}
--- /dev/null
+// use std::io::fs::{mkdir_recursive,rmdir_recursive};
+use std::io::fs;
+use std::os::tmpdir;
+use std::path::{Path};
+
+static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests";
+static MKDIR_PERM : u32 = 0o755;
+
+#[deriving(Eq,Clone)]
+struct FileBuilder {
+ path: Path,
+ body: ~str
+}
+
+impl FileBuilder {
+ pub fn new(path: Path, body: &str) -> FileBuilder {
+ FileBuilder { path: path, body: body.to_owned() }
+ }
+
+ fn mk(&self) -> Result<(), ~str> {
+ try!(mkdir_recursive(&self.dirname()));
+
+ let mut file = try!(
+ fs::File::create(&self.path)
+ .with_err_msg(format!("Could not create file; path={}", self.path.display())));
+
+ file.write_str(self.body.as_slice())
+ .with_err_msg(format!("Could not write to file; path={}", self.path.display()))
+ }
+
+ fn dirname(&self) -> Path {
+ Path::new(self.path.dirname())
+ }
+}
+
+#[deriving(Eq,Clone)]
+struct ProjectBuilder {
+ name: ~str,
+ root: Path,
+ files: ~[FileBuilder]
+}
+
+impl ProjectBuilder {
+ pub fn new(name: &str, root: Path) -> ProjectBuilder {
+ ProjectBuilder {
+ name: name.to_owned(),
+ root: root,
+ files: ~[]
+ }
+ }
+
+ pub fn file(mut self, path: &str, body: &str) -> ProjectBuilder {
+ self.files.push(FileBuilder::new(self.root.join(path), body));
+ self
+ }
+
+ pub fn build(self) {
+ match self.build_with_result() {
+ Err(e) => fail!(e),
+ _ => return
+ }
+ }
+
+ pub fn build_with_result(self) -> Result<(), ~str> {
+ // First, clean the directory if it already exists
+ try!(self.rm_root());
+
+ // Create the empty directory
+ try!(mkdir_recursive(&self.root));
+
+ for file in self.files.iter() {
+ try!(file.mk());
+ }
+
+ println!("{}", self.root.display());
+ println!("{:?}", self);
+ Ok(())
+ }
+
+ fn rm_root(&self) -> Result<(), ~str> {
+ if self.root.exists() {
+ rmdir_recursive(&self.root)
+ }
+ else {
+ Ok(())
+ }
+ }
+}
+
+// Generates a project layout
+pub fn project(name: &str) -> ProjectBuilder {
+ ProjectBuilder::new(name, tmpdir().join(CARGO_INTEGRATION_TEST_DIR))
+}
+
+// === Helpers ===
+
+pub fn mkdir_recursive(path: &Path) -> Result<(), ~str> {
+ fs::mkdir_recursive(path, MKDIR_PERM)
+ .with_err_msg(format!("could not create directory; path={}", path.display()))
+}
+
+pub fn rmdir_recursive(path: &Path) -> Result<(), ~str> {
+ fs::rmdir_recursive(path)
+ .with_err_msg(format!("could not rm directory; path={}", path.display()))
+}
+
+trait ErrMsg<T> {
+ fn with_err_msg(self, val: ~str) -> Result<T, ~str>;
+}
+
+impl<T, E> ErrMsg<T> for Result<T, E> {
+ fn with_err_msg(self, val: ~str) -> Result<T, ~str> {
+ match self {
+ Ok(val) => Ok(val),
+ Err(_) => Err(val)
+ }
+ }
+}
--- /dev/null
+use support::project;
+
+fn setup() {
+
+}
+
+test!(cargo_compile_with_explicit_manifest_path {
+ project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[lib]]
+
+ name = "foo"
+ "#)
+ .file("src/foo.rs", r#"
+ fn main() {
+ println!("i am foo");
+ }"#)
+ .build();
+
+ fail!("not implemented");
+ // 1) Setup project
+ // 2) Run cargo-compile --manifest-path /tmp/bar/zomg
+ // 3) assertThat(target/foo) exists assertThat("target/foo", isCompiledBin())
+ // 4) Run target/foo, assert that output is ass expected (foo.rs == println!("i am foo"))
+})
+
+// test!(compiling_project_with_invalid_manifest)
--- /dev/null
+#[feature(macro_rules)];
+
+extern crate cargo;
+
+macro_rules! test(
+ ($name:ident $expr:expr) => (
+ #[test]
+ fn $name() {
+ setup();
+ $expr;
+ }
+ )
+)
+
+mod support;
+mod test_cargo_compile;