Don't fail workspace loading if sysroot can't be found

This commit is contained in:
Lukas Wirth 2023-01-27 13:49:28 +01:00
parent 9814d79841
commit b2598f4801
4 changed files with 121 additions and 79 deletions

View file

@ -70,6 +70,10 @@ impl Sysroot {
pub fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ { pub fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
self.crates.iter().map(|(id, _data)| id) self.crates.iter().map(|(id, _data)| id)
} }
pub fn is_empty(&self) -> bool {
self.crates.is_empty()
}
} }
impl Sysroot { impl Sysroot {
@ -79,8 +83,7 @@ impl Sysroot {
let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
let sysroot_src_dir = let sysroot_src_dir =
discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?; discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?;
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
Ok(res)
} }
pub fn discover_rustc( pub fn discover_rustc(
@ -97,11 +100,10 @@ impl Sysroot {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
format_err!("can't load standard library from sysroot {}", sysroot_dir.display()) format_err!("can't load standard library from sysroot {}", sysroot_dir.display())
})?; })?;
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
Ok(res)
} }
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> { pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot {
let mut sysroot = let mut sysroot =
Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };
@ -152,14 +154,14 @@ impl Sysroot {
} else { } else {
"" ""
}; };
anyhow::bail!( tracing::error!(
"could not find libcore in sysroot path `{}`{}", "could not find libcore in sysroot path `{}`{}",
sysroot.src_root.as_path().display(), sysroot.src_root.as_path().display(),
var_note, var_note,
); );
} }
Ok(sysroot) sysroot
} }
fn by_name(&self, name: &str) -> Option<SysrootCrate> { fn by_name(&self, name: &str) -> Option<SysrootCrate> {

View file

@ -81,7 +81,7 @@ fn get_fake_sysroot() -> Sysroot {
// fake sysroot, so we give them both the same path: // fake sysroot, so we give them both the same path:
let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_dir = AbsPathBuf::assert(sysroot_path);
let sysroot_src_dir = sysroot_dir.clone(); let sysroot_src_dir = sysroot_dir.clone();
Sysroot::load(sysroot_dir, sysroot_src_dir).unwrap() Sysroot::load(sysroot_dir, sysroot_src_dir)
} }
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {

View file

@ -63,7 +63,7 @@ pub struct PackageRoot {
pub exclude: Vec<AbsPathBuf>, pub exclude: Vec<AbsPathBuf>,
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone)]
pub enum ProjectWorkspace { pub enum ProjectWorkspace {
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
Cargo { Cargo {
@ -83,7 +83,6 @@ pub enum ProjectWorkspace {
}, },
/// Project workspace was manually specified using a `rust-project.json` file. /// Project workspace was manually specified using a `rust-project.json` file.
Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning. // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for. // That's not the end user experience we should strive for.
// Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working. // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
@ -163,7 +162,7 @@ impl ProjectWorkspace {
project_json, project_json,
config.target.as_deref(), config.target.as_deref(),
&config.extra_env, &config.extra_env,
)? )
} }
ProjectManifest::CargoToml(cargo_toml) => { ProjectManifest::CargoToml(cargo_toml) => {
let cargo_version = utf8_stdout({ let cargo_version = utf8_stdout({
@ -193,20 +192,27 @@ impl ProjectWorkspace {
let sysroot = match &config.sysroot { let sysroot = match &config.sysroot {
Some(RustcSource::Path(path)) => { Some(RustcSource::Path(path)) => {
Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| { match Sysroot::with_sysroot_dir(path.clone()) {
format!("Failed to find sysroot at {}.", path.display()) Ok(it) => Some(it),
})?) Err(e) => {
tracing::error!(%e, "Failed to find sysroot at {}.", path.display());
None
}
}
}
Some(RustcSource::Discover) => {
match Sysroot::discover(cargo_toml.parent(), &config.extra_env) {
Ok(it) => Some(it),
Err(e) => {
tracing::error!(
%e,
"Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
cargo_toml.display()
);
None
}
}
} }
Some(RustcSource::Discover) => Some(
Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context(
|| {
format!(
"Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
cargo_toml.display()
)
},
)?,
),
None => None, None => None,
}; };
if let Some(sysroot) = &sysroot { if let Some(sysroot) = &sysroot {
@ -225,18 +231,22 @@ impl ProjectWorkspace {
} }
let rustc = match rustc_dir { let rustc = match rustc_dir {
Some(rustc_dir) => Some({ Some(rustc_dir) => match CargoWorkspace::fetch_metadata(
let meta = CargoWorkspace::fetch_metadata( &rustc_dir,
&rustc_dir, cargo_toml.parent(),
cargo_toml.parent(), config,
config, progress,
progress, ) {
) Ok(meta) => Some(CargoWorkspace::new(meta)),
.with_context(|| { Err(e) => {
"Failed to read Cargo metadata for Rust sources".to_string() tracing::error!(
})?; %e,
CargoWorkspace::new(meta) "Failed to read Cargo metadata from rustc source at {}",
}), rustc_dir.display()
);
None
}
},
None => None, None => None,
}; };
@ -272,15 +282,14 @@ impl ProjectWorkspace {
project_json: ProjectJson, project_json: ProjectJson,
target: Option<&str>, target: Option<&str>,
extra_env: &FxHashMap<String, String>, extra_env: &FxHashMap<String, String>,
) -> Result<ProjectWorkspace> { ) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?), (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)),
(Some(sysroot), None) => { (Some(sysroot), None) => {
// assume sysroot is structured like rustup's and guess `sysroot_src` // assume sysroot is structured like rustup's and guess `sysroot_src`
let sysroot_src = let sysroot_src =
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library"); sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
Some(Sysroot::load(sysroot, sysroot_src))
Some(Sysroot::load(sysroot, sysroot_src)?)
} }
(None, Some(sysroot_src)) => { (None, Some(sysroot_src)) => {
// assume sysroot is structured like rustup's and guess `sysroot` // assume sysroot is structured like rustup's and guess `sysroot`
@ -288,7 +297,7 @@ impl ProjectWorkspace {
for _ in 0..5 { for _ in 0..5 {
sysroot.pop(); sysroot.pop();
} }
Some(Sysroot::load(sysroot, sysroot_src)?) Some(Sysroot::load(sysroot, sysroot_src))
} }
(None, None) => None, (None, None) => None,
}; };
@ -297,7 +306,7 @@ impl ProjectWorkspace {
} }
let rustc_cfg = rustc_cfg::get(None, target, extra_env); let rustc_cfg = rustc_cfg::get(None, target, extra_env);
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }
} }
pub fn load_detached_files( pub fn load_detached_files(
@ -305,18 +314,29 @@ impl ProjectWorkspace {
config: &CargoConfig, config: &CargoConfig,
) -> Result<ProjectWorkspace> { ) -> Result<ProjectWorkspace> {
let sysroot = match &config.sysroot { let sysroot = match &config.sysroot {
Some(RustcSource::Path(path)) => Some( Some(RustcSource::Path(path)) => match Sysroot::with_sysroot_dir(path.clone()) {
Sysroot::with_sysroot_dir(path.clone()) Ok(it) => Some(it),
.with_context(|| format!("Failed to find sysroot at {}.", path.display()))?, Err(e) => {
), tracing::error!(%e, "Failed to find sysroot at {}.", path.display());
None
}
},
Some(RustcSource::Discover) => { Some(RustcSource::Discover) => {
let dir = &detached_files let dir = &detached_files
.first() .first()
.and_then(|it| it.parent()) .and_then(|it| it.parent())
.ok_or_else(|| format_err!("No detached files to load"))?; .ok_or_else(|| format_err!("No detached files to load"))?;
Some(Sysroot::discover(dir, &config.extra_env).with_context(|| { match Sysroot::discover(dir, &config.extra_env) {
format!("Failed to find sysroot in {}. Is rust-src installed?", dir.display()) Ok(it) => Some(it),
})?) Err(e) => {
tracing::error!(
%e,
"Failed to find sysroot for {}. Is rust-src installed?",
dir.display()
);
None
}
}
} }
None => None, None => None,
}; };
@ -541,7 +561,7 @@ impl ProjectWorkspace {
load_proc_macro, load_proc_macro,
load, load,
project, project,
sysroot, sysroot.as_ref(),
extra_env, extra_env,
Err("rust-project.json projects have no target layout set".into()), Err("rust-project.json projects have no target layout set".into()),
), ),
@ -585,6 +605,49 @@ impl ProjectWorkspace {
} }
crate_graph crate_graph
} }
pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
match (self, other) {
(
Self::Cargo {
cargo,
sysroot,
rustc,
rustc_cfg,
cfg_overrides,
toolchain,
build_scripts: _,
target_layout: _,
},
Self::Cargo {
cargo: o_cargo,
sysroot: o_sysroot,
rustc: o_rustc,
rustc_cfg: o_rustc_cfg,
cfg_overrides: o_cfg_overrides,
toolchain: o_toolchain,
build_scripts: _,
target_layout: _,
},
) => {
cargo == o_cargo
&& rustc == o_rustc
&& rustc_cfg == o_rustc_cfg
&& cfg_overrides == o_cfg_overrides
&& toolchain == o_toolchain
&& sysroot == o_sysroot
}
(
Self::Json { project, sysroot, rustc_cfg },
Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot,
(
Self::DetachedFiles { files, sysroot, rustc_cfg },
Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
) => files == o_files && sysroot == o_sysroot && rustc_cfg == o_rustc_cfg,
_ => false,
}
}
} }
fn project_json_to_crate_graph( fn project_json_to_crate_graph(
@ -592,7 +655,7 @@ fn project_json_to_crate_graph(
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson, project: &ProjectJson,
sysroot: &Option<Sysroot>, sysroot: Option<&Sysroot>,
extra_env: &FxHashMap<String, String>, extra_env: &FxHashMap<String, String>,
target_layout: TargetLayoutLoadResult, target_layout: TargetLayoutLoadResult,
) -> CrateGraph { ) -> CrateGraph {

View file

@ -148,11 +148,11 @@ impl GlobalState {
) )
} }
LinkedProject::InlineJsonProject(it) => { LinkedProject::InlineJsonProject(it) => {
project_model::ProjectWorkspace::load_inline( Ok(project_model::ProjectWorkspace::load_inline(
it.clone(), it.clone(),
cargo_config.target.as_deref(), cargo_config.target.as_deref(),
&cargo_config.extra_env, &cargo_config.extra_env,
) ))
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -212,35 +212,11 @@ impl GlobalState {
let workspaces = let workspaces =
workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>(); workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
fn eq_ignore_build_data<'a>(
left: &'a ProjectWorkspace,
right: &'a ProjectWorkspace,
) -> bool {
let key = |p: &'a ProjectWorkspace| match p {
ProjectWorkspace::Cargo {
cargo,
sysroot,
rustc,
rustc_cfg,
cfg_overrides,
build_scripts: _,
toolchain: _,
target_layout: _,
} => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)),
_ => None,
};
match (key(left), key(right)) {
(Some(lk), Some(rk)) => lk == rk,
_ => left == right,
}
}
let same_workspaces = workspaces.len() == self.workspaces.len() let same_workspaces = workspaces.len() == self.workspaces.len()
&& workspaces && workspaces
.iter() .iter()
.zip(self.workspaces.iter()) .zip(self.workspaces.iter())
.all(|(l, r)| eq_ignore_build_data(l, r)); .all(|(l, r)| l.eq_ignore_build_data(r));
if same_workspaces { if same_workspaces {
let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result(); let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
@ -270,7 +246,8 @@ impl GlobalState {
// Here, we completely changed the workspace (Cargo.toml edit), so // Here, we completely changed the workspace (Cargo.toml edit), so
// we don't care about build-script results, they are stale. // we don't care about build-script results, they are stale.
self.workspaces = Arc::new(workspaces) // FIXME: can we abort the build scripts here?
self.workspaces = Arc::new(workspaces);
} }
if let FilesWatcher::Client = self.config.files().watcher { if let FilesWatcher::Client = self.config.files().watcher {