From 4a356466b199df2a829da0274b28a316f6d7013d Mon Sep 17 00:00:00 2001 From: Niklas Eicker Date: Thu, 10 Apr 2025 22:29:07 +0200 Subject: [PATCH] rust: Enable required features when executing `main` functions in tasks (#27312) Closes #13344 This PR causes required features to be read from `cargo metadata` and enabled when executing an example/bin in Rust. Release Notes: - Added enabling required features when executing a Rust example or bin through a task --- crates/languages/src/rust.rs | 119 +++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 40 deletions(-) diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 303d1ce4f308d87e13e2039d3b93c1068e41c85e..daee36b6ad5d601142bc35f0a9de0d4b5322f18b 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -516,6 +516,14 @@ const RUST_BIN_NAME_TASK_VARIABLE: VariableName = const RUST_BIN_KIND_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowed("RUST_BIN_KIND")); +/// The flag to list required features for executing a bin, if any +const RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE: VariableName = + VariableName::Custom(Cow::Borrowed("RUST_BIN_REQUIRED_FEATURES_FLAG")); + +/// The list of required features for executing a bin, if any +const RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE: VariableName = + VariableName::Custom(Cow::Borrowed("RUST_BIN_REQUIRED_FEATURES")); + const RUST_TEST_FRAGMENT_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowed("RUST_TEST_FRAGMENT")); @@ -544,8 +552,8 @@ impl ContextProvider for RustContextProvider { let mut variables = TaskVariables::default(); - if let Some(target) = local_abs_path - .and_then(|path| package_name_and_bin_name_from_abs_path(path, project_env.as_ref())) + if let Some(target) = + local_abs_path.and_then(|path| target_info_from_abs_path(path, project_env.as_ref())) { variables.extend(TaskVariables::from_iter([ (RUST_PACKAGE_TASK_VARIABLE.clone(), target.package_name), @@ -555,6 +563,19 @@ impl ContextProvider for RustContextProvider { target.target_kind.to_string(), ), ])); + if target.required_features.is_empty() { + variables.insert(RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE, "".into()); + variables.insert(RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE, "".into()); + } else { + variables.insert( + RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE.clone(), + "--features".to_string(), + ); + variables.insert( + RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE.clone(), + target.required_features.join(","), + ); + } } if let Some(package_name) = local_abs_path @@ -730,6 +751,8 @@ impl ContextProvider for RustContextProvider { RUST_PACKAGE_TASK_VARIABLE.template_value(), format!("--{}", RUST_BIN_KIND_TASK_VARIABLE.template_value()), RUST_BIN_NAME_TASK_VARIABLE.template_value(), + RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE.template_value(), + RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE.template_value(), ], cwd: Some("$ZED_DIRNAME".to_owned()), tags: vec!["rust-main".to_owned()], @@ -827,6 +850,8 @@ struct CargoTarget { name: String, kind: Vec, src_path: String, + #[serde(rename = "required-features", default)] + required_features: Vec, } #[derive(Debug, PartialEq)] @@ -855,13 +880,15 @@ impl TryFrom<&str> for TargetKind { } } /// Which package and binary target are we in? +#[derive(Debug, PartialEq)] struct TargetInfo { package_name: String, target_name: String, target_kind: TargetKind, + required_features: Vec, } -fn package_name_and_bin_name_from_abs_path( +fn target_info_from_abs_path( abs_path: &Path, project_env: Option<&HashMap>, ) -> Option { @@ -881,23 +908,10 @@ fn package_name_and_bin_name_from_abs_path( let metadata: CargoMetadata = serde_json::from_slice(&output).log_err()?; - retrieve_package_id_and_bin_name_from_metadata(metadata, abs_path).and_then( - |(package_id, bin_name, target_kind)| { - let package_name = package_name_from_pkgid(&package_id); - - package_name.map(|package_name| TargetInfo { - package_name: package_name.to_owned(), - target_name: bin_name, - target_kind, - }) - }, - ) + target_info_from_metadata(metadata, abs_path) } -fn retrieve_package_id_and_bin_name_from_metadata( - metadata: CargoMetadata, - abs_path: &Path, -) -> Option<(String, String, TargetKind)> { +fn target_info_from_metadata(metadata: CargoMetadata, abs_path: &Path) -> Option { for package in metadata.packages { for target in package.targets { let Some(bin_kind) = target @@ -909,7 +923,12 @@ fn retrieve_package_id_and_bin_name_from_metadata( }; let target_path = PathBuf::from(target.src_path); if target_path == abs_path { - return Some((package.id, target.name, bin_kind)); + return package_name_from_pkgid(&package.id).map(|package_name| TargetInfo { + package_name: package_name.to_owned(), + target_name: target.name, + required_features: target.required_features, + target_kind: bin_kind, + }); } } } @@ -1321,34 +1340,57 @@ mod tests { } #[test] - fn test_retrieve_package_id_and_bin_name_from_metadata() { + fn test_target_info_from_metadata() { for (input, absolute_path, expected) in [ ( - r#"{"packages":[{"id":"path+file:///path/to/zed/crates/zed#0.131.0","targets":[{"name":"zed","kind":["bin"],"src_path":"/path/to/zed/src/main.rs"}]}]}"#, + r#"{"packages":[{"id":"path+file:///absolute/path/to/project/zed/crates/zed#0.131.0","targets":[{"name":"zed","kind":["bin"],"src_path":"/path/to/zed/src/main.rs"}]}]}"#, "/path/to/zed/src/main.rs", - Some(( - "path+file:///path/to/zed/crates/zed#0.131.0", - "zed", - TargetKind::Bin, - )), + Some(TargetInfo { + package_name: "zed".into(), + target_name: "zed".into(), + required_features: Vec::new(), + target_kind: TargetKind::Bin, + }), ), ( r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-bin","kind":["bin"],"src_path":"/path/to/custom-package/src/main.rs"}]}]}"#, "/path/to/custom-package/src/main.rs", - Some(( - "path+file:///path/to/custom-package#my-custom-package@0.1.0", - "my-custom-bin", - TargetKind::Bin, - )), + Some(TargetInfo { + package_name: "my-custom-package".into(), + target_name: "my-custom-bin".into(), + required_features: Vec::new(), + target_kind: TargetKind::Bin, + }), ), ( r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-bin","kind":["example"],"src_path":"/path/to/custom-package/src/main.rs"}]}]}"#, "/path/to/custom-package/src/main.rs", - Some(( - "path+file:///path/to/custom-package#my-custom-package@0.1.0", - "my-custom-bin", - TargetKind::Example, - )), + Some(TargetInfo { + package_name: "my-custom-package".into(), + target_name: "my-custom-bin".into(), + required_features: Vec::new(), + target_kind: TargetKind::Example, + }), + ), + ( + r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-bin","kind":["example"],"src_path":"/path/to/custom-package/src/main.rs","required-features":["foo","bar"]}]}]}"#, + "/path/to/custom-package/src/main.rs", + Some(TargetInfo { + package_name: "my-custom-package".into(), + target_name: "my-custom-bin".into(), + required_features: vec!["foo".to_owned(), "bar".to_owned()], + target_kind: TargetKind::Example, + }), + ), + ( + r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-bin","kind":["example"],"src_path":"/path/to/custom-package/src/main.rs","required-features":[]}]}]}"#, + "/path/to/custom-package/src/main.rs", + Some(TargetInfo { + package_name: "my-custom-package".into(), + target_name: "my-custom-bin".into(), + required_features: vec![], + target_kind: TargetKind::Example, + }), ), ( r#"{"packages":[{"id":"path+file:///path/to/custom-package#my-custom-package@0.1.0","targets":[{"name":"my-custom-package","kind":["lib"],"src_path":"/path/to/custom-package/src/main.rs"}]}]}"#, @@ -1360,10 +1402,7 @@ mod tests { let absolute_path = Path::new(absolute_path); - assert_eq!( - retrieve_package_id_and_bin_name_from_metadata(metadata, absolute_path), - expected.map(|(pkgid, name, kind)| (pkgid.to_owned(), name.to_owned(), kind)) - ); + assert_eq!(target_info_from_metadata(metadata, absolute_path), expected); } }