Merge pull request #20315 from Veykril/push-pvmslwwouzzx

internal: Fix lockfile temp dir usage and use it for build scripts as well
This commit is contained in:
Lukas Wirth 2025-07-27 18:39:39 +00:00 committed by GitHub
commit 68810295f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 246 additions and 182 deletions

View File

@ -30,7 +30,8 @@ pub type ProcMacroPaths =
pub enum ProcMacroLoadingError { pub enum ProcMacroLoadingError {
Disabled, Disabled,
FailedToBuild, FailedToBuild,
MissingDylibPath, ExpectedProcMacroArtifact,
MissingDylibPath(Box<[String]>),
NotYetBuilt, NotYetBuilt,
NoProcMacros, NoProcMacros,
ProcMacroSrvError(Box<str>), ProcMacroSrvError(Box<str>),
@ -39,8 +40,9 @@ impl ProcMacroLoadingError {
pub fn is_hard_error(&self) -> bool { pub fn is_hard_error(&self) -> bool {
match self { match self {
ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false, ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false,
ProcMacroLoadingError::FailedToBuild ProcMacroLoadingError::ExpectedProcMacroArtifact
| ProcMacroLoadingError::MissingDylibPath | ProcMacroLoadingError::FailedToBuild
| ProcMacroLoadingError::MissingDylibPath(_)
| ProcMacroLoadingError::NoProcMacros | ProcMacroLoadingError::NoProcMacros
| ProcMacroLoadingError::ProcMacroSrvError(_) => true, | ProcMacroLoadingError::ProcMacroSrvError(_) => true,
} }
@ -51,10 +53,23 @@ impl Error for ProcMacroLoadingError {}
impl fmt::Display for ProcMacroLoadingError { impl fmt::Display for ProcMacroLoadingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
ProcMacroLoadingError::ExpectedProcMacroArtifact => {
write!(f, "proc-macro crate did not build proc-macro artifact")
}
ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"), ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"),
ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"), ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"),
ProcMacroLoadingError::MissingDylibPath => { ProcMacroLoadingError::MissingDylibPath(candidates) if candidates.is_empty() => {
write!(f, "proc-macro crate build data is missing a dylib path") write!(
f,
"proc-macro crate built but the dylib path is missing, this indicates a problem with your build system."
)
}
ProcMacroLoadingError::MissingDylibPath(candidates) => {
write!(
f,
"proc-macro crate built but the dylib path is missing, this indicates a problem with your build system. Candidates not considered due to not having a dynamic library extension: {}",
candidates.join(", ")
)
} }
ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"), ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"),
ProcMacroLoadingError::NoProcMacros => { ProcMacroLoadingError::NoProcMacros => {

View File

@ -16,11 +16,13 @@ use la_arena::ArenaMap;
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize as _; use serde::Deserialize as _;
use stdx::never;
use toolchain::Tool; use toolchain::Tool;
use crate::{ use crate::{
CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot,
TargetKind, utf8_stdout, TargetKind, cargo_config_file::make_lockfile_copy,
cargo_workspace::MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH, utf8_stdout,
}; };
/// Output of the build script and proc-macro building steps for a workspace. /// Output of the build script and proc-macro building steps for a workspace.
@ -30,6 +32,15 @@ pub struct WorkspaceBuildScripts {
error: Option<String>, error: Option<String>,
} }
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum ProcMacroDylibPath {
Path(AbsPathBuf),
DylibNotFound(Box<[Utf8PathBuf]>),
NotProcMacro,
#[default]
NotBuilt,
}
/// Output of the build script and proc-macro building step for a concrete package. /// Output of the build script and proc-macro building step for a concrete package.
#[derive(Debug, Clone, Default, PartialEq, Eq)] #[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput { pub(crate) struct BuildScriptOutput {
@ -43,7 +54,7 @@ pub(crate) struct BuildScriptOutput {
/// Directory where a build script might place its output. /// Directory where a build script might place its output.
pub(crate) out_dir: Option<AbsPathBuf>, pub(crate) out_dir: Option<AbsPathBuf>,
/// Path to the proc-macro library file if this package exposes proc-macros. /// Path to the proc-macro library file if this package exposes proc-macros.
pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, pub(crate) proc_macro_dylib_path: ProcMacroDylibPath,
} }
impl BuildScriptOutput { impl BuildScriptOutput {
@ -51,7 +62,7 @@ impl BuildScriptOutput {
self.cfgs.is_empty() self.cfgs.is_empty()
&& self.envs.is_empty() && self.envs.is_empty()
&& self.out_dir.is_none() && self.out_dir.is_none()
&& self.proc_macro_dylib_path.is_none() && self.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt
} }
} }
@ -67,7 +78,7 @@ impl WorkspaceBuildScripts {
let current_dir = workspace.workspace_root(); let current_dir = workspace.workspace_root();
let allowed_features = workspace.workspace_features(); let allowed_features = workspace.workspace_features();
let cmd = Self::build_command( let (_guard, cmd) = Self::build_command(
config, config,
&allowed_features, &allowed_features,
workspace.manifest_path(), workspace.manifest_path(),
@ -88,7 +99,7 @@ impl WorkspaceBuildScripts {
) -> io::Result<Vec<WorkspaceBuildScripts>> { ) -> io::Result<Vec<WorkspaceBuildScripts>> {
assert_eq!(config.invocation_strategy, InvocationStrategy::Once); assert_eq!(config.invocation_strategy, InvocationStrategy::Once);
let cmd = Self::build_command( let (_guard, cmd) = Self::build_command(
config, config,
&Default::default(), &Default::default(),
// This is not gonna be used anyways, so just construct a dummy here // This is not gonna be used anyways, so just construct a dummy here
@ -126,6 +137,8 @@ impl WorkspaceBuildScripts {
|package, cb| { |package, cb| {
if let Some(&(package, workspace)) = by_id.get(package) { if let Some(&(package, workspace)) = by_id.get(package) {
cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]);
} else {
never!("Received compiler message for unknown package: {}", package);
} }
}, },
progress, progress,
@ -140,12 +153,9 @@ impl WorkspaceBuildScripts {
if tracing::enabled!(tracing::Level::INFO) { if tracing::enabled!(tracing::Level::INFO) {
for (idx, workspace) in workspaces.iter().enumerate() { for (idx, workspace) in workspaces.iter().enumerate() {
for package in workspace.packages() { for package in workspace.packages() {
let package_build_data = &mut res[idx].outputs[package]; let package_build_data: &mut BuildScriptOutput = &mut res[idx].outputs[package];
if !package_build_data.is_empty() { if !package_build_data.is_empty() {
tracing::info!( tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,);
"{}: {package_build_data:?}",
workspace[package].manifest.parent(),
);
} }
} }
} }
@ -198,10 +208,33 @@ impl WorkspaceBuildScripts {
let path = dir_entry.path(); let path = dir_entry.path();
let extension = path.extension()?; let extension = path.extension()?;
if extension == std::env::consts::DLL_EXTENSION { if extension == std::env::consts::DLL_EXTENSION {
let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); let name = path
let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) .file_stem()?
.ok()?; .to_str()?
return Some((name, path)); .split_once('-')?
.0
.trim_start_matches("lib")
.to_owned();
let path = match Utf8PathBuf::from_path_buf(path) {
Ok(path) => path,
Err(path) => {
tracing::warn!(
"Proc-macro dylib path contains non-UTF8 characters: {:?}",
path.display()
);
return None;
}
};
return match AbsPathBuf::try_from(path) {
Ok(path) => Some((name, path)),
Err(path) => {
tracing::error!(
"proc-macro dylib path is not absolute: {:?}",
path
);
None
}
};
} }
} }
None None
@ -209,28 +242,24 @@ impl WorkspaceBuildScripts {
.collect(); .collect();
for p in rustc.packages() { for p in rustc.packages() {
let package = &rustc[p]; let package = &rustc[p];
if package bs.outputs[p].proc_macro_dylib_path =
.targets if package.targets.iter().any(|&it| {
.iter() matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })
.any(|&it| matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })) }) {
{ match proc_macro_dylibs.iter().find(|(name, _)| *name == package.name) {
if let Some((_, path)) = proc_macro_dylibs Some((_, path)) => ProcMacroDylibPath::Path(path.clone()),
.iter() _ => ProcMacroDylibPath::DylibNotFound(Box::default()),
.find(|(name, _)| *name.trim_start_matches("lib") == package.name) }
{ } else {
bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); ProcMacroDylibPath::NotProcMacro
} }
}
} }
if tracing::enabled!(tracing::Level::INFO) { if tracing::enabled!(tracing::Level::INFO) {
for package in rustc.packages() { for package in rustc.packages() {
let package_build_data = &bs.outputs[package]; let package_build_data = &bs.outputs[package];
if !package_build_data.is_empty() { if !package_build_data.is_empty() {
tracing::info!( tracing::info!("{}: {package_build_data:?}", rustc[package].manifest,);
"{}: {package_build_data:?}",
rustc[package].manifest.parent(),
);
} }
} }
} }
@ -263,6 +292,12 @@ impl WorkspaceBuildScripts {
|package, cb| { |package, cb| {
if let Some(&package) = by_id.get(package) { if let Some(&package) = by_id.get(package) {
cb(&workspace[package].name, &mut outputs[package]); cb(&workspace[package].name, &mut outputs[package]);
} else {
never!(
"Received compiler message for unknown package: {}\n {}",
package,
by_id.keys().join(", ")
);
} }
}, },
progress, progress,
@ -272,10 +307,7 @@ impl WorkspaceBuildScripts {
for package in workspace.packages() { for package in workspace.packages() {
let package_build_data = &outputs[package]; let package_build_data = &outputs[package];
if !package_build_data.is_empty() { if !package_build_data.is_empty() {
tracing::info!( tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,);
"{}: {package_build_data:?}",
workspace[package].manifest.parent(),
);
} }
} }
} }
@ -348,15 +380,21 @@ impl WorkspaceBuildScripts {
progress(format!( progress(format!(
"building compile-time-deps: proc-macro {name} built" "building compile-time-deps: proc-macro {name} built"
)); ));
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
}
if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro)
{ {
// Skip rmeta file data.proc_macro_dylib_path =
if let Some(filename) = match message.filenames.iter().find(|file| is_dylib(file)) {
message.filenames.iter().find(|file| is_dylib(file)) Some(filename) => {
{ let filename = AbsPath::assert(filename);
let filename = AbsPath::assert(filename); ProcMacroDylibPath::Path(filename.to_owned())
data.proc_macro_dylib_path = Some(filename.to_owned()); }
} None => ProcMacroDylibPath::DylibNotFound(
message.filenames.clone().into_boxed_slice(),
),
};
} }
}); });
} }
@ -393,14 +431,15 @@ impl WorkspaceBuildScripts {
current_dir: &AbsPath, current_dir: &AbsPath,
sysroot: &Sysroot, sysroot: &Sysroot,
toolchain: Option<&semver::Version>, toolchain: Option<&semver::Version>,
) -> io::Result<Command> { ) -> io::Result<(Option<temp_dir::TempDir>, Command)> {
match config.run_build_script_command.as_deref() { match config.run_build_script_command.as_deref() {
Some([program, args @ ..]) => { Some([program, args @ ..]) => {
let mut cmd = toolchain::command(program, current_dir, &config.extra_env); let mut cmd = toolchain::command(program, current_dir, &config.extra_env);
cmd.args(args); cmd.args(args);
Ok(cmd) Ok((None, cmd))
} }
_ => { _ => {
let mut requires_unstable_options = false;
let mut cmd = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env); let mut cmd = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
@ -416,7 +455,19 @@ impl WorkspaceBuildScripts {
if let Some(target) = &config.target { if let Some(target) = &config.target {
cmd.args(["--target", target]); cmd.args(["--target", target]);
} }
let mut temp_dir_guard = None;
if toolchain
.is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
{
let lockfile_path =
<_ as AsRef<Utf8Path>>::as_ref(manifest_path).with_extension("lock");
if let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile_path) {
temp_dir_guard = Some(temp_dir);
cmd.arg("--lockfile-path");
cmd.arg(target_lockfile.as_str());
requires_unstable_options = true;
}
}
match &config.features { match &config.features {
CargoFeatures::All => { CargoFeatures::All => {
cmd.arg("--all-features"); cmd.arg("--all-features");
@ -438,6 +489,7 @@ impl WorkspaceBuildScripts {
} }
if manifest_path.is_rust_manifest() { if manifest_path.is_rust_manifest() {
requires_unstable_options = true;
cmd.arg("-Zscript"); cmd.arg("-Zscript");
} }
@ -447,7 +499,7 @@ impl WorkspaceBuildScripts {
// available in current toolchain's cargo, use it to build compile time deps only. // available in current toolchain's cargo, use it to build compile time deps only.
const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version {
major: 1, major: 1,
minor: 89, minor: 189,
patch: 0, patch: 0,
pre: semver::Prerelease::EMPTY, pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY, build: semver::BuildMetadata::EMPTY,
@ -457,8 +509,7 @@ impl WorkspaceBuildScripts {
toolchain.is_some_and(|v| *v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION); toolchain.is_some_and(|v| *v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION);
if cargo_comp_time_deps_available { if cargo_comp_time_deps_available {
cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); requires_unstable_options = true;
cmd.arg("-Zunstable-options");
cmd.arg("--compile-time-deps"); cmd.arg("--compile-time-deps");
// we can pass this unconditionally, because we won't actually build the // we can pass this unconditionally, because we won't actually build the
// binaries, and as such, this will succeed even on targets without libtest // binaries, and as such, this will succeed even on targets without libtest
@ -481,7 +532,11 @@ impl WorkspaceBuildScripts {
cmd.env("RA_RUSTC_WRAPPER", "1"); cmd.env("RA_RUSTC_WRAPPER", "1");
} }
} }
Ok(cmd) if requires_unstable_options {
cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly");
cmd.arg("-Zunstable-options");
}
Ok((temp_dir_guard, cmd))
} }
} }
} }

View File

@ -1,4 +1,5 @@
//! Read `.cargo/config.toml` as a JSON object //! Read `.cargo/config.toml` as a JSON object
use paths::{Utf8Path, Utf8PathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use toolchain::Tool; use toolchain::Tool;
@ -32,3 +33,24 @@ pub(crate) fn read(
Some(json) Some(json)
} }
pub(crate) fn make_lockfile_copy(
lockfile_path: &Utf8Path,
) -> Option<(temp_dir::TempDir, Utf8PathBuf)> {
let temp_dir = temp_dir::TempDir::with_prefix("rust-analyzer").ok()?;
let target_lockfile = temp_dir.path().join("Cargo.lock").try_into().ok()?;
match std::fs::copy(lockfile_path, &target_lockfile) {
Ok(_) => {
tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, target_lockfile);
Some((temp_dir, target_lockfile))
}
// lockfile does not yet exist, so we can just create a new one in the temp dir
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Some((temp_dir, target_lockfile)),
Err(e) => {
tracing::warn!(
"Failed to copy lock file from `{lockfile_path}` to `{target_lockfile}`: {e}",
);
None
}
}
}

View File

@ -15,16 +15,18 @@ use span::Edition;
use stdx::process::spawn_with_streaming_output; use stdx::process::spawn_with_streaming_output;
use toolchain::Tool; use toolchain::Tool;
use crate::cargo_config_file::make_lockfile_copy;
use crate::{CfgOverrides, InvocationStrategy}; use crate::{CfgOverrides, InvocationStrategy};
use crate::{ManifestPath, Sysroot}; use crate::{ManifestPath, Sysroot};
const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = semver::Version { pub(crate) const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version =
major: 1, semver::Version {
minor: 82, major: 1,
patch: 0, minor: 82,
pre: semver::Prerelease::EMPTY, patch: 0,
build: semver::BuildMetadata::EMPTY, pre: semver::Prerelease::EMPTY,
}; build: semver::BuildMetadata::EMPTY,
};
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output. /// workspace. It pretty closely mirrors `cargo metadata` output.
@ -245,7 +247,7 @@ pub enum TargetKind {
} }
impl TargetKind { impl TargetKind {
fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind { pub fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind {
for kind in kinds { for kind in kinds {
return match kind { return match kind {
cargo_metadata::TargetKind::Bin => TargetKind::Bin, cargo_metadata::TargetKind::Bin => TargetKind::Bin,
@ -552,8 +554,10 @@ impl CargoWorkspace {
pub(crate) struct FetchMetadata { pub(crate) struct FetchMetadata {
command: cargo_metadata::MetadataCommand, command: cargo_metadata::MetadataCommand,
#[expect(dead_code)]
manifest_path: ManifestPath, manifest_path: ManifestPath,
lockfile_path: Option<Utf8PathBuf>, lockfile_path: Option<Utf8PathBuf>,
#[expect(dead_code)]
kind: &'static str, kind: &'static str,
no_deps: bool, no_deps: bool,
no_deps_result: anyhow::Result<cargo_metadata::Metadata>, no_deps_result: anyhow::Result<cargo_metadata::Metadata>,
@ -604,12 +608,12 @@ impl FetchMetadata {
// but nothing else // but nothing else
let mut extra_args = config.extra_args.iter(); let mut extra_args = config.extra_args.iter();
while let Some(arg) = extra_args.next() { while let Some(arg) = extra_args.next() {
if arg == "-Z" { if arg == "-Z"
if let Some(arg) = extra_args.next() { && let Some(arg) = extra_args.next()
needs_nightly = true; {
other_options.push("-Z".to_owned()); needs_nightly = true;
other_options.push(arg.to_owned()); other_options.push("-Z".to_owned());
} other_options.push(arg.to_owned());
} }
} }
@ -634,7 +638,7 @@ impl FetchMetadata {
command.other_options(other_options.clone()); command.other_options(other_options.clone());
if needs_nightly { if needs_nightly {
command.env("RUSTC_BOOTSTRAP", "1"); command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly");
} }
// Pre-fetch basic metadata using `--no-deps`, which: // Pre-fetch basic metadata using `--no-deps`, which:
@ -681,11 +685,12 @@ impl FetchMetadata {
locked: bool, locked: bool,
progress: &dyn Fn(String), progress: &dyn Fn(String),
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> { ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
_ = target_dir;
let Self { let Self {
mut command, mut command,
manifest_path, manifest_path: _,
lockfile_path, lockfile_path,
kind, kind: _,
no_deps, no_deps,
no_deps_result, no_deps_result,
mut other_options, mut other_options,
@ -696,54 +701,18 @@ impl FetchMetadata {
} }
let mut using_lockfile_copy = false; let mut using_lockfile_copy = false;
let mut _temp_dir_guard = None; let mut _temp_dir_guard;
// The manifest is a rust file, so this means its a script manifest if let Some(lockfile) = lockfile_path
if let Some(lockfile) = lockfile_path { && let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile)
_temp_dir_guard = temp_dir::TempDir::with_prefix("rust-analyzer").ok(); {
let target_lockfile = _temp_dir_guard _temp_dir_guard = temp_dir;
.and_then(|tmp| tmp.path().join("Cargo.lock").try_into().ok()) other_options.push("--lockfile-path".to_owned());
.unwrap_or_else(|| { other_options.push(target_lockfile.to_string());
// When multiple workspaces share the same target dir, they might overwrite into a using_lockfile_copy = true;
// single lockfile path.
// See https://github.com/rust-lang/rust-analyzer/issues/20189#issuecomment-3073520255
let manifest_path_hash = std::hash::BuildHasher::hash_one(
&std::hash::BuildHasherDefault::<rustc_hash::FxHasher>::default(),
&manifest_path,
);
let disambiguator = format!(
"{}_{manifest_path_hash}",
manifest_path.components().nth_back(1).map_or("", |c| c.as_str())
);
target_dir
.join("rust-analyzer")
.join("metadata")
.join(kind)
.join(disambiguator)
.join("Cargo.lock")
});
match std::fs::copy(&lockfile, &target_lockfile) {
Ok(_) => {
using_lockfile_copy = true;
other_options.push("--lockfile-path".to_owned());
other_options.push(target_lockfile.to_string());
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// There exists no lockfile yet
using_lockfile_copy = true;
other_options.push("--lockfile-path".to_owned());
other_options.push(target_lockfile.to_string());
}
Err(e) => {
tracing::warn!(
"Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
);
}
}
} }
if using_lockfile_copy { if using_lockfile_copy {
command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly");
other_options.push("-Zunstable-options".to_owned()); other_options.push("-Zunstable-options".to_owned());
command.env("RUSTC_BOOTSTRAP", "1");
} }
// No need to lock it if we copied the lockfile, we won't modify the original after all/ // No need to lock it if we copied the lockfile, we won't modify the original after all/
// This way cargo cannot error out on us if the lockfile requires updating. // This way cargo cannot error out on us if the lockfile requires updating.
@ -752,13 +721,11 @@ impl FetchMetadata {
} }
command.other_options(other_options); command.other_options(other_options);
// FIXME: Fetching metadata is a slow process, as it might require
// calling crates.io. We should be reporting progress here, but it's
// unclear whether cargo itself supports it.
progress("cargo metadata: started".to_owned()); progress("cargo metadata: started".to_owned());
let res = (|| -> anyhow::Result<(_, _)> { let res = (|| -> anyhow::Result<(_, _)> {
let mut errored = false; let mut errored = false;
tracing::debug!("Running `{:?}`", command.cargo_command());
let output = let output =
spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| { spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| {
errored = errored || line.starts_with("error") || line.starts_with("warning"); errored = errored || line.starts_with("error") || line.starts_with("warning");

View File

@ -59,7 +59,7 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
pub use crate::{ pub use crate::{
build_dependencies::WorkspaceBuildScripts, build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts},
cargo_workspace::{ cargo_workspace::{
CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
PackageDependency, RustLibSource, Target, TargetData, TargetKind, PackageDependency, RustLibSource, Target, TargetData, TargetKind,
@ -139,21 +139,22 @@ impl ProjectManifest {
} }
fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<ManifestPath> { fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<ManifestPath> {
if path.file_name().unwrap_or_default() == target_file_name { if path.file_name().unwrap_or_default() == target_file_name
if let Ok(manifest) = ManifestPath::try_from(path.to_path_buf()) { && let Ok(manifest) = ManifestPath::try_from(path.to_path_buf())
return Some(manifest); {
} return Some(manifest);
} }
let mut curr = Some(path); let mut curr = Some(path);
while let Some(path) = curr { while let Some(path) = curr {
let candidate = path.join(target_file_name); let candidate = path.join(target_file_name);
if fs::metadata(&candidate).is_ok() { if fs::metadata(&candidate).is_ok()
if let Ok(manifest) = ManifestPath::try_from(candidate) { && let Ok(manifest) = ManifestPath::try_from(candidate)
return Some(manifest); {
} return Some(manifest);
} }
curr = path.parent(); curr = path.parent();
} }

View File

@ -143,12 +143,11 @@ impl Sysroot {
Some(root) => { Some(root) => {
// special case rustc, we can look that up directly in the sysroot's bin folder // special case rustc, we can look that up directly in the sysroot's bin folder
// as it should never invoke another cargo binary // as it should never invoke another cargo binary
if let Tool::Rustc = tool { if let Tool::Rustc = tool
if let Some(path) = && let Some(path) =
probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into())
{ {
return toolchain::command(path, current_dir, envs); return toolchain::command(path, current_dir, envs);
}
} }
let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs); let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs);
@ -291,29 +290,26 @@ impl Sysroot {
pub fn set_workspace(&mut self, workspace: RustLibSrcWorkspace) { pub fn set_workspace(&mut self, workspace: RustLibSrcWorkspace) {
self.workspace = workspace; self.workspace = workspace;
if self.error.is_none() { if self.error.is_none()
if let Some(src_root) = &self.rust_lib_src_root { && let Some(src_root) = &self.rust_lib_src_root
let has_core = match &self.workspace { {
RustLibSrcWorkspace::Workspace(ws) => { let has_core = match &self.workspace {
ws.packages().any(|p| ws[p].name == "core") RustLibSrcWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
} RustLibSrcWorkspace::Json(project_json) => project_json
RustLibSrcWorkspace::Json(project_json) => project_json .crates()
.crates() .filter_map(|(_, krate)| krate.display_name.clone())
.filter_map(|(_, krate)| krate.display_name.clone()) .any(|name| name.canonical_name().as_str() == "core"),
.any(|name| name.canonical_name().as_str() == "core"), RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), RustLibSrcWorkspace::Empty => true,
RustLibSrcWorkspace::Empty => true, };
if !has_core {
let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
" (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
} else {
", try running `rustup component add rust-src` to possibly fix this"
}; };
if !has_core { self.error =
let var_note = if env::var_os("RUST_SRC_PATH").is_some() { Some(format!("sysroot at `{src_root}` is missing a `core` library{var_note}",));
" (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
} else {
", try running `rustup component add rust-src` to possibly fix this"
};
self.error = Some(format!(
"sysroot at `{src_root}` is missing a `core` library{var_note}",
));
}
} }
} }
} }

View File

@ -24,7 +24,7 @@ use crate::{
CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package,
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind, ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
WorkspaceBuildScripts, WorkspaceBuildScripts,
build_dependencies::BuildScriptOutput, build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
cargo_config_file, cargo_config_file,
cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource}, cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
@ -424,12 +424,12 @@ impl ProjectWorkspace {
sysroot.set_workspace(loaded_sysroot); sysroot.set_workspace(loaded_sysroot);
} }
if !cargo.requires_rustc_private() { if !cargo.requires_rustc_private()
if let Err(e) = &mut rustc { && let Err(e) = &mut rustc
// We don't need the rustc sources here, {
// so just discard the error. // We don't need the rustc sources here,
_ = e.take(); // so just discard the error.
} _ = e.take();
} }
Ok(ProjectWorkspace { Ok(ProjectWorkspace {
@ -1163,17 +1163,15 @@ fn project_json_to_crate_graph(
crate = display_name.as_ref().map(|name| name.canonical_name().as_str()), crate = display_name.as_ref().map(|name| name.canonical_name().as_str()),
"added root to crate graph" "added root to crate graph"
); );
if *is_proc_macro { if *is_proc_macro && let Some(path) = proc_macro_dylib_path.clone() {
if let Some(path) = proc_macro_dylib_path.clone() { let node = Ok((
let node = Ok(( display_name
display_name .as_ref()
.as_ref() .map(|it| it.canonical_name().as_str().to_owned())
.map(|it| it.canonical_name().as_str().to_owned()) .unwrap_or_else(|| format!("crate{}", idx.0)),
.unwrap_or_else(|| format!("crate{}", idx.0)), path,
path, ));
)); proc_macros.insert(crate_graph_crate_id, node);
proc_macros.insert(crate_graph_crate_id, node);
}
} }
(idx, crate_graph_crate_id) (idx, crate_graph_crate_id)
}, },
@ -1319,14 +1317,12 @@ fn cargo_to_crate_graph(
// Add dep edge of all targets to the package's lib target // Add dep edge of all targets to the package's lib target
if let Some((to, name)) = lib_tgt.clone() { if let Some((to, name)) = lib_tgt.clone() {
if to != from && kind != TargetKind::BuildScript { match to != from && kind != TargetKind::BuildScript {
// (build script can not depend on its library target) true => {
let name = CrateName::normalize_dashes(&name);
// For root projects with dashes in their name, add_dep(crate_graph, from, name, to);
// cargo metadata does not do any normalization, }
// so we do it ourselves currently false => (),
let name = CrateName::normalize_dashes(&name);
add_dep(crate_graph, from, name, to);
} }
} }
} }
@ -1638,9 +1634,21 @@ fn add_target_crate_root(
let proc_macro = match build_data { let proc_macro = match build_data {
Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => { Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
match proc_macro_dylib_path { match proc_macro_dylib_path {
Some(path) => Ok((cargo_name.to_owned(), path.clone())), ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())),
None if has_errors => Err(ProcMacroLoadingError::FailedToBuild), ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt),
None => Err(ProcMacroLoadingError::MissingDylibPath), ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound(_)
if has_errors =>
{
Err(ProcMacroLoadingError::FailedToBuild)
}
ProcMacroDylibPath::NotProcMacro => {
Err(ProcMacroLoadingError::ExpectedProcMacroArtifact)
}
ProcMacroDylibPath::DylibNotFound(candidates) => {
Err(ProcMacroLoadingError::MissingDylibPath(
candidates.iter().map(ToString::to_string).collect(),
))
}
} }
} }
None => Err(ProcMacroLoadingError::NotYetBuilt), None => Err(ProcMacroLoadingError::NotYetBuilt),