## The `[project]` Section
-* `name`: the name of the project (`~str`)
-* `version`: the version of the project, (`~str` that can be parsed
+* `name`: the name of the project (`String`)
+* `version`: the version of the project, (`String` that can be parsed
via `semver::parse`)
* `readme`: a Markdown-formatted file in the project that can be used as
a description of the document in indexes (`Option<Path>`, relative to
the project root, defaults to "./README.md", if found).
-* `tags`: an array of tags that can be used in indexes (`~[~str]`)
-* `authors`: a list of authors in `name <email>` format (`~[~str]`). At
+* `tags`: an array of tags that can be used in indexes (`Vec<String>`)
+* `authors`: a list of authors in `name <email>` format (`Vec<String>`). At
least one `author` with email will probably be required to submit to
the Cargo repository.
* `src`: the root directory containing source files (`Option<Path>`,
multiple libs, you would want projects to be able to depend on them
separately. If you don't care about that, why do you have separate libs?
-* `name`: the name of the library (`~str`, `hammer` would create a `libhammer`)
+* `name`: the name of the library (`String`, `hammer` would create a `libhammer`)
* `path`: the location of the main crate file (`Option<Path>`, defaults to
`src/<name>.rs`)
dependency on the main library to keep the usage requirements of the
standalone library limited to the bare minimum requirements.
-* `name`: the name of the executable (`~str`, `hammer` would create a
+* `name`: the name of the executable (`String`, `hammer` would create a
`hammer` executable)
* `path`: the location of the main crate file for the executable
(`Option<Path>`, defaults to `src/<name>.rs` if the project has only
an executable, `src/bin/<name>.rs` if the project has both a lib and
executable, see below)
+## The `[dependencies]` Section
+
+```toml
+[dependencies]
+
+rust-http = "1.x"
+hammer = ["> 1.2", "< 1.3.5"]
+
+[dependencies.hamcrest]
+
+version = "1.2.x"
+git = "http://github.com/carllerche/hamcrest"
+```
+
## Projects Containing Both `lib` and `executable`
Most projects will primarily produce either a library or an executable.
use std::fmt::{Show,Formatter};
use std::collections::HashMap;
use semver::Version;
-use serialize::{Encoder,Encodable};
+use serialize::{Encoder,Decoder,Encodable,Decodable};
use core::{
Dependency,
NameVer,
Summary
};
use core::dependency::SerializedDependency;
-use util::CargoResult;
+use util::{CargoResult,Require,toml_error,simple_human};
+use toml;
+use toml::{Table, ParseError};
#[deriving(PartialEq,Clone)]
pub struct Manifest {
* TODO: Make all struct fields private
*/
-#[deriving(Decodable,Encodable,PartialEq,Clone)]
+#[deriving(Encodable,PartialEq,Clone,Show)]
+pub enum TomlDependency {
+ SimpleDep(String),
+ DetailedDep(HashMap<String, String>)
+}
+
+#[deriving(Encodable,PartialEq,Clone)]
pub struct TomlManifest {
project: Box<Project>,
- lib: Option<~[TomlLibTarget]>,
- bin: Option<~[TomlBinTarget]>,
- dependencies: Option<HashMap<String, String>>,
+ lib: Option<Vec<TomlLibTarget>>,
+ bin: Option<Vec<TomlBinTarget>>,
+ dependencies: Option<HashMap<String, TomlDependency>>,
}
impl TomlManifest {
+ pub fn from_toml(root: toml::Value) -> CargoResult<TomlManifest> {
+ fn decode<T: Decodable<toml::Decoder,toml::Error>>(root: &toml::Value, path: &str) -> Result<T, toml::Error> {
+ let root = match root.lookup(path) {
+ Some(val) => val,
+ None => return Err(toml::ParseError)
+ };
+ toml::from_toml(root.clone())
+ }
+
+ let project = try!(decode(&root, "project").map_err(|e| toml_error("ZOMG", e)));
+ let lib = decode(&root, "lib").ok();
+ let bin = decode(&root, "bin").ok();
+
+ let deps = root.lookup("dependencies");
+
+ let deps = match deps {
+ Some(deps) => {
+ let table = try!(deps.get_table().require(simple_human("dependencies must be a table"))).clone();
+
+ let mut deps: HashMap<String, TomlDependency> = HashMap::new();
+
+ for (k, v) in table.iter() {
+ match v {
+ &toml::String(ref string) => { deps.insert(k.clone(), SimpleDep(string.clone())); },
+ &toml::Table(ref table) => {
+ let mut details = HashMap::<String, String>::new();
+
+ for (k, v) in table.iter() {
+ let v = try!(v.get_str()
+ .require(simple_human("dependency values must be string")));
+
+ details.insert(k.clone(), v.clone());
+ }
+
+ deps.insert(k.clone(), DetailedDep(details));
+ },
+ _ => ()
+ }
+ }
+
+ Some(deps)
+ },
+ None => None
+ };
+
+ Ok(TomlManifest { project: box project, lib: lib, bin: bin, dependencies: deps })
+ }
+
pub fn to_package(&self, path: &str) -> CargoResult<Package> {
// TODO: Convert hte argument to take a Path
let path = Path::new(path);
// Get targets
- let targets = normalize(&self.lib, &self.bin);
+ let targets = normalize(self.lib.as_ref().map(|l| l.as_slice()), self.bin.as_ref().map(|b| b.as_slice()));
if targets.is_empty() {
debug!("manifest has no build targets; project={}", self.project);
match self.dependencies {
Some(ref dependencies) => {
for (n, v) in dependencies.iter() {
- deps.push(try!(Dependency::parse(n.as_slice(), v.as_slice())));
+ let version = match *v {
+ SimpleDep(ref string) => string,
+ DetailedDep(ref map) => try!(map.find_equiv(&"version")
+ .require(simple_human("dependencies must include a version")))
+ };
+
+ deps.push(try!(Dependency::parse(n.as_slice(), version.as_slice())))
}
}
None => ()
path: Option<String>
}
-fn normalize(lib: &Option<~[TomlLibTarget]>, bin: &Option<~[TomlBinTarget]>) -> Vec<Target> {
+fn normalize(lib: Option<&[TomlLibTarget]>, bin: Option<&[TomlBinTarget]>) -> Vec<Target> {
log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);
fn lib_targets(dst: &mut Vec<Target>, libs: &[TomlLibTarget]) {
let mut ret = Vec::new();
match (lib, bin) {
- (&Some(ref libs), &Some(ref bins)) => {
+ (Some(ref libs), Some(ref bins)) => {
lib_targets(&mut ret, libs.as_slice());
bin_targets(&mut ret, bins.as_slice(), |bin| format!("src/bin/{}.rs", bin.name));
},
- (&Some(ref libs), &None) => {
+ (Some(ref libs), None) => {
lib_targets(&mut ret, libs.as_slice());
},
- (&None, &Some(ref bins)) => {
+ (None, Some(ref bins)) => {
bin_targets(&mut ret, bins.as_slice(), |bin| format!("src/{}.rs", bin.name));
},
- (&None, &None) => ()
+ (None, None) => ()
}
ret
execs().with_stdout("test passed\n"));
})
+test!(cargo_compile_with_nested_deps_longhand {
+ let mut p = project("foo");
+ let bar = p.root().join("bar");
+ let baz = p.root().join("baz");
+
+ p = p
+ .file(".cargo/config", format!(r#"
+ paths = ["{}", "{}"]
+ "#, bar.display(), baz.display()).as_slice())
+ .file("Cargo.toml", r#"
+ [project]
+
+ name = "foo"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.bar]
+
+ version = "0.5.0"
+
+ [[bin]]
+
+ name = "foo"
+ "#)
+ .file("src/foo.rs", main_file(r#""{}", bar::gimme()"#, ["bar"]).as_slice())
+ .file("bar/Cargo.toml", r#"
+ [project]
+
+ name = "bar"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [dependencies.baz]
+
+ version = "0.5.0"
+
+ [[lib]]
+
+ name = "bar"
+ "#)
+ .file("bar/src/bar.rs", r#"
+ extern crate baz;
+
+ pub fn gimme() -> String {
+ baz::gimme()
+ }
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+
+ name = "baz"
+ version = "0.5.0"
+ authors = ["wycats@example.com"]
+
+ [[lib]]
+
+ name = "baz"
+ "#)
+ .file("baz/src/baz.rs", r#"
+ pub fn gimme() -> String {
+ "test passed".to_str()
+ }
+ "#);
+
+ assert_that(p.cargo_process("cargo-compile"), execs());
+
+ assert_that(&p.root().join("target/foo"), existing_file());
+
+ assert_that(
+ cargo::util::process("foo").extra_path(p.root().join("target")),
+ execs().with_stdout("test passed\n"));
+})
+
fn main_file(println: &str, deps: &[&str]) -> String {
let mut buf = String::new();