123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765 |
- use crate::CliSettings;
- use crate::{config::DioxusConfig, TargetArgs};
- use crate::{Platform, Result};
- use anyhow::Context;
- use itertools::Itertools;
- use krates::{cm::Target, KrateDetails};
- use krates::{cm::TargetKind, Cmd, Krates, NodeId};
- use std::path::Path;
- use std::path::PathBuf;
- use std::sync::Arc;
- use toml_edit::Item;
- // Contains information about the crate we are currently in and the dioxus config for that crate
- #[derive(Clone)]
- pub(crate) struct DioxusCrate {
- pub(crate) krates: Arc<Krates>,
- pub(crate) package: NodeId,
- pub(crate) config: DioxusConfig,
- pub(crate) target: Target,
- pub(crate) settings: CliSettings,
- }
- pub(crate) static PROFILE_WASM: &str = "wasm-dev";
- pub(crate) static PROFILE_ANDROID: &str = "android-dev";
- pub(crate) static PROFILE_SERVER: &str = "server-dev";
- impl DioxusCrate {
- pub(crate) fn new(target: &TargetArgs) -> Result<Self> {
- tracing::debug!("Loading crate");
- let cmd = Cmd::new();
- let builder = krates::Builder::new();
- let krates = builder
- .build(cmd, |_| {})
- .context("Failed to run cargo metadata")?;
- let package = find_main_package(&krates, target.package.clone())?;
- tracing::debug!("Found package {package:?}");
- let dioxus_config = DioxusConfig::load(&krates, package)?.unwrap_or_default();
- let package_name = krates[package].name.clone();
- let target_kind = if target.example.is_some() {
- TargetKind::Example
- } else {
- TargetKind::Bin
- };
- let main_package = &krates[package];
- let target_name = target
- .example
- .clone()
- .or(target.bin.clone())
- .or_else(|| {
- if let Some(default_run) = &main_package.default_run {
- return Some(default_run.to_string());
- }
- let bin_count = main_package
- .targets
- .iter()
- .filter(|x| x.kind.contains(&target_kind))
- .count();
- if bin_count != 1 {
- return None;
- }
- main_package.targets.iter().find_map(|x| {
- if x.kind.contains(&target_kind) {
- Some(x.name.clone())
- } else {
- None
- }
- })
- })
- .unwrap_or(package_name);
- let target = main_package
- .targets
- .iter()
- .find(|target| {
- target_name == target.name.as_str() && target.kind.contains(&target_kind)
- })
- .with_context(|| {
- let target_of_kind = |kind|-> String {
- let filtered_packages = main_package
- .targets
- .iter()
- .filter_map(|target| {
- target.kind.contains(kind).then_some(target.name.as_str())
- }).collect::<Vec<_>>();
- filtered_packages.join(", ")};
- if let Some(example) = &target.example {
- let examples = target_of_kind(&TargetKind::Example);
- format!("Failed to find example {example}. \nAvailable examples are:\n{}", examples)
- } else if let Some(bin) = &target.bin {
- let binaries = target_of_kind(&TargetKind::Bin);
- format!("Failed to find binary {bin}. \nAvailable binaries are:\n{}", binaries)
- } else {
- format!("Failed to find target {target_name}. \nIt looks like you are trying to build dioxus in a library crate. \
- You either need to run dx from inside a binary crate or build a specific example with the `--example` flag. \
- Available examples are:\n{}", target_of_kind(&TargetKind::Example))
- }
- })?
- .clone();
- let settings = CliSettings::load();
- Ok(Self {
- krates: Arc::new(krates),
- package,
- config: dioxus_config,
- target,
- settings,
- })
- }
- /// The asset dir we used to support before manganis became the default.
- /// This generally was just a folder in your Dioxus.toml called "assets" or "public" where users
- /// would store their assets.
- ///
- /// With manganis you now use `asset!()` and we pick it up automatically.
- pub(crate) fn legacy_asset_dir(&self) -> Option<PathBuf> {
- self.config
- .application
- .asset_dir
- .clone()
- .map(|dir| self.crate_dir().join(dir))
- }
- /// Get the list of files in the "legacy" asset directory
- pub(crate) fn legacy_asset_dir_files(&self) -> Vec<PathBuf> {
- let mut files = vec![];
- let Some(legacy_asset_dir) = self.legacy_asset_dir() else {
- return files;
- };
- let Ok(read_dir) = legacy_asset_dir.read_dir() else {
- return files;
- };
- for entry in read_dir.flatten() {
- files.push(entry.path());
- }
- files
- }
- /// Get the outdir specified by the Dioxus.toml, relative to the crate directory.
- /// We don't support workspaces yet since that would cause a collision of bundles per project.
- pub(crate) fn crate_out_dir(&self) -> Option<PathBuf> {
- self.config
- .application
- .out_dir
- .as_ref()
- .map(|out_dir| self.crate_dir().join(out_dir))
- }
- /// Compose an out directory. Represents the typical "dist" directory that
- /// is "distributed" after building an application (configurable in the
- /// `Dioxus.toml`).
- fn internal_out_dir(&self) -> PathBuf {
- let dir = self.workspace_dir().join("target").join("dx");
- std::fs::create_dir_all(&dir).unwrap();
- dir
- }
- /// Create a workdir for the given platform
- /// This can be used as a temporary directory for the build, but in an observable way such that
- /// you can see the files in the directory via `target`
- ///
- /// target/dx/build/app/web/
- /// target/dx/build/app/web/public/
- /// target/dx/build/app/web/server.exe
- pub(crate) fn build_dir(&self, platform: Platform, release: bool) -> PathBuf {
- self.internal_out_dir()
- .join(self.executable_name())
- .join(if release { "release" } else { "debug" })
- .join(platform.build_folder_name())
- }
- /// target/dx/bundle/app/
- /// target/dx/bundle/app/blah.app
- /// target/dx/bundle/app/blah.exe
- /// target/dx/bundle/app/public/
- pub(crate) fn bundle_dir(&self, platform: Platform) -> PathBuf {
- self.internal_out_dir()
- .join(self.executable_name())
- .join("bundle")
- .join(platform.build_folder_name())
- }
- /// Get the workspace directory for the crate
- pub(crate) fn workspace_dir(&self) -> PathBuf {
- self.krates.workspace_root().as_std_path().to_path_buf()
- }
- /// Get the directory of the crate
- pub(crate) fn crate_dir(&self) -> PathBuf {
- self.package()
- .manifest_path
- .parent()
- .unwrap()
- .as_std_path()
- .to_path_buf()
- }
- /// Get the main source file of the target
- pub(crate) fn main_source_file(&self) -> PathBuf {
- self.target.src_path.as_std_path().to_path_buf()
- }
- /// Get the package we are currently in
- pub(crate) fn package(&self) -> &krates::cm::Package {
- &self.krates[self.package]
- }
- /// Get the name of the package we are compiling
- pub(crate) fn executable_name(&self) -> &str {
- &self.target.name
- }
- /// Get the type of executable we are compiling
- pub(crate) fn executable_type(&self) -> krates::cm::TargetKind {
- self.target.kind[0].clone()
- }
- /// Try to autodetect the platform from the package by reading its features
- ///
- /// Read the default-features list and/or the features list on dioxus to see if we can autodetect the platform
- pub(crate) fn autodetect_platform(&self) -> Option<(Platform, String)> {
- let krate = self.krates.krates_by_name("dioxus").next()?;
- // We're going to accumulate the platforms that are enabled
- // This will let us create a better warning if multiple platforms are enabled
- let manually_enabled_platforms = self
- .krates
- .get_enabled_features(krate.kid)?
- .iter()
- .flat_map(|feature| {
- tracing::trace!("Autodetecting platform from feature {feature}");
- Platform::autodetect_from_cargo_feature(feature).map(|f| (f, feature.to_string()))
- })
- .collect::<Vec<_>>();
- if manually_enabled_platforms.len() > 1 {
- tracing::error!("Multiple platforms are enabled. Please specify a platform with `--platform <platform>` or set a single default platform using a cargo feature.");
- for platform in manually_enabled_platforms {
- tracing::error!(" - {platform:?}");
- }
- return None;
- }
- if manually_enabled_platforms.len() == 1 {
- return manually_enabled_platforms.first().cloned();
- }
- // Let's try and find the list of platforms from the feature list
- // This lets apps that specify web + server to work without specifying the platform.
- // This is because we treat `server` as a binary thing rather than a dedicated platform, so at least we can disambiguate it
- let possible_platforms = self
- .package()
- .features
- .iter()
- .filter_map(|(feature, _features)| {
- match Platform::autodetect_from_cargo_feature(feature) {
- Some(platform) => Some((platform, feature.to_string())),
- None => {
- let auto_implicit = _features
- .iter()
- .filter_map(|f| {
- if !f.starts_with("dioxus?/") && !f.starts_with("dioxus/") {
- return None;
- }
- let rest = f
- .trim_start_matches("dioxus/")
- .trim_start_matches("dioxus?/");
- Platform::autodetect_from_cargo_feature(rest)
- })
- .collect::<Vec<_>>();
- if auto_implicit.len() == 1 {
- Some((auto_implicit.first().copied().unwrap(), feature.to_string()))
- } else {
- None
- }
- }
- }
- })
- .filter(|platform| platform.0 != Platform::Server)
- .collect::<Vec<_>>();
- if possible_platforms.len() == 1 {
- return possible_platforms.first().cloned();
- }
- None
- }
- /// Check if dioxus is being built with a particular feature
- pub(crate) fn has_dioxus_feature(&self, filter: &str) -> bool {
- self.krates.krates_by_name("dioxus").any(|dioxus| {
- self.krates
- .get_enabled_features(dioxus.kid)
- .map(|features| features.contains(filter))
- .unwrap_or_default()
- })
- }
- /// Get the features required to build for the given platform
- pub(crate) fn feature_for_platform(&self, platform: Platform) -> String {
- let package = self.package();
- // Try to find the feature that activates the dioxus feature for the given platform
- let dioxus_feature = platform.feature_name();
- let res = package.features.iter().find_map(|(key, features)| {
- // if the feature is just the name of the platform, we use that
- if key == dioxus_feature {
- return Some(key.clone());
- }
- // Otherwise look for the feature that starts with dioxus/ or dioxus?/ and matches the platform
- for feature in features {
- if let Some((_, after_dioxus)) = feature.split_once("dioxus") {
- if let Some(dioxus_feature_enabled) =
- after_dioxus.trim_start_matches('?').strip_prefix('/')
- {
- // If that enables the feature we are looking for, return that feature
- if dioxus_feature_enabled == dioxus_feature {
- return Some(key.clone());
- }
- }
- }
- }
- None
- });
- res.unwrap_or_else(|| {
- let fallback = format!("dioxus/{}", platform.feature_name()) ;
- tracing::debug!(
- "Could not find explicit feature for platform {platform}, passing `fallback` instead"
- );
- fallback
- })
- }
- /// Check if assets should be pre_compressed. This will only be true in release mode if the user
- /// has enabled pre_compress in the web config.
- pub(crate) fn should_pre_compress_web_assets(&self, release: bool) -> bool {
- self.config.web.pre_compress && release
- }
- // The `opt-level=1` increases build times, but can noticeably decrease time
- // between saving changes and being able to interact with an app (for wasm/web). The "overall"
- // time difference (between having and not having the optimization) can be
- // almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends
- // on setup (hardware, OS, browser, idle load).
- //
- // Find or create the client and server profiles in the top-level Cargo.toml file
- // todo(jon): we should/could make these optional by placing some defaults somewhere
- pub(crate) fn initialize_profiles(&self) -> crate::Result<()> {
- let config_path = self.workspace_dir().join("Cargo.toml");
- let mut config = match std::fs::read_to_string(&config_path) {
- Ok(config) => config.parse::<toml_edit::DocumentMut>().map_err(|e| {
- crate::Error::Other(anyhow::anyhow!("Failed to parse Cargo.toml: {}", e))
- })?,
- Err(_) => Default::default(),
- };
- if let Item::Table(table) = config
- .as_table_mut()
- .entry("profile")
- .or_insert(Item::Table(Default::default()))
- {
- if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_WASM) {
- let mut client = toml_edit::Table::new();
- client.insert("inherits", Item::Value("dev".into()));
- client.insert("opt-level", Item::Value(1.into()));
- entry.insert(Item::Table(client));
- }
- if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_SERVER) {
- let mut server = toml_edit::Table::new();
- server.insert("inherits", Item::Value("dev".into()));
- entry.insert(Item::Table(server));
- }
- if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_ANDROID) {
- let mut android = toml_edit::Table::new();
- android.insert("inherits", Item::Value("dev".into()));
- entry.insert(Item::Table(android));
- }
- }
- std::fs::write(config_path, config.to_string())
- .context("Failed to write profiles to Cargo.toml")?;
- Ok(())
- }
- fn default_ignore_list(&self) -> Vec<&'static str> {
- vec![
- ".git",
- ".github",
- ".vscode",
- "target",
- "node_modules",
- "dist",
- "*~",
- ".*",
- "*.lock",
- "*.log",
- ]
- }
- /// Create a new gitignore map for this target crate
- ///
- /// todo(jon): this is a bit expensive to build, so maybe we should cache it?
- pub fn workspace_gitignore(&self) -> ignore::gitignore::Gitignore {
- let crate_dir = self.crate_dir();
- let mut ignore_builder = ignore::gitignore::GitignoreBuilder::new(&crate_dir);
- ignore_builder.add(crate_dir.join(".gitignore"));
- let workspace_dir = self.workspace_dir();
- ignore_builder.add(workspace_dir.join(".gitignore"));
- for path in self.default_ignore_list() {
- ignore_builder
- .add_line(None, path)
- .expect("failed to add path to file excluded");
- }
- ignore_builder.build().unwrap()
- }
- /// Return the version of the wasm-bindgen crate if it exists
- pub fn wasm_bindgen_version(&self) -> Option<String> {
- self.krates
- .krates_by_name("wasm-bindgen")
- .next()
- .map(|krate| krate.krate.version.to_string())
- }
- pub(crate) fn default_platform(&self) -> Option<Platform> {
- let default = self.package().features.get("default")?;
- // we only trace features 1 level deep..
- for feature in default.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/");
- let auto = Platform::autodetect_from_cargo_feature(dx_feature);
- if auto.is_some() {
- return auto;
- }
- }
- // If the user is specifying an internal feature that points to a platform, we can use that
- let internal_feature = self.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 auto.is_some() {
- return auto;
- }
- }
- }
- }
- }
- None
- }
- /// Gather the features that are enabled for the package
- pub(crate) fn platformless_features(&self) -> Vec<String> {
- let default = self.package().features.get("default").unwrap();
- 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;
- }
- }
- // Don't keep features that point to a platform via an internal feature
- if let Some(internal_feature) = self.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;
- }
- }
- }
- }
- // Otherwise we can keep it
- kept_features.push(feature.to_string());
- }
- kept_features
- }
- /// Return the list of paths that we should watch for changes.
- pub(crate) fn watch_paths(&self) -> Vec<PathBuf> {
- let mut watched_paths = vec![];
- // Get a list of *all* the crates with Rust code that we need to watch.
- // This will end up being dependencies in the workspace and non-workspace dependencies on the user's computer.
- let mut watched_crates = self.local_dependencies();
- watched_crates.push(self.crate_dir());
- // Now, watch all the folders in the crates, but respecting their respective ignore files
- for krate_root in watched_crates {
- // Build the ignore builder for this crate, but with our default ignore list as well
- let ignore = self.ignore_for_krate(&krate_root);
- for entry in krate_root.read_dir().unwrap() {
- let Ok(entry) = entry else {
- continue;
- };
- if ignore
- .matched(entry.path(), entry.path().is_dir())
- .is_ignore()
- {
- continue;
- }
- watched_paths.push(entry.path().to_path_buf());
- }
- }
- watched_paths.dedup();
- watched_paths
- }
- fn ignore_for_krate(&self, path: &Path) -> ignore::gitignore::Gitignore {
- let mut ignore_builder = ignore::gitignore::GitignoreBuilder::new(path);
- for path in self.default_ignore_list() {
- ignore_builder
- .add_line(None, path)
- .expect("failed to add path to file excluded");
- }
- ignore_builder.build().unwrap()
- }
- /// Get all the Manifest paths for dependencies that we should watch. Will not return anything
- /// in the `.cargo` folder - only local dependencies will be watched.
- ///
- /// This returns a list of manifest paths
- ///
- /// Extend the watch path to include:
- ///
- /// - the assets directory - this is so we can hotreload CSS and other assets by default
- /// - the Cargo.toml file - this is so we can hotreload the project if the user changes dependencies
- /// - the Dioxus.toml file - this is so we can hotreload the project if the user changes the Dioxus config
- pub(crate) fn local_dependencies(&self) -> Vec<PathBuf> {
- let mut paths = vec![];
- for (dependency, _edge) in self.krates.get_deps(self.package) {
- let krate = match dependency {
- krates::Node::Krate { krate, .. } => krate,
- krates::Node::Feature { krate_index, .. } => &self.krates[krate_index.index()],
- };
- if krate
- .manifest_path
- .components()
- .any(|c| c.as_str() == ".cargo")
- {
- continue;
- }
- paths.push(
- krate
- .manifest_path
- .parent()
- .unwrap()
- .to_path_buf()
- .into_std_path_buf(),
- );
- }
- paths
- }
- pub(crate) fn all_watched_crates(&self) -> Vec<PathBuf> {
- let mut krates: Vec<PathBuf> = self
- .local_dependencies()
- .into_iter()
- .map(|p| {
- p.parent()
- .expect("Local manifest to exist and have a parent")
- .to_path_buf()
- })
- .chain(Some(self.crate_dir()))
- .collect();
- krates.dedup();
- krates
- }
- pub(crate) fn android_ndk(&self) -> Option<PathBuf> {
- // "/Users/jonkelley/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang"
- static PATH: once_cell::sync::Lazy<Option<PathBuf>> = once_cell::sync::Lazy::new(|| {
- use std::env::var;
- use tracing::debug;
- fn var_or_debug(name: &str) -> Option<PathBuf> {
- var(name)
- .inspect_err(|_| debug!("{name} not set"))
- .ok()
- .map(PathBuf::from)
- }
- // attempt to autodetect the ndk path from env vars (usually set by the shell)
- let auto_detected_ndk =
- var_or_debug("NDK_HOME").or_else(|| var_or_debug("ANDROID_NDK_HOME"));
- if let Some(home) = auto_detected_ndk {
- return Some(home);
- }
- let sdk = var_or_debug("ANDROID_SDK_ROOT")
- .or_else(|| var_or_debug("ANDROID_SDK"))
- .or_else(|| var_or_debug("ANDROID_HOME"))?;
- let ndk = sdk.join("ndk");
- ndk.read_dir()
- .ok()?
- .flatten()
- .map(|dir| (dir.file_name(), dir.path()))
- .sorted()
- .last()
- .map(|(_, path)| path.to_path_buf())
- });
- PATH.clone()
- }
- pub(crate) fn mobile_org(&self) -> String {
- let identifier = self.bundle_identifier();
- let mut split = identifier.splitn(3, '.');
- let sub = split
- .next()
- .expect("Identifier to have at least 3 periods like `com.example.app`");
- let tld = split
- .next()
- .expect("Identifier to have at least 3 periods like `com.example.app`");
- format!("{}.{}", sub, tld)
- }
- pub(crate) fn bundled_app_name(&self) -> String {
- use convert_case::{Case, Casing};
- self.executable_name().to_case(Case::Pascal)
- }
- pub(crate) fn full_mobile_app_name(&self) -> String {
- format!("{}.{}", self.mobile_org(), self.bundled_app_name())
- }
- pub(crate) fn bundle_identifier(&self) -> String {
- if let Some(identifier) = self.config.bundle.identifier.clone() {
- return identifier.clone();
- }
- format!("com.example.{}", self.bundled_app_name())
- }
- }
- impl std::fmt::Debug for DioxusCrate {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("DioxusCrate")
- .field("package", &self.krates[self.package])
- .field("dioxus_config", &self.config)
- .field("target", &self.target)
- .finish()
- }
- }
- // Find the main package in the workspace
- fn find_main_package(krates: &Krates, package: Option<String>) -> Result<NodeId> {
- if let Some(package) = package {
- let mut workspace_members = krates.workspace_members();
- let found = workspace_members.find_map(|node| {
- if let krates::Node::Krate { id, krate, .. } = node {
- if krate.name == package {
- return Some(id);
- }
- }
- None
- });
- if found.is_none() {
- tracing::error!("Could not find package {package} in the workspace. Did you forget to add it to the workspace?");
- tracing::error!("Packages in the workspace:");
- for package in krates.workspace_members() {
- if let krates::Node::Krate { krate, .. } = package {
- tracing::error!("{}", krate.name());
- }
- }
- }
- let kid = found.ok_or_else(|| anyhow::anyhow!("Failed to find package {package}"))?;
- return Ok(krates.nid_for_kid(kid).unwrap());
- };
- // Otherwise find the package that is the closest parent of the current directory
- let current_dir = std::env::current_dir()?;
- let current_dir = current_dir.as_path();
- // Go through each member and find the path that is a parent of the current directory
- let mut closest_parent = None;
- for member in krates.workspace_members() {
- if let krates::Node::Krate { id, krate, .. } = member {
- let member_path = krate.manifest_path.parent().unwrap();
- if let Ok(path) = current_dir.strip_prefix(member_path.as_std_path()) {
- let len = path.components().count();
- match closest_parent {
- Some((_, closest_parent_len)) => {
- if len < closest_parent_len {
- closest_parent = Some((id, len));
- }
- }
- None => {
- closest_parent = Some((id, len));
- }
- }
- }
- }
- }
- let kid = closest_parent
- .map(|(id, _)| id)
- .with_context(|| {
- let bin_targets = krates.workspace_members().filter_map(|krate|match krate {
- krates::Node::Krate { krate, .. } if krate.targets.iter().any(|t| t.kind.contains(&krates::cm::TargetKind::Bin))=> {
- Some(format!("- {}", krate.name))
- }
- _ => None
- }).collect::<Vec<_>>();
- format!("Failed to find binary package to build.\nYou need to either run dx from inside a binary crate or specify a binary package to build with the `--package` flag. Try building again with one of the binary packages in the workspace:\n{}", bin_targets.join("\n"))
- })?;
- let package = krates.nid_for_kid(kid).unwrap();
- Ok(package)
- }
|