Add support for a license-file manifest key

This key will support projects with nonstandard licenses and the registry will
display the license as "nonstandard".

Closes #940
This commit is contained in:
Alex Crichton 2014-11-24 22:18:54 -08:00
parent b9ff1a3af5
commit 5acb5f567b
8 changed files with 59 additions and 13 deletions

View file

@ -45,6 +45,7 @@ pub struct ManifestMetadata {
pub authors: Vec<String>, pub authors: Vec<String>,
pub keywords: Vec<String>, pub keywords: Vec<String>,
pub license: Option<String>, pub license: Option<String>,
pub license_file: Option<String>,
pub description: Option<String>, // not markdown pub description: Option<String>, // not markdown
pub readme: Option<String>, // file, not contents pub readme: Option<String>, // file, not contents
pub homepage: Option<String>, // url pub homepage: Option<String>, // url

View file

@ -75,15 +75,15 @@ fn check_metadata(pkg: &Package, shell: &mut MultiShell) -> CargoResult<()> {
let mut missing = vec![]; let mut missing = vec![];
macro_rules! lacking { macro_rules! lacking {
($($field: ident),*) => {{ ($( $($field: ident)||* ),*) => {{
$( $(
if md.$field.as_ref().map_or(true, |s| s.is_empty()) { if $(md.$field.as_ref().map_or(true, |s| s.is_empty()))&&* {
missing.push(stringify!($field)) $(missing.push(stringify!($field).replace("_", "-"));)*
} }
)* )*
}} }}
} }
lacking!(description, license) lacking!(description, license || license_file)
if !missing.is_empty() { if !missing.is_empty() {
let mut things = missing.slice_to(missing.len() - 1).connect(", "); let mut things = missing.slice_to(missing.len() - 1).connect(", ");
@ -91,10 +91,10 @@ fn check_metadata(pkg: &Package, shell: &mut MultiShell) -> CargoResult<()> {
if !things.is_empty() { if !things.is_empty() {
things.push_str(" or "); things.push_str(" or ");
} }
things.push_str(*missing.last().unwrap()); things.push_str(missing.last().unwrap().as_slice());
try!(shell.warn( try!(shell.warn(
format!("Warning: manifest has no {things}. \ format!("warning: manifest has no {things}. \
See http://doc.crates.io/manifest.html#package-metadata for more info.", See http://doc.crates.io/manifest.html#package-metadata for more info.",
things = things).as_slice())) things = things).as_slice()))
} }

View file

@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::io::File; use std::io::File;
use std::io::fs::PathExtensions;
use std::os; use std::os;
use term::color::BLACK; use term::color::BLACK;
@ -87,7 +88,7 @@ fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
let manifest = pkg.get_manifest(); let manifest = pkg.get_manifest();
let ManifestMetadata { let ManifestMetadata {
ref authors, ref description, ref homepage, ref documentation, ref authors, ref description, ref homepage, ref documentation,
ref keywords, ref readme, ref repository, ref license, ref keywords, ref readme, ref repository, ref license, ref license_file,
} = *manifest.get_metadata(); } = *manifest.get_metadata();
let readme = match *readme { let readme = match *readme {
Some(ref readme) => { Some(ref readme) => {
@ -98,6 +99,15 @@ fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
} }
None => None, None => None,
}; };
match *license_file {
Some(ref file) => {
if !pkg.get_root().join(file).exists() {
return Err(human(format!("the license file `{}` does not exist",
file)))
}
}
None => {}
}
registry.publish(&NewCrate { registry.publish(&NewCrate {
name: pkg.get_name().to_string(), name: pkg.get_name().to_string(),
vers: pkg.get_version().to_string(), vers: pkg.get_version().to_string(),
@ -111,6 +121,7 @@ fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
readme: readme, readme: readme,
repository: repository.clone(), repository: repository.clone(),
license: license.clone(), license: license.clone(),
license_file: license_file.clone(),
}, tarball).map_err(|e| { }, tarball).map_err(|e| {
human(e.to_string()) human(e.to_string())
}) })

View file

@ -267,6 +267,7 @@ pub struct TomlProject {
readme: Option<String>, readme: Option<String>,
keywords: Option<Vec<String>>, keywords: Option<Vec<String>>,
license: Option<String>, license: Option<String>,
license_file: Option<String>,
repository: Option<String>, repository: Option<String>,
} }
@ -518,6 +519,7 @@ impl TomlManifest {
readme: project.readme.clone(), readme: project.readme.clone(),
authors: project.authors.clone(), authors: project.authors.clone(),
license: project.license.clone(), license: project.license.clone(),
license_file: project.license_file.clone(),
repository: project.repository.clone(), repository: project.repository.clone(),
keywords: project.keywords.clone().unwrap_or(Vec::new()), keywords: project.keywords.clone().unwrap_or(Vec::new()),
}; };
@ -541,6 +543,11 @@ impl TomlManifest {
manifest.add_warning(format!(" For more information, see \ manifest.add_warning(format!(" For more information, see \
http://doc.crates.io/build-script.html")); http://doc.crates.io/build-script.html"));
} }
if project.license_file.is_some() && project.license.is_some() {
manifest.add_warning(format!("warning: only one of `license` or \
`license-file` is necessary"));
}
Ok((manifest, nested_paths)) Ok((manifest, nested_paths))
} }
} }

View file

@ -82,9 +82,14 @@ keywords = ["...", "..."]
# This is a string description of the license for this package. Currently # This is a string description of the license for this package. Currently
# crates.io will validate the license provided against a whitelist of known # crates.io will validate the license provided against a whitelist of known
# license identifiers from http://spdx.org/licenses/. Multiple licenses can # license identifiers from http://spdx.org/licenses/. Multiple licenses can
# be separated with a `/` # be separated with a `/`
license = "..." license = "..."
# If a project is using a nonstandard license, then this key may be specified in
# lieu of the above key and must point to a file relative to this manifest
# (similar to the readme key)
license-file = "..."
``` ```
The [crates.io](https://crates.io) registry will render the description, display The [crates.io](https://crates.io) registry will render the description, display

View file

@ -55,6 +55,7 @@ pub struct NewCrate {
pub readme: Option<String>, pub readme: Option<String>,
pub keywords: Vec<String>, pub keywords: Vec<String>,
pub license: Option<String>, pub license: Option<String>,
pub license_file: Option<String>,
pub repository: Option<String>, pub repository: Option<String>,
} }

View file

@ -80,8 +80,9 @@ test!(metadata_warning {
verifying = VERIFYING, verifying = VERIFYING,
compiling = COMPILING, compiling = COMPILING,
dir = p.url()).as_slice()) dir = p.url()).as_slice())
.with_stderr("Warning: manifest has no description or license. See \ .with_stderr("\
http://doc.crates.io/manifest.html#package-metadata for more info.")); warning: manifest has no description, license or license-file. See \
http://doc.crates.io/manifest.html#package-metadata for more info."));
let p = project("one") let p = project("one")
.file("Cargo.toml", r#" .file("Cargo.toml", r#"
@ -104,8 +105,9 @@ test!(metadata_warning {
verifying = VERIFYING, verifying = VERIFYING,
compiling = COMPILING, compiling = COMPILING,
dir = p.url()).as_slice()) dir = p.url()).as_slice())
.with_stderr("Warning: manifest has no description. See \ .with_stderr("\
http://doc.crates.io/manifest.html#package-metadata for more info.")); warning: manifest has no description. See \
http://doc.crates.io/manifest.html#package-metadata for more info."));
let p = project("both") let p = project("both")
.file("Cargo.toml", format!(r#" .file("Cargo.toml", format!(r#"

View file

@ -480,3 +480,22 @@ test!(login_with_no_cargo_dir {
.env("HOME", Some(home)), .env("HOME", Some(home)),
execs().with_status(0)); execs().with_status(0));
}) })
test!(bad_license_file {
let p = project("all")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.0.1"
authors = []
license-file = "foo"
description = "bar"
"#)
.file("src/main.rs", r#"
fn main() {}
"#);
assert_that(p.cargo_process("publish"),
execs().with_status(101)
.with_stderr("\
the license file `foo` does not exist"));
})