mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-29 02:52:11 +00:00
Merge pull request #20178 from ShoyuVanilla/cargo-config-cleanup
Some checks failed
metrics / build_metrics (push) Has been cancelled
rustdoc / rustdoc (push) Has been cancelled
metrics / other_metrics (diesel-1.4.8) (push) Has been cancelled
metrics / other_metrics (hyper-0.14.18) (push) Has been cancelled
metrics / other_metrics (ripgrep-13.0.0) (push) Has been cancelled
metrics / other_metrics (self) (push) Has been cancelled
metrics / other_metrics (webrender-2022) (push) Has been cancelled
metrics / generate_final_metrics (push) Has been cancelled
Some checks failed
metrics / build_metrics (push) Has been cancelled
rustdoc / rustdoc (push) Has been cancelled
metrics / other_metrics (diesel-1.4.8) (push) Has been cancelled
metrics / other_metrics (hyper-0.14.18) (push) Has been cancelled
metrics / other_metrics (ripgrep-13.0.0) (push) Has been cancelled
metrics / other_metrics (self) (push) Has been cancelled
metrics / other_metrics (webrender-2022) (push) Has been cancelled
metrics / generate_final_metrics (push) Has been cancelled
chore: Cleanup cargo config queries
This commit is contained in:
commit
f76d2ef4d9
12 changed files with 420 additions and 375 deletions
34
crates/project-model/src/cargo_config_file.rs
Normal file
34
crates/project-model/src/cargo_config_file.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
//! Read `.cargo/config.toml` as a JSON object
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use toolchain::Tool;
|
||||||
|
|
||||||
|
use crate::{ManifestPath, Sysroot, utf8_stdout};
|
||||||
|
|
||||||
|
pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
|
||||||
|
|
||||||
|
pub(crate) fn read(
|
||||||
|
manifest: &ManifestPath,
|
||||||
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
) -> Option<CargoConfigFile> {
|
||||||
|
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
||||||
|
cargo_config
|
||||||
|
.args(["-Z", "unstable-options", "config", "get", "--format", "json"])
|
||||||
|
.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
if manifest.is_rust_manifest() {
|
||||||
|
cargo_config.arg("-Zscript");
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::debug!("Discovering cargo config by {:?}", cargo_config);
|
||||||
|
let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
|
||||||
|
.inspect(|json| {
|
||||||
|
tracing::debug!("Discovered cargo config: {:?}", json);
|
||||||
|
})
|
||||||
|
.inspect_err(|err| {
|
||||||
|
tracing::debug!("Failed to discover cargo config: {:?}", err);
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.and_then(|stdout| serde_json::from_str(&stdout).ok())?;
|
||||||
|
|
||||||
|
Some(json)
|
||||||
|
}
|
||||||
|
|
@ -300,8 +300,6 @@ pub struct CargoMetadataConfig {
|
||||||
pub extra_args: Vec<String>,
|
pub extra_args: Vec<String>,
|
||||||
/// Extra env vars to set when invoking the cargo command
|
/// Extra env vars to set when invoking the cargo command
|
||||||
pub extra_env: FxHashMap<String, Option<String>>,
|
pub extra_env: FxHashMap<String, Option<String>>,
|
||||||
/// The target dir for this workspace load.
|
|
||||||
pub target_dir: Utf8PathBuf,
|
|
||||||
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
|
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
|
||||||
pub kind: &'static str,
|
pub kind: &'static str,
|
||||||
/// The toolchain version, if known.
|
/// The toolchain version, if known.
|
||||||
|
|
@ -317,188 +315,6 @@ struct PackageMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CargoWorkspace {
|
impl CargoWorkspace {
|
||||||
/// Fetches the metadata for the given `cargo_toml` manifest.
|
|
||||||
/// A successful result may contain another metadata error if the initial fetching failed but
|
|
||||||
/// the `--no-deps` retry succeeded.
|
|
||||||
///
|
|
||||||
/// The sysroot is used to set the `RUSTUP_TOOLCHAIN` env var when invoking cargo
|
|
||||||
/// to ensure that the rustup proxy uses the correct toolchain.
|
|
||||||
pub fn fetch_metadata(
|
|
||||||
cargo_toml: &ManifestPath,
|
|
||||||
current_dir: &AbsPath,
|
|
||||||
config: &CargoMetadataConfig,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
no_deps: bool,
|
|
||||||
locked: bool,
|
|
||||||
progress: &dyn Fn(String),
|
|
||||||
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
|
||||||
let res = Self::fetch_metadata_(
|
|
||||||
cargo_toml,
|
|
||||||
current_dir,
|
|
||||||
config,
|
|
||||||
sysroot,
|
|
||||||
no_deps,
|
|
||||||
locked,
|
|
||||||
progress,
|
|
||||||
);
|
|
||||||
if let Ok((_, Some(ref e))) = res {
|
|
||||||
tracing::warn!(
|
|
||||||
%cargo_toml,
|
|
||||||
?e,
|
|
||||||
"`cargo metadata` failed, but retry with `--no-deps` succeeded"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_metadata_(
|
|
||||||
cargo_toml: &ManifestPath,
|
|
||||||
current_dir: &AbsPath,
|
|
||||||
config: &CargoMetadataConfig,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
no_deps: bool,
|
|
||||||
locked: bool,
|
|
||||||
progress: &dyn Fn(String),
|
|
||||||
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
|
||||||
let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
|
|
||||||
let mut meta = MetadataCommand::new();
|
|
||||||
meta.cargo_path(cargo.get_program());
|
|
||||||
cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
|
|
||||||
meta.manifest_path(cargo_toml.to_path_buf());
|
|
||||||
match &config.features {
|
|
||||||
CargoFeatures::All => {
|
|
||||||
meta.features(CargoOpt::AllFeatures);
|
|
||||||
}
|
|
||||||
CargoFeatures::Selected { features, no_default_features } => {
|
|
||||||
if *no_default_features {
|
|
||||||
meta.features(CargoOpt::NoDefaultFeatures);
|
|
||||||
}
|
|
||||||
if !features.is_empty() {
|
|
||||||
meta.features(CargoOpt::SomeFeatures(features.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta.current_dir(current_dir);
|
|
||||||
|
|
||||||
let mut other_options = vec![];
|
|
||||||
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
|
||||||
// the only relevant flags for metadata here are unstable ones, so we pass those along
|
|
||||||
// but nothing else
|
|
||||||
let mut extra_args = config.extra_args.iter();
|
|
||||||
while let Some(arg) = extra_args.next() {
|
|
||||||
if arg == "-Z" {
|
|
||||||
if let Some(arg) = extra_args.next() {
|
|
||||||
other_options.push("-Z".to_owned());
|
|
||||||
other_options.push(arg.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.targets.is_empty() {
|
|
||||||
other_options.extend(
|
|
||||||
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if no_deps {
|
|
||||||
other_options.push("--no-deps".to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut using_lockfile_copy = false;
|
|
||||||
// The manifest is a rust file, so this means its a script manifest
|
|
||||||
if cargo_toml.is_rust_manifest() {
|
|
||||||
other_options.push("-Zscript".to_owned());
|
|
||||||
} else if config
|
|
||||||
.toolchain_version
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
|
|
||||||
{
|
|
||||||
let lockfile = <_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock");
|
|
||||||
let target_lockfile = config
|
|
||||||
.target_dir
|
|
||||||
.join("rust-analyzer")
|
|
||||||
.join("metadata")
|
|
||||||
.join(config.kind)
|
|
||||||
.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 {
|
|
||||||
other_options.push("-Zunstable-options".to_owned());
|
|
||||||
meta.env("RUSTC_BOOTSTRAP", "1");
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
if !using_lockfile_copy && locked {
|
|
||||||
other_options.push("--locked".to_owned());
|
|
||||||
}
|
|
||||||
meta.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());
|
|
||||||
|
|
||||||
let res = (|| -> anyhow::Result<(_, _)> {
|
|
||||||
let mut errored = false;
|
|
||||||
let output =
|
|
||||||
spawn_with_streaming_output(meta.cargo_command(), &mut |_| (), &mut |line| {
|
|
||||||
errored = errored || line.starts_with("error") || line.starts_with("warning");
|
|
||||||
if errored {
|
|
||||||
progress("cargo metadata: ?".to_owned());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
progress(format!("cargo metadata: {line}"));
|
|
||||||
})?;
|
|
||||||
if !output.status.success() {
|
|
||||||
progress(format!("cargo metadata: failed {}", output.status));
|
|
||||||
let error = cargo_metadata::Error::CargoMetadata {
|
|
||||||
stderr: String::from_utf8(output.stderr)?,
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
if !no_deps {
|
|
||||||
// If we failed to fetch metadata with deps, try again without them.
|
|
||||||
// This makes r-a still work partially when offline.
|
|
||||||
if let Ok((metadata, _)) = Self::fetch_metadata_(
|
|
||||||
cargo_toml,
|
|
||||||
current_dir,
|
|
||||||
config,
|
|
||||||
sysroot,
|
|
||||||
true,
|
|
||||||
locked,
|
|
||||||
progress,
|
|
||||||
) {
|
|
||||||
return Ok((metadata, Some(error)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
let stdout = from_utf8(&output.stdout)?
|
|
||||||
.lines()
|
|
||||||
.find(|line| line.starts_with('{'))
|
|
||||||
.ok_or(cargo_metadata::Error::NoJson)?;
|
|
||||||
Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
|
|
||||||
})()
|
|
||||||
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()));
|
|
||||||
progress("cargo metadata: finished".to_owned());
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut meta: cargo_metadata::Metadata,
|
mut meta: cargo_metadata::Metadata,
|
||||||
ws_manifest_path: ManifestPath,
|
ws_manifest_path: ManifestPath,
|
||||||
|
|
@ -733,3 +549,214 @@ impl CargoWorkspace {
|
||||||
self.requires_rustc_private
|
self.requires_rustc_private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FetchMetadata {
|
||||||
|
command: cargo_metadata::MetadataCommand,
|
||||||
|
lockfile_path: Option<Utf8PathBuf>,
|
||||||
|
kind: &'static str,
|
||||||
|
no_deps: bool,
|
||||||
|
no_deps_result: anyhow::Result<cargo_metadata::Metadata>,
|
||||||
|
other_options: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchMetadata {
|
||||||
|
/// Builds a command to fetch metadata for the given `cargo_toml` manifest.
|
||||||
|
///
|
||||||
|
/// Performs a lightweight pre-fetch using the `--no-deps` option,
|
||||||
|
/// available via [`FetchMetadata::no_deps_metadata`], to gather basic
|
||||||
|
/// information such as the `target-dir`.
|
||||||
|
///
|
||||||
|
/// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN`
|
||||||
|
/// environment variable when invoking Cargo, ensuring that the
|
||||||
|
/// rustup proxy selects the correct toolchain.
|
||||||
|
pub(crate) fn new(
|
||||||
|
cargo_toml: &ManifestPath,
|
||||||
|
current_dir: &AbsPath,
|
||||||
|
config: &CargoMetadataConfig,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
no_deps: bool,
|
||||||
|
) -> Self {
|
||||||
|
let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
|
||||||
|
let mut command = MetadataCommand::new();
|
||||||
|
command.cargo_path(cargo.get_program());
|
||||||
|
cargo.get_envs().for_each(|(var, val)| _ = command.env(var, val.unwrap_or_default()));
|
||||||
|
command.manifest_path(cargo_toml.to_path_buf());
|
||||||
|
match &config.features {
|
||||||
|
CargoFeatures::All => {
|
||||||
|
command.features(CargoOpt::AllFeatures);
|
||||||
|
}
|
||||||
|
CargoFeatures::Selected { features, no_default_features } => {
|
||||||
|
if *no_default_features {
|
||||||
|
command.features(CargoOpt::NoDefaultFeatures);
|
||||||
|
}
|
||||||
|
if !features.is_empty() {
|
||||||
|
command.features(CargoOpt::SomeFeatures(features.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.current_dir(current_dir);
|
||||||
|
|
||||||
|
let mut needs_nightly = false;
|
||||||
|
let mut other_options = vec![];
|
||||||
|
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
||||||
|
// the only relevant flags for metadata here are unstable ones, so we pass those along
|
||||||
|
// but nothing else
|
||||||
|
let mut extra_args = config.extra_args.iter();
|
||||||
|
while let Some(arg) = extra_args.next() {
|
||||||
|
if arg == "-Z" {
|
||||||
|
if let Some(arg) = extra_args.next() {
|
||||||
|
needs_nightly = true;
|
||||||
|
other_options.push("-Z".to_owned());
|
||||||
|
other_options.push(arg.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lockfile_path = None;
|
||||||
|
if cargo_toml.is_rust_manifest() {
|
||||||
|
needs_nightly = true;
|
||||||
|
other_options.push("-Zscript".to_owned());
|
||||||
|
} else if config
|
||||||
|
.toolchain_version
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
|
||||||
|
{
|
||||||
|
lockfile_path = Some(<_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.targets.is_empty() {
|
||||||
|
other_options.extend(
|
||||||
|
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
command.other_options(other_options.clone());
|
||||||
|
|
||||||
|
if needs_nightly {
|
||||||
|
command.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-fetch basic metadata using `--no-deps`, which:
|
||||||
|
// - avoids fetching registries like crates.io,
|
||||||
|
// - skips dependency resolution and does not modify lockfiles,
|
||||||
|
// - and thus doesn't require progress reporting or copying lockfiles.
|
||||||
|
//
|
||||||
|
// Useful as a fast fallback to extract info like `target-dir`.
|
||||||
|
let cargo_command;
|
||||||
|
let no_deps_result = if no_deps {
|
||||||
|
command.no_deps();
|
||||||
|
cargo_command = command.cargo_command();
|
||||||
|
command.exec()
|
||||||
|
} else {
|
||||||
|
let mut no_deps_command = command.clone();
|
||||||
|
no_deps_command.no_deps();
|
||||||
|
cargo_command = no_deps_command.cargo_command();
|
||||||
|
no_deps_command.exec()
|
||||||
|
}
|
||||||
|
.with_context(|| format!("Failed to run `{cargo_command:?}`"));
|
||||||
|
|
||||||
|
Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
|
||||||
|
self.no_deps_result.as_ref().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the metadata-fetching command.
|
||||||
|
///
|
||||||
|
/// A successful result may still contain a metadata error if the full fetch failed,
|
||||||
|
/// but the fallback `--no-deps` pre-fetch succeeded during command construction.
|
||||||
|
pub(crate) fn exec(
|
||||||
|
self,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
|
locked: bool,
|
||||||
|
progress: &dyn Fn(String),
|
||||||
|
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
|
||||||
|
let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } =
|
||||||
|
self;
|
||||||
|
|
||||||
|
if no_deps {
|
||||||
|
return no_deps_result.map(|m| (m, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut using_lockfile_copy = false;
|
||||||
|
// The manifest is a rust file, so this means its a script manifest
|
||||||
|
if let Some(lockfile) = lockfile_path {
|
||||||
|
let target_lockfile =
|
||||||
|
target_dir.join("rust-analyzer").join("metadata").join(kind).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 {
|
||||||
|
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/
|
||||||
|
// This way cargo cannot error out on us if the lockfile requires updating.
|
||||||
|
if !using_lockfile_copy && locked {
|
||||||
|
other_options.push("--locked".to_owned());
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
|
||||||
|
let res = (|| -> anyhow::Result<(_, _)> {
|
||||||
|
let mut errored = false;
|
||||||
|
let output =
|
||||||
|
spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| {
|
||||||
|
errored = errored || line.starts_with("error") || line.starts_with("warning");
|
||||||
|
if errored {
|
||||||
|
progress("cargo metadata: ?".to_owned());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progress(format!("cargo metadata: {line}"));
|
||||||
|
})?;
|
||||||
|
if !output.status.success() {
|
||||||
|
progress(format!("cargo metadata: failed {}", output.status));
|
||||||
|
let error = cargo_metadata::Error::CargoMetadata {
|
||||||
|
stderr: String::from_utf8(output.stderr)?,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
if !no_deps {
|
||||||
|
// If we failed to fetch metadata with deps, return pre-fetched result without them.
|
||||||
|
// This makes r-a still work partially when offline.
|
||||||
|
if let Ok(metadata) = no_deps_result {
|
||||||
|
tracing::warn!(
|
||||||
|
?error,
|
||||||
|
"`cargo metadata` failed and returning succeeded result with `--no-deps`"
|
||||||
|
);
|
||||||
|
return Ok((metadata, Some(error)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
let stdout = from_utf8(&output.stdout)?
|
||||||
|
.lines()
|
||||||
|
.find(|line| line.starts_with('{'))
|
||||||
|
.ok_or(cargo_metadata::Error::NoJson)?;
|
||||||
|
Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
|
||||||
|
})()
|
||||||
|
.with_context(|| format!("Failed to run `{:?}`", command.cargo_command()));
|
||||||
|
progress("cargo metadata: finished".to_owned());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
//! Cargo-like environment variables injection.
|
//! Cargo-like environment variables injection.
|
||||||
use base_db::Env;
|
use base_db::Env;
|
||||||
use paths::{Utf8Path, Utf8PathBuf};
|
use paths::Utf8Path;
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use toolchain::Tool;
|
use toolchain::Tool;
|
||||||
|
|
||||||
use crate::{ManifestPath, PackageData, Sysroot, TargetKind, utf8_stdout};
|
use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
|
||||||
|
|
||||||
/// Recreates the compile-time environment variables that Cargo sets.
|
/// Recreates the compile-time environment variables that Cargo sets.
|
||||||
///
|
///
|
||||||
|
|
@ -61,104 +60,68 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
|
||||||
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
|
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cargo_config_env(
|
pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
|
||||||
manifest: &ManifestPath,
|
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
|
||||||
sysroot: &Sysroot,
|
|
||||||
) -> Env {
|
|
||||||
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
|
||||||
cargo_config
|
|
||||||
.args(["-Z", "unstable-options", "config", "get", "env"])
|
|
||||||
.env("RUSTC_BOOTSTRAP", "1");
|
|
||||||
if manifest.is_rust_manifest() {
|
|
||||||
cargo_config.arg("-Zscript");
|
|
||||||
}
|
|
||||||
// if successful we receive `env.key.value = "value" per entry
|
|
||||||
tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
|
|
||||||
utf8_stdout(&mut cargo_config)
|
|
||||||
.map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
|
|
||||||
.inspect(|env| {
|
|
||||||
tracing::debug!("Discovered cargo config env: {:?}", env);
|
|
||||||
})
|
|
||||||
.inspect_err(|err| {
|
|
||||||
tracing::debug!("Failed to discover cargo config env: {:?}", err);
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
|
|
||||||
let mut env = Env::default();
|
let mut env = Env::default();
|
||||||
let mut relatives = vec![];
|
let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
|
||||||
for (key, val) in
|
else {
|
||||||
stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
|
return env;
|
||||||
{
|
};
|
||||||
let val = val.trim_matches('"').to_owned();
|
|
||||||
if let Some((key, modifier)) = key.split_once('.') {
|
|
||||||
match modifier {
|
|
||||||
"relative" => relatives.push((key, val)),
|
|
||||||
"value" => _ = env.insert(key, val),
|
|
||||||
_ => {
|
|
||||||
tracing::warn!(
|
|
||||||
"Unknown modifier in cargo config env: {}, expected `relative` or `value`",
|
|
||||||
modifier
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
env.insert(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
|
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
|
||||||
// But cargo does not provide this information.
|
// But cargo does not provide this information.
|
||||||
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
|
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
|
||||||
for (key, relative) in relatives {
|
|
||||||
if relative != "true" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(suffix) = env.get(key) {
|
|
||||||
env.insert(key, base.join(suffix).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn cargo_config_build_target_dir(
|
for (key, entry) in env_json {
|
||||||
manifest: &ManifestPath,
|
let serde_json::Value::Object(entry) = entry else {
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
continue;
|
||||||
sysroot: &Sysroot,
|
};
|
||||||
) -> Option<Utf8PathBuf> {
|
let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
|
||||||
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
continue;
|
||||||
cargo_config
|
};
|
||||||
.args(["-Z", "unstable-options", "config", "get", "build.target-dir"])
|
|
||||||
.env("RUSTC_BOOTSTRAP", "1");
|
let value = if entry
|
||||||
if manifest.is_rust_manifest() {
|
.get("relative")
|
||||||
cargo_config.arg("-Zscript");
|
.and_then(|v| v.as_bool())
|
||||||
|
.is_some_and(std::convert::identity)
|
||||||
|
{
|
||||||
|
base.join(value).to_string()
|
||||||
|
} else {
|
||||||
|
value.to_owned()
|
||||||
|
};
|
||||||
|
env.insert(key, value);
|
||||||
}
|
}
|
||||||
utf8_stdout(&mut cargo_config)
|
|
||||||
.map(|stdout| {
|
env
|
||||||
Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"'))
|
|
||||||
.to_owned()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_output_cargo_config_env_works() {
|
fn parse_output_cargo_config_env_works() {
|
||||||
let stdout = r#"
|
let raw = r#"
|
||||||
env.CARGO_WORKSPACE_DIR.relative = true
|
{
|
||||||
env.CARGO_WORKSPACE_DIR.value = ""
|
"env": {
|
||||||
env.RELATIVE.relative = true
|
"CARGO_WORKSPACE_DIR": {
|
||||||
env.RELATIVE.value = "../relative"
|
"relative": true,
|
||||||
env.INVALID.relative = invalidbool
|
"value": ""
|
||||||
env.INVALID.value = "../relative"
|
},
|
||||||
env.TEST.value = "test"
|
"INVALID": {
|
||||||
"#
|
"relative": "invalidbool",
|
||||||
.trim();
|
"value": "../relative"
|
||||||
|
},
|
||||||
|
"RELATIVE": {
|
||||||
|
"relative": true,
|
||||||
|
"value": "../relative"
|
||||||
|
},
|
||||||
|
"TEST": {
|
||||||
|
"value": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
|
||||||
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
|
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
|
||||||
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
|
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
|
||||||
let manifest = ManifestPath::try_from(manifest).unwrap();
|
let manifest = ManifestPath::try_from(manifest).unwrap();
|
||||||
let env = parse_output_cargo_config_env(&manifest, stdout);
|
let env = cargo_config_env(&manifest, &Some(config));
|
||||||
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
|
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
|
||||||
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
|
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
|
||||||
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
|
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub mod toolchain_info {
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{ManifestPath, Sysroot};
|
use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum QueryConfig<'a> {
|
pub enum QueryConfig<'a> {
|
||||||
|
|
@ -32,11 +32,12 @@ pub mod toolchain_info {
|
||||||
Rustc(&'a Sysroot, &'a Path),
|
Rustc(&'a Sysroot, &'a Path),
|
||||||
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
|
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
|
||||||
/// If this fails, falls back to invoking `rustc` directly.
|
/// If this fails, falls back to invoking `rustc` directly.
|
||||||
Cargo(&'a Sysroot, &'a ManifestPath),
|
Cargo(&'a Sysroot, &'a ManifestPath, &'a Option<CargoConfigFile>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod build_dependencies;
|
mod build_dependencies;
|
||||||
|
mod cargo_config_file;
|
||||||
mod cargo_workspace;
|
mod cargo_workspace;
|
||||||
mod env;
|
mod env;
|
||||||
mod manifest_path;
|
mod manifest_path;
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ use std::{env, fs, ops::Not, path::Path, process::Command};
|
||||||
|
|
||||||
use anyhow::{Result, format_err};
|
use anyhow::{Result, format_err};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use toolchain::{Tool, probe_for_binary};
|
use toolchain::{Tool, probe_for_binary};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
|
CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
|
||||||
cargo_workspace::CargoMetadataConfig, utf8_stdout,
|
cargo_workspace::{CargoMetadataConfig, FetchMetadata},
|
||||||
|
utf8_stdout,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -211,6 +212,7 @@ impl Sysroot {
|
||||||
sysroot_source_config: &RustSourceWorkspaceConfig,
|
sysroot_source_config: &RustSourceWorkspaceConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> Option<RustLibSrcWorkspace> {
|
) -> Option<RustLibSrcWorkspace> {
|
||||||
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
|
||||||
|
|
@ -224,6 +226,7 @@ impl Sysroot {
|
||||||
match self.load_library_via_cargo(
|
match self.load_library_via_cargo(
|
||||||
&library_manifest,
|
&library_manifest,
|
||||||
current_dir,
|
current_dir,
|
||||||
|
target_dir,
|
||||||
cargo_config,
|
cargo_config,
|
||||||
no_deps,
|
no_deps,
|
||||||
progress,
|
progress,
|
||||||
|
|
@ -319,6 +322,7 @@ impl Sysroot {
|
||||||
&self,
|
&self,
|
||||||
library_manifest: &ManifestPath,
|
library_manifest: &ManifestPath,
|
||||||
current_dir: &AbsPath,
|
current_dir: &AbsPath,
|
||||||
|
target_dir: &Utf8Path,
|
||||||
cargo_config: &CargoMetadataConfig,
|
cargo_config: &CargoMetadataConfig,
|
||||||
no_deps: bool,
|
no_deps: bool,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
|
|
@ -331,16 +335,11 @@ impl Sysroot {
|
||||||
Some("nightly".to_owned()),
|
Some("nightly".to_owned()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (mut res, _) = CargoWorkspace::fetch_metadata(
|
// Make sure we never attempt to write to the sysroot
|
||||||
library_manifest,
|
let locked = true;
|
||||||
current_dir,
|
let (mut res, _) =
|
||||||
&cargo_config,
|
FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
|
||||||
self,
|
.exec(target_dir, locked, progress)?;
|
||||||
no_deps,
|
|
||||||
// Make sure we never attempt to write to the sysroot
|
|
||||||
true,
|
|
||||||
progress,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
// Patch out `rustc-std-workspace-*` crates to point to the real crates.
|
||||||
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
// This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
|
||||||
|
|
|
||||||
|
|
@ -239,8 +239,13 @@ fn smoke_test_real_sysroot_cargo() {
|
||||||
);
|
);
|
||||||
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
|
||||||
std::fs::create_dir_all(&cwd).unwrap();
|
std::fs::create_dir_all(&cwd).unwrap();
|
||||||
let loaded_sysroot =
|
let loaded_sysroot = sysroot.load_workspace(
|
||||||
sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &cwd, &|_| ());
|
&RustSourceWorkspaceConfig::default_cargo(),
|
||||||
|
false,
|
||||||
|
&cwd,
|
||||||
|
&Utf8PathBuf::default(),
|
||||||
|
&|_| (),
|
||||||
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ fn rustc_print_cfg(
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
|
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
||||||
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
|
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
|
|
@ -109,7 +109,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
|
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub fn get(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
||||||
cmd.env("RUSTC_BOOTSTRAP", "1");
|
cmd.env("RUSTC_BOOTSTRAP", "1");
|
||||||
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
|
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
|
||||||
|
|
@ -66,7 +66,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ use anyhow::Context;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use toolchain::Tool;
|
use toolchain::Tool;
|
||||||
|
|
||||||
use crate::{ManifestPath, Sysroot, toolchain_info::QueryConfig, utf8_stdout};
|
use crate::{
|
||||||
|
Sysroot, cargo_config_file::CargoConfigFile, toolchain_info::QueryConfig, utf8_stdout,
|
||||||
|
};
|
||||||
|
|
||||||
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
|
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
|
||||||
/// For rustc, runs `rustc --print -vV` to get the host target.
|
/// For rustc, runs `rustc --print -vV` to get the host target.
|
||||||
|
|
@ -20,8 +22,8 @@ pub fn get(
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sysroot, current_dir) = match config {
|
let (sysroot, current_dir) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, config_file) => {
|
||||||
match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
|
match config_file.as_ref().and_then(cargo_config_build_target) {
|
||||||
Some(it) => return Ok(it),
|
Some(it) => return Ok(it),
|
||||||
None => (sysroot, cargo_toml.parent().as_ref()),
|
None => (sysroot, cargo_toml.parent().as_ref()),
|
||||||
}
|
}
|
||||||
|
|
@ -50,30 +52,30 @@ fn rustc_discover_host_tuple(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_config_build_target(
|
fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
|
||||||
cargo_toml: &ManifestPath,
|
match parse_json_cargo_config_build_target(config) {
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
Ok(v) => v,
|
||||||
sysroot: &Sysroot,
|
Err(e) => {
|
||||||
) -> Option<Vec<String>> {
|
tracing::debug!("Failed to discover cargo config build target {e:?}");
|
||||||
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
|
None
|
||||||
cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
|
}
|
||||||
cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
|
}
|
||||||
// if successful we receive `build.target = "target-tuple"`
|
|
||||||
// or `build.target = ["<target 1>", ..]`
|
|
||||||
// this might be `error: config value `build.target` is not set` in which case we
|
|
||||||
// don't wanna log the error
|
|
||||||
utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
|
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
|
||||||
fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
|
fn parse_json_cargo_config_build_target(
|
||||||
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
|
config: &CargoConfigFile,
|
||||||
|
) -> anyhow::Result<Option<Vec<String>>> {
|
||||||
if !trimmed.starts_with('[') {
|
let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
|
||||||
return Ok([trimmed.to_owned()].to_vec());
|
match target {
|
||||||
|
Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
|
||||||
|
Some(v) => serde_json::from_value(v.clone())
|
||||||
|
.map(Option::Some)
|
||||||
|
.context("Failed to parse `build.target` as an array of target"),
|
||||||
|
// t`error: config value `build.target` is not set`, in which case we
|
||||||
|
// don't wanna log the error
|
||||||
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
|
|
||||||
serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -90,7 +92,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub(crate) fn get(
|
||||||
extra_env: &FxHashMap<String, Option<String>>,
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
) -> Result<Option<Version>, anyhow::Error> {
|
) -> Result<Option<Version>, anyhow::Error> {
|
||||||
let (mut cmd, prefix) = match config {
|
let (mut cmd, prefix) = match config {
|
||||||
QueryConfig::Cargo(sysroot, cargo_toml) => {
|
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
|
||||||
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
|
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
|
||||||
}
|
}
|
||||||
QueryConfig::Rustc(sysroot, current_dir) => {
|
QueryConfig::Rustc(sysroot, current_dir) => {
|
||||||
|
|
@ -44,7 +44,7 @@ mod tests {
|
||||||
let sysroot = Sysroot::empty();
|
let sysroot = Sysroot::empty();
|
||||||
let manifest_path =
|
let manifest_path =
|
||||||
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
|
||||||
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
|
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
|
||||||
assert!(get(cfg, &FxHashMap::default()).is_ok());
|
assert!(get(cfg, &FxHashMap::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,9 @@ use crate::{
|
||||||
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
|
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
|
||||||
WorkspaceBuildScripts,
|
WorkspaceBuildScripts,
|
||||||
build_dependencies::BuildScriptOutput,
|
build_dependencies::BuildScriptOutput,
|
||||||
cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
|
cargo_config_file,
|
||||||
env::{
|
cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
|
||||||
cargo_config_build_target_dir, cargo_config_env, inject_cargo_env,
|
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
|
||||||
inject_cargo_package_env, inject_rustc_tool_env,
|
|
||||||
},
|
|
||||||
project_json::{Crate, CrateArrayIdx},
|
project_json::{Crate, CrateArrayIdx},
|
||||||
sysroot::RustLibSrcWorkspace,
|
sysroot::RustLibSrcWorkspace,
|
||||||
toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
|
toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
|
||||||
|
|
@ -270,7 +268,9 @@ impl ProjectWorkspace {
|
||||||
|
|
||||||
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
|
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
|
||||||
progress("querying project metadata".to_owned());
|
progress("querying project metadata".to_owned());
|
||||||
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
|
let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
|
||||||
|
let config_file_ = config_file.clone();
|
||||||
|
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
|
||||||
let targets =
|
let targets =
|
||||||
target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
|
target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
|
||||||
let toolchain = version::get(toolchain_config, extra_env)
|
let toolchain = version::get(toolchain_config, extra_env)
|
||||||
|
|
@ -282,10 +282,24 @@ impl ProjectWorkspace {
|
||||||
.ok()
|
.ok()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
|
let fetch_metadata = FetchMetadata::new(
|
||||||
|
cargo_toml,
|
||||||
|
workspace_dir,
|
||||||
|
&CargoMetadataConfig {
|
||||||
|
features: features.clone(),
|
||||||
|
targets: targets.clone(),
|
||||||
|
extra_args: extra_args.clone(),
|
||||||
|
extra_env: extra_env.clone(),
|
||||||
|
toolchain_version: toolchain.clone(),
|
||||||
|
kind: "workspace",
|
||||||
|
},
|
||||||
|
&sysroot,
|
||||||
|
*no_deps,
|
||||||
|
);
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| cargo_config_build_target_dir(cargo_toml, extra_env, &sysroot))
|
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
||||||
.unwrap_or_else(|| workspace_dir.join("target").into());
|
.unwrap_or_else(|| workspace_dir.join("target").into());
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
|
|
@ -319,7 +333,7 @@ impl ProjectWorkspace {
|
||||||
};
|
};
|
||||||
rustc_dir.and_then(|rustc_dir| {
|
rustc_dir.and_then(|rustc_dir| {
|
||||||
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
|
info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
|
||||||
match CargoWorkspace::fetch_metadata(
|
match FetchMetadata::new(
|
||||||
&rustc_dir,
|
&rustc_dir,
|
||||||
workspace_dir,
|
workspace_dir,
|
||||||
&CargoMetadataConfig {
|
&CargoMetadataConfig {
|
||||||
|
|
@ -327,15 +341,12 @@ impl ProjectWorkspace {
|
||||||
targets: targets.clone(),
|
targets: targets.clone(),
|
||||||
extra_args: extra_args.clone(),
|
extra_args: extra_args.clone(),
|
||||||
extra_env: extra_env.clone(),
|
extra_env: extra_env.clone(),
|
||||||
target_dir: target_dir.clone(),
|
|
||||||
toolchain_version: toolchain.clone(),
|
toolchain_version: toolchain.clone(),
|
||||||
kind: "rustc-dev"
|
kind: "rustc-dev"
|
||||||
},
|
},
|
||||||
&sysroot,
|
&sysroot,
|
||||||
*no_deps,
|
*no_deps,
|
||||||
true,
|
).exec(&target_dir, true, progress) {
|
||||||
progress,
|
|
||||||
) {
|
|
||||||
Ok((meta, _error)) => {
|
Ok((meta, _error)) => {
|
||||||
let workspace = CargoWorkspace::new(
|
let workspace = CargoWorkspace::new(
|
||||||
meta,
|
meta,
|
||||||
|
|
@ -364,40 +375,22 @@ impl ProjectWorkspace {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let cargo_metadata = s.spawn(|| {
|
let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
|
||||||
CargoWorkspace::fetch_metadata(
|
|
||||||
cargo_toml,
|
|
||||||
workspace_dir,
|
|
||||||
&CargoMetadataConfig {
|
|
||||||
features: features.clone(),
|
|
||||||
targets: targets.clone(),
|
|
||||||
extra_args: extra_args.clone(),
|
|
||||||
extra_env: extra_env.clone(),
|
|
||||||
target_dir: target_dir.clone(),
|
|
||||||
toolchain_version: toolchain.clone(),
|
|
||||||
kind: "workspace",
|
|
||||||
},
|
|
||||||
&sysroot,
|
|
||||||
*no_deps,
|
|
||||||
false,
|
|
||||||
progress,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let loaded_sysroot = s.spawn(|| {
|
let loaded_sysroot = s.spawn(|| {
|
||||||
sysroot.load_workspace(
|
sysroot.load_workspace(
|
||||||
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir.clone(),
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
workspace_dir,
|
workspace_dir,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let cargo_config_extra_env =
|
let cargo_config_extra_env =
|
||||||
s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
|
s.spawn(move || cargo_config_env(cargo_toml, &config_file));
|
||||||
thread::Result::Ok((
|
thread::Result::Ok((
|
||||||
rustc_cfg.join()?,
|
rustc_cfg.join()?,
|
||||||
data_layout.join()?,
|
data_layout.join()?,
|
||||||
|
|
@ -476,9 +469,7 @@ impl ProjectWorkspace {
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| {
|
.or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
|
||||||
cargo_config_build_target_dir(project_json.manifest()?, &config.extra_env, &sysroot)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| project_root.join("target").into());
|
.unwrap_or_else(|| project_root.join("target").into());
|
||||||
|
|
||||||
// We spawn a bunch of processes to query various information about the workspace's
|
// We spawn a bunch of processes to query various information about the workspace's
|
||||||
|
|
@ -502,6 +493,7 @@ impl ProjectWorkspace {
|
||||||
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
&RustSourceWorkspaceConfig::Json(*sysroot_project),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
project_root,
|
project_root,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -510,10 +502,10 @@ impl ProjectWorkspace {
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir,
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
project_root,
|
project_root,
|
||||||
|
&target_dir,
|
||||||
progress,
|
progress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -554,7 +546,8 @@ impl ProjectWorkspace {
|
||||||
None => Sysroot::empty(),
|
None => Sysroot::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let query_config = QueryConfig::Cargo(&sysroot, detached_file);
|
let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
|
||||||
|
let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
|
||||||
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
|
||||||
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -563,7 +556,7 @@ impl ProjectWorkspace {
|
||||||
let target_dir = config
|
let target_dir = config
|
||||||
.target_dir
|
.target_dir
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| cargo_config_build_target_dir(detached_file, &config.extra_env, &sysroot))
|
.or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
|
||||||
.unwrap_or_else(|| dir.join("target").into());
|
.unwrap_or_else(|| dir.join("target").into());
|
||||||
|
|
||||||
let loaded_sysroot = sysroot.load_workspace(
|
let loaded_sysroot = sysroot.load_workspace(
|
||||||
|
|
@ -571,17 +564,17 @@ impl ProjectWorkspace {
|
||||||
config,
|
config,
|
||||||
&targets,
|
&targets,
|
||||||
toolchain.clone(),
|
toolchain.clone(),
|
||||||
target_dir.clone(),
|
|
||||||
)),
|
)),
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
dir,
|
dir,
|
||||||
|
&target_dir,
|
||||||
&|_| (),
|
&|_| (),
|
||||||
);
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
sysroot.set_workspace(loaded_sysroot);
|
sysroot.set_workspace(loaded_sysroot);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cargo_script = CargoWorkspace::fetch_metadata(
|
let fetch_metadata = FetchMetadata::new(
|
||||||
detached_file,
|
detached_file,
|
||||||
dir,
|
dir,
|
||||||
&CargoMetadataConfig {
|
&CargoMetadataConfig {
|
||||||
|
|
@ -589,25 +582,26 @@ impl ProjectWorkspace {
|
||||||
targets,
|
targets,
|
||||||
extra_args: config.extra_args.clone(),
|
extra_args: config.extra_args.clone(),
|
||||||
extra_env: config.extra_env.clone(),
|
extra_env: config.extra_env.clone(),
|
||||||
target_dir,
|
|
||||||
toolchain_version: toolchain.clone(),
|
toolchain_version: toolchain.clone(),
|
||||||
kind: "detached-file",
|
kind: "detached-file",
|
||||||
},
|
},
|
||||||
&sysroot,
|
&sysroot,
|
||||||
config.no_deps,
|
config.no_deps,
|
||||||
false,
|
);
|
||||||
&|_| (),
|
let target_dir = config
|
||||||
)
|
.target_dir
|
||||||
.ok()
|
.clone()
|
||||||
.map(|(ws, error)| {
|
.or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
|
||||||
let cargo_config_extra_env =
|
.unwrap_or_else(|| dir.join("target").into());
|
||||||
cargo_config_env(detached_file, &config.extra_env, &sysroot);
|
let cargo_script =
|
||||||
(
|
fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
|
||||||
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
|
||||||
WorkspaceBuildScripts::default(),
|
(
|
||||||
error.map(Arc::new),
|
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
|
||||||
)
|
WorkspaceBuildScripts::default(),
|
||||||
});
|
error.map(Arc::new),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
Ok(ProjectWorkspace {
|
Ok(ProjectWorkspace {
|
||||||
kind: ProjectWorkspaceKind::DetachedFile {
|
kind: ProjectWorkspaceKind::DetachedFile {
|
||||||
|
|
@ -1889,15 +1883,33 @@ fn sysroot_metadata_config(
|
||||||
config: &CargoConfig,
|
config: &CargoConfig,
|
||||||
targets: &[String],
|
targets: &[String],
|
||||||
toolchain_version: Option<Version>,
|
toolchain_version: Option<Version>,
|
||||||
target_dir: Utf8PathBuf,
|
|
||||||
) -> CargoMetadataConfig {
|
) -> CargoMetadataConfig {
|
||||||
CargoMetadataConfig {
|
CargoMetadataConfig {
|
||||||
features: Default::default(),
|
features: Default::default(),
|
||||||
targets: targets.to_vec(),
|
targets: targets.to_vec(),
|
||||||
extra_args: Default::default(),
|
extra_args: Default::default(),
|
||||||
extra_env: config.extra_env.clone(),
|
extra_env: config.extra_env.clone(),
|
||||||
target_dir,
|
|
||||||
toolchain_version,
|
toolchain_version,
|
||||||
kind: "sysroot",
|
kind: "sysroot",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cargo_target_dir(
|
||||||
|
manifest: &ManifestPath,
|
||||||
|
extra_env: &FxHashMap<String, Option<String>>,
|
||||||
|
sysroot: &Sysroot,
|
||||||
|
) -> Option<Utf8PathBuf> {
|
||||||
|
let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
|
||||||
|
let mut meta = cargo_metadata::MetadataCommand::new();
|
||||||
|
meta.cargo_path(cargo.get_program());
|
||||||
|
meta.manifest_path(manifest);
|
||||||
|
// `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
|
||||||
|
// So we can use it to get `target_directory` before copying lockfiles
|
||||||
|
let mut other_options = vec!["--no-deps".to_owned()];
|
||||||
|
if manifest.is_rust_manifest() {
|
||||||
|
meta.env("RUSTC_BOOTSTRAP", "1");
|
||||||
|
other_options.push("-Zscript".to_owned());
|
||||||
|
}
|
||||||
|
meta.other_options(other_options);
|
||||||
|
meta.exec().map(|m| m.target_directory).ok()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use hir::{ChangeWithProcMacros, Crate};
|
||||||
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
|
||||||
use ide_db::base_db;
|
use ide_db::base_db;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
use paths::Utf8PathBuf;
|
||||||
use profile::StopWatch;
|
use profile::StopWatch;
|
||||||
use project_model::toolchain_info::{QueryConfig, target_data_layout};
|
use project_model::toolchain_info::{QueryConfig, target_data_layout};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
|
|
@ -79,6 +80,7 @@ impl Tester {
|
||||||
&RustSourceWorkspaceConfig::default_cargo(),
|
&RustSourceWorkspaceConfig::default_cargo(),
|
||||||
false,
|
false,
|
||||||
&path,
|
&path,
|
||||||
|
&Utf8PathBuf::default(),
|
||||||
&|_| (),
|
&|_| (),
|
||||||
);
|
);
|
||||||
if let Some(loaded_sysroot) = loaded_sysroot {
|
if let Some(loaded_sysroot) = loaded_sysroot {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue