Skip to content

Commit

Permalink
Cleanup target fetching for cargo metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Dec 24, 2024
1 parent 633a10c commit 4265f4a
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 140 deletions.
105 changes: 18 additions & 87 deletions crates/project-model/src/cargo_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use serde_json::from_value;
use span::Edition;
use toolchain::Tool;

use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata};
use crate::{CfgOverrides, InvocationStrategy};
use crate::{ManifestPath, Sysroot, SysrootQueryMetadata};

/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
Expand Down Expand Up @@ -251,6 +251,18 @@ impl TargetKind {
}
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoMetadataConfig {
/// List of features to activate.
pub features: CargoFeatures,
/// rustc targets
pub targets: Vec<String>,
/// Extra args to pass to the cargo command.
pub extra_args: Vec<String>,
/// Extra env vars to set when invoking the cargo command
pub extra_env: FxHashMap<String, String>,
}

// Deserialize helper for the cargo metadata
#[derive(Deserialize, Default)]
struct PackageMetadata {
Expand All @@ -265,7 +277,7 @@ impl CargoWorkspace {
pub fn fetch_metadata(
cargo_toml: &ManifestPath,
current_dir: &AbsPath,
config: &CargoConfig,
config: &CargoMetadataConfig,
sysroot: &Sysroot,
locked: bool,
progress: &dyn Fn(String),
Expand All @@ -276,14 +288,12 @@ impl CargoWorkspace {
fn fetch_metadata_(
cargo_toml: &ManifestPath,
current_dir: &AbsPath,
config: &CargoConfig,
config: &CargoMetadataConfig,
sysroot: &Sysroot,
locked: bool,
no_deps: bool,
progress: &dyn Fn(String),
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
let targets = find_list_of_build_targets(config, cargo_toml, sysroot);

let cargo = sysroot.tool(Tool::Cargo);
let mut meta = MetadataCommand::new();
meta.cargo_path(cargo.get_program());
Expand Down Expand Up @@ -319,12 +329,9 @@ impl CargoWorkspace {
}
}

if !targets.is_empty() {
other_options.append(
&mut targets
.into_iter()
.flat_map(|target| ["--filter-platform".to_owned(), target])
.collect(),
if !config.targets.is_empty() {
other_options.extend(
config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
);
}
// The manifest is a rust file, so this means its a script manifest
Expand Down Expand Up @@ -596,79 +603,3 @@ impl CargoWorkspace {
self.is_virtual_workspace
}
}

fn find_list_of_build_targets(
config: &CargoConfig,
cargo_toml: &ManifestPath,
sysroot: &Sysroot,
) -> Vec<String> {
if let Some(target) = &config.target {
return [target.into()].to_vec();
}

let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot);
if !build_targets.is_empty() {
return build_targets;
}

rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect()
}

fn rustc_discover_host_triple(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: &Sysroot,
) -> Option<String> {
let mut rustc = sysroot.tool(Tool::Rustc);
rustc.envs(extra_env);
rustc.current_dir(cargo_toml.parent()).arg("-vV");
tracing::debug!("Discovering host platform by {:?}", rustc);
match utf8_stdout(rustc) {
Ok(stdout) => {
let field = "host: ";
let target = stdout.lines().find_map(|l| l.strip_prefix(field));
if let Some(target) = target {
Some(target.to_owned())
} else {
// If we fail to resolve the host platform, it's not the end of the world.
tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout);
None
}
}
Err(e) => {
tracing::warn!("Failed to discover host platform: {}", e);
None
}
}
}

fn cargo_config_build_target(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: &Sysroot,
) -> Vec<String> {
let mut cargo_config = sysroot.tool(Tool::Cargo);
cargo_config.envs(extra_env);
cargo_config
.current_dir(cargo_toml.parent())
.args(["-Z", "unstable-options", "config", "get", "build.target"])
.env("RUSTC_BOOTSTRAP", "1");
// if successful we receive `build.target = "target-triple"`
// or `build.target = ["<target 1>", ..]`
tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
}

fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> {
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');

if !trimmed.starts_with('[') {
return [trimmed.to_owned()].to_vec();
}

let res = serde_json::from_str(trimmed);
if let Err(e) = &res {
tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e);
}
res.unwrap_or_default()
}
16 changes: 11 additions & 5 deletions crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod project_json;
mod rustc_cfg;
mod sysroot;
pub mod target_data_layout;
mod target_triple;
mod workspace;

#[cfg(test)]
Expand All @@ -42,8 +43,8 @@ use rustc_hash::FxHashSet;
pub use crate::{
build_dependencies::WorkspaceBuildScripts,
cargo_workspace::{
CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
RustLibSource, Target, TargetData, TargetKind,
CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
PackageDependency, RustLibSource, Target, TargetData, TargetKind,
},
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
Expand Down Expand Up @@ -241,9 +242,14 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
Ok(res)
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SysrootQueryMetadata {
#[default]
CargoMetadata,
CargoMetadata(CargoMetadataConfig),
None,
}

impl Default for SysrootQueryMetadata {
fn default() -> Self {
SysrootQueryMetadata::CargoMetadata(Default::default())
}
}
31 changes: 19 additions & 12 deletions crates/project-model/src/sysroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap;
use toolchain::{probe_for_binary, Tool};

use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata};
use crate::{
cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
SysrootQueryMetadata,
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sysroot {
Expand Down Expand Up @@ -126,7 +129,7 @@ impl Sysroot {
pub fn discover(
dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_dir = discover_sysroot_dir(dir, extra_env);
let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
Expand All @@ -139,7 +142,7 @@ impl Sysroot {
current_dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
sysroot_src_dir: AbsPathBuf,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
Sysroot::load_core_check(
Expand All @@ -151,7 +154,7 @@ impl Sysroot {

pub fn discover_sysroot_src_dir(
sysroot_dir: AbsPathBuf,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
.ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
Expand Down Expand Up @@ -205,15 +208,15 @@ impl Sysroot {
pub fn load(
sysroot_dir: Option<AbsPathBuf>,
sysroot_src_dir: Option<AbsPathBuf>,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata)
}

fn load_core_check(
sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata);
if sysroot.error.is_none() {
Expand Down Expand Up @@ -241,7 +244,7 @@ impl Sysroot {
fn load_(
sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
sysroot_query_metadata: SysrootQueryMetadata,
sysroot_query_metadata: &SysrootQueryMetadata,
) -> Sysroot {
let sysroot_dir = match sysroot_dir {
Some(Ok(sysroot_dir)) => Some(sysroot_dir),
Expand Down Expand Up @@ -274,13 +277,16 @@ impl Sysroot {
}
}
};
if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata {
if let SysrootQueryMetadata::CargoMetadata(cargo_config) = sysroot_query_metadata {
let library_manifest =
ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
if fs::metadata(&library_manifest).is_ok() {
if let Some(sysroot) =
Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir)
{
if let Some(sysroot) = Self::load_library_via_cargo(
library_manifest,
&sysroot_dir,
&sysroot_src_dir,
cargo_config,
) {
return sysroot;
}
}
Expand Down Expand Up @@ -341,9 +347,10 @@ impl Sysroot {
library_manifest: ManifestPath,
sysroot_dir: &Option<AbsPathBuf>,
sysroot_src_dir: &AbsPathBuf,
cargo_config: &CargoMetadataConfig,
) -> Option<Sysroot> {
tracing::debug!("Loading library metadata: {library_manifest}");
let mut cargo_config = CargoConfig::default();
let mut cargo_config = cargo_config.clone();
// the sysroot uses `public-dependency`, so we make cargo think it's a nightly
cargo_config.extra_env.insert(
"__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(),
Expand Down
86 changes: 86 additions & 0 deletions crates/project-model/src/target_triple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use anyhow::Context;
use rustc_hash::FxHashMap;
use toolchain::Tool;

use crate::{utf8_stdout, ManifestPath, Sysroot};

/// Determines how `rustc --print target-spec-json` is discovered and invoked.
pub(super) enum TargetTipleConfig<'a> {
/// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::rustc`].
#[expect(dead_code)]
Rustc(&'a Sysroot),
/// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via
/// [`toolchain::cargo`].
Cargo(&'a Sysroot, &'a ManifestPath),
}

pub(super) fn get(
config: TargetTipleConfig<'_>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
) -> anyhow::Result<Vec<String>> {
if let Some(target) = target {
return Ok(vec![target.to_owned()]);
}

let sysroot = match config {
TargetTipleConfig::Cargo(sysroot, cargo_toml) => {
match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
Ok(it) => return Ok(it),
Err(e) => {
tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}");
sysroot
}
}
}
TargetTipleConfig::Rustc(sysroot) => sysroot,
};
rustc_discover_host_triple(extra_env, sysroot).map(|it| vec![it])
}

fn rustc_discover_host_triple(
extra_env: &FxHashMap<String, String>,
sysroot: &Sysroot,
) -> anyhow::Result<String> {
let mut rustc = sysroot.tool(Tool::Rustc);
rustc.envs(extra_env);
rustc.arg("-vV");
tracing::debug!("Discovering host platform by {:?}", rustc);
let stdout = utf8_stdout(rustc).context("Failed to discover host platform")?;
let field = "host: ";
let target = stdout.lines().find_map(|l| l.strip_prefix(field));
if let Some(target) = target {
Ok(target.to_owned())
} else {
// If we fail to resolve the host platform, it's not the end of the world.
Err(anyhow::format_err!("rustc -vV did not report host platform, got:\n{}", stdout))
}
}

fn cargo_config_build_target(
cargo_toml: &ManifestPath,
extra_env: &FxHashMap<String, String>,
sysroot: &Sysroot,
) -> anyhow::Result<Vec<String>> {
let mut cargo_config = sysroot.tool(Tool::Cargo);
cargo_config.envs(extra_env);
cargo_config
.current_dir(cargo_toml.parent())
.args(["-Z", "unstable-options", "config", "get", "build.target"])
.env("RUSTC_BOOTSTRAP", "1");
// if successful we receive `build.target = "target-triple"`
// or `build.target = ["<target 1>", ..]`
tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
utf8_stdout(cargo_config).and_then(parse_output_cargo_config_build_target)
}

fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');

if !trimmed.starts_with('[') {
return Ok([trimmed.to_owned()].to_vec());
}

serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
}
4 changes: 2 additions & 2 deletions crates/project-model/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn get_fake_sysroot() -> Sysroot {
// fake sysroot, so we give them both the same path:
let sysroot_dir = AbsPathBuf::assert(sysroot_path);
let sysroot_src_dir = sysroot_dir.clone();
Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata)
Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), &SysrootQueryMetadata::default())
}

fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
Expand Down Expand Up @@ -232,7 +232,7 @@ fn smoke_test_real_sysroot_cargo() {
let sysroot = Sysroot::discover(
AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
&Default::default(),
SysrootQueryMetadata::CargoMetadata,
&SysrootQueryMetadata::default(),
);
assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_)));
let project_workspace = ProjectWorkspace {
Expand Down
Loading

0 comments on commit 4265f4a

Please sign in to comment.