mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-17 02:42:54 +00:00
Copy lockfile when building build scripts
This commit is contained in:
parent
7950da3940
commit
df85aac1d4
3 changed files with 76 additions and 73 deletions
|
|
@ -16,12 +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::{always, never};
|
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.
|
||||||
|
|
@ -77,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(),
|
||||||
|
|
@ -98,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
|
||||||
|
|
@ -379,10 +380,6 @@ impl WorkspaceBuildScripts {
|
||||||
progress(format!(
|
progress(format!(
|
||||||
"building compile-time-deps: proc-macro {name} built"
|
"building compile-time-deps: proc-macro {name} built"
|
||||||
));
|
));
|
||||||
always!(
|
|
||||||
data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt,
|
|
||||||
"received multiple compiler artifacts for the same package: {message:?}"
|
|
||||||
);
|
|
||||||
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
|
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
|
||||||
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
|
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
|
||||||
}
|
}
|
||||||
|
|
@ -434,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"]);
|
||||||
|
|
@ -457,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");
|
||||||
|
|
@ -479,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -488,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,
|
||||||
|
|
@ -498,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
|
||||||
|
|
@ -522,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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@ 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 =
|
||||||
|
semver::Version {
|
||||||
major: 1,
|
major: 1,
|
||||||
minor: 82,
|
minor: 82,
|
||||||
patch: 0,
|
patch: 0,
|
||||||
|
|
@ -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>,
|
||||||
|
|
@ -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())
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
// When multiple workspaces share the same target dir, they might overwrite into a
|
|
||||||
// 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("--lockfile-path".to_owned());
|
||||||
other_options.push(target_lockfile.to_string());
|
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;
|
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");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue