auto merge of #142 : rust-lang/cargo/new-names, r=alexcrichton

This PR moves Cargo over to the new naming based on [RFC 35](https://github.com/rust-lang/rfcs/blob/master/complete/0035-remove-crate-id.md).

* You should no longer use `#[crate_name]` or `#[crate_id]` in any crate managed by Cargo.
* You no longer need a `[[lib]]` section in your `Cargo.toml` if you name your library `src/lib.rs`.
* You no longer need a `[[bin]]` section in your `Cargo.toml` if you name your library `src/main.rs`.
* You can include multiple `[[bins]]` in `src/bin/*.rs` and Cargo will treat them as if they were specified via `[[bin]]`.

This commit does not yet add support for `-C metadata` or `-C extra-file-name`, but that is coming soon.
This commit is contained in:
bors 2014-07-08 06:14:56 +00:00
commit a6061db0d0
7 changed files with 345 additions and 33 deletions

View File

@ -49,9 +49,6 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
}))
};
let update = options.update_remotes;
let jobs = options.jobs;
let env = if options.release {
"release"
} else {

View File

@ -9,7 +9,7 @@ extern crate serialize;
extern crate hammer;
use std::os;
use std::io::fs;
use std::io::{UserExecute, fs};
use cargo::ops;
use cargo::{execute_main_without_stdin};
@ -65,7 +65,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
// 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.is_file() { continue; }
if !is_executable(&file) { continue; }
try!(util::process(file).exec().map_err(|e| {
CliError::from_boxed(e.box_error(), 1)
@ -74,3 +74,8 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
Ok(None)
}
fn is_executable(path: &Path) -> bool {
if !path.is_file() { return false; }
path.stat().map(|stat| stat.perm.intersects(UserExecute)).unwrap_or(false)
}

View File

@ -310,6 +310,10 @@ impl Target {
}
}
pub fn get_name<'a>(&'a self) -> &'a str {
self.name.as_slice()
}
pub fn get_path<'a>(&'a self) -> &'a Path {
&self.path
}

View File

@ -4,11 +4,12 @@ use util;
use core::{Package,Manifest,SourceId};
use util::{CargoResult, human};
use util::important_paths::find_project_manifest_exact;
use util::toml::{Layout, project_layout};
pub fn read_manifest(contents: &[u8], source_id: &SourceId)
pub fn read_manifest(contents: &[u8], layout: Layout, source_id: &SourceId)
-> CargoResult<(Manifest, Vec<Path>)>
{
util::toml::to_manifest(contents, source_id).map_err(human)
util::toml::to_manifest(contents, source_id, layout).map_err(human)
}
pub fn read_package(path: &Path, source_id: &SourceId)
@ -17,8 +18,10 @@ pub fn read_package(path: &Path, source_id: &SourceId)
log!(5, "read_package; path={}; source-id={}", path.display(), source_id);
let mut file = try!(File::open(path));
let data = try!(file.read_to_end());
let (manifest, nested) = try!(read_manifest(data.as_slice(),
source_id));
let layout = project_layout(&path.dir_path());
let (manifest, nested) =
try!(read_manifest(data.as_slice(), layout, source_id));
Ok((Package::new(manifest, path, source_id), nested))
}

View File

@ -218,7 +218,7 @@ fn rustc(root: &Path, target: &Target, cx: &mut Context) -> Job {
log!(5, "command={}", rustc);
cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_str()));
let _ = cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_str()));
proc() {
if primary {
@ -248,12 +248,16 @@ fn build_base_args(into: &mut Args, target: &Target, crate_types: Vec<&str>,
cx: &Context) {
// TODO: Handle errors in converting paths into args
into.push(target.get_path().display().to_str());
into.push("--crate-name".to_str());
into.push(target.get_name().to_str());
for crate_type in crate_types.iter() {
into.push("--crate-type".to_str());
into.push(crate_type.to_str());
}
let mut out = cx.dest.clone();
let out = cx.dest.clone();
let profile = target.get_profile();
if profile.get_opt_level() != 0 {
@ -269,8 +273,13 @@ fn build_base_args(into: &mut Args, target: &Target, crate_types: Vec<&str>,
into.push("--test".to_str());
}
into.push("--out-dir".to_str());
into.push(out.display().to_str());
if target.is_lib() {
into.push("--out-dir".to_str());
into.push(out.display().to_str());
} else {
into.push("-o".to_str());
into.push(out.join(target.get_name()).display().to_str());
}
}
fn build_deps_args(dst: &mut Args, cx: &Context) {

View File

@ -1,6 +1,7 @@
use serialize::Decodable;
use std::collections::HashMap;
use std::str;
use std::io::fs;
use toml;
use core::{SourceId, GitKind};
@ -9,8 +10,52 @@ use core::{Summary, Manifest, Target, Dependency, PackageId};
use core::source::Location;
use util::{CargoResult, Require, human};
#[deriving(Clone)]
pub struct Layout {
lib: Option<Path>,
bins: Vec<Path>
}
impl Layout {
fn main<'a>(&'a self) -> Option<&'a Path> {
self.bins.iter().find(|p| {
match p.filename_str() {
Some(s) => s == "main.rs",
None => false
}
})
}
}
pub fn project_layout(root: &Path) -> Layout {
let mut lib = None;
let mut bins = vec!();
if root.join("src/lib.rs").exists() {
lib = Some(root.join("src/lib.rs"));
}
if root.join("src/main.rs").exists() {
bins.push(root.join("src/main.rs"));
}
fs::readdir(&root.join("src/bin"))
.map(|v| v.move_iter())
.map(|i| i.filter(|f| f.extension_str() == Some("rs")))
.map(|mut i| i.collect())
.map(|found| bins.push_all_move(found));
Layout {
lib: lib,
bins: bins
}
}
pub fn to_manifest(contents: &[u8],
source_id: &SourceId) -> CargoResult<(Manifest, Vec<Path>)> {
source_id: &SourceId,
layout: Layout)
-> CargoResult<(Manifest, Vec<Path>)>
{
let contents = try!(str::from_utf8(contents).require(|| {
human("Cargo.toml is not valid UTF-8")
}));
@ -22,7 +67,7 @@ pub fn to_manifest(contents: &[u8],
manifest\n\n{}", e)))
};
let pair = try!(toml_manifest.to_manifest(source_id).map_err(|err| {
let pair = try!(toml_manifest.to_manifest(source_id, &layout).map_err(|err| {
human(format!("Cargo.toml is not a valid manifest\n\n{}", err))
}));
let (mut manifest, paths) = pair;
@ -55,8 +100,6 @@ pub fn to_manifest(contents: &[u8],
_ => m.add_unused_key(key),
}
}
}
pub fn parse(toml: &str, file: &str) -> CargoResult<toml::Table> {
@ -145,16 +188,98 @@ struct Context<'a> {
nested_paths: &'a mut Vec<Path>
}
// These functions produce the equivalent of specific manifest entries. One
// wrinkle is that certain paths cannot be represented in the manifest due
// to Toml's UTF-8 requirement. This could, in theory, mean that certain
// otherwise acceptable executable names are not used when inside of
// `src/bin/*`, but it seems ok to not build executables with non-UTF8
// paths.
fn inferred_lib_target(name: &str, layout: &Layout) -> Option<Vec<TomlTarget>> {
layout.lib.as_ref().map(|lib| {
vec![TomlTarget {
name: name.to_str(),
crate_type: None,
path: Some(lib.display().to_str()),
test: None
}]
})
}
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())
} else {
bin.filestem_str().map(|f| f.to_str())
};
name.map(|name| {
TomlTarget {
name: name,
crate_type: None,
path: Some(bin.display().to_str()),
test: None
}
})
}).collect())
}
impl TomlManifest {
pub fn to_manifest(&self, source_id: &SourceId)
pub fn to_manifest(&self, source_id: &SourceId, layout: &Layout)
-> CargoResult<(Manifest, Vec<Path>)>
{
let mut sources = vec!();
let mut nested_paths = vec!();
let project = self.project.as_ref().or_else(|| self.package.as_ref());
let project = try!(project.require(|| {
human("No `package` or `project` section found.")
}));
// If we have no lib at all, use the inferred lib if available
// If we have a lib with a path, we're done
// If we have a lib with no path, use the inferred lib or_else package name
let lib = if self.lib.is_none() || self.lib.get_ref().is_empty() {
inferred_lib_target(project.name.as_slice(), layout)
} else {
Some(self.lib.get_ref().iter().map(|t| {
if layout.lib.is_some() && t.path.is_none() {
TomlTarget {
name: t.name.clone(),
crate_type: t.crate_type.clone(),
path: layout.lib.as_ref().map(|p| p.display().to_str()),
test: t.test
}
} else {
t.clone()
}
}).collect())
};
let bins = if self.bin.is_none() || self.bin.get_ref().is_empty() {
inferred_bin_targets(project.name.as_slice(), layout)
} else {
let bin = layout.main();
Some(self.bin.get_ref().iter().map(|t| {
if bin.is_some() && t.path.is_none() {
TomlTarget {
name: t.name.clone(),
crate_type: t.crate_type.clone(),
path: bin.as_ref().map(|p| p.display().to_str()),
test: t.test
}
} else {
t.clone()
}
}).collect())
};
// Get targets
let targets = normalize(self.lib.as_ref().map(|l| l.as_slice()),
self.bin.as_ref().map(|b| b.as_slice()));
let targets = normalize(lib.as_ref().map(|l| l.as_slice()),
bins.as_ref().map(|b| b.as_slice()));
if targets.is_empty() {
debug!("manifest has no build targets; project={}", self.project);
@ -176,11 +301,6 @@ impl TomlManifest {
try!(process_dependencies(&mut cx, true, self.dev_dependencies.as_ref()));
}
let project = self.project.as_ref().or_else(|| self.package.as_ref());
let project = try!(project.require(|| {
human("No `package` or `project` section found.")
}));
let pkgid = try!(project.to_package_id(source_id.get_location()));
let summary = Summary::new(&pkgid, deps.as_slice());
Ok((Manifest::new(
@ -257,7 +377,9 @@ struct TomlTarget {
}
fn normalize(lib: Option<&[TomlLibTarget]>,
bin: Option<&[TomlBinTarget]>) -> Vec<Target> {
bin: Option<&[TomlBinTarget]>)
-> Vec<Target>
{
log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);
fn target_profiles(target: &TomlTarget) -> Vec<Profile> {

View File

@ -92,8 +92,8 @@ test!(cargo_compile_with_invalid_code {
{filename}:1 invalid rust code!
^~~~~~~
Could not execute process \
`rustc {filename} --crate-type bin -g --out-dir {} -L {} -L {}` (status=101)\n",
target.display(),
`rustc {filename} --crate-name foo --crate-type bin -g -o {} -L {} -L {}` (status=101)\n",
target.join("foo").display(),
target.display(),
target.join("deps").display(),
filename = format!("src{}foo.rs", path::SEP)).as_slice()));
@ -176,6 +176,142 @@ test!(cargo_compile_with_warnings_in_a_dep_package {
execs().with_stdout("test passed\n"));
})
test!(cargo_compile_with_nested_deps_inferred {
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 = ["{}", "{}"]
"#, escape_path(&bar), escape_path(&baz)).as_slice())
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies]
bar = "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 = "0.5.0"
"#)
.file("bar/src/lib.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"]
"#)
.file("baz/src/lib.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
}
"#);
p.cargo_process("cargo-build")
.exec_with_output()
.assert();
assert_that(&p.bin("foo"), existing_file());
assert_that(
cargo::util::process(p.bin("foo")),
execs().with_stdout("test passed\n"));
})
test!(cargo_compile_with_nested_deps_correct_bin {
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 = ["{}", "{}"]
"#, escape_path(&bar), escape_path(&baz)).as_slice())
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies]
bar = "0.5.0"
[[bin]]
name = "foo"
"#)
.file("src/main.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 = "0.5.0"
"#)
.file("bar/src/lib.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"]
"#)
.file("baz/src/lib.rs", r#"
pub fn gimme() -> String {
"test passed".to_str()
}
"#);
p.cargo_process("cargo-build")
.exec_with_output()
.assert();
assert_that(&p.bin("foo"), existing_file());
assert_that(
cargo::util::process(p.bin("foo")),
execs().with_stdout("test passed\n"));
})
test!(cargo_compile_with_nested_deps_shorthand {
let mut p = project("foo");
let bar = p.root().join("bar");
@ -636,18 +772,16 @@ test!(custom_build_in_dependency {
version = "0.5.0"
authors = ["wycats@example.com"]
build = "{}"
[[lib]]
name = "bar"
"#, escape_path(&build.bin("foo"))))
.file("bar/src/bar.rs", r#"
.file("bar/src/lib.rs", r#"
pub fn bar() {}
"#);
assert_that(p.cargo_process("cargo-build"),
execs().with_status(0));
})
test!(many_crate_types {
// this is testing that src/<pkg-name>.rs still works (for now)
test!(many_crate_types_old_style_lib_location {
let mut p = project("foo");
p = p
.file("Cargo.toml", r#"
@ -685,6 +819,44 @@ test!(many_crate_types {
file1.ends_with(os::consts::DLL_SUFFIX));
})
test!(many_crate_types_correct {
let mut p = project("foo");
p = p
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[[lib]]
name = "foo"
crate_type = ["rlib", "dylib"]
"#)
.file("src/lib.rs", r#"
pub fn foo() {}
"#);
assert_that(p.cargo_process("cargo-build"),
execs().with_status(0));
let files = fs::readdir(&p.root().join("target")).assert();
let mut files: Vec<String> = files.iter().filter_map(|f| {
match f.filename_str().unwrap() {
"deps" => None,
s if s.contains("fingerprint") || s.contains("dSYM") => None,
s => Some(s.to_str())
}
}).collect();
files.sort();
let file0 = files.get(0).as_slice();
let file1 = files.get(1).as_slice();
println!("{} {}", file0, file1);
assert!(file0.ends_with(".rlib") || file1.ends_with(".rlib"));
assert!(file0.ends_with(os::consts::DLL_SUFFIX) ||
file1.ends_with(os::consts::DLL_SUFFIX));
})
test!(unused_keys {
let mut p = project("foo");
p = p