mirror of https://github.com/rust-lang/cargo
Add support for detailed manifest dependencies
This commit supports the following format: ```toml [dependencies.hamcrest] version = "1.0" git = "http://github.com/carllerche/hamcrest-rust" ```
This commit is contained in:
parent
9a1c63ea17
commit
92602f6db0
26
MANIFEST.md
26
MANIFEST.md
|
@ -23,14 +23,14 @@ into Rust structs that are used throughout the built-in commands.
|
|||
|
||||
## 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>`,
|
||||
|
@ -45,7 +45,7 @@ We only plan to support a single lib at the moment because if you have
|
|||
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`)
|
||||
|
||||
|
@ -64,13 +64,27 @@ main library, it should be shipped as a separate package with a
|
|||
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.
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt;
|
|||
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,
|
||||
|
@ -10,7 +10,9 @@ use core::{
|
|||
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 {
|
||||
|
@ -192,21 +194,75 @@ pub struct Project {
|
|||
* 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);
|
||||
|
@ -218,7 +274,13 @@ impl TomlManifest {
|
|||
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 => ()
|
||||
|
@ -248,7 +310,7 @@ struct TomlTarget {
|
|||
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]) {
|
||||
|
@ -267,17 +329,17 @@ fn normalize(lib: &Option<~[TomlLibTarget]>, bin: &Option<~[TomlBinTarget]>) ->
|
|||
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
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#![allow(deprecated_owned_vector)]
|
||||
#![feature(macro_rules,phase)]
|
||||
|
||||
extern crate debug;
|
||||
extern crate term;
|
||||
extern crate url;
|
||||
extern crate serialize;
|
||||
|
|
|
@ -19,7 +19,7 @@ fn parse_from_file(path: &str) -> CargoResult<toml::Value> {
|
|||
}
|
||||
|
||||
fn load_toml(root: toml::Value) -> CargoResult<TomlManifest> {
|
||||
from_toml::<TomlManifest>(root).map_err(to_cargo_err)
|
||||
TomlManifest::from_toml(root)
|
||||
}
|
||||
|
||||
fn to_cargo_err(err: toml::Error) -> CargoError {
|
||||
|
|
|
@ -49,6 +49,7 @@ impl Source for GitSource {
|
|||
}
|
||||
|
||||
fn get(&self, packages: &[NameVer]) -> CargoResult<Vec<Package>> {
|
||||
// TODO: Support multiple manifests per repo
|
||||
let pkg = try!(read_manifest(&self.checkout_path));
|
||||
|
||||
if packages.iter().any(|nv| pkg.is_for_name_ver(nv)) {
|
||||
|
|
|
@ -273,7 +273,7 @@ impl ham::Matcher<ProcessBuilder> for Execs {
|
|||
match res {
|
||||
Ok(out) => self.match_output(&out),
|
||||
Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()),
|
||||
Err(_) => Err(format!("could not exec process {}", process))
|
||||
Err(e) => Err(format!("could not exec process {}: {}", process, e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,6 +207,79 @@ test!(cargo_compile_with_nested_deps {
|
|||
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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue