diff --git a/src/main.rs b/src/main.rs index 5b2294ac..39e832e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -209,6 +209,8 @@ async fn entry() -> Result<()> { let mut uithread = UIThread::new(!opts.no_confirm); + let desired_targets = get_desired_targets(&opts.targets); + // Compute install directory let install_path = get_install_path(opts.install_path.as_deref()).ok_or_else(|| { error!("No viable install path found of specified, try `--install-path`"); @@ -258,22 +260,11 @@ async fn entry() -> Result<()> { manifest.bin, ); - let desired_targets = { - let from_opts = opts - .targets - .as_ref() - .map(|ts| ts.split(',').map(|t| t.to_string()).collect()); - - if let Some(ts) = from_opts { - ts - } else { - detect_targets().await - } - }; - let mut fetchers = MultiFetcher::default(); - for target in &desired_targets { + let desired_targets = desired_targets.get().await; + + for target in desired_targets { debug!("Building metadata for target: {target}"); let mut target_meta = meta.clone(); diff --git a/src/target.rs b/src/target.rs index 1ce8d308..487f7040 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,10 +1,68 @@ use std::io::{BufRead, Cursor}; use std::process::Output; +use std::sync::Arc; + use tokio::process::Command; +use tokio::sync::OnceCell; /// Compiled target triple, used as default for binary fetching pub const TARGET: &str = env!("TARGET"); +#[derive(Debug)] +enum DesiredTargetsInner { + AutoDetect(Arc>>), + Initialized(Vec), +} + +#[derive(Debug)] +pub struct DesiredTargets(DesiredTargetsInner); + +impl DesiredTargets { + fn initialized(targets: Vec) -> Self { + Self(DesiredTargetsInner::Initialized(targets)) + } + + fn auto_detect() -> Self { + let arc = Arc::new(OnceCell::new()); + + let once_cell = arc.clone(); + tokio::spawn(async move { + once_cell.get_or_init(detect_targets).await; + }); + + Self(DesiredTargetsInner::AutoDetect(arc)) + } + + pub async fn get(&self) -> &[String] { + use DesiredTargetsInner::*; + + match &self.0 { + Initialized(targets) => targets, + + // This will mostly just wait for the spawned task, + // on rare occausion though, it will poll the future + // returned by `detect_targets`. + AutoDetect(once_cell) => once_cell.get_or_init(detect_targets).await, + } + } +} + +/// If opts_targets is `Some`, then it will be used. +/// Otherwise, call `detect_targets` using `tokio::spawn` to detect targets. +/// +/// Since `detect_targets` internally spawns a process and wait for it, +/// it's pretty costy. +/// +/// Calling it through `tokio::spawn` would enable other tasks, such as +/// fetching the crate tarballs, to be executed concurrently. +pub fn get_desired_targets(opts_targets: &Option) -> DesiredTargets { + if let Some(targets) = opts_targets.as_ref() { + DesiredTargets::initialized(targets.split(',').map(|t| t.to_string()).collect()) + } else { + DesiredTargets::auto_detect() + } +} + /// Detect the targets supported at runtime, /// which might be different from `TARGET` which is detected /// at compile-time.