mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-30 19:49:36 +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
	
	 Shoyu Vanilla (Flint)
						Shoyu Vanilla (Flint)