| use std; |
| use std::fs::File; |
| use std::io::prelude::*; |
| use std::path::Path; |
| |
| use crate::support::cargo_process; |
| use crate::support::paths::CargoPathExt; |
| use crate::support::registry::Package; |
| use crate::support::{ |
| basic_manifest, git, path2url, paths, project, publish::validate_crate_contents, registry, |
| }; |
| use git2; |
| |
| #[cargo_test] |
| fn simple() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| exclude = ["*.txt"] |
| license = "MIT" |
| description = "foo" |
| "#, |
| ) |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .file("src/bar.txt", "") // should be ignored when packaging |
| .build(); |
| |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no documentation[..] |
| See [..] |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); |
| p.cargo("package -l") |
| .with_stdout( |
| "\ |
| Cargo.lock |
| Cargo.toml |
| src/main.rs |
| ", |
| ) |
| .run(); |
| p.cargo("package").with_stdout("").run(); |
| |
| let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); |
| validate_crate_contents( |
| f, |
| "foo-0.0.1.crate", |
| &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], |
| &[], |
| ); |
| } |
| |
| #[cargo_test] |
| fn metadata_warning() { |
| let p = project().file("src/main.rs", "fn main() {}").build(); |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| warning: manifest has no description, license, license-file, documentation, \ |
| homepage or repository. |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| warning: manifest has no description, documentation, homepage or repository. |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| repository = "bar" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn package_verbose() { |
| let root = paths::root().join("all"); |
| let repo = git::repo(&root) |
| .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) |
| .file("src/main.rs", "fn main() {}") |
| .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) |
| .file("a/src/lib.rs", "") |
| .build(); |
| cargo_process("build").cwd(repo.root()).run(); |
| |
| println!("package main repo"); |
| cargo_process("package -v --no-verify") |
| .cwd(repo.root()) |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no description[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] foo v0.0.1 ([..]) |
| [ARCHIVING] Cargo.toml |
| [ARCHIVING] src/main.rs |
| [ARCHIVING] .cargo_vcs_info.json |
| [ARCHIVING] Cargo.lock |
| ", |
| ) |
| .run(); |
| |
| let f = File::open(&repo.root().join("target/package/foo-0.0.1.crate")).unwrap(); |
| let vcs_contents = format!( |
| r#"{{ |
| "git": {{ |
| "sha1": "{}" |
| }} |
| }} |
| "#, |
| repo.revparse_head() |
| ); |
| validate_crate_contents( |
| f, |
| "foo-0.0.1.crate", |
| &[ |
| "Cargo.lock", |
| "Cargo.toml", |
| "Cargo.toml.orig", |
| "src/main.rs", |
| ".cargo_vcs_info.json", |
| ], |
| &[(".cargo_vcs_info.json", &vcs_contents)], |
| ); |
| |
| println!("package sub-repo"); |
| cargo_process("package -v --no-verify") |
| .cwd(repo.root().join("a")) |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no description[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] a v0.0.1 ([..]) |
| [ARCHIVING] Cargo.toml |
| [ARCHIVING] src/lib.rs |
| [ARCHIVING] .cargo_vcs_info.json |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn package_verification() { |
| let p = project().file("src/main.rs", "fn main() {}").build(); |
| p.cargo("build").run(); |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no description[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn vcs_file_collision() { |
| let p = project().build(); |
| let _ = git::repo(&paths::root().join("foo")) |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| description = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| documentation = "foo" |
| homepage = "foo" |
| repository = "foo" |
| exclude = ["*.no-existe"] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| r#" |
| fn main() {} |
| "#, |
| ) |
| .file(".cargo_vcs_info.json", "foo") |
| .build(); |
| p.cargo("package") |
| .arg("--no-verify") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] Invalid inclusion of reserved file name .cargo_vcs_info.json \ |
| in package source |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn path_dependency_no_version() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| |
| [dependencies.bar] |
| path = "bar" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no documentation, homepage or repository. |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [ERROR] all path dependencies must have a version specified when packaging. |
| dependency `bar` does not specify a version. |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn exclude() { |
| let root = paths::root().join("exclude"); |
| let repo = git::repo(&root) |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| exclude = [ |
| "*.txt", |
| # file in root |
| "file_root_1", # NO_CHANGE (ignored) |
| "/file_root_2", # CHANGING (packaged -> ignored) |
| "file_root_3/", # NO_CHANGE (packaged) |
| "file_root_4/*", # NO_CHANGE (packaged) |
| "file_root_5/**", # NO_CHANGE (packaged) |
| # file in sub-dir |
| "file_deep_1", # CHANGING (packaged -> ignored) |
| "/file_deep_2", # NO_CHANGE (packaged) |
| "file_deep_3/", # NO_CHANGE (packaged) |
| "file_deep_4/*", # NO_CHANGE (packaged) |
| "file_deep_5/**", # NO_CHANGE (packaged) |
| # dir in root |
| "dir_root_1", # CHANGING (packaged -> ignored) |
| "/dir_root_2", # CHANGING (packaged -> ignored) |
| "dir_root_3/", # CHANGING (packaged -> ignored) |
| "dir_root_4/*", # NO_CHANGE (ignored) |
| "dir_root_5/**", # NO_CHANGE (ignored) |
| # dir in sub-dir |
| "dir_deep_1", # CHANGING (packaged -> ignored) |
| "/dir_deep_2", # NO_CHANGE |
| "dir_deep_3/", # CHANGING (packaged -> ignored) |
| "dir_deep_4/*", # CHANGING (packaged -> ignored) |
| "dir_deep_5/**", # CHANGING (packaged -> ignored) |
| ] |
| "#, |
| ) |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .file("bar.txt", "") |
| .file("src/bar.txt", "") |
| // File in root. |
| .file("file_root_1", "") |
| .file("file_root_2", "") |
| .file("file_root_3", "") |
| .file("file_root_4", "") |
| .file("file_root_5", "") |
| // File in sub-dir. |
| .file("some_dir/file_deep_1", "") |
| .file("some_dir/file_deep_2", "") |
| .file("some_dir/file_deep_3", "") |
| .file("some_dir/file_deep_4", "") |
| .file("some_dir/file_deep_5", "") |
| // Dir in root. |
| .file("dir_root_1/some_dir/file", "") |
| .file("dir_root_2/some_dir/file", "") |
| .file("dir_root_3/some_dir/file", "") |
| .file("dir_root_4/some_dir/file", "") |
| .file("dir_root_5/some_dir/file", "") |
| // Dir in sub-dir. |
| .file("some_dir/dir_deep_1/some_dir/file", "") |
| .file("some_dir/dir_deep_2/some_dir/file", "") |
| .file("some_dir/dir_deep_3/some_dir/file", "") |
| .file("some_dir/dir_deep_4/some_dir/file", "") |
| .file("some_dir/dir_deep_5/some_dir/file", "") |
| .build(); |
| |
| cargo_process("package --no-verify -v") |
| .cwd(repo.root()) |
| .with_stdout("") |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no description[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [WARNING] [..] file `dir_root_1/some_dir/file` is now excluded. |
| See [..] |
| [WARNING] [..] file `dir_root_2/some_dir/file` is now excluded. |
| See [..] |
| [WARNING] [..] file `dir_root_3/some_dir/file` is now excluded. |
| See [..] |
| [WARNING] [..] file `some_dir/dir_deep_1/some_dir/file` is now excluded. |
| See [..] |
| [WARNING] [..] file `some_dir/dir_deep_3/some_dir/file` is now excluded. |
| See [..] |
| [WARNING] [..] file `some_dir/file_deep_1` is now excluded. |
| See [..] |
| [PACKAGING] foo v0.0.1 ([..]) |
| [ARCHIVING] Cargo.toml |
| [ARCHIVING] file_root_3 |
| [ARCHIVING] file_root_4 |
| [ARCHIVING] file_root_5 |
| [ARCHIVING] some_dir/dir_deep_2/some_dir/file |
| [ARCHIVING] some_dir/dir_deep_4/some_dir/file |
| [ARCHIVING] some_dir/dir_deep_5/some_dir/file |
| [ARCHIVING] some_dir/file_deep_2 |
| [ARCHIVING] some_dir/file_deep_3 |
| [ARCHIVING] some_dir/file_deep_4 |
| [ARCHIVING] some_dir/file_deep_5 |
| [ARCHIVING] src/main.rs |
| [ARCHIVING] .cargo_vcs_info.json |
| [ARCHIVING] Cargo.lock |
| ", |
| ) |
| .run(); |
| |
| assert!(repo.root().join("target/package/foo-0.0.1.crate").is_file()); |
| |
| cargo_process("package -l") |
| .cwd(repo.root()) |
| .with_stdout( |
| "\ |
| .cargo_vcs_info.json |
| Cargo.lock |
| Cargo.toml |
| file_root_3 |
| file_root_4 |
| file_root_5 |
| some_dir/dir_deep_2/some_dir/file |
| some_dir/dir_deep_4/some_dir/file |
| some_dir/dir_deep_5/some_dir/file |
| some_dir/file_deep_2 |
| some_dir/file_deep_3 |
| some_dir/file_deep_4 |
| some_dir/file_deep_5 |
| src/main.rs |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn include() { |
| let root = paths::root().join("include"); |
| let repo = git::repo(&root) |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| exclude = ["*.txt"] |
| include = ["foo.txt", "**/*.rs", "Cargo.toml"] |
| "#, |
| ) |
| .file("foo.txt", "") |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| // Should be ignored when packaging. |
| .file("src/bar.txt", "") |
| .build(); |
| |
| cargo_process("package --no-verify -v") |
| .cwd(repo.root()) |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no description[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [WARNING] both package.include and package.exclude are specified; the exclude list will be ignored |
| [PACKAGING] foo v0.0.1 ([..]) |
| [ARCHIVING] Cargo.toml |
| [ARCHIVING] foo.txt |
| [ARCHIVING] src/main.rs |
| [ARCHIVING] .cargo_vcs_info.json |
| [ARCHIVING] Cargo.lock |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn package_lib_with_bin() { |
| let p = project() |
| .file("src/main.rs", "extern crate foo; fn main() {}") |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package -v").run(); |
| } |
| |
| #[cargo_test] |
| fn package_git_submodule() { |
| let project = git::new("foo", |project| { |
| project |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = ["foo@example.com"] |
| license = "MIT" |
| description = "foo" |
| repository = "foo" |
| "#, |
| ) |
| .file("src/lib.rs", "pub fn foo() {}") |
| }) |
| .unwrap(); |
| let library = git::new("bar", |library| { |
| library.no_manifest().file("Makefile", "all:") |
| }) |
| .unwrap(); |
| |
| let repository = git2::Repository::open(&project.root()).unwrap(); |
| let url = path2url(library.root()).to_string(); |
| git::add_submodule(&repository, &url, Path::new("bar")); |
| git::commit(&repository); |
| |
| let repository = git2::Repository::open(&project.root().join("bar")).unwrap(); |
| repository |
| .reset( |
| &repository.revparse_single("HEAD").unwrap(), |
| git2::ResetType::Hard, |
| None, |
| ) |
| .unwrap(); |
| |
| project |
| .cargo("package --no-verify -v") |
| .with_stderr_contains("[ARCHIVING] bar/Makefile") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn no_duplicates_from_modified_tracked_files() { |
| let root = paths::root().join("all"); |
| let p = git::repo(&root) |
| .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| File::create(p.root().join("src/main.rs")) |
| .unwrap() |
| .write_all(br#"fn main() { println!("A change!"); }"#) |
| .unwrap(); |
| cargo_process("build").cwd(p.root()).run(); |
| cargo_process("package --list --allow-dirty") |
| .cwd(p.root()) |
| .with_stdout( |
| "\ |
| Cargo.lock |
| Cargo.toml |
| src/main.rs |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn ignore_nested() { |
| let cargo_toml = r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| "#; |
| let main_rs = r#" |
| fn main() { println!("hello"); } |
| "#; |
| let p = project() |
| .file("Cargo.toml", cargo_toml) |
| .file("src/main.rs", main_rs) |
| // If a project happens to contain a copy of itself, we should |
| // ignore it. |
| .file("a_dir/foo/Cargo.toml", cargo_toml) |
| .file("a_dir/foo/src/main.rs", main_rs) |
| .build(); |
| |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| [WARNING] manifest has no documentation[..] |
| See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); |
| p.cargo("package -l") |
| .with_stdout( |
| "\ |
| Cargo.lock |
| Cargo.toml |
| src[..]main.rs |
| ", |
| ) |
| .run(); |
| p.cargo("package").with_stdout("").run(); |
| |
| let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); |
| validate_crate_contents( |
| f, |
| "foo-0.0.1.crate", |
| &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], |
| &[], |
| ); |
| } |
| |
| // Windows doesn't allow these characters in filenames. |
| #[cfg(unix)] |
| #[cargo_test] |
| fn package_weird_characters() { |
| let p = project() |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .file("src/:foo", "") |
| .build(); |
| |
| p.cargo("package") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| warning: [..] |
| See [..] |
| [PACKAGING] foo [..] |
| [ERROR] failed to prepare local package for uploading |
| |
| Caused by: |
| cannot package a filename with a special character `:`: src/:foo |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn repackage_on_source_change() { |
| let p = project() |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .build(); |
| |
| p.cargo("package").run(); |
| |
| // Add another source file |
| let mut file = File::create(p.root().join("src").join("foo.rs")).unwrap_or_else(|e| { |
| panic!( |
| "could not create file {}: {}", |
| p.root().join("src/foo.rs").display(), |
| e |
| ) |
| }); |
| |
| file.write_all(br#"fn main() { println!("foo"); }"#) |
| .unwrap(); |
| std::mem::drop(file); |
| |
| // Check that cargo rebuilds the tarball |
| p.cargo("package") |
| .with_stderr( |
| "\ |
| [WARNING] [..] |
| See [..] |
| [PACKAGING] foo v0.0.1 ([CWD]) |
| [VERIFYING] foo v0.0.1 ([CWD]) |
| [COMPILING] foo v0.0.1 ([CWD][..]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| |
| // Check that the tarball contains the added file |
| let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); |
| validate_crate_contents( |
| f, |
| "foo-0.0.1.crate", |
| &[ |
| "Cargo.lock", |
| "Cargo.toml", |
| "Cargo.toml.orig", |
| "src/main.rs", |
| "src/foo.rs", |
| ], |
| &[], |
| ); |
| } |
| |
| #[cargo_test] |
| #[cfg(unix)] |
| fn broken_symlink() { |
| use std::os::unix::fs; |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = 'foo' |
| documentation = 'foo' |
| homepage = 'foo' |
| repository = 'foo' |
| "#, |
| ) |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .build(); |
| t!(fs::symlink("nowhere", &p.root().join("src/foo.rs"))); |
| |
| p.cargo("package -v") |
| .with_status(101) |
| .with_stderr_contains( |
| "\ |
| error: failed to prepare local package for uploading |
| |
| Caused by: |
| failed to open for archiving: `[..]foo.rs` |
| |
| Caused by: |
| [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn do_not_package_if_repository_is_dirty() { |
| let p = project().build(); |
| |
| // Create a Git repository containing a minimal Rust project. |
| let _ = git::repo(&paths::root().join("foo")) |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| license = "MIT" |
| description = "foo" |
| documentation = "foo" |
| homepage = "foo" |
| repository = "foo" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| // Modify Cargo.toml without committing the change. |
| p.change_file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| license = "MIT" |
| description = "foo" |
| documentation = "foo" |
| homepage = "foo" |
| repository = "foo" |
| # change |
| "#, |
| ); |
| |
| p.cargo("package") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| error: 1 files in the working directory contain changes that were not yet \ |
| committed into git: |
| |
| Cargo.toml |
| |
| to proceed despite this and include the uncommited changes, pass the `--allow-dirty` flag |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn generated_manifest() { |
| Package::new("abc", "1.0.0").publish(); |
| Package::new("def", "1.0.0").alternative(true).publish(); |
| Package::new("ghi", "1.0.0").publish(); |
| Package::new("bar", "0.1.0").publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| exclude = ["*.txt"] |
| license = "MIT" |
| description = "foo" |
| |
| [project.metadata] |
| foo = 'bar' |
| |
| [workspace] |
| |
| [dependencies] |
| bar = { path = "bar", version = "0.1" } |
| def = { version = "1.0", registry = "alternative" } |
| ghi = "1.0" |
| abc = "1.0" |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package --no-verify").run(); |
| |
| let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); |
| 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 |
| # with all versions of Cargo and also rewrite `path` dependencies |
| # to registry (e.g., crates.io) dependencies |
| # |
| # If you believe there's an error in this file please file an |
| # issue against the rust-lang/cargo repository. If you're |
| # editing this file be aware that the upstream Cargo.toml |
| # will likely look very different (and much more reasonable) |
| |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| exclude = ["*.txt"] |
| description = "foo" |
| license = "MIT" |
| |
| [package.metadata] |
| foo = "bar" |
| [dependencies.abc] |
| version = "1.0" |
| |
| [dependencies.bar] |
| version = "0.1" |
| |
| [dependencies.def] |
| version = "1.0" |
| registry-index = "{}" |
| |
| [dependencies.ghi] |
| version = "1.0" |
| "#, |
| registry::alt_registry_url() |
| ); |
| |
| validate_crate_contents( |
| f, |
| "foo-0.0.1.crate", |
| &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], |
| &[("Cargo.toml", &rewritten_toml)], |
| ); |
| } |
| |
| #[cargo_test] |
| fn ignore_workspace_specifier() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| |
| authors = [] |
| |
| [workspace] |
| |
| [dependencies] |
| bar = { path = "bar", version = "0.1" } |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .file( |
| "bar/Cargo.toml", |
| r#" |
| [package] |
| name = "bar" |
| version = "0.1.0" |
| authors = [] |
| workspace = ".." |
| "#, |
| ) |
| .file("bar/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package --no-verify").cwd("bar").run(); |
| |
| let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap(); |
| 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 |
| # with all versions of Cargo and also rewrite `path` dependencies |
| # to registry (e.g., crates.io) dependencies |
| # |
| # If you believe there's an error in this file please file an |
| # issue against the rust-lang/cargo repository. If you're |
| # editing this file be aware that the upstream Cargo.toml |
| # will likely look very different (and much more reasonable) |
| |
| [package] |
| 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)], |
| ); |
| } |
| |
| #[cargo_test] |
| fn package_two_kinds_of_deps() { |
| Package::new("other", "1.0.0").publish(); |
| Package::new("other1", "1.0.0").publish(); |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dependencies] |
| other = "1.0" |
| other1 = { version = "1.0" } |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("package --no-verify").run(); |
| } |
| |
| #[cargo_test] |
| fn test_edition() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["edition"] |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| edition = "2018" |
| "#, |
| ) |
| .file("src/lib.rs", r#" "#) |
| .build(); |
| |
| p.cargo("build -v") |
| .with_stderr_contains( |
| "\ |
| [COMPILING] foo v0.0.1 ([..]) |
| [RUNNING] `rustc [..]--edition=2018 [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn edition_with_metadata() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| edition = "2018" |
| |
| [package.metadata.docs.rs] |
| features = ["foobar"] |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package").run(); |
| } |
| |
| #[cargo_test] |
| fn test_edition_malformed() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| edition = "chicken" |
| "#, |
| ) |
| .file("src/lib.rs", r#" "#) |
| .build(); |
| |
| p.cargo("build -v") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| error: failed to parse manifest at `[..]` |
| |
| Caused by: |
| failed to parse the `edition` key |
| |
| Caused by: |
| supported edition values are `2015` or `2018`, but `chicken` is unknown |
| " |
| .to_string(), |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn do_not_package_if_src_was_modified() { |
| let p = project() |
| .file("src/main.rs", r#"fn main() { println!("hello"); }"#) |
| .file("dir/foo.txt", "") |
| .file("bar.txt", "") |
| .file( |
| "build.rs", |
| r#" |
| use std::fs; |
| |
| fn main() { |
| fs::write("src/generated.txt", |
| "Hello, world of generated files." |
| ).expect("failed to create file"); |
| fs::remove_file("dir/foo.txt").expect("failed to remove file"); |
| fs::remove_dir("dir").expect("failed to remove dir"); |
| fs::write("bar.txt", "updated content").expect("failed to update"); |
| fs::create_dir("new-dir").expect("failed to create dir"); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("package") |
| .with_status(101) |
| .with_stderr_contains( |
| "\ |
| error: failed to verify package tarball |
| |
| Caused by: |
| Source directory was modified by build.rs during cargo publish. \ |
| Build scripts should not modify anything outside of OUT_DIR. |
| Changed: [CWD]/target/package/foo-0.0.1/bar.txt |
| Added: [CWD]/target/package/foo-0.0.1/new-dir |
| <tab>[CWD]/target/package/foo-0.0.1/src/generated.txt |
| Removed: [CWD]/target/package/foo-0.0.1/dir |
| <tab>[CWD]/target/package/foo-0.0.1/dir/foo.txt |
| |
| To proceed despite this, pass the `--no-verify` flag.", |
| ) |
| .run(); |
| |
| p.cargo("package --no-verify").run(); |
| } |
| |
| #[cargo_test] |
| fn package_with_select_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| |
| [features] |
| required = [] |
| optional = [] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| "#[cfg(not(feature = \"required\"))] |
| compile_error!(\"This crate requires `required` feature!\"); |
| fn main() {}", |
| ) |
| .build(); |
| |
| p.cargo("package --features required").run(); |
| } |
| |
| #[cargo_test] |
| fn package_with_all_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| |
| [features] |
| required = [] |
| optional = [] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| "#[cfg(not(feature = \"required\"))] |
| compile_error!(\"This crate requires `required` feature!\"); |
| fn main() {}", |
| ) |
| .build(); |
| |
| p.cargo("package --all-features").run(); |
| } |
| |
| #[cargo_test] |
| fn package_no_default_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| |
| [features] |
| default = ["required"] |
| required = [] |
| "#, |
| ) |
| .file( |
| "src/main.rs", |
| "#[cfg(not(feature = \"required\"))] |
| compile_error!(\"This crate requires `required` feature!\"); |
| fn main() {}", |
| ) |
| .build(); |
| |
| p.cargo("package --no-default-features") |
| .with_stderr_contains("error: This crate requires `required` feature!") |
| .with_status(101) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn include_cargo_toml_implicit() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| include = ["src/lib.rs"] |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .build(); |
| |
| p.cargo("package --list") |
| .with_stdout("Cargo.toml\nsrc/lib.rs\n") |
| .run(); |
| } |
| |
| fn include_exclude_test( |
| include: &str, |
| exclude: &str, |
| files: &[&str], |
| expected: &str, |
| has_warnings: bool, |
| ) { |
| let mut pb = project().file( |
| "Cargo.toml", |
| &format!( |
| r#" |
| [package] |
| name = "foo" |
| version = "0.1.0" |
| authors = [] |
| license = "MIT" |
| description = "foo" |
| documentation = "foo" |
| homepage = "foo" |
| repository = "foo" |
| include = {} |
| exclude = {} |
| "#, |
| include, exclude |
| ), |
| ); |
| for file in files { |
| pb = pb.file(file, ""); |
| } |
| let p = pb.build(); |
| |
| let mut e = p.cargo("package --list"); |
| if has_warnings { |
| e.with_stderr_contains("[..]"); |
| } else { |
| e.with_stderr(""); |
| } |
| e.with_stdout(expected).run(); |
| p.root().rm_rf(); |
| } |
| |
| #[cargo_test] |
| fn package_include_ignore_only() { |
| // Test with a gitignore pattern that fails to parse with glob. |
| // This is a somewhat nonsense pattern, but is an example of something git |
| // allows and glob does not. |
| assert!(glob::Pattern::new("src/abc**").is_err()); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "src/abc**", "src/lib.rs"]"#, |
| "[]", |
| &["src/lib.rs", "src/abc1.rs", "src/abc2.rs", "src/abc/mod.rs"], |
| "Cargo.toml\n\ |
| src/abc/mod.rs\n\ |
| src/abc1.rs\n\ |
| src/abc2.rs\n\ |
| src/lib.rs\n\ |
| ", |
| false, |
| ) |
| } |
| |
| #[cargo_test] |
| fn gitignore_patterns() { |
| include_exclude_test( |
| r#"["Cargo.toml", "foo"]"#, // include |
| "[]", |
| &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], |
| "Cargo.toml\n\ |
| a/b/foo\n\ |
| a/foo\n\ |
| foo\n\ |
| x/foo/y\n\ |
| ", |
| true, |
| ); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "/foo"]"#, // include |
| "[]", |
| &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], |
| "Cargo.toml\n\ |
| foo\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| "[]", |
| r#"["foo/"]"#, // exclude |
| &["src/lib.rs", "foo", "a/foo", "x/foo/y", "bar"], |
| "Cargo.toml\n\ |
| a/foo\n\ |
| bar\n\ |
| foo\n\ |
| src/lib.rs\n\ |
| ", |
| true, |
| ); |
| |
| include_exclude_test( |
| "[]", |
| r#"["*.txt", "[ab]", "[x-z]"]"#, // exclude |
| &[ |
| "src/lib.rs", |
| "foo.txt", |
| "bar/foo.txt", |
| "other", |
| "a", |
| "b", |
| "c", |
| "x", |
| "y", |
| "z", |
| ], |
| "Cargo.toml\n\ |
| c\n\ |
| other\n\ |
| src/lib.rs\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "**/foo/bar"]"#, // include |
| "[]", |
| &["src/lib.rs", "a/foo/bar", "foo", "bar"], |
| "Cargo.toml\n\ |
| a/foo/bar\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "foo/**"]"#, // include |
| "[]", |
| &["src/lib.rs", "a/foo/bar", "foo/x/y/z"], |
| "Cargo.toml\n\ |
| foo/x/y/z\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "a/**/b"]"#, // include |
| "[]", |
| &["src/lib.rs", "a/b", "a/x/b", "a/x/y/b"], |
| "Cargo.toml\n\ |
| a/b\n\ |
| a/x/b\n\ |
| a/x/y/b\n\ |
| ", |
| false, |
| ); |
| } |
| |
| #[cargo_test] |
| fn gitignore_negate() { |
| include_exclude_test( |
| r#"["Cargo.toml", "*.rs", "!foo.rs", "\\!important"]"#, // include |
| "[]", |
| &["src/lib.rs", "foo.rs", "!important"], |
| "!important\n\ |
| Cargo.toml\n\ |
| src/lib.rs\n\ |
| ", |
| false, |
| ); |
| |
| // NOTE: This is unusual compared to git. Git treats `src/` as a |
| // short-circuit which means rules like `!src/foo.rs` would never run. |
| // However, because Cargo only works by iterating over *files*, it doesn't |
| // short-circuit. |
| include_exclude_test( |
| r#"["Cargo.toml", "src/", "!src/foo.rs"]"#, // include |
| "[]", |
| &["src/lib.rs", "src/foo.rs"], |
| "Cargo.toml\n\ |
| src/lib.rs\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| r#"["Cargo.toml", "src/*.rs", "!foo.rs"]"#, // include |
| "[]", |
| &["src/lib.rs", "foo.rs", "src/foo.rs", "src/bar/foo.rs"], |
| "Cargo.toml\n\ |
| src/lib.rs\n\ |
| ", |
| false, |
| ); |
| |
| include_exclude_test( |
| "[]", |
| r#"["*.rs", "!foo.rs", "\\!important"]"#, // exclude |
| &["src/lib.rs", "foo.rs", "!important"], |
| "Cargo.toml\n\ |
| foo.rs\n\ |
| ", |
| false, |
| ); |
| } |