auto merge of #157 : rust-lang/cargo/graph-resolve, r=alexcrichton

This incorporates previous bugfixes by @alexcrichton that are still stuck in bors.
This commit is contained in:
bors 2014-07-10 19:52:04 +00:00
commit 0355cb6584
37 changed files with 734 additions and 268 deletions

View File

@ -7,9 +7,11 @@ ifeq ($(wildcard rustc/bin),)
export RUSTC := rustc
else
export RUSTC := $(CURDIR)/rustc/bin/rustc
export LD_LIBRARY_PATH := $(CURDIR)/rustc/lib:$(LD_LIBRARY_PATH)
export DYLD_LIBRARY_PATH := $(CURDIR)/rustc/lib:$(DYLD_LIBRARY_PATH)
endif
export PATH := $(PATH):$(CURDIR)/rustc/bin
export PATH := $(CURDIR)/rustc/bin:$(PATH)
# Link flags to pull in dependencies
BINS = cargo \

View File

@ -16,9 +16,9 @@ $ ./cargo-nightly/bin/cargo build
The current nightlies available are:
* `cargo-nightly-linux`
* `cargo-nightly-win`
* `cargo-nightly-mac`
* [`cargo-nightly-linux`](http://static.rust-lang.org/cargo-dist/cargo-nightly-linux.tar.gz)
* [`cargo-nightly-win`](http://static.rust-lang.org/cargo-dist/cargo-nightly-win.tar.gz)
* [`cargo-nightly-mac`](http://static.rust-lang.org/cargo-dist/cargo-nightly-mac.tar.gz)
## Compiling cargo

@ -1 +1 @@
Subproject commit a3844d6e0c6b84934078b7ee0e6e702c59cc5242
Subproject commit 60b649957b556c934929b7b6205ec95e20a2cd9e

@ -1 +1 @@
Subproject commit bbb7848676698ec94d186e2c910ab82452d07433
Subproject commit fa1255d4ba109f82d1b0e8be61dde4ea70047be9

@ -1 +1 @@
Subproject commit 624d5398184ccd500c3ce02338006f32f380fcc9
Subproject commit a0f1ea65fc80379f0c6c095d92fad840004aaa56

View File

@ -22,11 +22,12 @@ use cargo::util::important_paths::find_project_manifest;
struct Options {
manifest_path: Option<String>,
jobs: Option<uint>,
update: bool,
rest: Vec<String>,
}
hammer_config!(Options "Run the package's test suite", |c| {
c.short("jobs", 'j')
c.short("jobs", 'j').short("update", 'u')
})
fn main() {
@ -45,7 +46,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
};
let compile_opts = ops::CompileOptions {
update: false,
update: options.update,
env: "test",
shell: shell,
jobs: options.jobs
@ -64,7 +65,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
for file in walk {
// TODO: The proper fix is to have target knows its expected
// output and only run expected executables.
if file.display().to_str().as_slice().contains("dSYM") { continue; }
if file.display().to_string().as_slice().contains("dSYM") { continue; }
if !is_executable(&file) { continue; }
try!(util::process(file).exec().map_err(|e| {

View File

@ -34,7 +34,7 @@ fn main() {
}
};
let file = Path::new(manifest);
let contents = match File::open(&file).read_to_str() {
let contents = match File::open(&file).read_to_string() {
Ok(s) => s,
Err(e) => return fail("invalid", format!("error reading file: {}",
e).as_slice())

View File

@ -81,7 +81,7 @@ fn execute() {
fn process(args: Vec<String>) -> (String, Vec<String>) {
let mut args = Vec::from_slice(args.tail());
let head = args.shift().unwrap_or("--help".to_str());
let head = args.shift().unwrap_or("--help".to_string());
(head, args)
}
@ -156,5 +156,5 @@ fn locate_project(_: NoFlags, _: &mut MultiShell) -> CliResult<Option<ProjectLoc
not representable in Unicode"))
.map_err(|e| CliError::from_boxed(e, 1)));
Ok(Some(ProjectLocation { root: string.to_str() }))
Ok(Some(ProjectLocation { root: string.to_string() }))
}

View File

@ -19,7 +19,7 @@ impl Dependency {
};
Ok(Dependency {
name: name.to_str(),
name: name.to_string(),
namespace: namespace.clone(),
req: version,
transitive: true
@ -67,8 +67,8 @@ pub struct SerializedDependency {
impl SerializedDependency {
pub fn from_dependency(dep: &Dependency) -> SerializedDependency {
SerializedDependency {
name: dep.get_name().to_str(),
req: dep.get_version_req().to_str()
name: dep.get_name().to_string(),
req: dep.get_version_req().to_string()
}
}
}

View File

@ -32,8 +32,8 @@ pub struct CLIError {
impl CLIError {
pub fn new<T: Show, U: Show>(msg: T, detail: Option<U>,
exit_code: uint) -> CLIError {
let detail = detail.map(|d| d.to_str());
CLIError { msg: msg.to_str(), detail: detail, exit_code: exit_code }
let detail = detail.map(|d| d.to_string());
CLIError { msg: msg.to_string(), detail: detail, exit_code: exit_code }
}
}
@ -84,7 +84,7 @@ impl CargoError {
}
pub fn described<T: Show>(description: T) -> CargoError {
CargoInternalError(Described(description.to_str()))
CargoInternalError(Described(description.to_string()))
}
pub fn other() -> CargoError {

View File

@ -47,14 +47,14 @@ pub struct SerializedManifest {
impl<E, S: Encoder<E>> Encodable<S, E> for Manifest {
fn encode(&self, s: &mut S) -> Result<(), E> {
SerializedManifest {
name: self.summary.get_name().to_str(),
version: self.summary.get_version().to_str(),
name: self.summary.get_name().to_string(),
version: self.summary.get_version().to_string(),
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_string(),
build: if self.build.len() == 0 { None } else { Some(self.build.clone()) },
}.encode(s)
}
@ -100,7 +100,7 @@ pub enum TargetKind {
BinTarget
}
#[deriving(Encodable, Decodable, Clone, Hash, PartialEq)]
#[deriving(Encodable, Decodable, Clone, Hash, PartialEq, Show)]
pub struct Profile {
env: String, // compile, test, dev, bench, etc.
opt_level: uint,
@ -112,7 +112,7 @@ pub struct Profile {
impl Profile {
pub fn default_dev() -> Profile {
Profile {
env: "compile".to_str(), // run in the default environment only
env: "compile".to_string(), // run in the default environment only
opt_level: 0,
debug: true,
test: false, // whether or not to pass --test
@ -122,31 +122,31 @@ impl Profile {
pub fn default_test() -> Profile {
Profile {
env: "test".to_str(), // run in the default environment only
env: "test".to_string(), // run in the default environment only
opt_level: 0,
debug: true,
test: true, // whether or not to pass --test
dest: Some("test".to_str())
dest: Some("test".to_string())
}
}
pub fn default_bench() -> Profile {
Profile {
env: "bench".to_str(), // run in the default environment only
env: "bench".to_string(), // run in the default environment only
opt_level: 3,
debug: false,
test: true, // whether or not to pass --test
dest: Some("bench".to_str())
dest: Some("bench".to_string())
}
}
pub fn default_release() -> Profile {
Profile {
env: "release".to_str(), // run in the default environment only
env: "release".to_string(), // run in the default environment only
opt_level: 3,
debug: false,
test: false, // whether or not to pass --test
dest: Some("release".to_str())
dest: Some("release".to_string())
}
}
@ -218,7 +218,7 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Target {
SerializedTarget {
kind: kind,
name: self.name.clone(),
src_path: self.src_path.display().to_str(),
src_path: self.src_path.display().to_string(),
profile: self.profile.clone(),
metadata: self.metadata.clone()
}.encode(s)
@ -227,8 +227,8 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Target {
impl Show for Target {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}(name={}, path={})", self.kind, self.name,
self.src_path.display())
write!(f, "{}(name={}, path={}, profile={})", self.kind, self.name,
self.src_path.display(), self.profile)
}
}
@ -298,6 +298,13 @@ impl Manifest {
}
impl Target {
pub fn file_stem(&self) -> String {
match self.metadata {
Some(ref metadata) => format!("{}{}", self.name, metadata.extra_filename),
None => self.name.clone()
}
}
pub fn lib_target(name: &str, crate_targets: Vec<LibKind>,
src_path: &Path, profile: &Profile,
metadata: &Metadata)
@ -305,7 +312,7 @@ impl Target {
{
Target {
kind: LibTarget(crate_targets),
name: name.to_str(),
name: name.to_string(),
src_path: src_path.clone(),
profile: profile.clone(),
metadata: Some(metadata.clone())
@ -315,7 +322,7 @@ impl Target {
pub fn bin_target(name: &str, src_path: &Path, profile: &Profile) -> Target {
Target {
kind: BinTarget,
name: name.to_str(),
name: name.to_string(),
src_path: src_path.clone(),
profile: profile.clone(),
metadata: None
@ -337,6 +344,21 @@ impl Target {
}
}
pub fn is_dylib(&self) -> bool {
match self.kind {
LibTarget(ref kinds) => kinds.iter().any(|&k| k == Dylib),
_ => false
}
}
pub fn is_rlib(&self) -> bool {
match self.kind {
LibTarget(ref kinds) =>
kinds.iter().any(|&k| k == Rlib || k == Lib),
_ => false
}
}
pub fn is_bin(&self) -> bool {
match self.kind {
BinTarget => true,

View File

@ -42,6 +42,7 @@ pub use self::dependency::{
};
pub use self::version_req::VersionReq;
pub use self::resolver::Resolve;
pub mod errors;
pub mod source;

View File

@ -45,14 +45,14 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Package {
let package_id = summary.get_package_id();
SerializedPackage {
name: package_id.get_name().to_str(),
version: package_id.get_version().to_str(),
name: package_id.get_name().to_string(),
version: package_id.get_version().to_string(),
dependencies: summary.get_dependencies().iter().map(|d| {
SerializedDependency::from_dependency(d)
}).collect(),
authors: Vec::from_slice(manifest.get_authors()),
targets: Vec::from_slice(manifest.get_targets()),
manifest_path: self.manifest_path.display().to_str()
manifest_path: self.manifest_path.display().to_string()
}.encode(s)
}
}
@ -123,7 +123,7 @@ impl Package {
// Sort the sources just to make sure we have a consistent fingerprint.
sources.sort_by(|a, b| {
cmp::lexical_ordering(a.kind.cmp(&b.kind),
a.location.to_str().cmp(&b.location.to_str()))
a.location.to_string().cmp(&b.location.to_string()))
});
let sources = sources.iter().map(|source_id| {
source_id.load(config)

View File

@ -1,7 +1,9 @@
use semver;
use url::Url;
use std::hash::Hash;
use std::fmt;
use std::fmt::{Show,Formatter};
use collections::hash;
use serialize::{
Encodable,
Encoder,
@ -60,6 +62,16 @@ pub struct PackageId {
source_id: SourceId,
}
impl<S: hash::Writer> Hash<S> for PackageId {
fn hash(&self, state: &mut S) {
self.name.hash(state);
self.version.to_string().hash(state);
self.source_id.hash(state);
}
}
impl Eq for PackageId {}
#[deriving(Clone, Show, PartialEq)]
pub enum PackageIdError {
InvalidVersion(String),
@ -87,7 +99,7 @@ impl PackageId {
sid: &SourceId) -> CargoResult<PackageId> {
let v = try!(version.to_version().map_err(InvalidVersion));
Ok(PackageId {
name: name.to_str(),
name: name.to_string(),
version: v,
source_id: sid.clone()
})
@ -108,9 +120,9 @@ impl PackageId {
pub fn generate_metadata(&self) -> Metadata {
let metadata = format!("{}:-:{}:-:{}", self.name, self.version, self.source_id);
let extra_filename = short_hash(
&(self.name.as_slice(), self.version.to_str(), &self.source_id));
&(self.name.as_slice(), self.version.to_string(), &self.source_id));
Metadata { metadata: metadata, extra_filename: extra_filename }
Metadata { metadata: metadata, extra_filename: format!("-{}", extra_filename) }
}
}
@ -120,7 +132,7 @@ impl Show for PackageId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "{} v{}", self.name, self.version));
if self.source_id.to_str().as_slice() != central_repo {
if self.source_id.to_string().as_slice() != central_repo {
try!(write!(f, " ({})", self.source_id));
}
@ -141,7 +153,7 @@ impl<D: Decoder<Box<CargoError + Send>>>
impl<E, S: Encoder<E>> Encodable<S,E> for PackageId {
fn encode(&self, e: &mut S) -> Result<(), E> {
(self.name.clone(), self.version.to_str(), self.source_id.clone()).encode(e)
(self.name.clone(), self.version.to_string(), self.source_id.clone()).encode(e)
}
}

View File

@ -1,68 +1,136 @@
use std::collections::HashMap;
use util::graph::{Nodes,Edges};
use core::{
Dependency,
PackageId,
Summary,
Registry,
SourceId,
};
use util::{CargoResult, human, internal};
use semver;
/* TODO:
* - The correct input here is not a registry. Resolves should be performable
* on package summaries vs. the packages themselves.
*/
pub fn resolve<R: Registry>(deps: &[Dependency],
registry: &mut R) -> CargoResult<Vec<PackageId>> {
use util::{CargoResult, Graph, human, internal};
pub struct Resolve {
graph: Graph<PackageId>
}
impl Resolve {
fn new() -> Resolve {
Resolve { graph: Graph::new() }
}
pub fn iter<'a>(&'a self) -> Nodes<'a, PackageId> {
self.graph.iter()
}
pub fn deps<'a>(&'a self, pkg: &PackageId) -> Option<Edges<'a, PackageId>> {
self.graph.edges(pkg)
}
}
struct Context<'a, R> {
registry: &'a mut R,
resolve: Resolve,
// Eventually, we will have smarter logic for checking for conflicts in the resolve,
// but without the registry, conflicts should not exist in practice, so this is just
// a sanity check.
seen: HashMap<(String, SourceId), semver::Version>
}
impl<'a, R: Registry> Context<'a, R> {
fn new(registry: &'a mut R) -> Context<'a, R> {
Context {
registry: registry,
resolve: Resolve::new(),
seen: HashMap::new()
}
}
}
pub fn resolve<R: Registry>(root: &PackageId, deps: &[Dependency], registry: &mut R)
-> CargoResult<Resolve>
{
log!(5, "resolve; deps={}", deps);
let mut remaining = Vec::from_slice(deps);
let mut resolve = HashMap::<String, Summary>::new();
let mut context = Context::new(registry);
try!(resolve_deps(root, deps, &mut context));
Ok(context.resolve)
}
loop {
let curr = match remaining.pop() {
Some(curr) => curr,
None => {
let ret = resolve.values().map(|summary| {
summary.get_package_id().clone()
}).collect();
log!(5, "resolve complete; ret={}", ret);
return Ok(ret);
fn resolve_deps<'a, R: Registry>(parent: &PackageId,
deps: &[Dependency],
ctx: &mut Context<'a, R>)
-> CargoResult<()>
{
if deps.is_empty() {
return Ok(());
}
for dep in deps.iter() {
let pkgs = try!(ctx.registry.query(dep));
if pkgs.is_empty() {
return Err(human(format!("No package named {} found", dep)));
}
if pkgs.len() > 1 {
return Err(internal(format!("At the moment, Cargo only supports a \
single source for a particular package name ({}).", dep)));
}
let summary = pkgs.get(0).clone();
let name = summary.get_name().to_string();
let source_id = summary.get_source_id().clone();
let version = summary.get_version().clone();
ctx.resolve.graph.link(parent.clone(), summary.get_package_id().clone());
let found = {
let found = ctx.seen.find(&(name.clone(), source_id.clone()));
if found.is_some() {
if found == Some(&version) { continue; }
return Err(human(format!("Cargo found multiple copies of {} in {}. This \
is not currently supported",
summary.get_name(), summary.get_source_id())));
} else {
false
}
};
let opts = try!(registry.query(&curr));
if opts.len() == 0 {
return Err(human(format!("No package named {} found", curr.get_name())));
if !found {
ctx.seen.insert((name, source_id), version);
}
if opts.len() > 1 {
return Err(internal(format!("At the moment, Cargo only supports a \
single source for a particular package name ({}).", curr.get_name())));
}
ctx.resolve.graph.add(summary.get_package_id().clone(), []);
let pkg = opts.get(0).clone();
resolve.insert(pkg.get_name().to_str(), pkg.clone());
let deps: Vec<Dependency> = summary.get_dependencies().iter()
.filter(|d| d.is_transitive())
.map(|d| d.clone())
.collect();
for dep in pkg.get_dependencies().iter() {
if !dep.is_transitive() { continue; }
if !resolve.contains_key_equiv(&dep.get_name()) {
remaining.push(dep.clone());
}
}
try!(resolve_deps(summary.get_package_id(), deps.as_slice(), ctx));
}
Ok(())
}
#[cfg(test)]
mod test {
use hamcrest::{assert_that, equal_to, contains};
use core::source::{SourceId, RegistryKind, Location, Remote};
use core::{Dependency, PackageId, Summary};
use super::resolve;
use core::source::{SourceId, RegistryKind, GitKind, Location, Remote};
use core::{Dependency, PackageId, Summary, Registry};
use util::CargoResult;
fn resolve<R: Registry>(pkg: &PackageId, deps: &[Dependency], registry: &mut R)
-> CargoResult<Vec<PackageId>>
{
Ok(try!(super::resolve(pkg, deps, registry)).iter().map(|p| p.clone()).collect())
}
trait ToDep {
fn to_dep(self) -> Dependency;
@ -102,8 +170,23 @@ mod test {
}
fn pkg(name: &str) -> Summary {
Summary::new(&PackageId::new(name, "1.0.0", &registry_loc()).unwrap(),
&[])
Summary::new(&pkg_id(name), &[])
}
fn pkg_id(name: &str) -> PackageId {
PackageId::new(name, "1.0.0", &registry_loc()).unwrap()
}
fn pkg_id_loc(name: &str, loc: &str) -> PackageId {
let remote = Location::parse(loc);
let source_id = SourceId::new(GitKind("master".to_string()),
remote.unwrap());
PackageId::new(name, "1.0.0", &source_id).unwrap()
}
fn pkg_loc(name: &str, loc: &str) -> Summary {
Summary::new(&pkg_id_loc(name, loc), &[])
}
fn dep(name: &str) -> Dependency {
@ -112,6 +195,12 @@ mod test {
Dependency::parse(name, Some("1.0.0"), &source_id).unwrap()
}
fn dep_loc(name: &str, location: &str) -> Dependency {
let url = from_str(location).unwrap();
let source_id = SourceId::new(GitKind("master".to_string()), Remote(url));
Dependency::parse(name, Some("1.0.0"), &source_id).unwrap()
}
fn registry(pkgs: Vec<Summary>) -> Vec<Summary> {
pkgs
}
@ -122,9 +211,14 @@ mod test {
.collect()
}
fn loc_names(names: &[(&'static str, &'static str)]) -> Vec<PackageId> {
names.iter()
.map(|&(name, loc)| pkg_id_loc(name, loc)).collect()
}
#[test]
pub fn test_resolving_empty_dependency_list() {
let res = resolve([], &mut registry(vec!())).unwrap();
let res = resolve(&pkg_id("root"), [], &mut registry(vec!())).unwrap();
assert_that(&res, equal_to(&names([])));
}
@ -132,41 +226,60 @@ mod test {
#[test]
pub fn test_resolving_only_package() {
let mut reg = registry(vec!(pkg("foo")));
let res = resolve([dep("foo")], &mut reg);
let res = resolve(&pkg_id("root"), [dep("foo")], &mut reg);
assert_that(&res.unwrap(), equal_to(&names(["foo"])));
assert_that(&res.unwrap(), contains(names(["root", "foo"])).exactly());
}
#[test]
pub fn test_resolving_one_dep() {
let mut reg = registry(vec!(pkg("foo"), pkg("bar")));
let res = resolve([dep("foo")], &mut reg);
let res = resolve(&pkg_id("root"), [dep("foo")], &mut reg);
assert_that(&res.unwrap(), equal_to(&names(["foo"])));
assert_that(&res.unwrap(), contains(names(["root", "foo"])).exactly());
}
#[test]
pub fn test_resolving_multiple_deps() {
let mut reg = registry(vec!(pkg!("foo"), pkg!("bar"), pkg!("baz")));
let res = resolve([dep("foo"), dep("baz")], &mut reg).unwrap();
let res = resolve(&pkg_id("root"), [dep("foo"), dep("baz")], &mut reg).unwrap();
assert_that(&res, contains(names(["foo", "baz"])).exactly());
assert_that(&res, contains(names(["root", "foo", "baz"])).exactly());
}
#[test]
pub fn test_resolving_transitive_deps() {
let mut reg = registry(vec!(pkg!("foo"), pkg!("bar" => "foo")));
let res = resolve([dep("bar")], &mut reg).unwrap();
let res = resolve(&pkg_id("root"), [dep("bar")], &mut reg).unwrap();
assert_that(&res, contains(names(["foo", "bar"])));
assert_that(&res, contains(names(["root", "foo", "bar"])));
}
#[test]
pub fn test_resolving_common_transitive_deps() {
let mut reg = registry(vec!(pkg!("foo" => "bar"), pkg!("bar")));
let res = resolve([dep("foo"), dep("bar")], &mut reg).unwrap();
let res = resolve(&pkg_id("root"), [dep("foo"), dep("bar")], &mut reg).unwrap();
assert_that(&res, contains(names(["foo", "bar"])));
assert_that(&res, contains(names(["root", "foo", "bar"])));
}
#[test]
pub fn test_resolving_with_same_name() {
let list = vec!(pkg_loc("foo", "http://first.example.com"),
pkg_loc("foo", "http://second.example.com"));
let mut reg = registry(list);
let res = resolve(&pkg_id("root"),
[dep_loc("foo", "http://first.example.com"),
dep_loc("foo", "http://second.example.com")],
&mut reg);
let mut names = loc_names([("foo", "http://first.example.com"),
("foo", "http://second.example.com")]);
names.push(pkg_id("root"));
assert_that(&res.unwrap(), contains(names).exactly());
}
#[test]
@ -178,8 +291,8 @@ mod test {
pkg!("bat")
));
let res = resolve([dep("foo"), dep("baz").as_dev()], &mut reg).unwrap();
let res = resolve(&pkg_id("root"), [dep("foo"), dep("baz").as_dev()], &mut reg).unwrap();
assert_that(&res, contains(names(["foo", "bar", "baz"])));
assert_that(&res, contains(names(["root", "foo", "bar", "baz"])));
}
}

View File

@ -42,7 +42,7 @@ impl MultiShell {
&mut self.err
}
pub fn say<T: ToStr>(&mut self, message: T, color: Color) -> IoResult<()> {
pub fn say<T: ToString>(&mut self, message: T, color: Color) -> IoResult<()> {
self.out().say(message, color)
}
@ -60,11 +60,11 @@ impl MultiShell {
Ok(())
}
pub fn error<T: ToStr>(&mut self, message: T) -> IoResult<()> {
pub fn error<T: ToString>(&mut self, message: T) -> IoResult<()> {
self.err().say(message, RED)
}
pub fn warn<T: ToStr>(&mut self, message: T) -> IoResult<()> {
pub fn warn<T: ToString>(&mut self, message: T) -> IoResult<()> {
self.err().say(message, YELLOW)
}
}
@ -96,10 +96,10 @@ impl Shell {
Ok(())
}
pub fn say<T: ToStr>(&mut self, message: T, color: Color) -> IoResult<()> {
pub fn say<T: ToString>(&mut self, message: T, color: Color) -> IoResult<()> {
try!(self.reset());
if color != BLACK { try!(self.fg(color)); }
try!(self.write_line(message.to_str().as_slice()));
try!(self.write_line(message.to_string().as_slice()));
try!(self.reset());
try!(self.flush());
Ok(())

View File

@ -73,7 +73,7 @@ impl<E, D: Decoder<E>> Decodable<D, E> for Location {
impl<E, S: Encoder<E>> Encodable<S, E> for Location {
fn encode(&self, e: &mut S) -> Result<(), E> {
self.to_str().encode(e)
self.to_string().encode(e)
}
}
@ -137,8 +137,8 @@ impl PartialEq for SourceId {
match (&self.kind, &other.kind, &self.location, &other.location) {
(&GitKind(..), &GitKind(..),
&Remote(ref u1), &Remote(ref u2)) => {
git::canonicalize_url(u1.to_str().as_slice()) ==
git::canonicalize_url(u2.to_str().as_slice())
git::canonicalize_url(u1.to_string().as_slice()) ==
git::canonicalize_url(u2.to_string().as_slice())
}
_ => false,
}
@ -156,7 +156,7 @@ impl SourceId {
}
pub fn for_git(url: &Url, reference: &str) -> SourceId {
SourceId::new(GitKind(reference.to_str()), Remote(url.clone()))
SourceId::new(GitKind(reference.to_string()), Remote(url.clone()))
}
pub fn for_central() -> SourceId {

View File

@ -48,7 +48,7 @@ pub trait SummaryVec {
impl SummaryVec for Vec<Summary> {
// TODO: Move to Registry
fn names(&self) -> Vec<String> {
self.iter().map(|summary| summary.get_name().to_str()).collect()
self.iter().map(|summary| summary.get_name().to_string()).collect()
}
}

View File

@ -487,14 +487,14 @@ mod test {
pub fn test_parsing_exact() {
let r = req("1.0.0");
assert!(r.to_str() == "= 1.0.0".to_str());
assert!(r.to_string() == "= 1.0.0".to_string());
assert_match(&r, ["1.0.0"]);
assert_not_match(&r, ["1.0.1", "0.9.9", "0.10.0", "0.1.0"]);
let r = req("0.9.0");
assert!(r.to_str() == "= 0.9.0".to_str());
assert!(r.to_string() == "= 0.9.0".to_string());
assert_match(&r, ["0.9.0"]);
assert_not_match(&r, ["0.9.1", "1.9.0", "0.0.9"]);
@ -504,7 +504,7 @@ mod test {
pub fn test_parsing_greater_than() {
let r = req(">= 1.0.0");
assert!(r.to_str() == ">= 1.0.0".to_str());
assert!(r.to_string() == ">= 1.0.0".to_string());
assert_match(&r, ["1.0.0"]);
}

View File

@ -2,9 +2,11 @@
#![crate_type="rlib"]
#![feature(macro_rules, phase)]
#![feature(default_type_params)]
extern crate debug;
extern crate term;
extern crate collections;
extern crate url;
extern crate serialize;
extern crate semver;
@ -204,7 +206,7 @@ pub fn handle_error(err: CliError, shell: &mut MultiShell) {
if unknown {
let _ = shell.error("An unknown error occurred");
} else {
let _ = shell.error(error.to_str());
let _ = shell.error(error.to_string());
}
if error.cause().is_some() {
@ -246,7 +248,7 @@ fn global_flags() -> CliResult<GlobalFlags> {
fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
let mut reader = io::stdin();
let input = try!(reader.read_to_str().map_err(|_| {
let input = try!(reader.read_to_string().map_err(|_| {
CliError::new("Standard in did not exist or was not UTF-8", 1)
}));

View File

@ -24,7 +24,7 @@
use std::os;
use util::config::{Config, ConfigValue};
use core::{MultiShell, Source, SourceId, PackageSet, Target, resolver};
use core::{MultiShell, Source, SourceId, PackageSet, Target, PackageId, resolver};
use core::registry::PackageRegistry;
use ops;
use sources::{PathSource};
@ -57,18 +57,22 @@ pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<()>
let override_ids = try!(source_ids_from_config());
let source_ids = package.get_source_ids();
let packages = {
let (packages, resolve) = {
let mut config = try!(Config::new(shell, update, jobs));
let mut registry =
try!(PackageRegistry::new(source_ids, override_ids, &mut config));
let resolved =
try!(resolver::resolve(package.get_dependencies(), &mut registry));
let resolved = try!(resolver::resolve(package.get_package_id(),
package.get_dependencies(),
&mut registry));
try!(registry.get(resolved.as_slice()).wrap({
let req: Vec<PackageId> = resolved.iter().map(|r| r.clone()).collect();
let packages = try!(registry.get(req.as_slice()).wrap({
human("Unable to get packages from source")
}))
}));
(packages, resolved)
};
debug!("packages={}", packages);
@ -78,8 +82,9 @@ pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult<()>
}).collect::<Vec<&Target>>();
let mut config = try!(Config::new(shell, update, jobs));
try!(ops::compile_targets(targets.as_slice(), &package,
&PackageSet::new(packages.as_slice()), &mut config));
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
&PackageSet::new(packages.as_slice()), &resolve, &mut config));
Ok(())
}

View File

@ -6,9 +6,9 @@ use std::os::args;
use std::str;
use term::color::YELLOW;
use core::{Package, PackageSet, Target};
use core::{Package, PackageSet, Target, Resolve};
use util;
use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError};
use util::{CargoResult, ChainError, ProcessBuilder, CargoError, internal, human};
use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness};
type Args = Vec<String>;
@ -18,7 +18,10 @@ struct Context<'a, 'b> {
deps_dir: &'a Path,
primary: bool,
rustc_version: &'a str,
config: &'b mut Config<'b>
resolve: &'a Resolve,
package_set: &'a PackageSet,
config: &'b mut Config<'b>,
dylib: (String, String)
}
type Job = proc():Send -> CargoResult<()>;
@ -41,9 +44,10 @@ fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
curr.unwrap()
}
pub fn compile_targets<'a>(targets: &[&Target], pkg: &Package, deps: &PackageSet,
config: &'a mut Config<'a>) -> CargoResult<()> {
pub fn compile_targets<'a>(env: &str, targets: &[&Target], pkg: &Package,
deps: &PackageSet, resolve: &'a Resolve,
config: &'a mut Config<'a>) -> CargoResult<()>
{
if targets.is_empty() {
return Ok(());
}
@ -68,12 +72,27 @@ pub fn compile_targets<'a>(targets: &[&Target], pkg: &Package, deps: &PackageSet
internal(format!("Couldn't create the directory for dependencies for {} at {}",
pkg.get_name(), deps_target_dir.display()))));
let output = try!(util::process("rustc")
.arg("-")
.arg("--crate-name").arg("-")
.arg("--crate-type").arg("dylib")
.arg("--print-file-name")
.exec_with_output());
let output = str::from_utf8(output.output.as_slice()).unwrap();
let parts: Vec<&str> = output.slice_to(output.len() - 1).split('-').collect();
assert!(parts.len() == 2, "rustc --print-file-name output has changed");
let mut cx = Context {
dest: &deps_target_dir,
deps_dir: &deps_target_dir,
primary: false,
rustc_version: rustc_version.as_slice(),
config: config
resolve: resolve,
package_set: deps,
config: config,
dylib: (parts.get(0).to_string(), parts.get(1).to_string())
};
// Build up a list of pending jobs, each of which represent compiling a
@ -82,13 +101,17 @@ pub fn compile_targets<'a>(targets: &[&Target], pkg: &Package, deps: &PackageSet
// everything in order with proper parallelism.
let mut jobs = Vec::new();
for dep in deps.iter() {
if dep == pkg { continue; }
// Only compile lib targets for dependencies
let targets = dep.get_targets().iter().filter(|target| {
target.is_lib() && target.get_profile().is_compile()
target.is_lib() && match env {
"test" => target.get_profile().is_compile(),
_ => target.get_profile().get_env() == env,
}
}).collect::<Vec<&Target>>();
jobs.push((dep,
try!(compile(targets.as_slice(), dep, &mut cx))));
jobs.push((dep, try!(compile(targets.as_slice(), dep, &mut cx))));
}
cx.primary = true;
@ -101,7 +124,7 @@ pub fn compile_targets<'a>(targets: &[&Target], pkg: &Package, deps: &PackageSet
fn compile(targets: &[&Target], pkg: &Package,
cx: &mut Context) -> CargoResult<(Freshness, Job)> {
debug!("compile_pkg; pkg={}; targets={}", pkg, pkg.get_targets());
debug!("compile_pkg; pkg={}; targets={}", pkg, targets);
if targets.is_empty() {
return Ok((Fresh, proc() Ok(())))
@ -135,7 +158,7 @@ fn compile(targets: &[&Target], pkg: &Package,
// After the custom command has run, execute rustc for all targets of our
// package.
for &target in targets.iter() {
cmds.push(rustc(&pkg.get_root(), target, cx));
cmds.push(rustc(pkg, target, cx));
}
cmds.push(proc() {
@ -169,7 +192,7 @@ fn is_fresh(dep: &Package, loc: &Path,
Err(..) => return Ok((false, new_fingerprint)),
};
let old_fingerprint = try!(file.read_to_str());
let old_fingerprint = try!(file.read_to_string());
log!(5, "old fingerprint: {}", old_fingerprint);
log!(5, "new fingerprint: {}", new_fingerprint);
@ -207,37 +230,42 @@ fn compile_custom(pkg: &Package, cmd: &str,
proc() p.exec_with_output().map(|_| ()).map_err(|e| e.mark_human())
}
fn rustc(root: &Path, target: &Target, cx: &mut Context) -> Job {
fn rustc(package: &Package, target: &Target, cx: &mut Context) -> Job {
let crate_types = target.rustc_crate_types();
let root = package.get_root();
log!(5, "root={}; target={}; crate_types={}; dest={}; deps={}; verbose={}",
root.display(), target, crate_types, cx.dest.display(),
cx.deps_dir.display(), cx.primary);
let primary = cx.primary;
let rustc = prepare_rustc(root, target, crate_types, cx);
let rustc = prepare_rustc(package, target, crate_types, cx);
log!(5, "command={}", rustc);
let _ = cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_str()));
let _ = cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_string()));
proc() {
if primary {
rustc.exec().map_err(|err| human(err.to_str()))
log!(5, "executing primary");
rustc.exec().map_err(|err| human(err.to_string()))
} else {
log!(5, "executing deps");
rustc.exec_with_output().and(Ok(())).map_err(|err| {
human(err.to_str())
human(err.to_string())
})
}
}
}
fn prepare_rustc(root: &Path, target: &Target, crate_types: Vec<&str>,
cx: &Context) -> ProcessBuilder {
fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
cx: &Context) -> ProcessBuilder
{
let root = package.get_root();
let mut args = Vec::new();
build_base_args(&mut args, target, crate_types, cx);
build_deps_args(&mut args, cx);
build_deps_args(&mut args, package, cx);
util::process("rustc")
.cwd(root.clone())
@ -253,57 +281,96 @@ fn build_base_args(into: &mut Args,
let metadata = target.get_metadata();
// TODO: Handle errors in converting paths into args
into.push(target.get_src_path().display().to_str());
into.push(target.get_src_path().display().to_string());
into.push("--crate-name".to_str());
into.push(target.get_name().to_str());
into.push("--crate-name".to_string());
into.push(target.get_name().to_string());
for crate_type in crate_types.iter() {
into.push("--crate-type".to_str());
into.push(crate_type.to_str());
into.push("--crate-type".to_string());
into.push(crate_type.to_string());
}
let out = cx.dest.clone();
let profile = target.get_profile();
if profile.get_opt_level() != 0 {
into.push("--opt-level".to_str());
into.push(profile.get_opt_level().to_str());
into.push("--opt-level".to_string());
into.push(profile.get_opt_level().to_string());
}
if profile.get_debug() {
into.push("-g".to_str());
}
// Right now -g is a little buggy, so we're not passing -g just yet
// if profile.get_debug() {
// into.push("-g".to_string());
// }
if profile.is_test() {
into.push("--test".to_str());
into.push("--test".to_string());
}
match metadata {
Some(m) => {
into.push("-C".to_str());
into.push("-C".to_string());
into.push(format!("metadata={}", m.metadata));
into.push("-C".to_str());
into.push("-C".to_string());
into.push(format!("extra-filename={}", m.extra_filename));
}
None => {}
}
if target.is_lib() {
into.push("--out-dir".to_str());
into.push(out.display().to_str());
into.push("--out-dir".to_string());
into.push(out.display().to_string());
} else {
into.push("-o".to_str());
into.push(out.join(target.get_name()).display().to_str());
into.push("-o".to_string());
into.push(out.join(target.get_name()).display().to_string());
}
}
fn build_deps_args(dst: &mut Args, cx: &Context) {
dst.push("-L".to_str());
dst.push(cx.dest.display().to_str());
dst.push("-L".to_str());
dst.push(cx.deps_dir.display().to_str());
fn build_deps_args(dst: &mut Args, package: &Package, cx: &Context) {
dst.push("-L".to_string());
dst.push(cx.dest.display().to_string());
dst.push("-L".to_string());
dst.push(cx.deps_dir.display().to_string());
for target in dep_targets(package, cx).iter() {
dst.push("--extern".to_string());
dst.push(format!("{}={}/{}",
target.get_name(),
cx.deps_dir.display(),
target_filename(target, cx)));
}
}
fn target_filename(target: &Target, cx: &Context) -> String {
let stem = target.file_stem();
if target.is_dylib() {
let (ref prefix, ref suffix) = cx.dylib;
format!("{}{}{}", prefix, stem, suffix)
} else if target.is_rlib() {
format!("lib{}.rlib", stem)
} else {
unreachable!()
}
}
fn dep_targets(pkg: &Package, cx: &Context) -> Vec<Target> {
match cx.resolve.deps(pkg.get_package_id()) {
None => vec!(),
Some(deps) => deps
.map(|pkg_id| {
cx.package_set.iter()
.find(|pkg| pkg_id == pkg.get_package_id())
.expect("Should have found package")
})
.filter_map(|pkg| {
pkg.get_targets().iter().find(|&t| t.is_lib() && t.get_profile().is_compile())
})
.map(|t| t.clone())
.collect()
}
}
/// Execute all jobs necessary to build the dependency graph.

View File

@ -65,11 +65,11 @@ fn ident(location: &Location) -> String {
let ident = match *location {
Local(ref path) => {
let last = path.components().last().unwrap();
str::from_utf8(last).unwrap().to_str()
str::from_utf8(last).unwrap().to_string()
}
Remote(ref url) => {
let path = canonicalize_url(url.path.path.as_slice());
path.as_slice().split('/').last().unwrap().to_str()
path.as_slice().split('/').last().unwrap().to_string()
}
};
@ -79,7 +79,7 @@ fn ident(location: &Location) -> String {
ident
};
let location = canonicalize_url(location.to_str().as_slice());
let location = canonicalize_url(location.to_string().as_slice());
format!("{}-{}", ident, to_hex(hasher.hash(&location.as_slice())))
}

View File

@ -19,7 +19,7 @@ impl GitReference {
if string.as_slice() == "master" {
Master
} else {
Other(string.as_slice().to_str())
Other(string.as_slice().to_string())
}
}
}
@ -79,7 +79,7 @@ struct EncodableGitRemote {
impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
fn encode(&self, s: &mut S) -> Result<(), E> {
EncodableGitRemote {
location: self.location.to_str()
location: self.location.to_string()
}.encode(s)
}
}
@ -102,7 +102,7 @@ impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
fn encode(&self, s: &mut S) -> Result<(), E> {
EncodableGitDatabase {
remote: self.remote.clone(),
path: self.path.display().to_str()
path: self.path.display().to_string()
}.encode(s)
}
}
@ -129,9 +129,9 @@ impl<E, S: Encoder<E>> Encodable<S, E> for GitCheckout {
fn encode(&self, s: &mut S) -> Result<(), E> {
EncodableGitCheckout {
database: self.database.clone(),
location: self.location.display().to_str(),
reference: self.reference.to_str(),
revision: self.revision.to_str()
location: self.location.display().to_string(),
reference: self.reference.to_string(),
revision: self.revision.to_string()
}.encode(s)
}
}
@ -182,8 +182,8 @@ impl GitRemote {
fn fetch_location(&self) -> String {
match self.location {
Local(ref p) => p.display().to_str(),
Remote(ref u) => u.to_str(),
Local(ref p) => p.display().to_string(),
Remote(ref u) => u.to_string(),
}
}
}
@ -308,10 +308,10 @@ fn git_output(path: &Path, str: String) -> CargoResult<String> {
.chain_error(||
human(format!("Executing `git {}` failed", str))));
Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_str())
Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_string())
}
fn to_str(vec: &[u8]) -> String {
str::from_utf8_lossy(vec).to_str()
str::from_utf8_lossy(vec).to_string()
}

View File

@ -102,7 +102,7 @@ impl Source for PathSource {
let loc = pkg.get_manifest_path().dir_path();
max = cmp::max(max, try!(walk(&loc, true)));
}
return Ok(max.to_str());
return Ok(max.to_string());
fn walk(path: &Path, is_root: bool) -> CargoResult<u64> {
if !path.is_dir() {

View File

@ -118,7 +118,7 @@ impl<E, S: Encoder<E>> Encodable<S, E> for ConfigValue {
impl fmt::Show for ConfigValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let paths: Vec<String> = self.path.iter().map(|p| {
p.display().to_str()
p.display().to_string()
}).collect();
write!(f, "{} (from {})", self.value, paths)
}
@ -184,16 +184,16 @@ fn walk_tree(pwd: &Path,
}
fn extract_config(mut file: io::fs::File, key: &str) -> CargoResult<ConfigValue> {
let contents = try!(file.read_to_str());
let contents = try!(file.read_to_string());
let toml = try!(cargo_toml::parse(contents.as_slice(),
file.path().filename_display()
.to_str().as_slice()));
.to_string().as_slice()));
let val = try!(toml.find_equiv(&key).require(|| internal("")));
let v = match *val {
toml::String(ref val) => String(val.clone()),
toml::Array(ref val) => {
List(val.iter().map(|s: &toml::Value| s.to_str()).collect())
List(val.iter().map(|s: &toml::Value| s.to_string()).collect())
}
_ => return Err(internal(""))
};
@ -204,8 +204,8 @@ fn extract_config(mut file: io::fs::File, key: &str) -> CargoResult<ConfigValue>
fn extract_all_configs(mut file: io::fs::File,
map: &mut HashMap<String, ConfigValue>) -> CargoResult<()> {
let path = file.path().clone();
let contents = try!(file.read_to_str());
let file = path.filename_display().to_str();
let contents = try!(file.read_to_string());
let file = path.filename_display().to_string();
let table = try!(cargo_toml::parse(contents.as_slice(),
file.as_slice()).chain_error(|| {
internal(format!("could not parse Toml manifest; path={}",

View File

@ -61,7 +61,7 @@ impl<T> DependencyQueue<T> {
///
/// Only registered packages will be returned from dequeue().
pub fn register(&mut self, pkg: &Package) {
self.reverse_dep_map.insert(pkg.get_name().to_str(), HashSet::new());
self.reverse_dep_map.insert(pkg.get_name().to_string(), HashSet::new());
}
/// Adds a new package to this dependency queue.
@ -70,10 +70,10 @@ impl<T> DependencyQueue<T> {
/// be added to the dependency queue.
pub fn enqueue(&mut self, pkg: &Package, fresh: Freshness, data: T) {
// ignore self-deps
if self.pkgs.contains_key(&pkg.get_name().to_str()) { return }
if self.pkgs.contains_key(&pkg.get_name().to_string()) { return }
if fresh == Dirty {
self.dirty.insert(pkg.get_name().to_str());
self.dirty.insert(pkg.get_name().to_string());
}
let mut my_dependencies = HashSet::new();
@ -84,12 +84,12 @@ impl<T> DependencyQueue<T> {
continue
}
let name = dep.get_name().to_str();
let name = dep.get_name().to_string();
assert!(my_dependencies.insert(name.clone()));
let rev = self.reverse_dep_map.find_or_insert(name, HashSet::new());
assert!(rev.insert(pkg.get_name().to_str()));
assert!(rev.insert(pkg.get_name().to_string()));
}
assert!(self.pkgs.insert(pkg.get_name().to_str(),
assert!(self.pkgs.insert(pkg.get_name().to_string(),
(my_dependencies, data)));
}
@ -100,7 +100,7 @@ impl<T> DependencyQueue<T> {
pub fn dequeue(&mut self) -> Option<(String, Freshness, T)> {
let pkg = match self.pkgs.iter()
.find(|&(_, &(ref deps, _))| deps.len() == 0)
.map(|(ref name, _)| name.to_str()) {
.map(|(ref name, _)| name.to_string()) {
Some(pkg) => pkg,
None => return None
};

View File

@ -120,20 +120,20 @@ impl<T, E: CargoError + Send> ChainError<T> for Result<T, E> {
}
impl CargoError for IoError {
fn description(&self) -> String { self.to_str() }
fn description(&self) -> String { self.to_string() }
}
from_error!(IoError)
impl CargoError for TomlError {
fn description(&self) -> String { self.to_str() }
fn description(&self) -> String { self.to_string() }
}
from_error!(TomlError)
impl CargoError for FormatError {
fn description(&self) -> String {
"formatting failed".to_str()
"formatting failed".to_string()
}
}
@ -152,8 +152,8 @@ from_error!(ProcessError)
impl Show for ProcessError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let exit = match self.exit {
Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
None => "never executed".to_str()
Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_string(),
None => "never executed".to_string()
};
try!(write!(f, "{} (status={})", self.msg, exit));
match self.output {
@ -178,7 +178,7 @@ impl Show for ProcessError {
}
impl CargoError for ProcessError {
fn description(&self) -> String { self.to_str() }
fn description(&self) -> String { self.to_string() }
fn detail(&self) -> Option<String> {
self.detail.clone()
@ -248,7 +248,7 @@ pub struct CliError {
impl CargoError for CliError {
fn description(&self) -> String {
self.error.to_str()
self.error.to_string()
}
}
@ -256,7 +256,7 @@ from_error!(CliError)
impl CliError {
pub fn new<S: Str>(error: S, code: uint) -> CliError {
let error = human(error.as_slice().to_str());
let error = human(error.as_slice().to_string());
CliError::from_boxed(error, code)
}
@ -276,7 +276,7 @@ pub fn process_error<S: Str>(msg: S,
status: Option<&ProcessExit>,
output: Option<&ProcessOutput>) -> ProcessError {
ProcessError {
msg: msg.as_slice().to_str(),
msg: msg.as_slice().to_string(),
exit: status.map(|o| o.clone()),
output: output.map(|o| o.clone()),
detail: None,
@ -287,8 +287,8 @@ pub fn process_error<S: Str>(msg: S,
pub fn internal_error<S1: Str, S2: Str>(error: S1,
detail: S2) -> Box<CargoError + Send> {
box ConcreteCargoError {
description: error.as_slice().to_str(),
detail: Some(detail.as_slice().to_str()),
description: error.as_slice().to_string(),
detail: Some(detail.as_slice().to_string()),
cause: None,
is_human: false
} as Box<CargoError + Send>
@ -296,7 +296,7 @@ pub fn internal_error<S1: Str, S2: Str>(error: S1,
pub fn internal<S: Show>(error: S) -> Box<CargoError + Send> {
box ConcreteCargoError {
description: error.to_str(),
description: error.to_string(),
detail: None,
cause: None,
is_human: false
@ -305,7 +305,7 @@ pub fn internal<S: Show>(error: S) -> Box<CargoError + Send> {
pub fn human<S: Show>(error: S) -> Box<CargoError + Send> {
box ConcreteCargoError {
description: error.to_str(),
description: error.to_string(),
detail: None,
cause: None,
is_human: true

View File

@ -1,8 +1,9 @@
use std::hash::Hash;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::collections::hashmap::{Keys, SetItems};
pub struct Graph<N> {
nodes: HashMap<N, Vec<N>>
nodes: HashMap<N, HashSet<N>>
}
enum Mark {
@ -10,13 +11,30 @@ enum Mark {
Done
}
pub type Nodes<'a, N> = Keys<'a, N, HashSet<N>>;
pub type Edges<'a, N> = SetItems<'a, N>;
impl<N: Eq + Hash + Clone> Graph<N> {
pub fn new() -> Graph<N> {
Graph { nodes: HashMap::new() }
}
pub fn add(&mut self, node: N, children: &[N]) {
self.nodes.insert(node, children.to_owned());
self.nodes.insert(node, children.iter().map(|n| n.clone()).collect());
}
pub fn link(&mut self, node: N, child: N) {
self.nodes
.find_or_insert_with(node, |_| HashSet::new())
.insert(child);
}
pub fn get_nodes<'a>(&'a self) -> &'a HashMap<N, HashSet<N>> {
&self.nodes
}
pub fn edges<'a>(&'a self, node: &N) -> Option<Edges<'a, N>> {
self.nodes.find(node).map(|set| set.iter())
}
pub fn sort(&self) -> Option<Vec<N>> {
@ -44,4 +62,8 @@ impl<N: Eq + Hash + Clone> Graph<N> {
dst.push(node.clone());
marks.insert(node.clone(), Done);
}
pub fn iter<'a>(&'a self) -> Nodes<'a, N> {
self.nodes.keys()
}
}

View File

@ -8,6 +8,7 @@ pub use self::paths::realpath;
pub use self::hex::{to_hex, short_hash};
pub use self::pool::TaskPool;
pub use self::dependency_queue::{DependencyQueue, Fresh, Dirty, Freshness};
pub use self::graph::Graph;
pub mod graph;
pub mod process_builder;

View File

@ -36,12 +36,12 @@ static PATH_SEP : &'static str = ";";
impl ProcessBuilder {
pub fn arg<T: Str>(mut self, arg: T) -> ProcessBuilder {
self.args.push(arg.as_slice().to_str());
self.args.push(arg.as_slice().to_string());
self
}
pub fn args<T: Str>(mut self, arguments: &[T]) -> ProcessBuilder {
self.args = arguments.iter().map(|a| a.as_slice().to_str()).collect();
self.args = arguments.iter().map(|a| a.as_slice().to_string()).collect();
self
}
@ -51,7 +51,7 @@ impl ProcessBuilder {
pub fn extra_path(mut self, path: Path) -> ProcessBuilder {
// For now, just convert to a string, but we should do something better
self.path.unshift(path.display().to_str());
self.path.unshift(path.display().to_string());
self
}
@ -63,10 +63,10 @@ impl ProcessBuilder {
pub fn env(mut self, key: &str, val: Option<&str>) -> ProcessBuilder {
match val {
Some(v) => {
self.env.insert(key.to_str(), v.to_str());
self.env.insert(key.to_string(), v.to_string());
},
None => {
self.env.remove(&key.to_str());
self.env.remove(&key.to_string());
}
}
@ -139,7 +139,7 @@ impl ProcessBuilder {
}
match self.build_path() {
Some(path) => ret.push(("PATH".to_str(), path)),
Some(path) => ret.push(("PATH".to_string(), path)),
_ => ()
}

View File

@ -198,9 +198,9 @@ struct Context<'a> {
fn inferred_lib_target(name: &str, layout: &Layout) -> Option<Vec<TomlTarget>> {
layout.lib.as_ref().map(|lib| {
vec![TomlTarget {
name: name.to_str(),
name: name.to_string(),
crate_type: None,
path: Some(lib.display().to_str()),
path: Some(lib.display().to_string()),
test: None
}]
})
@ -209,16 +209,16 @@ fn inferred_lib_target(name: &str, layout: &Layout) -> Option<Vec<TomlTarget>> {
fn inferred_bin_targets(name: &str, layout: &Layout) -> Option<Vec<TomlTarget>> {
Some(layout.bins.iter().filter_map(|bin| {
let name = if bin.as_str() == Some("src/main.rs") {
Some(name.to_str())
Some(name.to_string())
} else {
bin.filestem_str().map(|f| f.to_str())
bin.filestem_str().map(|f| f.to_string())
};
name.map(|name| {
TomlTarget {
name: name,
crate_type: None,
path: Some(bin.display().to_str()),
path: Some(bin.display().to_string()),
test: None
}
})
@ -252,7 +252,7 @@ impl TomlManifest {
TomlTarget {
name: t.name.clone(),
crate_type: t.crate_type.clone(),
path: layout.lib.as_ref().map(|p| p.display().to_str()),
path: layout.lib.as_ref().map(|p| p.display().to_string()),
test: t.test
}
} else {
@ -271,7 +271,7 @@ impl TomlManifest {
TomlTarget {
name: t.name.clone(),
crate_type: t.crate_type.clone(),
path: bin.as_ref().map(|p| p.display().to_str()),
path: bin.as_ref().map(|p| p.display().to_string()),
test: t.test
}
} else {
@ -336,7 +336,7 @@ fn process_dependencies<'a>(cx: &mut Context<'a>, dev: bool,
let reference = details.branch.clone()
.or_else(|| details.tag.clone())
.or_else(|| details.rev.clone())
.unwrap_or_else(|| "master".to_str());
.unwrap_or_else(|| "master".to_string());
let new_source_id = match details.git {
Some(ref git) => {
@ -386,25 +386,34 @@ fn normalize(lib: Option<&[TomlLibTarget]>,
{
log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);
fn target_profiles(target: &TomlTarget) -> Vec<Profile> {
enum TestDep { Needed, NotNeeded }
fn target_profiles(target: &TomlTarget,
dep: Option<TestDep>) -> Vec<Profile> {
let mut ret = vec!(Profile::default_dev(), Profile::default_release());
match target.test {
Some(true) | None => ret.push(Profile::default_test()),
Some(false) => {}
}
match dep {
Some(Needed) => ret.push(Profile::default_test().test(false)),
_ => {}
};
}
ret
}
fn lib_targets(dst: &mut Vec<Target>, libs: &[TomlLibTarget], metadata: &Metadata) {
fn lib_targets(dst: &mut Vec<Target>, libs: &[TomlLibTarget],
dep: TestDep, metadata: &Metadata) {
let l = &libs[0];
let path = l.path.clone().unwrap_or_else(|| format!("src/{}.rs", l.name));
let crate_types = l.crate_type.clone().and_then(|kinds| {
LibKind::from_strs(kinds).ok()
}).unwrap_or_else(|| vec!(Lib));
for profile in target_profiles(l).iter() {
for profile in target_profiles(l, Some(dep)).iter() {
dst.push(Target::lib_target(l.name.as_slice(), crate_types.clone(),
&Path::new(path.as_slice()), profile,
metadata));
@ -416,7 +425,7 @@ fn normalize(lib: Option<&[TomlLibTarget]>,
for bin in bins.iter() {
let path = bin.path.clone().unwrap_or_else(|| default(bin));
for profile in target_profiles(bin).iter() {
for profile in target_profiles(bin, None).iter() {
dst.push(Target::bin_target(bin.name.as_slice(),
&Path::new(path.as_slice()),
profile));
@ -428,12 +437,12 @@ fn normalize(lib: Option<&[TomlLibTarget]>,
match (lib, bin) {
(Some(ref libs), Some(ref bins)) => {
lib_targets(&mut ret, libs.as_slice(), metadata);
lib_targets(&mut ret, libs.as_slice(), Needed, metadata);
bin_targets(&mut ret, bins.as_slice(),
|bin| format!("src/bin/{}.rs", bin.name));
},
(Some(ref libs), None) => {
lib_targets(&mut ret, libs.as_slice(), metadata);
lib_targets(&mut ret, libs.as_slice(), NotNeeded, metadata);
},
(None, Some(ref bins)) => {
bin_targets(&mut ret, bins.as_slice(),

View File

@ -30,7 +30,7 @@ struct FileBuilder {
impl FileBuilder {
pub fn new(path: Path, body: &str) -> FileBuilder {
FileBuilder { path: path, body: body.to_str() }
FileBuilder { path: path, body: body.to_string() }
}
fn mk(&self) -> Result<(), String> {
@ -86,7 +86,7 @@ pub struct ProjectBuilder {
impl ProjectBuilder {
pub fn new(name: &str, root: Path) -> ProjectBuilder {
ProjectBuilder {
name: name.to_str(),
name: name.to_string(),
root: root,
files: vec!(),
symlinks: vec!()
@ -108,7 +108,7 @@ impl ProjectBuilder {
pub fn process<T: ToCStr>(&self, program: T) -> ProcessBuilder {
process(program)
.cwd(self.root())
.env("HOME", Some(paths::home().display().to_str().as_slice()))
.env("HOME", Some(paths::home().display().to_string().as_slice()))
.extra_path(cargo_dir())
}
@ -195,7 +195,7 @@ pub fn main_file<T: Str>(println: T, deps: &[&str]) -> String {
buf.push_str(println.as_slice());
buf.push_str("); }\n");
buf.to_str()
buf.to_string()
}
trait ErrMsg<T> {
@ -238,13 +238,13 @@ struct Execs {
impl Execs {
pub fn with_stdout<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
self.expect_stdout = Some(expected.to_str());
pub fn with_stdout<S: ToString>(mut ~self, expected: S) -> Box<Execs> {
self.expect_stdout = Some(expected.to_string());
self
}
pub fn with_stderr<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
self.expect_stderr = Some(expected.to_str());
pub fn with_stderr<S: ToString>(mut ~self, expected: S) -> Box<Execs> {
self.expect_stderr = Some(expected.to_string());
self
}
@ -310,7 +310,7 @@ impl Execs {
impl ham::SelfDescribing for Execs {
fn describe(&self) -> String {
"execs".to_str()
"execs".to_string()
}
}
@ -354,13 +354,13 @@ impl<'a> ham::Matcher<&'a [u8]> for ShellWrites {
{
println!("{}", actual);
let actual = std::str::from_utf8_lossy(actual);
let actual = actual.to_str();
let actual = actual.to_string();
ham::expect(actual == self.expected, actual)
}
}
pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
box ShellWrites { expected: string.to_str() }
box ShellWrites { expected: string.to_string() }
}
pub trait ResultTest<T,E> {
@ -397,7 +397,7 @@ impl<T> Tap for T {
}
pub fn escape_path(p: &Path) -> String {
p.display().to_str().as_slice().replace("\\", "\\\\")
p.display().to_string().as_slice().replace("\\", "\\\\")
}
pub fn basic_bin_manifest(name: &str) -> String {
@ -414,6 +414,7 @@ pub fn basic_bin_manifest(name: &str) -> String {
"#, name, name)
}
pub static RUNNING: &'static str = " Running";
pub static COMPILING: &'static str = " Compiling";
pub static FRESH: &'static str = " Fresh";
pub static UPDATING: &'static str = " Updating";

View File

@ -1,9 +1,10 @@
use std::io::fs;
use std::os;
use std::path;
use std::str;
use support::{ResultTest, project, execs, main_file, escape_path, basic_bin_manifest};
use support::COMPILING;
use support::{COMPILING, RUNNING};
use hamcrest::{assert_that, existing_file};
use cargo;
use cargo::util::{process, realpath};
@ -92,7 +93,7 @@ test!(cargo_compile_with_invalid_code {
{filename}:1 invalid rust code!
^~~~~~~
Could not execute process \
`rustc {filename} --crate-name foo --crate-type bin -g -o {} -L {} -L {}` (status=101)\n",
`rustc {filename} --crate-name foo --crate-type bin -o {} -L {} -L {}` (status=101)\n",
target.join("foo").display(),
target.display(),
target.join("deps").display(),
@ -152,7 +153,7 @@ test!(cargo_compile_with_warnings_in_a_dep_package {
"#)
.file("bar/src/bar.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
fn dead() {}
@ -229,7 +230,7 @@ test!(cargo_compile_with_nested_deps_inferred {
"#)
.file("baz/src/lib.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
"#);
@ -297,7 +298,7 @@ test!(cargo_compile_with_nested_deps_correct_bin {
"#)
.file("baz/src/lib.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
"#);
@ -373,7 +374,7 @@ test!(cargo_compile_with_nested_deps_shorthand {
"#)
.file("baz/src/baz.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
"#);
@ -449,7 +450,7 @@ test!(cargo_compile_with_nested_deps_longhand {
"#)
.file("baz/src/baz.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
"#);
@ -691,8 +692,8 @@ test!(custom_build_env_vars {
.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());
assert_eq!(os::getenv("OUT_DIR").unwrap(), "{}".to_string());
assert_eq!(os::getenv("DEPS_DIR").unwrap(), "{}".to_string());
}}
"#,
escape_path(&p.root().join("target")),
@ -736,8 +737,8 @@ test!(custom_build_in_dependency {
.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());
assert_eq!(os::getenv("OUT_DIR").unwrap(), "{}".to_string());
assert_eq!(os::getenv("DEPS_DIR").unwrap(), "{}".to_string());
}}
"#,
escape_path(&p.root().join("target/deps")),
@ -807,7 +808,7 @@ test!(many_crate_types_old_style_lib_location {
match f.filename_str().unwrap() {
"deps" => None,
s if s.contains("fingerprint") || s.contains("dSYM") => None,
s => Some(s.to_str())
s => Some(s.to_string())
}
}).collect();
files.sort();
@ -845,7 +846,7 @@ test!(many_crate_types_correct {
match f.filename_str().unwrap() {
"deps" => None,
s if s.contains("fingerprint") || s.contains("dSYM") => None,
s => Some(s.to_str())
s => Some(s.to_string())
}
}).collect();
files.sort();
@ -954,3 +955,120 @@ test!(missing_lib_and_bin {
.with_stderr("either a [[lib]] or [[bin]] section \
must be present\n"));
})
test!(verbose_build {
let mut p = project("foo");
p = p
.file("Cargo.toml", r#"
[package]
name = "test"
version = "0.0.0"
authors = []
"#)
.file("src/lib.rs", "");
let output = p.cargo_process("cargo-build").arg("-v")
.exec_with_output().assert();
let out = str::from_utf8(output.output.as_slice()).assert();
let hash = out.slice_from(out.find_str("extra-filename=").unwrap() + 15);
let hash = hash.slice_to(17);
assert_eq!(out, format!("\
{} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \
-C metadata=test:-:0.0.0:-:file:{dir} \
-C extra-filename={hash} \
--out-dir {dir}{sep}target \
-L {dir}{sep}target \
-L {dir}{sep}target{sep}deps`
{} test v0.0.0 (file:{dir})\n",
RUNNING, COMPILING,
dir = p.root().display(),
sep = path::SEP,
hash = hash).as_slice());
})
test!(verbose_release_build {
let mut p = project("foo");
p = p
.file("Cargo.toml", r#"
[package]
name = "test"
version = "0.0.0"
authors = []
"#)
.file("src/lib.rs", "");
let output = p.cargo_process("cargo-build").arg("-v").arg("--release")
.exec_with_output().assert();
let out = str::from_utf8(output.output.as_slice()).assert();
let hash = out.slice_from(out.find_str("extra-filename=").unwrap() + 15);
let hash = hash.slice_to(17);
assert_eq!(out, format!("\
{} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \
--opt-level 3 \
-C metadata=test:-:0.0.0:-:file:{dir} \
-C extra-filename={hash} \
--out-dir {dir}{sep}target{sep}release \
-L {dir}{sep}target{sep}release \
-L {dir}{sep}target{sep}release{sep}deps`
{} test v0.0.0 (file:{dir})\n",
RUNNING, COMPILING,
dir = p.root().display(),
sep = path::SEP,
hash = hash).as_slice());
})
test!(verbose_release_build_deps {
let mut p = project("foo");
p = p
.file("Cargo.toml", r#"
[package]
name = "test"
version = "0.0.0"
authors = []
[dependencies.foo]
path = "foo"
"#)
.file("src/lib.rs", "")
.file("foo/Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.0"
authors = []
"#)
.file("foo/src/lib.rs", "");
let output = p.cargo_process("cargo-build").arg("-v").arg("--release")
.exec_with_output().assert();
let out = str::from_utf8(output.output.as_slice()).assert();
let pos1 = out.find_str("extra-filename=").unwrap();
let hash1 = out.slice_from(pos1 + 15).slice_to(17);
let pos2 = out.slice_from(pos1 + 10).find_str("extra-filename=").unwrap();
let hash2 = out.slice_from(pos1 + 10 + pos2 + 15).slice_to(17);
assert_eq!(out, format!("\
{running} `rustc {dir}{sep}foo{sep}src{sep}lib.rs --crate-name foo \
--crate-type lib \
--opt-level 3 \
-C metadata=foo:-:0.0.0:-:file:{dir} \
-C extra-filename={hash1} \
--out-dir {dir}{sep}target{sep}release{sep}deps \
-L {dir}{sep}target{sep}release{sep}deps \
-L {dir}{sep}target{sep}release{sep}deps`
{running} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \
--opt-level 3 \
-C metadata=test:-:0.0.0:-:file:{dir} \
-C extra-filename={hash2} \
--out-dir {dir}{sep}target{sep}release \
-L {dir}{sep}target{sep}release \
-L {dir}{sep}target{sep}release{sep}deps \
--extern foo={dir}{sep}target{sep}release{sep}deps/libfoo{hash1}.rlib`
{compiling} foo v0.0.0 (file:{dir})
{compiling} test v0.0.0 (file:{dir})\n",
running = RUNNING,
compiling = COMPILING,
dir = p.root().display(),
sep = path::SEP,
hash1 = hash1,
hash2 = hash2).as_slice());
})

View File

@ -66,7 +66,7 @@ test!(cargo_compile_with_nested_deps_shorthand {
"#)
.file("bar/baz/src/baz.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
"test passed".to_string()
}
"#);
@ -97,32 +97,28 @@ test!(cargo_compile_with_root_dev_deps {
[dev-dependencies.bar]
version = "0.5.0"
path = "bar"
path = "../bar"
[[bin]]
name = "foo"
"#)
.file("src/foo.rs",
main_file(r#""{}", bar::gimme()"#, ["bar"]).as_slice())
.file("bar/Cargo.toml", r#"
[project]
.file("src/main.rs",
main_file(r#""{}", bar::gimme()"#, ["bar"]).as_slice());
let p2 = project("bar")
.file("Cargo.toml", r#"
[package]
name = "bar"
version = "0.5.0"
authors = ["wycats@example.com"]
[[lib]]
name = "bar"
"#)
.file("bar/src/bar.rs", r#"
.file("src/lib.rs", r#"
pub fn gimme() -> &'static str {
"zoidberg"
}
"#)
;
"#);
p2.build();
assert_that(p.cargo_process("cargo-build"),
execs().with_stdout(format!("{} bar v0.5.0 (file:{})\n\
{} foo v0.5.0 (file:{})\n",

View File

@ -1,4 +1,7 @@
use std::str;
use support::{project, execs, basic_bin_manifest, COMPILING, cargo_dir};
use support::{ResultTest};
use hamcrest::{assert_that, existing_file};
use cargo::util::process;
@ -38,3 +41,94 @@ test!(cargo_test_simple {
assert_that(&p.bin("test/foo"), existing_file());
})
test!(test_with_lib_dep {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
"#)
.file("src/lib.rs", "
pub fn foo(){}
#[test] fn lib_test() {}
")
.file("src/main.rs", "
extern crate foo;
fn main() {}
#[test]
fn bin_test() {}
");
let output = p.cargo_process("cargo-test")
.exec_with_output().assert();
let out = str::from_utf8(output.output.as_slice()).assert();
let bin = "\
running 1 test
test bin_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
let lib = "\
running 1 test
test lib_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
let head = format!("{compiling} foo v0.0.1 (file:{dir})",
compiling = COMPILING, dir = p.root().display());
assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice());
})
test!(test_with_deep_lib_dep {
let p = project("bar")
.file("Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []
[dependencies.foo]
path = "../foo"
"#)
.file("src/lib.rs", "
extern crate foo;
#[test]
fn bar_test() {
foo::foo();
}
");
let p2 = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
"#)
.file("src/lib.rs", "
pub fn foo() {}
#[test]
fn foo_test() {}
");
p2.build();
assert_that(p.cargo_process("cargo-test"),
execs().with_status(0)
.with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})
{compiling} bar v0.0.1 (file:{dir})
running 1 test
test bar_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
",
compiling = COMPILING,
dir = p.root().display()).as_slice()));
})

View File

@ -56,5 +56,5 @@ fn colored_output<S: Str>(string: S, color: color::Color) -> IoResult<String> {
try!(term.write_str(string.as_slice()));
try!(term.reset());
try!(term.flush());
Ok(from_utf8_lossy(term.get_ref().get_ref()).to_str())
Ok(from_utf8_lossy(term.get_ref().get_ref()).to_string())
}