mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
Add dependency registry
to cargo metadata
.
This adds the `registry` field for dependencies for alternate registries in `cargo metadata`.
This commit is contained in:
parent
15e3b5a3c8
commit
3d84d0ad77
@ -19,6 +19,7 @@ path = "src/cargo/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
byteorder = "1.2"
|
||||
bytesize = "1.0"
|
||||
crates-io = { path = "src/crates-io", version = "0.22" }
|
||||
crossbeam-utils = "0.6"
|
||||
@ -57,6 +58,7 @@ tempfile = "3.0"
|
||||
termcolor = "1.0"
|
||||
toml = "0.4.2"
|
||||
url = "1.1"
|
||||
url_serde = "0.2.0"
|
||||
clap = "2.31.2"
|
||||
unicode-width = "0.1.5"
|
||||
openssl = { version = '0.10.11', optional = true }
|
||||
|
@ -7,6 +7,7 @@ use semver::ReqParseError;
|
||||
use semver::VersionReq;
|
||||
use serde::ser;
|
||||
use serde::Serialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::core::interning::InternedString;
|
||||
use crate::core::{PackageId, SourceId, Summary};
|
||||
@ -25,6 +26,12 @@ pub struct Dependency {
|
||||
struct Inner {
|
||||
name: InternedString,
|
||||
source_id: SourceId,
|
||||
/// Source ID for the registry as specified in the manifest.
|
||||
///
|
||||
/// This will be None if it is not specified (crates.io dependency).
|
||||
/// This is different from `source_id` for example when both a `path` and
|
||||
/// `registry` is specified. Or in the case of a crates.io dependency,
|
||||
/// `source_id` will be crates.io and this will be None.
|
||||
registry_id: Option<SourceId>,
|
||||
req: VersionReq,
|
||||
specified_req: bool,
|
||||
@ -59,6 +66,10 @@ struct SerializedDependency<'a> {
|
||||
uses_default_features: bool,
|
||||
features: &'a [InternedString],
|
||||
target: Option<&'a Platform>,
|
||||
/// The registry URL this dependency is from.
|
||||
/// If None, then it comes from the default registry (crates.io).
|
||||
#[serde(with = "url_serde")]
|
||||
registry: Option<Url>,
|
||||
}
|
||||
|
||||
impl ser::Serialize for Dependency {
|
||||
@ -76,6 +87,7 @@ impl ser::Serialize for Dependency {
|
||||
features: self.features(),
|
||||
target: self.platform(),
|
||||
rename: self.explicit_name_in_toml().map(|s| s.as_str()),
|
||||
registry: self.registry_id().map(|sid| sid.url().clone()),
|
||||
}
|
||||
.serialize(s)
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ struct SourceIdInner {
|
||||
// e.g. the exact git revision of the specified branch for a Git Source
|
||||
precise: Option<String>,
|
||||
/// Name of the registry source for alternative registries
|
||||
/// WARNING: This is not always set for alt-registries when the name is
|
||||
/// not known.
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
@ -247,6 +249,8 @@ impl SourceId {
|
||||
}
|
||||
|
||||
/// Is this source from an alternative registry
|
||||
/// DEPRECATED: This is not correct if the registry name is not known
|
||||
/// (for example when loaded from an index).
|
||||
pub fn is_alt_registry(self) -> bool {
|
||||
self.is_registry() && self.inner.name.is_some()
|
||||
}
|
||||
|
@ -159,8 +159,10 @@ fn transmit(
|
||||
// registry in the dependency.
|
||||
let dep_registry_id = match dep.registry_id() {
|
||||
Some(id) => id,
|
||||
None => failure::bail!("dependency missing registry ID"),
|
||||
None => SourceId::crates_io(config)?,
|
||||
};
|
||||
// In the index and Web API, None means "from the same registry"
|
||||
// whereas in Cargo.toml, it means "from crates.io".
|
||||
let dep_registry = if dep_registry_id != registry_id {
|
||||
Some(dep_registry_id.url().to_string())
|
||||
} else {
|
||||
|
@ -299,7 +299,7 @@ impl<'a> RegistryDependency<'a> {
|
||||
package,
|
||||
} = self;
|
||||
|
||||
let id = if let Some(registry) = registry {
|
||||
let id = if let Some(registry) = ®istry {
|
||||
SourceId::for_registry(®istry.to_url()?)?
|
||||
} else {
|
||||
default
|
||||
@ -328,6 +328,12 @@ impl<'a> RegistryDependency<'a> {
|
||||
// out here.
|
||||
features.retain(|s| !s.is_empty());
|
||||
|
||||
// In index, "registry" is null if it is from the same index.
|
||||
// In Cargo.toml, "registry" is None if it is from the default
|
||||
if !id.is_default_registry() {
|
||||
dep.set_registry_id(id);
|
||||
}
|
||||
|
||||
dep.set_optional(optional)
|
||||
.set_default_features(default_features)
|
||||
.set_features(features)
|
||||
@ -486,22 +492,7 @@ impl<'cfg> RegistrySource<'cfg> {
|
||||
MaybePackage::Ready(pkg) => pkg,
|
||||
MaybePackage::Download { .. } => unreachable!(),
|
||||
};
|
||||
|
||||
// Unfortunately the index and the actual Cargo.toml in the index can
|
||||
// differ due to historical Cargo bugs. To paper over these we trash the
|
||||
// *summary* loaded from the Cargo.toml we just downloaded with the one
|
||||
// we loaded from the index.
|
||||
let summaries = self
|
||||
.index
|
||||
.summaries(package.name().as_str(), &mut *self.ops)?;
|
||||
let summary = summaries
|
||||
.iter()
|
||||
.map(|s| &s.0)
|
||||
.find(|s| s.package_id() == package)
|
||||
.expect("summary not found");
|
||||
let mut manifest = pkg.manifest().clone();
|
||||
manifest.set_summary(summary.clone());
|
||||
Ok(Package::new(manifest, pkg.manifest_path()))
|
||||
Ok(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1307,14 +1307,6 @@ impl DetailedTomlDependency {
|
||||
}
|
||||
}
|
||||
|
||||
let registry_id = match self.registry {
|
||||
Some(ref registry) => {
|
||||
cx.features.require(Feature::alternative_registries())?;
|
||||
SourceId::alt_registry(cx.config, registry)?
|
||||
}
|
||||
None => SourceId::crates_io(cx.config)?,
|
||||
};
|
||||
|
||||
let new_source_id = match (
|
||||
self.git.as_ref(),
|
||||
self.path.as_ref(),
|
||||
@ -1410,8 +1402,13 @@ impl DetailedTomlDependency {
|
||||
.unwrap_or(true),
|
||||
)
|
||||
.set_optional(self.optional.unwrap_or(false))
|
||||
.set_platform(cx.platform.clone())
|
||||
.set_registry_id(registry_id);
|
||||
.set_platform(cx.platform.clone());
|
||||
if let Some(registry) = &self.registry {
|
||||
cx.features.require(Feature::alternative_registries())?;
|
||||
let registry_id = SourceId::alt_registry(cx.config, registry)?;
|
||||
dep.set_registry_id(registry_id);
|
||||
}
|
||||
|
||||
if let Some(kind) = kind {
|
||||
dep.set_kind(kind);
|
||||
}
|
||||
|
@ -61,7 +61,9 @@ The output has the following format:
|
||||
{
|
||||
/* The name of the dependency. */
|
||||
"name": "bitflags",
|
||||
/* The source ID of the dependency. */
|
||||
/* The source ID of the dependency. May be null, see
|
||||
description for the package source.
|
||||
*/
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
/* The version requirement for the dependency.
|
||||
Dependencies without a version requirement have a value of "*".
|
||||
@ -84,7 +86,12 @@ The output has the following format:
|
||||
/* The target platform for the dependency.
|
||||
null if not a target dependency.
|
||||
*/
|
||||
"target": "cfg(windows)"
|
||||
"target": "cfg(windows)",
|
||||
/* A string of the URL of the registry this dependency is from.
|
||||
If not specified or null, the dependency is from the default
|
||||
registry (crates.io).
|
||||
*/
|
||||
"registry": null
|
||||
}
|
||||
],
|
||||
/* Array of Cargo targets. */
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::support::registry::{self, alt_api_path, Package};
|
||||
use crate::support::publish::validate_alt_upload;
|
||||
use crate::support::registry::{self, Package};
|
||||
use crate::support::{basic_manifest, git, paths, project};
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
@ -107,7 +108,7 @@ fn depend_on_alt_registry_depends_on_same_registry_no_index() {
|
||||
|
||||
Package::new("baz", "0.0.1").alternative(true).publish();
|
||||
Package::new("bar", "0.0.1")
|
||||
.dep("baz", "0.0.1")
|
||||
.registry_dep("baz", "0.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
|
||||
@ -152,7 +153,7 @@ fn depend_on_alt_registry_depends_on_same_registry() {
|
||||
|
||||
Package::new("baz", "0.0.1").alternative(true).publish();
|
||||
Package::new("bar", "0.0.1")
|
||||
.registry_dep("baz", "0.0.1", registry::alt_registry().as_str())
|
||||
.registry_dep("baz", "0.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
|
||||
@ -197,7 +198,7 @@ fn depend_on_alt_registry_depends_on_crates_io() {
|
||||
|
||||
Package::new("baz", "0.0.1").publish();
|
||||
Package::new("bar", "0.0.1")
|
||||
.registry_dep("baz", "0.0.1", registry::registry().as_str())
|
||||
.dep("baz", "0.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
|
||||
@ -210,7 +211,7 @@ fn depend_on_alt_registry_depends_on_crates_io() {
|
||||
[DOWNLOADING] crates ...
|
||||
[DOWNLOADED] baz v0.0.1 (registry `[ROOT][..]`)
|
||||
[DOWNLOADED] bar v0.0.1 (registry `[ROOT][..]`)
|
||||
[COMPILING] baz v0.0.1 (registry `[ROOT][..]`)
|
||||
[COMPILING] baz v0.0.1
|
||||
[COMPILING] bar v0.0.1 (registry `[ROOT][..]`)
|
||||
[COMPILING] foo v0.0.1 ([CWD])
|
||||
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
|
||||
@ -345,6 +346,40 @@ fn publish_with_registry_dependency() {
|
||||
p.cargo("publish --registry alternative -Zunstable-options")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
|
||||
validate_alt_upload(
|
||||
r#"{
|
||||
"authors": [],
|
||||
"badges": {},
|
||||
"categories": [],
|
||||
"deps": [
|
||||
{
|
||||
"default_features": true,
|
||||
"features": [],
|
||||
"kind": "normal",
|
||||
"name": "bar",
|
||||
"optional": false,
|
||||
"target": null,
|
||||
"version_req": "^0.0.1"
|
||||
}
|
||||
],
|
||||
"description": null,
|
||||
"documentation": null,
|
||||
"features": {},
|
||||
"homepage": null,
|
||||
"keywords": [],
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"name": "foo",
|
||||
"readme": null,
|
||||
"readme_file": null,
|
||||
"repository": null,
|
||||
"vers": "0.0.1"
|
||||
}"#,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -440,8 +475,29 @@ fn publish_to_alt_registry() {
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
|
||||
// Ensure that the crate is uploaded
|
||||
assert!(alt_api_path().join("api/v1/crates/new").exists());
|
||||
validate_alt_upload(
|
||||
r#"{
|
||||
"authors": [],
|
||||
"badges": {},
|
||||
"categories": [],
|
||||
"deps": [],
|
||||
"description": null,
|
||||
"documentation": null,
|
||||
"features": {},
|
||||
"homepage": null,
|
||||
"keywords": [],
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"name": "foo",
|
||||
"readme": null,
|
||||
"readme_file": null,
|
||||
"repository": null,
|
||||
"vers": "0.0.1"
|
||||
}"#,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -476,6 +532,41 @@ fn publish_with_crates_io_dep() {
|
||||
p.cargo("publish --registry alternative -Zunstable-options")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
|
||||
validate_alt_upload(
|
||||
r#"{
|
||||
"authors": ["me"],
|
||||
"badges": {},
|
||||
"categories": [],
|
||||
"deps": [
|
||||
{
|
||||
"default_features": true,
|
||||
"features": [],
|
||||
"kind": "normal",
|
||||
"name": "bar",
|
||||
"optional": false,
|
||||
"registry": "https://github.com/rust-lang/crates.io-index",
|
||||
"target": null,
|
||||
"version_req": "^0.0.1"
|
||||
}
|
||||
],
|
||||
"description": "foo",
|
||||
"documentation": null,
|
||||
"features": {},
|
||||
"homepage": null,
|
||||
"keywords": [],
|
||||
"license": "MIT",
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"name": "foo",
|
||||
"readme": null,
|
||||
"readme_file": null,
|
||||
"repository": null,
|
||||
"vers": "0.0.1"
|
||||
}"#,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -696,3 +787,279 @@ fn no_api() {
|
||||
.with_status(101)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_reg_metadata() {
|
||||
// Check for "registry" entries in `cargo metadata` with alternative registries.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["alternative-registries"]
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
|
||||
[dependencies]
|
||||
altdep = { version = "0.0.1", registry = "alternative" }
|
||||
iodep = { version = "0.0.1" }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
Package::new("bar", "0.0.1").publish();
|
||||
Package::new("altdep", "0.0.1")
|
||||
.dep("bar", "0.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("altdep2", "0.0.1").alternative(true).publish();
|
||||
Package::new("iodep", "0.0.1")
|
||||
.registry_dep("altdep2", "0.0.1")
|
||||
.publish();
|
||||
|
||||
// The important thing to check here is the "registry" value in `deps`.
|
||||
// They should be:
|
||||
// foo -> altdep: alternative-registry
|
||||
// foo -> iodep: null (because it is in crates.io)
|
||||
// altdep -> bar: null (because it is in crates.io)
|
||||
// iodep -> altdep2: alternative-registry
|
||||
p.cargo("metadata --format-version=1 --no-deps")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_json(
|
||||
r#"
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "foo",
|
||||
"version": "0.0.1",
|
||||
"id": "foo 0.0.1 (path+file:[..]/foo)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": null,
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "altdep",
|
||||
"source": "registry+file:[..]/alternative-registry",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": "file:[..]/alternative-registry"
|
||||
},
|
||||
{
|
||||
"name": "iodep",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": null
|
||||
}
|
||||
],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/foo/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"workspace_members": [
|
||||
"foo 0.0.1 (path+file:[..]/foo)"
|
||||
],
|
||||
"resolve": null,
|
||||
"target_directory": "[..]/foo/target",
|
||||
"version": 1,
|
||||
"workspace_root": "[..]/foo"
|
||||
}"#,
|
||||
)
|
||||
.run();
|
||||
|
||||
// --no-deps uses a different code path, make sure both work.
|
||||
p.cargo("metadata --format-version=1")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_json(
|
||||
r#"
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "altdep2",
|
||||
"version": "0.0.1",
|
||||
"id": "altdep2 0.0.1 (registry+file:[..]/alternative-registry)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": "registry+file:[..]/alternative-registry",
|
||||
"dependencies": [],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/altdep2-0.0.1/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "altdep",
|
||||
"version": "0.0.1",
|
||||
"id": "altdep 0.0.1 (registry+file:[..]/alternative-registry)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": "registry+file:[..]/alternative-registry",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "bar",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": null
|
||||
}
|
||||
],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/altdep-0.0.1/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "foo",
|
||||
"version": "0.0.1",
|
||||
"id": "foo 0.0.1 (path+file:[..]/foo)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": null,
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "altdep",
|
||||
"source": "registry+file:[..]/alternative-registry",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": "file:[..]/alternative-registry"
|
||||
},
|
||||
{
|
||||
"name": "iodep",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": null
|
||||
}
|
||||
],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/foo/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "iodep",
|
||||
"version": "0.0.1",
|
||||
"id": "iodep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "altdep2",
|
||||
"source": "registry+file:[..]/alternative-registry",
|
||||
"req": "^0.0.1",
|
||||
"kind": null,
|
||||
"rename": null,
|
||||
"optional": false,
|
||||
"uses_default_features": true,
|
||||
"features": [],
|
||||
"target": null,
|
||||
"registry": "file:[..]/alternative-registry"
|
||||
}
|
||||
],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/iodep-0.0.1/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"version": "0.0.1",
|
||||
"id": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"description": null,
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dependencies": [],
|
||||
"targets": "{...}",
|
||||
"features": {},
|
||||
"manifest_path": "[..]/bar-0.0.1/Cargo.toml",
|
||||
"metadata": null,
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"keywords": [],
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"edition": "2015",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"workspace_members": [
|
||||
"foo 0.0.1 (path+file:[..]/foo)"
|
||||
],
|
||||
"resolve": "{...}",
|
||||
"target_directory": "[..]/foo/target",
|
||||
"version": 1,
|
||||
"workspace_root": "[..]/foo"
|
||||
}"#,
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::support::{cross_compile, project, publish};
|
||||
use flate2::read::GzDecoder;
|
||||
use tar::Archive;
|
||||
|
||||
#[test]
|
||||
fn simple_cross_package() {
|
||||
@ -54,17 +50,12 @@ fn simple_cross_package() {
|
||||
|
||||
// Check that the tarball contains the files
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let entries = ar.entries().unwrap();
|
||||
let entry_paths = entries
|
||||
.map(|entry| entry.unwrap().path().unwrap().into_owned())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
assert!(entry_paths.contains(&PathBuf::from("foo-0.0.0/Cargo.toml")));
|
||||
assert!(entry_paths.contains(&PathBuf::from("foo-0.0.0/Cargo.toml.orig")));
|
||||
assert!(entry_paths.contains(&PathBuf::from("foo-0.0.0/src/main.rs")));
|
||||
publish::validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.0.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -321,6 +321,7 @@ fn cargo_metadata_with_deps_and_version() {
|
||||
"kind": null,
|
||||
"name": "bar",
|
||||
"optional": false,
|
||||
"registry": null,
|
||||
"rename": null,
|
||||
"req": "*",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
@ -332,6 +333,7 @@ fn cargo_metadata_with_deps_and_version() {
|
||||
"kind": "dev",
|
||||
"name": "foobar",
|
||||
"optional": false,
|
||||
"registry": null,
|
||||
"rename": null,
|
||||
"req": "*",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
@ -410,6 +412,7 @@ fn cargo_metadata_with_deps_and_version() {
|
||||
"kind": null,
|
||||
"name": "baz",
|
||||
"optional": false,
|
||||
"registry": null,
|
||||
"rename": null,
|
||||
"req": "^0.0.1",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
@ -1417,6 +1420,7 @@ fn rename_dependency() {
|
||||
"name": "bar",
|
||||
"optional": false,
|
||||
"rename": null,
|
||||
"registry": null,
|
||||
"req": "^0.1.0",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"target": null,
|
||||
@ -1428,6 +1432,7 @@ fn rename_dependency() {
|
||||
"name": "bar",
|
||||
"optional": false,
|
||||
"rename": "baz",
|
||||
"registry": null,
|
||||
"req": "^0.2.0",
|
||||
"source": "registry+https://github.com/rust-lang/crates.io-index",
|
||||
"target": null,
|
||||
|
@ -1,14 +1,15 @@
|
||||
use std;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::support::registry::Package;
|
||||
use crate::support::{basic_manifest, git, is_nightly, path2url, paths, project, registry};
|
||||
use crate::support::{
|
||||
basic_manifest, git, is_nightly, path2url, paths, project, publish::validate_crate_contents,
|
||||
registry,
|
||||
};
|
||||
use crate::support::{cargo_process, sleep_ms};
|
||||
use flate2::read::GzDecoder;
|
||||
use git2;
|
||||
use tar::Archive;
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
@ -53,22 +54,12 @@ src/main.rs
|
||||
p.cargo("package").with_stdout("").run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for f in ar.entries().unwrap() {
|
||||
let f = f.unwrap();
|
||||
let fname = f.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
f.header().path()
|
||||
)
|
||||
}
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -168,29 +159,25 @@ See http://doc.crates.io/manifest.html#package-metadata for more info.
|
||||
.run();
|
||||
|
||||
let f = File::open(&repo.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let mut entry = ar
|
||||
.entries()
|
||||
.unwrap()
|
||||
.map(|f| f.unwrap())
|
||||
.find(|e| e.path().unwrap().ends_with(".cargo_vcs_info.json"))
|
||||
.unwrap();
|
||||
let mut contents = String::new();
|
||||
entry.read_to_string(&mut contents).unwrap();
|
||||
assert_eq!(
|
||||
&contents[..],
|
||||
&*format!(
|
||||
r#"{{
|
||||
let vcs_contents = format!(
|
||||
r#"{{
|
||||
"git": {{
|
||||
"sha1": "{}"
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
repo.revparse_head()
|
||||
)
|
||||
repo.revparse_head()
|
||||
);
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&[
|
||||
"Cargo.toml",
|
||||
"Cargo.toml.orig",
|
||||
"src/main.rs",
|
||||
".cargo_vcs_info.json",
|
||||
],
|
||||
&[(".cargo_vcs_info.json", &vcs_contents)],
|
||||
);
|
||||
|
||||
println!("package sub-repo");
|
||||
@ -602,22 +589,12 @@ src[..]main.rs
|
||||
p.cargo("package").with_stdout("").run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for f in ar.entries().unwrap() {
|
||||
let f = f.unwrap();
|
||||
let fname = f.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
f.header().path()
|
||||
)
|
||||
}
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)] // windows doesn't allow these characters in filenames
|
||||
@ -681,15 +658,12 @@ See [..]
|
||||
|
||||
// Check that the tarball contains the added file
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let entries = ar.entries().unwrap();
|
||||
let entry_paths = entries
|
||||
.map(|entry| entry.unwrap().path().unwrap().into_owned())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
assert!(entry_paths.contains(&PathBuf::from("foo-0.0.1/src/foo.rs")));
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "src/foo.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -827,24 +801,8 @@ fn generated_manifest() {
|
||||
.run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let mut entry = ar
|
||||
.entries()
|
||||
.unwrap()
|
||||
.map(|f| f.unwrap())
|
||||
.find(|e| e.path().unwrap().ends_with("Cargo.toml"))
|
||||
.unwrap();
|
||||
let mut contents = String::new();
|
||||
entry.read_to_string(&mut contents).unwrap();
|
||||
// BTreeMap makes the order of dependencies in the generated file deterministic
|
||||
// by sorting alphabetically
|
||||
assert_eq!(
|
||||
&contents[..],
|
||||
&*format!(
|
||||
r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
let rewritten_toml = format!(
|
||||
r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
@ -881,8 +839,14 @@ registry-index = "{}"
|
||||
[dependencies.ghi]
|
||||
version = "1.0"
|
||||
"#,
|
||||
registry::alt_registry()
|
||||
)
|
||||
registry::alt_registry()
|
||||
);
|
||||
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
&[("Cargo.toml", &rewritten_toml)],
|
||||
);
|
||||
}
|
||||
|
||||
@ -923,21 +887,7 @@ fn ignore_workspace_specifier() {
|
||||
.run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let mut entry = ar
|
||||
.entries()
|
||||
.unwrap()
|
||||
.map(|f| f.unwrap())
|
||||
.find(|e| e.path().unwrap().ends_with("Cargo.toml"))
|
||||
.unwrap();
|
||||
let mut contents = String::new();
|
||||
entry.read_to_string(&mut contents).unwrap();
|
||||
assert_eq!(
|
||||
&contents[..],
|
||||
r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
let rewritten_toml = r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
@ -953,7 +903,12 @@ fn ignore_workspace_specifier() {
|
||||
name = "bar"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
"#
|
||||
"#;
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"bar-0.1.0.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
|
||||
&[("Cargo.toml", &rewritten_toml)],
|
||||
);
|
||||
}
|
||||
|
||||
@ -1123,23 +1078,12 @@ src/main.rs
|
||||
.run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for f in ar.entries().unwrap() {
|
||||
let f = f.unwrap();
|
||||
let fname = f.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/Cargo.lock"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
f.header().path()
|
||||
)
|
||||
}
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1202,15 +1146,12 @@ fn no_lock_file_with_library() {
|
||||
p.cargo("package").masquerade_as_nightly_cargo().run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for f in ar.entries().unwrap() {
|
||||
let f = f.unwrap();
|
||||
let fname = f.header().path().unwrap();
|
||||
assert!(!fname.ends_with("Cargo.lock"));
|
||||
}
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1246,15 +1187,12 @@ fn lock_file_and_workspace() {
|
||||
.run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
assert!(ar.entries().unwrap().into_iter().any(|f| {
|
||||
let f = f.unwrap();
|
||||
let fname = f.header().path().unwrap();
|
||||
fname.ends_with("Cargo.lock")
|
||||
}));
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "Cargo.lock"],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,12 +1,72 @@
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
use crate::support::git::repo;
|
||||
use crate::support::paths;
|
||||
use crate::support::{basic_manifest, project, publish};
|
||||
use flate2::read::GzDecoder;
|
||||
use tar::Archive;
|
||||
|
||||
const CLEAN_FOO_JSON: &str = r#"
|
||||
{
|
||||
"authors": [],
|
||||
"badges": {},
|
||||
"categories": [],
|
||||
"deps": [],
|
||||
"description": "foo",
|
||||
"documentation": "foo",
|
||||
"features": {},
|
||||
"homepage": "foo",
|
||||
"keywords": [],
|
||||
"license": "MIT",
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"name": "foo",
|
||||
"readme": null,
|
||||
"readme_file": null,
|
||||
"repository": "foo",
|
||||
"vers": "0.0.1"
|
||||
}
|
||||
"#;
|
||||
|
||||
fn validate_upload_foo() {
|
||||
publish::validate_upload(
|
||||
r#"
|
||||
{
|
||||
"authors": [],
|
||||
"badges": {},
|
||||
"categories": [],
|
||||
"deps": [],
|
||||
"description": "foo",
|
||||
"documentation": null,
|
||||
"features": {},
|
||||
"homepage": null,
|
||||
"keywords": [],
|
||||
"license": "MIT",
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"name": "foo",
|
||||
"readme": null,
|
||||
"readme_file": null,
|
||||
"repository": null,
|
||||
"vers": "0.0.1"
|
||||
}
|
||||
"#,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_upload_foo_clean() {
|
||||
publish::validate_upload(
|
||||
CLEAN_FOO_JSON,
|
||||
"foo-0.0.1.crate",
|
||||
&[
|
||||
"Cargo.toml",
|
||||
"Cargo.toml.orig",
|
||||
"src/main.rs",
|
||||
".cargo_vcs_info.json",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
@ -41,37 +101,7 @@ See [..]
|
||||
))
|
||||
.run();
|
||||
|
||||
let mut f = File::open(&publish::upload_path().join("api/v1/crates/new")).unwrap();
|
||||
// Skip the metadata payload and the size of the tarball
|
||||
let mut sz = [0; 4];
|
||||
assert_eq!(f.read(&mut sz).unwrap(), 4);
|
||||
let sz = (u32::from(sz[0]) << 0)
|
||||
| (u32::from(sz[1]) << 8)
|
||||
| (u32::from(sz[2]) << 16)
|
||||
| (u32::from(sz[3]) << 24);
|
||||
f.seek(SeekFrom::Current(i64::from(sz) + 4)).unwrap();
|
||||
|
||||
// Verify the tarball
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
assert_eq!(
|
||||
rdr.header().unwrap().filename().unwrap(),
|
||||
b"foo-0.0.1.crate"
|
||||
);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
let fname = file.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
file.header().path()
|
||||
);
|
||||
}
|
||||
validate_upload_foo();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -116,37 +146,7 @@ See [..]
|
||||
))
|
||||
.run();
|
||||
|
||||
let mut f = File::open(&publish::upload_path().join("api/v1/crates/new")).unwrap();
|
||||
// Skip the metadata payload and the size of the tarball
|
||||
let mut sz = [0; 4];
|
||||
assert_eq!(f.read(&mut sz).unwrap(), 4);
|
||||
let sz = (u32::from(sz[0]) << 0)
|
||||
| (u32::from(sz[1]) << 8)
|
||||
| (u32::from(sz[2]) << 16)
|
||||
| (u32::from(sz[3]) << 24);
|
||||
f.seek(SeekFrom::Current(i64::from(sz) + 4)).unwrap();
|
||||
|
||||
// Verify the tarball
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
assert_eq!(
|
||||
rdr.header().unwrap().filename().unwrap(),
|
||||
b"foo-0.0.1.crate"
|
||||
);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
let fname = file.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
file.header().path()
|
||||
);
|
||||
}
|
||||
validate_upload_foo();
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
@ -193,37 +193,7 @@ See [..]
|
||||
))
|
||||
.run();
|
||||
|
||||
let mut f = File::open(&publish::upload_path().join("api/v1/crates/new")).unwrap();
|
||||
// Skip the metadata payload and the size of the tarball
|
||||
let mut sz = [0; 4];
|
||||
assert_eq!(f.read(&mut sz).unwrap(), 4);
|
||||
let sz = (u32::from(sz[0]) << 0)
|
||||
| (u32::from(sz[1]) << 8)
|
||||
| (u32::from(sz[2]) << 16)
|
||||
| (u32::from(sz[3]) << 24);
|
||||
f.seek(SeekFrom::Current(i64::from(sz) + 4)).unwrap();
|
||||
|
||||
// Verify the tarball
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
assert_eq!(
|
||||
rdr.header().unwrap().filename().unwrap(),
|
||||
b"foo-0.0.1.crate"
|
||||
);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
let fname = file.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
file.header().path()
|
||||
);
|
||||
}
|
||||
validate_upload_foo();
|
||||
}
|
||||
|
||||
// TODO: Deprecated
|
||||
@ -272,37 +242,7 @@ See [..]
|
||||
))
|
||||
.run();
|
||||
|
||||
let mut f = File::open(&publish::upload_path().join("api/v1/crates/new")).unwrap();
|
||||
// Skip the metadata payload and the size of the tarball
|
||||
let mut sz = [0; 4];
|
||||
assert_eq!(f.read(&mut sz).unwrap(), 4);
|
||||
let sz = (u32::from(sz[0]) << 0)
|
||||
| (u32::from(sz[1]) << 8)
|
||||
| (u32::from(sz[2]) << 16)
|
||||
| (u32::from(sz[3]) << 24);
|
||||
f.seek(SeekFrom::Current(i64::from(sz) + 4)).unwrap();
|
||||
|
||||
// Verify the tarball
|
||||
let mut rdr = GzDecoder::new(f);
|
||||
assert_eq!(
|
||||
rdr.header().unwrap().filename().unwrap(),
|
||||
b"foo-0.0.1.crate"
|
||||
);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
let fname = file.header().path_bytes();
|
||||
let fname = &*fname;
|
||||
assert!(
|
||||
fname == b"foo-0.0.1/Cargo.toml"
|
||||
|| fname == b"foo-0.0.1/Cargo.toml.orig"
|
||||
|| fname == b"foo-0.0.1/src/main.rs",
|
||||
"unexpected filename: {:?}",
|
||||
file.header().path()
|
||||
);
|
||||
}
|
||||
validate_upload_foo();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -479,6 +419,8 @@ fn publish_clean() {
|
||||
p.cargo("publish --index")
|
||||
.arg(publish::registry().to_string())
|
||||
.run();
|
||||
|
||||
validate_upload_foo_clean();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -510,6 +452,8 @@ fn publish_in_sub_repo() {
|
||||
.arg("--index")
|
||||
.arg(publish::registry().to_string())
|
||||
.run();
|
||||
|
||||
validate_upload_foo_clean();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -540,6 +484,18 @@ fn publish_when_ignored() {
|
||||
p.cargo("publish --index")
|
||||
.arg(publish::registry().to_string())
|
||||
.run();
|
||||
|
||||
publish::validate_upload(
|
||||
CLEAN_FOO_JSON,
|
||||
"foo-0.0.1.crate",
|
||||
&[
|
||||
"Cargo.toml",
|
||||
"Cargo.toml.orig",
|
||||
"src/main.rs",
|
||||
".gitignore",
|
||||
".cargo_vcs_info.json",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -570,6 +526,12 @@ fn ignore_when_crate_ignored() {
|
||||
.arg("--index")
|
||||
.arg(publish::registry().to_string())
|
||||
.run();
|
||||
|
||||
publish::validate_upload(
|
||||
CLEAN_FOO_JSON,
|
||||
"foo-0.0.1.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "baz"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -778,6 +740,7 @@ fn publish_allowed_registry() {
|
||||
description = "foo"
|
||||
documentation = "foo"
|
||||
homepage = "foo"
|
||||
repository = "foo"
|
||||
publish = ["alternative"]
|
||||
"#,
|
||||
)
|
||||
@ -787,6 +750,8 @@ fn publish_allowed_registry() {
|
||||
p.cargo("publish --registry alternative -Zunstable-options")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
|
||||
validate_upload_foo_clean();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1192,16 +1192,7 @@ impl Execs {
|
||||
Ok(actual) => actual,
|
||||
};
|
||||
|
||||
match find_mismatch(expected, &actual) {
|
||||
Some((expected_part, actual_part)) => Err(format!(
|
||||
"JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n",
|
||||
serde_json::to_string_pretty(expected).unwrap(),
|
||||
serde_json::to_string_pretty(&actual).unwrap(),
|
||||
serde_json::to_string_pretty(expected_part).unwrap(),
|
||||
serde_json::to_string_pretty(actual_part).unwrap(),
|
||||
)),
|
||||
None => Ok(()),
|
||||
}
|
||||
find_json_mismatch(expected, &actual)
|
||||
}
|
||||
|
||||
fn diff_lines<'a>(
|
||||
@ -1292,12 +1283,28 @@ fn lines_match_works() {
|
||||
assert!(!lines_match("b", "cb"));
|
||||
}
|
||||
|
||||
// Compares JSON object for approximate equality.
|
||||
// You can use `[..]` wildcard in strings (useful for OS dependent things such
|
||||
// as paths). You can use a `"{...}"` string literal as a wildcard for
|
||||
// arbitrary nested JSON (useful for parts of object emitted by other programs
|
||||
// (e.g. rustc) rather than Cargo itself). Arrays are sorted before comparison.
|
||||
fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
|
||||
/// Compares JSON object for approximate equality.
|
||||
/// You can use `[..]` wildcard in strings (useful for OS dependent things such
|
||||
/// as paths). You can use a `"{...}"` string literal as a wildcard for
|
||||
/// arbitrary nested JSON (useful for parts of object emitted by other programs
|
||||
/// (e.g. rustc) rather than Cargo itself). Arrays are sorted before comparison.
|
||||
pub fn find_json_mismatch(expected: &Value, actual: &Value) -> Result<(), String> {
|
||||
match find_json_mismatch_r(expected, &actual) {
|
||||
Some((expected_part, actual_part)) => Err(format!(
|
||||
"JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n",
|
||||
serde_json::to_string_pretty(expected).unwrap(),
|
||||
serde_json::to_string_pretty(&actual).unwrap(),
|
||||
serde_json::to_string_pretty(expected_part).unwrap(),
|
||||
serde_json::to_string_pretty(actual_part).unwrap(),
|
||||
)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_json_mismatch_r<'a>(
|
||||
expected: &'a Value,
|
||||
actual: &'a Value,
|
||||
) -> Option<(&'a Value, &'a Value)> {
|
||||
use serde_json::Value::*;
|
||||
match (expected, actual) {
|
||||
(&Number(ref l), &Number(ref r)) if l == r => None,
|
||||
@ -1312,7 +1319,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu
|
||||
let mut r = r.iter().collect::<Vec<_>>();
|
||||
|
||||
l.retain(
|
||||
|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
|
||||
|l| match r.iter().position(|r| find_json_mismatch_r(l, r).is_none()) {
|
||||
Some(i) => {
|
||||
r.remove(i);
|
||||
false
|
||||
@ -1337,7 +1344,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu
|
||||
|
||||
l.values()
|
||||
.zip(r.values())
|
||||
.filter_map(|(l, r)| find_mismatch(l, r))
|
||||
.filter_map(|(l, r)| find_json_mismatch_r(l, r))
|
||||
.nth(0)
|
||||
}
|
||||
(&Null, &Null) => None,
|
||||
|
@ -1,10 +1,15 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::io::{prelude::*, SeekFrom};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::support::git::{repo, Repository};
|
||||
use crate::support::paths;
|
||||
use crate::support::registry::alt_api_path;
|
||||
use crate::support::{find_json_mismatch, paths};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use flate2::read::GzDecoder;
|
||||
use tar::Archive;
|
||||
use url::Url;
|
||||
|
||||
pub fn setup() -> Repository {
|
||||
@ -61,3 +66,116 @@ pub fn upload_path() -> PathBuf {
|
||||
fn upload() -> Url {
|
||||
Url::from_file_path(&*upload_path()).ok().unwrap()
|
||||
}
|
||||
|
||||
/// Check the result of a crate publish.
|
||||
pub fn validate_upload(expected_json: &str, expected_crate_name: &str, expected_files: &[&str]) {
|
||||
let new_path = upload_path().join("api/v1/crates/new");
|
||||
_validate_upload(
|
||||
&new_path,
|
||||
expected_json,
|
||||
expected_crate_name,
|
||||
expected_files,
|
||||
);
|
||||
}
|
||||
|
||||
/// Check the result of a crate publish to an alternative registry.
|
||||
pub fn validate_alt_upload(
|
||||
expected_json: &str,
|
||||
expected_crate_name: &str,
|
||||
expected_files: &[&str],
|
||||
) {
|
||||
let new_path = alt_api_path().join("api/v1/crates/new");
|
||||
_validate_upload(
|
||||
&new_path,
|
||||
expected_json,
|
||||
expected_crate_name,
|
||||
expected_files,
|
||||
);
|
||||
}
|
||||
|
||||
fn _validate_upload(
|
||||
new_path: &Path,
|
||||
expected_json: &str,
|
||||
expected_crate_name: &str,
|
||||
expected_files: &[&str],
|
||||
) {
|
||||
let mut f = File::open(new_path).unwrap();
|
||||
// 32-bit little-endian integer of length of JSON data.
|
||||
let json_sz = f.read_u32::<LittleEndian>().expect("read json length");
|
||||
let mut json_bytes = vec![0; json_sz as usize];
|
||||
f.read_exact(&mut json_bytes).expect("read JSON data");
|
||||
let actual_json = serde_json::from_slice(&json_bytes).expect("uploaded JSON should be valid");
|
||||
let expected_json = serde_json::from_str(expected_json).expect("expected JSON does not parse");
|
||||
find_json_mismatch(&expected_json, &actual_json)
|
||||
.expect("uploaded JSON did not match expected JSON");
|
||||
|
||||
// 32-bit little-endian integer of length of crate file.
|
||||
let crate_sz = f.read_u32::<LittleEndian>().expect("read crate length");
|
||||
let mut krate_bytes = vec![0; crate_sz as usize];
|
||||
f.read_exact(&mut krate_bytes).expect("read crate data");
|
||||
// Check at end.
|
||||
let current = f.seek(SeekFrom::Current(0)).unwrap();
|
||||
assert_eq!(f.seek(SeekFrom::End(0)).unwrap(), current);
|
||||
|
||||
// Verify the tarball
|
||||
validate_crate_contents(&krate_bytes[..], expected_crate_name, expected_files, &[]);
|
||||
}
|
||||
|
||||
/// Check the contents of a `.crate` file.
|
||||
///
|
||||
/// - `expected_crate_name` should be something like `foo-0.0.1.crate`.
|
||||
/// - `expected_files` should be a complete list of files in the crate
|
||||
/// (relative to expected_crate_name).
|
||||
/// - `expected_contents` should be a list of `(file_name, contents)` tuples
|
||||
/// to validate the contents of the given file. Only the listed files will
|
||||
/// be checked (others will be ignored).
|
||||
pub fn validate_crate_contents(
|
||||
reader: impl Read,
|
||||
expected_crate_name: &str,
|
||||
expected_files: &[&str],
|
||||
expected_contents: &[(&str, &str)],
|
||||
) {
|
||||
let mut rdr = GzDecoder::new(reader);
|
||||
assert_eq!(
|
||||
rdr.header().unwrap().filename().unwrap(),
|
||||
expected_crate_name.as_bytes()
|
||||
);
|
||||
let mut contents = Vec::new();
|
||||
rdr.read_to_end(&mut contents).unwrap();
|
||||
let mut ar = Archive::new(&contents[..]);
|
||||
let files: HashMap<PathBuf, String> = ar
|
||||
.entries()
|
||||
.unwrap()
|
||||
.map(|entry| {
|
||||
let mut entry = entry.unwrap();
|
||||
let name = entry.path().unwrap().into_owned();
|
||||
let mut contents = String::new();
|
||||
entry.read_to_string(&mut contents).unwrap();
|
||||
(name, contents)
|
||||
})
|
||||
.collect();
|
||||
assert!(expected_crate_name.ends_with(".crate"));
|
||||
let base_crate_name = Path::new(&expected_crate_name[..expected_crate_name.len() - 6]);
|
||||
let actual_files: HashSet<PathBuf> = files.keys().cloned().collect();
|
||||
let expected_files: HashSet<PathBuf> = expected_files
|
||||
.iter()
|
||||
.map(|name| base_crate_name.join(name))
|
||||
.collect();
|
||||
let missing: Vec<&PathBuf> = expected_files.difference(&actual_files).collect();
|
||||
let extra: Vec<&PathBuf> = actual_files.difference(&expected_files).collect();
|
||||
if !missing.is_empty() || !extra.is_empty() {
|
||||
panic!(
|
||||
"uploaded archive does not match.\nMissing: {:?}\nExtra: {:?}\n",
|
||||
missing, extra
|
||||
);
|
||||
}
|
||||
if !expected_contents.is_empty() {
|
||||
for (e_file_name, e_file_contents) in expected_contents {
|
||||
let full_e_name = base_crate_name.join(e_file_name);
|
||||
let actual_contents = files
|
||||
.get(&full_e_name)
|
||||
.unwrap_or_else(|| panic!("file `{}` missing in archive", e_file_name));
|
||||
assert_eq!(actual_contents, e_file_contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use cargo::sources::CRATES_IO_INDEX;
|
||||
use cargo::util::Sha256;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
@ -277,10 +278,9 @@ impl Package {
|
||||
self.add_dep(Dependency::new(name, vers).target(target))
|
||||
}
|
||||
|
||||
/// Add a dependency to an alternative registry.
|
||||
/// The given registry should be a URI to the alternative registry.
|
||||
pub fn registry_dep(&mut self, name: &str, vers: &str, registry: &str) -> &mut Package {
|
||||
self.add_dep(Dependency::new(name, vers).registry(registry))
|
||||
/// Add a dependency to the alternative registry.
|
||||
pub fn registry_dep(&mut self, name: &str, vers: &str) -> &mut Package {
|
||||
self.add_dep(Dependency::new(name, vers).registry("alternative"))
|
||||
}
|
||||
|
||||
/// Add a dev-dependency. Example:
|
||||
@ -333,6 +333,16 @@ impl Package {
|
||||
.deps
|
||||
.iter()
|
||||
.map(|dep| {
|
||||
// In the index, the `registry` is null if it is from the same registry.
|
||||
// In Cargo.toml, it is None if it is from crates.io.
|
||||
let registry_url =
|
||||
match (self.alternative, dep.registry.as_ref().map(|s| s.as_ref())) {
|
||||
(false, None) => None,
|
||||
(false, Some("alternative")) => Some(alt_registry().to_string()),
|
||||
(true, None) => Some(CRATES_IO_INDEX.to_string()),
|
||||
(true, Some("alternative")) => None,
|
||||
_ => panic!("registry_dep currently only supports `alternative`"),
|
||||
};
|
||||
serde_json::json!({
|
||||
"name": dep.name,
|
||||
"req": dep.vers,
|
||||
@ -341,7 +351,7 @@ impl Package {
|
||||
"target": dep.target,
|
||||
"optional": dep.optional,
|
||||
"kind": dep.kind,
|
||||
"registry": dep.registry,
|
||||
"registry": registry_url,
|
||||
"package": dep.package,
|
||||
})
|
||||
})
|
||||
@ -412,14 +422,19 @@ impl Package {
|
||||
}
|
||||
|
||||
fn make_archive(&self) {
|
||||
let features = if self.deps.iter().any(|dep| dep.registry.is_some()) {
|
||||
"cargo-features = [\"alternative-registries\"]\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let mut manifest = format!(
|
||||
r#"
|
||||
[package]
|
||||
{}[package]
|
||||
name = "{}"
|
||||
version = "{}"
|
||||
authors = []
|
||||
"#,
|
||||
self.name, self.vers
|
||||
features, self.name, self.vers
|
||||
);
|
||||
for dep in self.deps.iter() {
|
||||
let target = match dep.target {
|
||||
@ -438,6 +453,9 @@ impl Package {
|
||||
"#,
|
||||
target, kind, dep.name, dep.vers
|
||||
));
|
||||
if let Some(registry) = &dep.registry {
|
||||
manifest.push_str(&format!("registry = \"{}\"", registry));
|
||||
}
|
||||
}
|
||||
|
||||
let dst = self.archive_dst();
|
||||
|
Loading…
x
Reference in New Issue
Block a user