cargo/tests/testsuite/git.rs

2883 lines
70 KiB
Rust

use git2;
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use crate::support::paths::{self, CargoPathExt};
use crate::support::sleep_ms;
use crate::support::Project;
use crate::support::{basic_lib_manifest, basic_manifest, git, main_file, path2url, project};
#[test]
fn cargo_compile_simple_git_dep() {
let project = project();
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
})
.unwrap();
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/main.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
let git_root = git_project.root();
project
.cargo("build")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[COMPILING] dep1 v0.5.0 ({}#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
path2url(&git_root),
path2url(&git_root),
))
.run();
assert!(project.bin("foo").is_file());
project
.process(&project.bin("foo"))
.with_stdout("hello world\n")
.run();
}
#[test]
fn cargo_compile_forbird_git_httpsrepo_offline() {
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["chabapok@example.com"]
[dependencies.dep1]
git = 'https://github.com/some_user/dep1.git'
"#,
)
.file("src/main.rs", "")
.build();
p.cargo("build -Zoffline").masquerade_as_nightly_cargo().with_status(101).
with_stderr("\
error: failed to load source for a dependency on `dep1`
Caused by:
Unable to update https://github.com/some_user/dep1.git
Caused by:
can't checkout from 'https://github.com/some_user/dep1.git': you are in the offline mode (-Z offline)").run();
}
#[test]
fn cargo_compile_offline_with_cached_git_dep() {
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"src/lib.rs",
r#"
pub static COOL_STR:&str = "cached git repo rev1";
"#,
)
})
.unwrap();
let repo = git2::Repository::open(&git_project.root()).unwrap();
let rev1 = repo.revparse_single("HEAD").unwrap().id();
// Commit the changes and make sure we trigger a recompile
File::create(&git_project.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub static COOL_STR:&str = "cached git repo rev2";"#)
.unwrap();
git::add(&repo);
let rev2 = git::commit(&repo);
{
// cache to registry rev1 and rev2
let prj = project()
.at("cache_git_dep")
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "cache_git_dep"
version = "0.5.0"
[dependencies.dep1]
git = '{}'
rev = "{}"
"#,
git_project.url(),
rev1
),
)
.file("src/main.rs", "fn main(){}")
.build();
prj.cargo("build").run();
File::create(&prj.root().join("Cargo.toml"))
.unwrap()
.write_all(
&format!(
r#"
[project]
name = "cache_git_dep"
version = "0.5.0"
[dependencies.dep1]
git = '{}'
rev = "{}"
"#,
git_project.url(),
rev2
)
.as_bytes(),
)
.unwrap();
prj.cargo("build").run();
}
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/main.rs",
&main_file(r#""hello from {}", dep1::COOL_STR"#, &["dep1"]),
)
.build();
let git_root = git_project.root();
p.cargo("build -Zoffline")
.masquerade_as_nightly_cargo()
.with_stderr(format!(
"\
[COMPILING] dep1 v0.5.0 ({}#[..])
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
path2url(git_root),
))
.run();
assert!(p.bin("foo").is_file());
p.process(&p.bin("foo"))
.with_stdout("hello from cached git repo rev2\n")
.run();
File::create(&p.root().join("Cargo.toml"))
.unwrap()
.write_all(
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
[dependencies.dep1]
git = '{}'
rev = "{}"
"#,
git_project.url(),
rev1
)
.as_bytes(),
)
.unwrap();
p.cargo("build -Zoffline")
.masquerade_as_nightly_cargo()
.run();
p.process(&p.bin("foo"))
.with_stdout("hello from cached git repo rev1\n")
.run();
}
#[test]
fn cargo_compile_git_dep_branch() {
let project = project();
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
})
.unwrap();
// Make a new branch based on the current HEAD commit
let repo = git2::Repository::open(&git_project.root()).unwrap();
let head = repo.head().unwrap().target().unwrap();
let head = repo.find_commit(head).unwrap();
repo.branch("branchy", &head, true).unwrap();
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
branch = "branchy"
"#,
git_project.url()
),
)
.file(
"src/main.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
let git_root = git_project.root();
project
.cargo("build")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[COMPILING] dep1 v0.5.0 ({}?branch=branchy#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
path2url(&git_root),
path2url(&git_root),
))
.run();
assert!(project.bin("foo").is_file());
project
.process(&project.bin("foo"))
.with_stdout("hello world\n")
.run();
}
#[test]
fn cargo_compile_git_dep_tag() {
let project = project();
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
})
.unwrap();
// Make a tag corresponding to the current HEAD
let repo = git2::Repository::open(&git_project.root()).unwrap();
let head = repo.head().unwrap().target().unwrap();
repo.tag(
"v0.1.0",
&repo.find_object(head, None).unwrap(),
&repo.signature().unwrap(),
"make a new tag",
false,
)
.unwrap();
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
tag = "v0.1.0"
"#,
git_project.url()
),
)
.file(
"src/main.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
let git_root = git_project.root();
project
.cargo("build")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[COMPILING] dep1 v0.5.0 ({}?tag=v0.1.0#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
path2url(&git_root),
path2url(&git_root),
))
.run();
assert!(project.bin("foo").is_file());
project
.process(&project.bin("foo"))
.with_stdout("hello world\n")
.run();
project.cargo("build").run();
}
#[test]
fn cargo_compile_with_nested_paths() {
let git_project = git::new("dep1", |project| {
project
.file(
"Cargo.toml",
r#"
[project]
name = "dep1"
version = "0.5.0"
authors = ["carlhuda@example.com"]
[dependencies.dep2]
version = "0.5.0"
path = "vendor/dep2"
[lib]
name = "dep1"
"#,
)
.file(
"src/dep1.rs",
r#"
extern crate dep2;
pub fn hello() -> &'static str {
dep2::hello()
}
"#,
)
.file("vendor/dep2/Cargo.toml", &basic_lib_manifest("dep2"))
.file(
"vendor/dep2/src/dep2.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
version = "0.5.0"
git = '{}'
[[bin]]
name = "foo"
"#,
git_project.url()
),
)
.file(
"src/foo.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
p.cargo("build").run();
assert!(p.bin("foo").is_file());
p.process(&p.bin("foo")).with_stdout("hello world\n").run();
}
#[test]
fn cargo_compile_with_malformed_nested_paths() {
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
.file("vendor/dep2/Cargo.toml", "!INVALID!")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
version = "0.5.0"
git = '{}'
[[bin]]
name = "foo"
"#,
git_project.url()
),
)
.file(
"src/foo.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
p.cargo("build").run();
assert!(p.bin("foo").is_file());
p.process(&p.bin("foo")).with_stdout("hello world\n").run();
}
#[test]
fn cargo_compile_with_meta_package() {
let git_project = git::new("meta-dep", |project| {
project
.file("dep1/Cargo.toml", &basic_lib_manifest("dep1"))
.file(
"dep1/src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"this is dep1"
}
"#,
)
.file("dep2/Cargo.toml", &basic_lib_manifest("dep2"))
.file(
"dep2/src/dep2.rs",
r#"
pub fn hello() -> &'static str {
"this is dep2"
}
"#,
)
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
version = "0.5.0"
git = '{}'
[dependencies.dep2]
version = "0.5.0"
git = '{}'
[[bin]]
name = "foo"
"#,
git_project.url(),
git_project.url()
),
)
.file(
"src/foo.rs",
&main_file(
r#""{} {}", dep1::hello(), dep2::hello()"#,
&["dep1", "dep2"],
),
)
.build();
p.cargo("build").run();
assert!(p.bin("foo").is_file());
p.process(&p.bin("foo"))
.with_stdout("this is dep1 this is dep2\n")
.run();
}
#[test]
fn cargo_compile_with_short_ssh_git() {
let url = "git@github.com:a/dep";
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep]
git = "{}"
[[bin]]
name = "foo"
"#,
url
),
)
.file(
"src/foo.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
p.cargo("build")
.with_status(101)
.with_stdout("")
.with_stderr(&format!(
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
invalid url `{}`: relative URL without a base
",
url
))
.run();
}
#[test]
fn two_revs_same_deps() {
let bar = git::new("meta-dep", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.0.0"))
.file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
})
.unwrap();
let repo = git2::Repository::open(&bar.root()).unwrap();
let rev1 = repo.revparse_single("HEAD").unwrap().id();
// Commit the changes and make sure we trigger a recompile
File::create(&bar.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub fn bar() -> i32 { 2 }"#)
.unwrap();
git::add(&repo);
let rev2 = git::commit(&repo);
let foo = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.0.0"
authors = []
[dependencies.bar]
git = '{}'
rev = "{}"
[dependencies.baz]
path = "../baz"
"#,
bar.url(),
rev1
),
)
.file(
"src/main.rs",
r#"
extern crate bar;
extern crate baz;
fn main() {
assert_eq!(bar::bar(), 1);
assert_eq!(baz::baz(), 2);
}
"#,
)
.build();
let _baz = project()
.at("baz")
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "baz"
version = "0.0.0"
authors = []
[dependencies.bar]
git = '{}'
rev = "{}"
"#,
bar.url(),
rev2
),
)
.file(
"src/lib.rs",
r#"
extern crate bar;
pub fn baz() -> i32 { bar::bar() }
"#,
)
.build();
foo.cargo("build -v").run();
assert!(foo.bin("foo").is_file());
foo.process(&foo.bin("foo")).run();
}
#[test]
fn recompilation() {
let git_project = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("bar"))
.file("src/bar.rs", "pub fn bar() {}")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.bar]
version = "0.5.0"
git = '{}'
"#,
git_project.url()
),
)
.file("src/main.rs", &main_file(r#""{:?}", bar::bar()"#, &["bar"]))
.build();
// First time around we should compile both foo and bar
p.cargo("build")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[COMPILING] bar v0.5.0 ({}#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) \
in [..]\n",
git_project.url(),
git_project.url(),
))
.run();
// Don't recompile the second time
p.cargo("build").with_stdout("").run();
// Modify a file manually, shouldn't trigger a recompile
File::create(&git_project.root().join("src/bar.rs"))
.unwrap()
.write_all(br#"pub fn bar() { println!("hello!"); }"#)
.unwrap();
p.cargo("build").with_stdout("").run();
p.cargo("update")
.with_stderr(&format!(
"[UPDATING] git repository `{}`",
git_project.url()
))
.run();
p.cargo("build").with_stdout("").run();
// Commit the changes and make sure we don't trigger a recompile because the
// lockfile says not to change
let repo = git2::Repository::open(&git_project.root()).unwrap();
git::add(&repo);
git::commit(&repo);
println!("compile after commit");
p.cargo("build").with_stdout("").run();
p.root().move_into_the_past();
// Update the dependency and carry on!
p.cargo("update")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[UPDATING] bar v0.5.0 ([..]) -> #[..]\n\
",
git_project.url()
))
.run();
println!("going for the last compile");
p.cargo("build")
.with_stderr(&format!(
"[COMPILING] bar v0.5.0 ({}#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) \
in [..]\n",
git_project.url(),
))
.run();
// Make sure clean only cleans one dep
p.cargo("clean -p foo").with_stdout("").run();
p.cargo("build")
.with_stderr(
"[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) \
in [..]",
)
.run();
}
#[test]
fn update_with_shared_deps() {
let git_project = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_lib_manifest("bar"))
.file("src/bar.rs", "pub fn bar() {}")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
path = "dep1"
[dependencies.dep2]
path = "dep2"
"#,
)
.file(
"src/main.rs",
r#"
#[allow(unused_extern_crates)]
extern crate dep1;
#[allow(unused_extern_crates)]
extern crate dep2;
fn main() {}
"#,
)
.file(
"dep1/Cargo.toml",
&format!(
r#"
[package]
name = "dep1"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.bar]
version = "0.5.0"
git = '{}'
"#,
git_project.url()
),
)
.file("dep1/src/lib.rs", "")
.file(
"dep2/Cargo.toml",
&format!(
r#"
[package]
name = "dep2"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.bar]
version = "0.5.0"
git = '{}'
"#,
git_project.url()
),
)
.file("dep2/src/lib.rs", "")
.build();
// First time around we should compile both foo and bar
p.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{git}`
[COMPILING] bar v0.5.0 ({git}#[..])
[COMPILING] [..] v0.5.0 ([..])
[COMPILING] [..] v0.5.0 ([..])
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
git = git_project.url(),
))
.run();
// Modify a file manually, and commit it
File::create(&git_project.root().join("src/bar.rs"))
.unwrap()
.write_all(br#"pub fn bar() { println!("hello!"); }"#)
.unwrap();
let repo = git2::Repository::open(&git_project.root()).unwrap();
let old_head = repo.head().unwrap().target().unwrap();
git::add(&repo);
git::commit(&repo);
sleep_ms(1000);
// By default, not transitive updates
println!("dep1 update");
p.cargo("update -p dep1").with_stdout("").run();
// Don't do anything bad on a weird --precise argument
println!("bar bad precise update");
p.cargo("update -p bar --precise 0.1.2")
.with_status(101)
.with_stderr(
"\
[UPDATING] git repository [..]
[ERROR] Unable to update [..]
Caused by:
revspec '0.1.2' not found; [..]
",
)
.run();
// Specifying a precise rev to the old rev shouldn't actually update
// anything because we already have the rev in the db.
println!("bar precise update");
p.cargo("update -p bar --precise")
.arg(&old_head.to_string())
.with_stdout("")
.run();
// Updating aggressively should, however, update the repo.
println!("dep1 aggressive update");
p.cargo("update -p dep1 --aggressive")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[UPDATING] bar v0.5.0 ([..]) -> #[..]\n\
",
git_project.url()
))
.run();
// Make sure we still only compile one version of the git repo
println!("build");
p.cargo("build")
.with_stderr(&format!(
"\
[COMPILING] bar v0.5.0 ({git}#[..])
[COMPILING] [..] v0.5.0 ([CWD][..]dep[..])
[COMPILING] [..] v0.5.0 ([CWD][..]dep[..])
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
git = git_project.url(),
))
.run();
// We should be able to update transitive deps
p.cargo("update -p bar")
.with_stderr(&format!(
"[UPDATING] git repository `{}`",
git_project.url()
))
.run();
}
#[test]
fn dep_with_submodule() {
let project = project();
let git_project = git::new("dep1", |project| {
project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
})
.unwrap();
let git_project2 =
git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")).unwrap();
let repo = git2::Repository::open(&git_project.root()).unwrap();
let url = path2url(git_project2.root()).to_string();
git::add_submodule(&repo, &url, Path::new("src"));
git::commit(&repo);
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/lib.rs",
"extern crate dep1; pub fn foo() { dep1::dep() }",
)
.build();
project
.cargo("build")
.with_stderr(
"\
[UPDATING] git repository [..]
[COMPILING] dep1 [..]
[COMPILING] foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
)
.run();
}
#[test]
fn dep_with_bad_submodule() {
let project = project();
let git_project = git::new("dep1", |project| {
project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
})
.unwrap();
let git_project2 =
git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")).unwrap();
let repo = git2::Repository::open(&git_project.root()).unwrap();
let url = path2url(git_project2.root()).to_string();
git::add_submodule(&repo, &url, Path::new("src"));
git::commit(&repo);
// now amend the first commit on git_project2 to make submodule ref point to not-found
// commit
let repo = git2::Repository::open(&git_project2.root()).unwrap();
let original_submodule_ref = repo.refname_to_id("refs/heads/master").unwrap();
let commit = repo.find_commit(original_submodule_ref).unwrap();
commit
.amend(
Some("refs/heads/master"),
None,
None,
None,
Some("something something"),
None,
)
.unwrap();
let p = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/lib.rs",
"extern crate dep1; pub fn foo() { dep1::dep() }",
)
.build();
let expected = format!(
"\
[UPDATING] git repository [..]
[ERROR] failed to load source for a dependency on `dep1`
Caused by:
Unable to update {}
Caused by:
failed to update submodule `src`
Caused by:
object not found - no match for id [..]
",
path2url(git_project.root())
);
p.cargo("build")
.with_stderr(expected)
.with_status(101)
.run();
}
#[test]
fn two_deps_only_update_one() {
let project = project();
let git1 = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let git2 = git::new("dep2", |project| {
project
.file("Cargo.toml", &basic_manifest("dep2", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let p = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
[dependencies.dep2]
git = '{}'
"#,
git1.url(),
git2.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
fn oid_to_short_sha(oid: git2::Oid) -> String {
oid.to_string()[..8].to_string()
}
fn git_repo_head_sha(p: &Project) -> String {
let repo = git2::Repository::open(p.root()).unwrap();
let head = repo.head().unwrap().target().unwrap();
oid_to_short_sha(head)
}
println!("dep1 head sha: {}", git_repo_head_sha(&git1));
println!("dep2 head sha: {}", git_repo_head_sha(&git2));
p.cargo("build")
.with_stderr(
"[UPDATING] git repository `[..]`\n\
[UPDATING] git repository `[..]`\n\
[COMPILING] [..] v0.5.0 ([..])\n\
[COMPILING] [..] v0.5.0 ([..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
)
.run();
File::create(&git1.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub fn foo() {}"#)
.unwrap();
let repo = git2::Repository::open(&git1.root()).unwrap();
git::add(&repo);
let oid = git::commit(&repo);
println!("dep1 head sha: {}", oid_to_short_sha(oid));
p.cargo("update -p dep1")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[UPDATING] dep1 v0.5.0 ([..]) -> #[..]\n\
",
git1.url()
))
.run();
}
#[test]
fn stale_cached_version() {
let bar = git::new("meta-dep", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.0.0"))
.file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
})
.unwrap();
// Update the git database in the cache with the current state of the git
// repo
let foo = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.0.0"
authors = []
[dependencies.bar]
git = '{}'
"#,
bar.url()
),
)
.file(
"src/main.rs",
r#"
extern crate bar;
fn main() { assert_eq!(bar::bar(), 1) }
"#,
)
.build();
foo.cargo("build").run();
foo.process(&foo.bin("foo")).run();
// Update the repo, and simulate someone else updating the lockfile and then
// us pulling it down.
File::create(&bar.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub fn bar() -> i32 { 1 + 0 }"#)
.unwrap();
let repo = git2::Repository::open(&bar.root()).unwrap();
git::add(&repo);
git::commit(&repo);
sleep_ms(1000);
let rev = repo.revparse_single("HEAD").unwrap().id();
File::create(&foo.root().join("Cargo.lock"))
.unwrap()
.write_all(
format!(
r#"
[[package]]
name = "foo"
version = "0.0.0"
dependencies = [
'bar 0.0.0 (git+{url}#{hash})'
]
[[package]]
name = "bar"
version = "0.0.0"
source = 'git+{url}#{hash}'
"#,
url = bar.url(),
hash = rev
)
.as_bytes(),
)
.unwrap();
// Now build!
foo.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{bar}`
[COMPILING] bar v0.0.0 ({bar}#[..])
[COMPILING] foo v0.0.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
bar = bar.url(),
))
.run();
foo.process(&foo.bin("foo")).run();
}
#[test]
fn dep_with_changed_submodule() {
let project = project();
let git_project = git::new("dep1", |project| {
project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
})
.unwrap();
let git_project2 = git::new("dep2", |project| {
project.file("lib.rs", "pub fn dep() -> &'static str { \"project2\" }")
})
.unwrap();
let git_project3 = git::new("dep3", |project| {
project.file("lib.rs", "pub fn dep() -> &'static str { \"project3\" }")
})
.unwrap();
let repo = git2::Repository::open(&git_project.root()).unwrap();
let mut sub = git::add_submodule(&repo, &git_project2.url().to_string(), Path::new("src"));
git::commit(&repo);
let p = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/main.rs",
"
extern crate dep1;
pub fn main() { println!(\"{}\", dep1::dep()) }
",
)
.build();
println!("first run");
p.cargo("run")
.with_stderr(
"[UPDATING] git repository `[..]`\n\
[COMPILING] dep1 v0.5.0 ([..])\n\
[COMPILING] foo v0.5.0 ([..])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in \
[..]\n\
[RUNNING] `target/debug/foo[EXE]`\n",
)
.with_stdout("project2\n")
.run();
File::create(&git_project.root().join(".gitmodules"))
.unwrap()
.write_all(
format!(
"[submodule \"src\"]\n\tpath = src\n\turl={}",
git_project3.url()
)
.as_bytes(),
)
.unwrap();
// Sync the submodule and reset it to the new remote.
sub.sync().unwrap();
{
let subrepo = sub.open().unwrap();
subrepo
.remote_add_fetch("origin", "refs/heads/*:refs/heads/*")
.unwrap();
subrepo
.remote_set_url("origin", &git_project3.url().to_string())
.unwrap();
let mut origin = subrepo.find_remote("origin").unwrap();
origin.fetch(&[], None, None).unwrap();
let id = subrepo.refname_to_id("refs/remotes/origin/master").unwrap();
let obj = subrepo.find_object(id, None).unwrap();
subrepo.reset(&obj, git2::ResetType::Hard, None).unwrap();
}
sub.add_to_index(true).unwrap();
git::add(&repo);
git::commit(&repo);
sleep_ms(1000);
// Update the dependency and carry on!
println!("update");
p.cargo("update -v")
.with_stderr("")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[UPDATING] dep1 v0.5.0 ([..]) -> #[..]\n\
",
git_project.url()
))
.run();
println!("last run");
p.cargo("run")
.with_stderr(
"[COMPILING] dep1 v0.5.0 ([..])\n\
[COMPILING] foo v0.5.0 ([..])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in \
[..]\n\
[RUNNING] `target/debug/foo[EXE]`\n",
)
.with_stdout("project3\n")
.run();
}
#[test]
fn dev_deps_with_testing() {
let p2 = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file(
"src/lib.rs",
r#"
pub fn gimme() -> &'static str { "zoidberg" }
"#,
)
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dev-dependencies.bar]
version = "0.5.0"
git = '{}'
"#,
p2.url()
),
)
.file(
"src/main.rs",
r#"
fn main() {}
#[cfg(test)]
mod tests {
extern crate bar;
#[test] fn foo() { bar::gimme(); }
}
"#,
)
.build();
// Generate a lockfile which did not use `bar` to compile, but had to update
// `bar` to generate the lockfile
p.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{bar}`
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
bar = p2.url()
))
.run();
// Make sure we use the previous resolution of `bar` instead of updating it
// a second time.
p.cargo("test")
.with_stderr(
"\
[COMPILING] [..] v0.5.0 ([..])
[COMPILING] [..] v0.5.0 ([..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] target/debug/deps/foo-[..][EXE]",
)
.with_stdout_contains("test tests::foo ... ok")
.run();
}
#[test]
fn git_build_cmd_freshness() {
let foo = git::new("foo", |project| {
project
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.0"
authors = []
build = "build.rs"
"#,
)
.file("build.rs", "fn main() {}")
.file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
.file(".gitignore", "src/bar.rs")
})
.unwrap();
foo.root().move_into_the_past();
sleep_ms(1000);
foo.cargo("build")
.with_stderr(
"\
[COMPILING] foo v0.0.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
// Smoke test to make sure it doesn't compile again
println!("first pass");
foo.cargo("build").with_stdout("").run();
// Modify an ignored file and make sure we don't rebuild
println!("second pass");
File::create(&foo.root().join("src/bar.rs")).unwrap();
foo.cargo("build").with_stdout("").run();
}
#[test]
fn git_name_not_always_needed() {
let p2 = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file(
"src/lib.rs",
r#"
pub fn gimme() -> &'static str { "zoidberg" }
"#,
)
})
.unwrap();
let repo = git2::Repository::open(&p2.root()).unwrap();
let mut cfg = repo.config().unwrap();
let _ = cfg.remove("user.name");
let _ = cfg.remove("user.email");
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dev-dependencies.bar]
git = '{}'
"#,
p2.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
// Generate a lockfile which did not use `bar` to compile, but had to update
// `bar` to generate the lockfile
p.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{bar}`
[COMPILING] foo v0.5.0 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
bar = p2.url()
))
.run();
}
#[test]
fn git_repo_changing_no_rebuild() {
let bar = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
})
.unwrap();
// Lock p1 to the first rev in the git repo
let p1 = project()
.at("p1")
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "p1"
version = "0.5.0"
authors = []
build = 'build.rs'
[dependencies.bar]
git = '{}'
"#,
bar.url()
),
)
.file("src/main.rs", "fn main() {}")
.file("build.rs", "fn main() {}")
.build();
p1.root().move_into_the_past();
p1.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{bar}`
[COMPILING] [..]
[COMPILING] [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
bar = bar.url()
))
.run();
// Make a commit to lock p2 to a different rev
File::create(&bar.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub fn bar() -> i32 { 2 }"#)
.unwrap();
let repo = git2::Repository::open(&bar.root()).unwrap();
git::add(&repo);
git::commit(&repo);
// Lock p2 to the second rev
let p2 = project()
.at("p2")
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "p2"
version = "0.5.0"
authors = []
[dependencies.bar]
git = '{}'
"#,
bar.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p2.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{bar}`
[COMPILING] [..]
[COMPILING] [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
bar = bar.url()
))
.run();
// And now for the real test! Make sure that p1 doesn't get rebuilt
// even though the git repo has changed.
p1.cargo("build").with_stdout("").run();
}
#[test]
fn git_dep_build_cmd() {
let p = git::new("foo", |project| {
project
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.bar]
version = "0.5.0"
path = "bar"
[[bin]]
name = "foo"
"#,
)
.file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"]))
.file(
"bar/Cargo.toml",
r#"
[project]
name = "bar"
version = "0.5.0"
authors = ["wycats@example.com"]
build = "build.rs"
[lib]
name = "bar"
path = "src/bar.rs"
"#,
)
.file(
"bar/src/bar.rs.in",
r#"
pub fn gimme() -> i32 { 0 }
"#,
)
.file(
"bar/build.rs",
r#"
use std::fs;
fn main() {
fs::copy("src/bar.rs.in", "src/bar.rs").unwrap();
}
"#,
)
})
.unwrap();
p.root().join("bar").move_into_the_past();
p.cargo("build").run();
p.process(&p.bin("foo")).with_stdout("0\n").run();
// Touching bar.rs.in should cause the `build` command to run again.
fs::File::create(&p.root().join("bar/src/bar.rs.in"))
.unwrap()
.write_all(b"pub fn gimme() -> i32 { 1 }")
.unwrap();
p.cargo("build").run();
p.process(&p.bin("foo")).with_stdout("1\n").run();
}
#[test]
fn fetch_downloads() {
let bar = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "pub fn bar() -> i32 { 1 }")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.bar]
git = '{}'
"#,
bar.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("fetch")
.with_stderr(&format!(
"[UPDATING] git repository `{url}`",
url = bar.url()
))
.run();
p.cargo("fetch").with_stdout("").run();
}
#[test]
fn warnings_in_git_dep() {
let bar = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "fn unused() {}")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.bar]
git = '{}'
"#,
bar.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("build")
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
[COMPILING] bar v0.5.0 ({}#[..])\n\
[COMPILING] foo v0.5.0 ([CWD])\n\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
bar.url(),
bar.url(),
))
.run();
}
#[test]
fn update_ambiguous() {
let bar1 = git::new("bar1", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let bar2 = git::new("bar2", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.6.0"))
.file("src/lib.rs", "")
})
.unwrap();
let baz = git::new("baz", |project| {
project
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "baz"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.bar]
git = '{}'
"#,
bar2.url()
),
)
.file("src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.bar]
git = '{}'
[dependencies.baz]
git = '{}'
"#,
bar1.url(),
baz.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("generate-lockfile").run();
p.cargo("update -p bar")
.with_status(101)
.with_stderr(
"\
[ERROR] There are multiple `bar` packages in your project, and the specification `bar` \
is ambiguous.
Please re-run this command with `-p <spec>` where `<spec>` is one of the \
following:
bar:0.[..].0
bar:0.[..].0
",
)
.run();
}
#[test]
fn update_one_dep_in_repo_with_many_deps() {
let bar = git::new("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "")
.file("a/Cargo.toml", &basic_manifest("a", "0.5.0"))
.file("a/src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.bar]
git = '{}'
[dependencies.a]
git = '{}'
"#,
bar.url(),
bar.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("generate-lockfile").run();
p.cargo("update -p bar")
.with_stderr(&format!("[UPDATING] git repository `{}`", bar.url()))
.run();
}
#[test]
fn switch_deps_does_not_update_transitive() {
let transitive = git::new("transitive", |project| {
project
.file("Cargo.toml", &basic_manifest("transitive", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let dep1 = git::new("dep1", |project| {
project
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "dep"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.transitive]
git = '{}'
"#,
transitive.url()
),
)
.file("src/lib.rs", "")
})
.unwrap();
let dep2 = git::new("dep2", |project| {
project
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "dep"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.transitive]
git = '{}'
"#,
transitive.url()
),
)
.file("src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.dep]
git = '{}'
"#,
dep1.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{}`
[UPDATING] git repository `{}`
[COMPILING] transitive [..]
[COMPILING] dep [..]
[COMPILING] foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
dep1.url(),
transitive.url()
))
.run();
// Update the dependency to point to the second repository, but this
// shouldn't update the transitive dependency which is the same.
File::create(&p.root().join("Cargo.toml"))
.unwrap()
.write_all(
format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.dep]
git = '{}'
"#,
dep2.url()
)
.as_bytes(),
)
.unwrap();
p.cargo("build")
.with_stderr(&format!(
"\
[UPDATING] git repository `{}`
[COMPILING] dep [..]
[COMPILING] foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
dep2.url()
))
.run();
}
#[test]
fn update_one_source_updates_all_packages_in_that_git_source() {
let dep = git::new("dep", |project| {
project
.file(
"Cargo.toml",
r#"
[package]
name = "dep"
version = "0.5.0"
authors = []
[dependencies.a]
path = "a"
"#,
)
.file("src/lib.rs", "")
.file("a/Cargo.toml", &basic_manifest("a", "0.5.0"))
.file("a/src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.dep]
git = '{}'
"#,
dep.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("build").run();
let repo = git2::Repository::open(&dep.root()).unwrap();
let rev1 = repo.revparse_single("HEAD").unwrap().id();
// Just be sure to change a file
File::create(&dep.root().join("src/lib.rs"))
.unwrap()
.write_all(br#"pub fn bar() -> i32 { 2 }"#)
.unwrap();
git::add(&repo);
git::commit(&repo);
p.cargo("update -p dep").run();
let mut lockfile = String::new();
File::open(&p.root().join("Cargo.lock"))
.unwrap()
.read_to_string(&mut lockfile)
.unwrap();
assert!(
!lockfile.contains(&rev1.to_string()),
"{} in {}",
rev1,
lockfile
);
}
#[test]
fn switch_sources() {
let a1 = git::new("a1", |project| {
project
.file("Cargo.toml", &basic_manifest("a", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let a2 = git::new("a2", |project| {
project
.file("Cargo.toml", &basic_manifest("a", "0.5.1"))
.file("src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies.b]
path = "b"
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
"b/Cargo.toml",
&format!(
r#"
[project]
name = "b"
version = "0.5.0"
authors = []
[dependencies.a]
git = '{}'
"#,
a1.url()
),
)
.file("b/src/lib.rs", "pub fn main() {}")
.build();
p.cargo("build")
.with_stderr(
"\
[UPDATING] git repository `file://[..]a1`
[COMPILING] a v0.5.0 ([..]a1#[..]
[COMPILING] b v0.5.0 ([..])
[COMPILING] foo v0.5.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
File::create(&p.root().join("b/Cargo.toml"))
.unwrap()
.write_all(
format!(
r#"
[project]
name = "b"
version = "0.5.0"
authors = []
[dependencies.a]
git = '{}'
"#,
a2.url()
)
.as_bytes(),
)
.unwrap();
p.cargo("build")
.with_stderr(
"\
[UPDATING] git repository `file://[..]a2`
[COMPILING] a v0.5.1 ([..]a2#[..]
[COMPILING] b v0.5.0 ([..])
[COMPILING] foo v0.5.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[test]
fn dont_require_submodules_are_checked_out() {
let p = project().build();
let git1 = git::new("dep1", |p| {
p.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
build = "build.rs"
"#,
)
.file("build.rs", "fn main() {}")
.file("src/lib.rs", "")
.file("a/foo", "")
})
.unwrap();
let git2 = git::new("dep2", |p| p).unwrap();
let repo = git2::Repository::open(&git1.root()).unwrap();
let url = path2url(git2.root()).to_string();
git::add_submodule(&repo, &url, Path::new("a/submodule"));
git::commit(&repo);
git2::Repository::init(&p.root()).unwrap();
let url = path2url(git1.root()).to_string();
let dst = paths::home().join("foo");
git2::Repository::clone(&url, &dst).unwrap();
git1.cargo("build -v").cwd(&dst).run();
}
#[test]
fn doctest_same_name() {
let a2 = git::new("a2", |p| {
p.file("Cargo.toml", &basic_manifest("a", "0.5.0"))
.file("src/lib.rs", "pub fn a2() {}")
})
.unwrap();
let a1 = git::new("a1", |p| {
p.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "a"
version = "0.5.0"
authors = []
[dependencies]
a = {{ git = '{}' }}
"#,
a2.url()
),
)
.file("src/lib.rs", "extern crate a; pub fn a1() {}")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
a = {{ git = '{}' }}
"#,
a1.url()
),
)
.file(
"src/lib.rs",
r#"
#[macro_use]
extern crate a;
"#,
)
.build();
p.cargo("test -v").run();
}
#[test]
fn lints_are_suppressed() {
let a = git::new("a", |p| {
p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file(
"src/lib.rs",
"
use std::option;
",
)
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
a = {{ git = '{}' }}
"#,
a.url()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("build")
.with_stderr(
"\
[UPDATING] git repository `[..]`
[COMPILING] a v0.5.0 ([..])
[COMPILING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[test]
fn denied_lints_are_allowed() {
let a = git::new("a", |p| {
p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file(
"src/lib.rs",
"
#![deny(warnings)]
use std::option;
",
)
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
a = {{ git = '{}' }}
"#,
a.url()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("build")
.with_stderr(
"\
[UPDATING] git repository `[..]`
[COMPILING] a v0.5.0 ([..])
[COMPILING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[test]
fn add_a_git_dep() {
let git = git::new("git", |p| {
p.file("Cargo.toml", &basic_manifest("git", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
a = {{ path = 'a' }}
git = {{ git = '{}' }}
"#,
git.url()
),
)
.file("src/lib.rs", "")
.file("a/Cargo.toml", &basic_manifest("a", "0.0.1"))
.file("a/src/lib.rs", "")
.build();
p.cargo("build").run();
File::create(p.root().join("a/Cargo.toml"))
.unwrap()
.write_all(
format!(
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[dependencies]
git = {{ git = '{}' }}
"#,
git.url()
)
.as_bytes(),
)
.unwrap();
p.cargo("build").run();
}
#[test]
fn two_at_rev_instead_of_tag() {
let git = git::new("git", |p| {
p.file("Cargo.toml", &basic_manifest("git1", "0.5.0"))
.file("src/lib.rs", "")
.file("a/Cargo.toml", &basic_manifest("git2", "0.5.0"))
.file("a/src/lib.rs", "")
})
.unwrap();
// Make a tag corresponding to the current HEAD
let repo = git2::Repository::open(&git.root()).unwrap();
let head = repo.head().unwrap().target().unwrap();
repo.tag(
"v0.1.0",
&repo.find_object(head, None).unwrap(),
&repo.signature().unwrap(),
"make a new tag",
false,
)
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
git1 = {{ git = '{0}', rev = 'v0.1.0' }}
git2 = {{ git = '{0}', rev = 'v0.1.0' }}
"#,
git.url()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("generate-lockfile").run();
p.cargo("build -v").run();
}
#[test]
#[ignore] // accesses crates.io
fn include_overrides_gitignore() {
let p = git::new("reduction", |repo| {
repo.file(
"Cargo.toml",
r#"
[package]
name = "reduction"
version = "0.5.0"
authors = ["pnkfelix"]
build = "tango-build.rs"
include = ["src/lib.rs", "src/incl.rs", "src/mod.md", "tango-build.rs", "Cargo.toml"]
[build-dependencies]
filetime = "0.1"
"#,
)
.file(
".gitignore",
r#"
target
Cargo.lock
# Below files represent generated code, thus not managed by `git`
src/incl.rs
src/not_incl.rs
"#,
)
.file(
"tango-build.rs",
r#"
extern crate filetime;
use filetime::FileTime;
use std::fs::{self, File};
fn main() {
// generate files, or bring their timestamps into sync.
let source = "src/mod.md";
let metadata = fs::metadata(source).unwrap();
let mtime = FileTime::from_last_modification_time(&metadata);
let atime = FileTime::from_last_access_time(&metadata);
// sync time stamps for generated files with time stamp of source file.
let files = ["src/not_incl.rs", "src/incl.rs"];
for file in files.iter() {
File::create(file).unwrap();
filetime::set_file_times(file, atime, mtime).unwrap();
}
}
"#,
)
.file("src/lib.rs", "mod not_incl; mod incl;")
.file(
"src/mod.md",
r#"
(The content of this file does not matter since we are not doing real codegen.)
"#,
)
})
.unwrap();
println!("build 1: all is new");
p.cargo("build -v")
.with_stderr(
"\
[UPDATING] `[..]` index
[DOWNLOADED] filetime [..]
[DOWNLOADED] libc [..]
[COMPILING] libc [..]
[RUNNING] `rustc --crate-name libc [..]`
[COMPILING] filetime [..]
[RUNNING] `rustc --crate-name filetime [..]`
[COMPILING] reduction [..]
[RUNNING] `rustc --crate-name build_script_tango_build tango-build.rs --crate-type bin [..]`
[RUNNING] `[..]/build-script-tango-build`
[RUNNING] `rustc --crate-name reduction src/lib.rs --crate-type lib [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
println!("build 2: nothing changed; file timestamps reset by build script");
p.cargo("build -v")
.with_stderr(
"\
[FRESH] libc [..]
[FRESH] filetime [..]
[FRESH] reduction [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
println!("build 3: touch `src/not_incl.rs`; expect build script *not* re-run");
sleep_ms(1000);
File::create(p.root().join("src").join("not_incl.rs")).unwrap();
p.cargo("build -v")
.with_stderr(
"\
[FRESH] libc [..]
[FRESH] filetime [..]
[COMPILING] reduction [..]
[RUNNING] `rustc --crate-name reduction src/lib.rs --crate-type lib [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
// This final case models the bug from rust-lang/cargo#4135: an
// explicitly included file should cause a build-script re-run,
// even if that same file is matched by `.gitignore`.
println!("build 4: touch `src/incl.rs`; expect build script re-run");
sleep_ms(1000);
File::create(p.root().join("src").join("incl.rs")).unwrap();
p.cargo("build -v")
.with_stderr(
"\
[FRESH] libc [..]
[FRESH] filetime [..]
[COMPILING] reduction [..]
[RUNNING] `[..]/build-script-tango-build`
[RUNNING] `rustc --crate-name reduction src/lib.rs --crate-type lib [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[test]
fn invalid_git_dependency_manifest() {
let project = project();
let git_project = git::new("dep1", |project| {
project
.file(
"Cargo.toml",
r#"
[project]
name = "dep1"
version = "0.5.0"
authors = ["carlhuda@example.com"]
categories = ["algorithms"]
categories = ["algorithms"]
[lib]
name = "dep1"
"#,
)
.file(
"src/dep1.rs",
r#"
pub fn hello() -> &'static str {
"hello world"
}
"#,
)
})
.unwrap();
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = ["wycats@example.com"]
[dependencies.dep1]
git = '{}'
"#,
git_project.url()
),
)
.file(
"src/main.rs",
&main_file(r#""{}", dep1::hello()"#, &["dep1"]),
)
.build();
let git_root = git_project.root();
project
.cargo("build")
.with_status(101)
.with_stderr(&format!(
"[UPDATING] git repository `{}`\n\
error: failed to load source for a dependency on `dep1`\n\
\n\
Caused by:\n \
Unable to update {}\n\
\n\
Caused by:\n \
failed to parse manifest at `[..]`\n\
\n\
Caused by:\n \
could not parse input as TOML\n\
\n\
Caused by:\n \
duplicate key: `categories` for key `project`",
path2url(&git_root),
path2url(&git_root),
))
.run();
}
#[test]
fn failed_submodule_checkout() {
let project = project();
let git_project = git::new("dep1", |project| {
project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
})
.unwrap();
let git_project2 = git::new("dep2", |project| project.file("lib.rs", "")).unwrap();
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let done = Arc::new(AtomicBool::new(false));
let done2 = done.clone();
let t = thread::spawn(move || {
while !done2.load(Ordering::SeqCst) {
if let Ok((mut socket, _)) = listener.accept() {
drop(socket.write_all(b"foo\r\n"));
}
}
});
let repo = git2::Repository::open(&git_project2.root()).unwrap();
let url = format!("https://{}:{}/", addr.ip(), addr.port());
{
let mut s = repo.submodule(&url, Path::new("bar"), false).unwrap();
let subrepo = s.open().unwrap();
let mut cfg = subrepo.config().unwrap();
cfg.set_str("user.email", "foo@bar.com").unwrap();
cfg.set_str("user.name", "Foo Bar").unwrap();
git::commit(&subrepo);
s.add_finalize().unwrap();
}
git::commit(&repo);
drop((repo, url));
let repo = git2::Repository::open(&git_project.root()).unwrap();
let url = path2url(git_project2.root()).to_string();
git::add_submodule(&repo, &url, Path::new("src"));
git::commit(&repo);
drop(repo);
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies]
dep1 = {{ git = '{}' }}
"#,
git_project.url()
),
)
.file("src/lib.rs", "")
.build();
project
.cargo("build")
.with_status(101)
.with_stderr_contains(" failed to update submodule `src`")
.with_stderr_contains(" failed to update submodule `bar`")
.run();
project
.cargo("build")
.with_status(101)
.with_stderr_contains(" failed to update submodule `src`")
.with_stderr_contains(" failed to update submodule `bar`")
.run();
done.store(true, Ordering::SeqCst);
drop(TcpStream::connect(&addr));
t.join().unwrap();
}
#[test]
fn use_the_cli() {
if env::var("CARGO_TEST_DISABLE_GIT_CLI") == Ok("1".to_string()) {
// mingw git on Windows does not support Windows-style file URIs.
// Appveyor in the rust repo has that git up front in the PATH instead
// of Git-for-Windows, which causes this to fail.
return;
}
let project = project();
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let project = project
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
[dependencies]
dep1 = {{ git = '{}' }}
"#,
git_project.url()
),
)
.file("src/lib.rs", "")
.file(
".cargo/config",
"
[net]
git-fetch-with-cli = true
",
)
.build();
let stderr = "\
[UPDATING] git repository `[..]`
[RUNNING] `git fetch [..]`
[COMPILING] dep1 [..]
[RUNNING] `rustc [..]`
[COMPILING] foo [..]
[RUNNING] `rustc [..]`
[FINISHED] [..]
";
project.cargo("build -v").with_stderr(stderr).run();
}
#[test]
fn templatedir_doesnt_cause_problems() {
let git_project2 = git::new("dep2", |project| {
project
.file("Cargo.toml", &basic_manifest("dep2", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
.file("src/lib.rs", "")
})
.unwrap();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[project]
name = "fo"
version = "0.5.0"
authors = []
[dependencies]
dep1 = {{ git = '{}' }}
"#,
git_project.url()
),
)
.file("src/main.rs", "fn main() {}")
.build();
File::create(paths::home().join(".gitconfig"))
.unwrap()
.write_all(
&format!(
r#"
[init]
templatedir = {}
"#,
git_project2
.url()
.to_file_path()
.unwrap()
.to_str()
.unwrap()
.replace("\\", "/")
)
.as_bytes(),
)
.unwrap();
p.cargo("build").run();
}