浏览代码

Make feature resolution recursive

Signed-off-by: Nico Burns <nico@nicoburns.com>
Nico Burns 1 天之前
父节点
当前提交
e88c79dd98
共有 2 个文件被更改,包括 74 次插入68 次删除
  1. 73 65
      packages/cli/src/build/request.rs
  2. 1 3
      packages/cli/src/platform.rs

+ 73 - 65
packages/cli/src/build/request.rs

@@ -541,10 +541,14 @@ impl BuildRequest {
             })?
             .clone();
 
+        // Recursively resolve features into a flattened, deduplicated list of all features enabled
+        // for the top-level crate being compiled.
+        let enabled_features =
+            Self::resolve_enabled_features(main_package, &args.features, !args.no_default_features);
+
         // The crate might enable multiple platforms or no platforms at
         // We collect all the platforms it enables first and then select based on the --platform arg
-        let enabled_platforms =
-            Self::enabled_cargo_toml_platforms(main_package, args.no_default_features);
+        let enabled_platforms = Self::enabled_cargo_toml_platforms(main_package, &enabled_features);
         let using_dioxus_explicitly = main_package
             .dependencies
             .iter()
@@ -560,7 +564,7 @@ impl BuildRequest {
                 // The user passed --platform XYZ but already has `default = ["ABC"]` in their Cargo.toml or dioxus = { features = ["abc"] }
                 // We want to strip out the default platform and use the one they passed, setting no-default-features
                 _ => {
-                    features.extend(Self::platformless_features(main_package));
+                    features.extend(Self::platformless_features(main_package, &enabled_features));
                     no_default_features = true;
                     Platform::from(platform_arg)
                 }
@@ -2904,12 +2908,48 @@ impl BuildRequest {
             .map(|krate| krate.krate.version.to_string())
     }
 
+    // Recursively resolve enabled features starting with:
+    //   - Default features if enabled
+    //   - Those enabled with an explicit `--features` CLI argument
+    pub(crate) fn resolve_enabled_features(
+        package: &krates::cm::Package,
+        cli_arg_features: &[String],
+        default_features_enabled: bool,
+    ) -> Vec<String> {
+        let mut enabled_features = cli_arg_features.to_vec();
+        if default_features_enabled {
+            enabled_features.push(String::from("default"));
+        }
+
+        let mut pointer = 0;
+        let mut len = enabled_features.len();
+        loop {
+            for i in pointer..len {
+                let feature = &enabled_features[i];
+                if let Some(subfeatures) = package.features.get(feature) {
+                    enabled_features.extend(subfeatures.iter().cloned());
+                }
+            }
+
+            if enabled_features.len() == len {
+                break;
+            }
+            pointer = len;
+            len = enabled_features.len();
+        }
+
+        enabled_features.sort();
+        enabled_features.dedup();
+
+        enabled_features
+    }
+
     /// Return the platforms that are enabled for the package
     ///
     /// Ideally only one platform is enabled but we need to be able to
     pub(crate) fn enabled_cargo_toml_platforms(
         package: &krates::cm::Package,
-        no_default_features: bool,
+        enabled_features: &[String],
     ) -> Vec<Platform> {
         let mut platforms = vec![];
 
@@ -2926,27 +2966,13 @@ impl BuildRequest {
             }
         }
 
-        // Start searching through the default features
-        //
-        // [features]
-        // default = ["dioxus/web"]
-        //
-        // or
-        //
-        // [features]
-        // default = ["web"]
-        // web = ["dioxus/web"]
-        if no_default_features {
+        // Recursively resolve all enabled features
+        if enabled_features.is_empty() {
             return platforms;
         }
 
-        let Some(default) = package.features.get("default") else {
-            return platforms;
-        };
-
-        // we only trace features 1 level deep..
-        // TODO: trace all enabled features, not just default features
-        for feature in default.iter() {
+        // Check for any enabled features which enable a dioxus platform
+        for feature in enabled_features.iter() {
             // If the user directly specified a platform we can just use that.
             if feature.starts_with("dioxus/") {
                 let dx_feature = feature.trim_start_matches("dioxus/");
@@ -2955,20 +2981,6 @@ impl BuildRequest {
                     platforms.push(auto);
                 }
             }
-
-            // If the user is specifying an internal feature that points to a platform, we can use that
-            let internal_feature = package.features.get(feature);
-            if let Some(internal_feature) = internal_feature {
-                for feature in internal_feature {
-                    if feature.starts_with("dioxus/") {
-                        let dx_feature = feature.trim_start_matches("dioxus/");
-                        let auto = Platform::autodetect_from_cargo_feature(dx_feature);
-                        if let Some(auto) = auto {
-                            platforms.push(auto);
-                        }
-                    }
-                }
-            }
         }
 
         platforms.sort();
@@ -2978,41 +2990,37 @@ impl BuildRequest {
     }
 
     /// Gather the features that are enabled for the package
-    fn platformless_features(package: &krates::cm::Package) -> Vec<String> {
-        let Some(default) = package.features.get("default") else {
-            return Vec::new();
-        };
-
-        let mut kept_features = vec![];
-
-        // Only keep the top-level features in the default list that don't point to a platform directly
-        // IE we want to drop `web` if default = ["web"]
-        'top: for feature in default {
-            // Don't keep features that point to a platform via dioxus/blah
-            if feature.starts_with("dioxus/") {
-                let dx_feature = feature.trim_start_matches("dioxus/");
-                if Platform::autodetect_from_cargo_feature(dx_feature).is_some() {
-                    continue 'top;
+    fn platformless_features(
+        package: &krates::cm::Package,
+        enabled_features: &[String],
+    ) -> Vec<String> {
+        enabled_features
+            .iter()
+            .filter(|feature| {
+                // Don't keep features that point to a platform via dioxus/blah
+                if feature.starts_with("dioxus/") {
+                    let dx_feature = feature.trim_start_matches("dioxus/");
+                    if Platform::autodetect_from_cargo_feature(dx_feature).is_some() {
+                        return false;
+                    }
                 }
-            }
 
-            // Don't keep features that point to a platform via an internal feature
-            if let Some(internal_feature) = package.features.get(feature) {
-                for feature in internal_feature {
-                    if feature.starts_with("dioxus/") {
-                        let dx_feature = feature.trim_start_matches("dioxus/");
-                        if Platform::autodetect_from_cargo_feature(dx_feature).is_some() {
-                            continue 'top;
+                // Don't keep features that point to a platform via an internal feature
+                if let Some(internal_feature) = package.features.get(*feature) {
+                    for feature in internal_feature {
+                        if feature.starts_with("dioxus/") {
+                            let dx_feature = feature.trim_start_matches("dioxus/");
+                            if Platform::autodetect_from_cargo_feature(dx_feature).is_some() {
+                                return false;
+                            }
                         }
                     }
                 }
-            }
-
-            // Otherwise we can keep it
-            kept_features.push(feature.to_string());
-        }
 
-        kept_features
+                true
+            })
+            .cloned()
+            .collect()
     }
 
     pub(crate) fn bundled_app_name(&self) -> String {

+ 1 - 3
packages/cli/src/platform.rs

@@ -189,9 +189,7 @@ impl From<PlatformArg> for Platform {
             PlatformArg::Liveview => Platform::Liveview,
 
             // The alias arguments
-            PlatformArg::Desktop | PlatformArg::Native => {
-                Platform::TARGET_PLATFORM.unwrap()
-            }
+            PlatformArg::Desktop | PlatformArg::Native => Platform::TARGET_PLATFORM.unwrap(),
         }
     }
 }