Skip to content

Commit c2d12c6

Browse files
committed
Add some help with updating the registry in offline mode.
This makes two changes: - If an index has never been downloaded, and `-Z offline` is used, provide a suggestion to run without `-Z offline`. - If an index needs to be updated, and the network appears to be down, suggest running with `-Z offline`.
1 parent 80b9d32 commit c2d12c6

File tree

4 files changed

+95
-11
lines changed

4 files changed

+95
-11
lines changed

src/cargo/sources/registry/index.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ impl<'cfg> RegistryIndex<'cfg> {
300300
let summaries = summaries
301301
.iter()
302302
.filter(|&(summary, yanked)| {
303+
// Note: This particular logic can cause problems with
304+
// optional dependencies when offline. If at least 1 version
305+
// of an optional dependency is downloaded, but that version
306+
// does not satisfy the requirements, then resolution will
307+
// fail. Unfortunately, whether or not something is optional
308+
// is not known here.
303309
(online || load.is_crate_downloaded(summary.package_id()))
304310
&& (!yanked || {
305311
log::debug!("{:?}", yanked_whitelist);

src/cargo/sources/registry/remote.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ use std::str;
99
use lazycell::LazyCell;
1010
use log::{debug, trace};
1111

12-
use crate::core::{PackageId, SourceId};
12+
use crate::core::{nightly_features_allowed, PackageId, SourceId};
1313
use crate::sources::git;
1414
use crate::sources::registry::MaybeLock;
1515
use crate::sources::registry::{
1616
RegistryConfig, RegistryData, CRATE_TEMPLATE, INDEX_LOCK, VERSION_TEMPLATE,
1717
};
1818
use crate::util::errors::{CargoResult, CargoResultExt};
19+
use crate::util::network::maybe_spurious;
1920
use crate::util::{Config, Sha256};
2021
use crate::util::{FileLock, Filesystem};
2122

@@ -176,6 +177,14 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
176177

177178
fn update_index(&mut self) -> CargoResult<()> {
178179
if self.config.cli_unstable().offline {
180+
if self.repo()?.is_empty()? {
181+
failure::bail!(
182+
"unable to fetch {} in offline mode\n\
183+
Run `cargo fetch` to download the registry index and all \
184+
of a project's dependencies before going offline.",
185+
self.source_id
186+
);
187+
}
179188
return Ok(());
180189
}
181190
if self.config.cli_unstable().no_index_update {
@@ -212,8 +221,18 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
212221
let url = self.source_id.url();
213222
let refspec = "refs/heads/master:refs/remotes/origin/master";
214223
let repo = self.repo.borrow_mut().unwrap();
215-
git::fetch(repo, url, refspec, self.config)
216-
.chain_err(|| format!("failed to fetch `{}`", url))?;
224+
let result = git::fetch(repo, url, refspec, self.config);
225+
if let Err(e) = result {
226+
let extra = if maybe_spurious(&e) && nightly_features_allowed() && !repo.is_empty()? {
227+
"\nIf the network is unavailable, consider running with the -Zoffline \
228+
flag to run in offline mode."
229+
} else {
230+
""
231+
};
232+
return Err(e
233+
.context(format!("failed to fetch `{}`{}", url, extra))
234+
.into());
235+
}
217236
self.config.updated_sources().insert(self.source_id);
218237
Ok(())
219238
}

src/cargo/util/network.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl<'a> Retry<'a> {
3636
}
3737
}
3838

39-
fn maybe_spurious(err: &Error) -> bool {
39+
pub fn maybe_spurious(err: &Error) -> bool {
4040
for e in err.iter_chain() {
4141
if let Some(git_err) = e.downcast_ref::<git2::Error>() {
4242
match git_err.class() {

tests/testsuite/offline.rs

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,15 @@ fn cargo_compile_offline_not_try_update() {
171171
.with_status(101)
172172
.with_stderr(
173173
"\
174-
error: no matching package named `not_cached_dep` found
175-
location searched: registry `[..]`
176-
required by package `bar v0.1.0 ([..])`
177-
As a reminder, you're using offline mode (-Z offline) \
178-
which can sometimes cause surprising resolution failures, \
179-
if this error is too confusing you may wish to retry \
180-
without the offline flag.",
174+
[ERROR] failed to load source for a dependency on `not_cached_dep`
175+
176+
Caused by:
177+
Unable to update registry `https://github.com/rust-lang/crates.io-index`
178+
179+
Caused by:
180+
unable to fetch registry `https://github.com/rust-lang/crates.io-index` in offline mode
181+
Run `cargo fetch` to download the registry index and all of a project's dependencies before going offline.
182+
",
181183
)
182184
.run();
183185
}
@@ -486,3 +488,60 @@ fn cargo_compile_offline_with_cached_git_dep() {
486488
.with_stdout("hello from cached git repo rev1\n")
487489
.run();
488490
}
491+
492+
#[test]
493+
fn offline_resolve_optional_fail() {
494+
// Example where resolve fails offline.
495+
//
496+
// This happens if at least 1 version of an optional dependency is
497+
// available, but none of them satisfy the requirements. The current logic
498+
// that handles this is `RegistryIndex::query_inner`, and it doesn't know
499+
// if the package being queried is an optional one. This is not ideal, it
500+
// would be best if it just ignored optional (unselected) dependencies.
501+
Package::new("dep", "1.0.0").publish();
502+
503+
let p = project()
504+
.file(
505+
"Cargo.toml",
506+
r#"
507+
[package]
508+
name = "foo"
509+
version = "0.1.0"
510+
511+
[dependencies]
512+
dep = { version = "1.0", optional = true }
513+
"#,
514+
)
515+
.file("src/lib.rs", "")
516+
.build();
517+
518+
p.cargo("fetch").run();
519+
520+
// Change dep to 2.0.
521+
p.change_file(
522+
"Cargo.toml",
523+
r#"
524+
[package]
525+
name = "foo"
526+
version = "0.1.0"
527+
528+
[dependencies]
529+
dep = { version = "2.0", optional = true }
530+
"#,
531+
);
532+
533+
p.cargo("build -Z offline")
534+
.masquerade_as_nightly_cargo()
535+
.with_status(101)
536+
.with_stderr("\
537+
[ERROR] failed to select a version for the requirement `dep = \"^2.0\"`
538+
candidate versions found which didn't match: 1.0.0
539+
location searched: `[..]` index (which is replacing registry `https://github.com/rust-lang/crates.io-index`)
540+
required by package `foo v0.1.0 ([..]/foo)`
541+
perhaps a crate was updated and forgotten to be re-vendored?
542+
As a reminder, you're using offline mode (-Z offline) which can sometimes cause \
543+
surprising resolution failures, if this error is too confusing you may wish to \
544+
retry without the offline flag.
545+
")
546+
.run();
547+
}

0 commit comments

Comments
 (0)