mirror of https://github.com/rust-lang/cargo
Add support for relative git submodule paths
This commit is contained in:
parent
531ce1321d
commit
942f7f9c59
|
@ -191,7 +191,8 @@ impl<'cfg> Source for GitSource<'cfg> {
|
|||
.join("checkouts")
|
||||
.join(&self.ident)
|
||||
.join(short_id.as_str());
|
||||
db.copy_to(actual_rev, &checkout_path, self.config)?;
|
||||
let parent_remote_url = self.url();
|
||||
db.copy_to(actual_rev, &checkout_path, self.config, parent_remote_url)?;
|
||||
|
||||
let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
|
||||
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::process::Command;
|
||||
use std::str;
|
||||
use std::time::{Duration, Instant};
|
||||
use url::Url;
|
||||
use url::{ParseError, Url};
|
||||
|
||||
fn serialize_str<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -151,6 +151,7 @@ impl GitDatabase {
|
|||
rev: git2::Oid,
|
||||
dest: &Path,
|
||||
cargo_config: &Config,
|
||||
parent_remote_url: &Url,
|
||||
) -> CargoResult<GitCheckout<'_>> {
|
||||
// If the existing checkout exists, and it is fresh, use it.
|
||||
// A non-fresh checkout can happen if the checkout operation was
|
||||
|
@ -164,7 +165,7 @@ impl GitDatabase {
|
|||
Some(co) => co,
|
||||
None => GitCheckout::clone_into(dest, self, rev, cargo_config)?,
|
||||
};
|
||||
checkout.update_submodules(cargo_config)?;
|
||||
checkout.update_submodules(cargo_config, parent_remote_url)?;
|
||||
Ok(checkout)
|
||||
}
|
||||
|
||||
|
@ -322,19 +323,25 @@ impl<'a> GitCheckout<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn update_submodules(&self, cargo_config: &Config) -> CargoResult<()> {
|
||||
return update_submodules(&self.repo, cargo_config);
|
||||
fn update_submodules(&self, cargo_config: &Config, parent_remote_url: &Url) -> CargoResult<()> {
|
||||
return update_submodules(&self.repo, cargo_config, parent_remote_url);
|
||||
|
||||
fn update_submodules(repo: &git2::Repository, cargo_config: &Config) -> CargoResult<()> {
|
||||
fn update_submodules(
|
||||
repo: &git2::Repository,
|
||||
cargo_config: &Config,
|
||||
parent_remote_url: &Url,
|
||||
) -> CargoResult<()> {
|
||||
debug!("update submodules for: {:?}", repo.workdir().unwrap());
|
||||
|
||||
for mut child in repo.submodules()? {
|
||||
update_submodule(repo, &mut child, cargo_config).with_context(|| {
|
||||
format!(
|
||||
"failed to update submodule `{}`",
|
||||
child.name().unwrap_or("")
|
||||
)
|
||||
})?;
|
||||
update_submodule(repo, &mut child, cargo_config, parent_remote_url).with_context(
|
||||
|| {
|
||||
format!(
|
||||
"failed to update submodule `{}`",
|
||||
child.name().unwrap_or("")
|
||||
)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -343,9 +350,11 @@ impl<'a> GitCheckout<'a> {
|
|||
parent: &git2::Repository,
|
||||
child: &mut git2::Submodule<'_>,
|
||||
cargo_config: &Config,
|
||||
parent_remote_url: &Url,
|
||||
) -> CargoResult<()> {
|
||||
child.init(false)?;
|
||||
let url = child.url().ok_or_else(|| {
|
||||
|
||||
let child_url_str = child.url().ok_or_else(|| {
|
||||
anyhow::format_err!("non-utf8 url for submodule {:?}?", child.path())
|
||||
})?;
|
||||
|
||||
|
@ -355,12 +364,52 @@ impl<'a> GitCheckout<'a> {
|
|||
"Skipping",
|
||||
format!(
|
||||
"git submodule `{}` due to update strategy in .gitmodules",
|
||||
url
|
||||
child_url_str
|
||||
),
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// There are a few possible cases here:
|
||||
// 1. Submodule URL is not relative.
|
||||
// Happy path, Url is just the submodule url.
|
||||
// 2. Submodule URL is relative and does specify ssh/https/file/etc.
|
||||
// We combine the parent path and relative path.
|
||||
// 3. Submodule URL is relative and does not specify ssh/https/file/etc.
|
||||
// In which case we fail to parse with the error ParseError::RelativeUrlWithoutBase.
|
||||
// We then combine the relative url with the host/protocol from the parent url.
|
||||
// 4. We fail to parse submodule url for some reason.
|
||||
// Error. Gets passed up.
|
||||
|
||||
let join_urls = || {
|
||||
let path = parent_remote_url.path();
|
||||
let mut new_parent_remote_url = parent_remote_url.clone();
|
||||
new_parent_remote_url.set_path(&format!("{}/", path));
|
||||
match new_parent_remote_url.join(child_url_str) {
|
||||
Ok(x) => Ok(x.to_string()),
|
||||
Err(err) => {
|
||||
Err(err).with_context(|| format!("Failed to parse child submodule url"))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let url = match Url::parse(child_url_str) {
|
||||
Ok(child_url) => {
|
||||
if Path::new(child_url.path()).is_relative() {
|
||||
join_urls()?
|
||||
} else {
|
||||
child_url.to_string()
|
||||
}
|
||||
}
|
||||
Err(ParseError::RelativeUrlWithoutBase) => join_urls()?,
|
||||
Err(err) => {
|
||||
return Err(anyhow::format_err!(
|
||||
"Error parsing submodule url: {:?}?",
|
||||
err
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// A submodule which is listed in .gitmodules but not actually
|
||||
// checked out will not have a head id, so we should ignore it.
|
||||
let head = match child.head_id() {
|
||||
|
@ -379,7 +428,7 @@ impl<'a> GitCheckout<'a> {
|
|||
let mut repo = match head_and_repo {
|
||||
Ok((head, repo)) => {
|
||||
if child.head_id() == head {
|
||||
return update_submodules(&repo, cargo_config);
|
||||
return update_submodules(&repo, cargo_config, parent_remote_url);
|
||||
}
|
||||
repo
|
||||
}
|
||||
|
@ -394,7 +443,7 @@ impl<'a> GitCheckout<'a> {
|
|||
cargo_config
|
||||
.shell()
|
||||
.status("Updating", format!("git submodule `{}`", url))?;
|
||||
fetch(&mut repo, url, &reference, cargo_config).with_context(|| {
|
||||
fetch(&mut repo, &url, &reference, cargo_config).with_context(|| {
|
||||
format!(
|
||||
"failed to fetch submodule `{}` from {}",
|
||||
child.name().unwrap_or(""),
|
||||
|
@ -404,7 +453,7 @@ impl<'a> GitCheckout<'a> {
|
|||
|
||||
let obj = repo.find_object(head, None)?;
|
||||
reset(&repo, &obj, cargo_config)?;
|
||||
update_submodules(&repo, cargo_config)
|
||||
update_submodules(&repo, cargo_config, parent_remote_url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -935,6 +935,73 @@ fn dep_with_submodule() {
|
|||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn dep_with_relative_submodule() {
|
||||
let foo = project();
|
||||
let base = git::new("base", |project| {
|
||||
project
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "base"
|
||||
version = "0.5.0"
|
||||
|
||||
[dependencies]
|
||||
deployment.path = "deployment"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn dep() {
|
||||
deployment::deployment_func();
|
||||
}
|
||||
"#,
|
||||
)
|
||||
});
|
||||
let _deployment = git::new("deployment", |project| {
|
||||
project
|
||||
.file("src/lib.rs", "pub fn deployment_func() {}")
|
||||
.file("Cargo.toml", &basic_lib_manifest("deployment"))
|
||||
});
|
||||
|
||||
let base_repo = git2::Repository::open(&base.root()).unwrap();
|
||||
git::add_submodule(&base_repo, "../deployment", Path::new("deployment"));
|
||||
git::commit(&base_repo);
|
||||
|
||||
let project = foo
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
|
||||
[dependencies.base]
|
||||
git = '{}'
|
||||
"#,
|
||||
base.url()
|
||||
),
|
||||
)
|
||||
.file("src/lib.rs", "pub fn foo() { }")
|
||||
.build();
|
||||
|
||||
project
|
||||
.cargo("build")
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] git repository [..]
|
||||
[UPDATING] git submodule `file://[..]/deployment`
|
||||
[COMPILING] deployment [..]
|
||||
[COMPILING] base [..]
|
||||
[COMPILING] foo [..]
|
||||
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn dep_with_bad_submodule() {
|
||||
let project = project();
|
||||
|
|
Loading…
Reference in New Issue