[ffx] Wrap plugin injections in single Injection
By creating a single Injector trait, the ffx plugin signature is
simplified.
This appears to help with memory usage as well as compile
time/parallelism while compiling:
HEAD: fx build ffx 495.12s user 170.13s system 384% cpu 2:52.93 total
CL: fx build ffx 362.33s user 166.46s system 1134% cpu 46.609 total
Bug: 74452
MULTIPLY: ffx_core_impl_lib_test
Change-Id: I767cf94166f42eaac2ea0a5e1f6e4856c9582ca9
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/518460
Commit-Queue: Matthew Boetger <boetger@google.com>
Reviewed-by: James Tucker <raggi@google.com>
diff --git a/src/developer/ffx/build/ffx.gni b/src/developer/ffx/build/ffx.gni
index e863af7..382523a 100644
--- a/src/developer/ffx/build/ffx.gni
+++ b/src/developer/ffx/build/ffx.gni
@@ -55,6 +55,7 @@
"//third_party/rust_crates:anyhow",
"//third_party/rust_crates:argh",
"//third_party/rust_crates:async-net",
+ "//third_party/rust_crates:async-trait",
"//third_party/rust_crates:futures",
"//third_party/rust_crates:hex",
"//third_party/rust_crates:lazy_static",
diff --git a/src/developer/ffx/build/templates/plugins.md b/src/developer/ffx/build/templates/plugins.md
index d4edd31..258a616 100644
--- a/src/developer/ffx/build/templates/plugins.md
+++ b/src/developer/ffx/build/templates/plugins.md
@@ -1,55 +1,32 @@
-pub async fn ffx_plugin_impl<D, DFut, R, RFut, E, EFut, F, FFut>(
+pub async fn ffx_plugin_impl<I: ffx_core::Injector>(
{% if not includes_execution and not includes_subcommands %}
- _daemon_factory: D,
- _remote_factory: R,
- _fastboot_factory: F,
- _is_experiment: E,
+ _injector: I,
_cmd: {{suite_args_lib}}::FfxPluginCommand,
{% else %}
- daemon_factory: D,
- remote_factory: R,
- fastboot_factory: F,
- is_experiment: E,
+ injector: I,
cmd: {{suite_args_lib}}::FfxPluginCommand,
{% endif %}
) -> Result<(), anyhow::Error>
- where
- D: Fn() -> DFut,
- DFut: std::future::Future<
- Output = anyhow::Result<fidl_fuchsia_developer_bridge::DaemonProxy>,
- >,
- R: Fn() -> RFut,
- RFut: std::future::Future<
- Output = anyhow::Result<
- fidl_fuchsia_developer_remotecontrol::RemoteControlProxy,
- >,
- >,
- F: Fn() -> FFut,
- FFut: std::future::Future<
- Output = anyhow::Result<fidl_fuchsia_developer_bridge::FastbootProxy>,
- >,
- E: Fn(&'static str) -> EFut,
- EFut: std::future::Future<Output = bool>,
{
{% if includes_execution %}
{% if includes_subcommands %}
match cmd.subcommand {
Some(sub) => match sub {
{% for plugin in plugins %}
- {{suite_subcommand_lib}}::Subcommand::{{plugin.enum}}(c) => {{plugin.lib}}_suite::ffx_plugin_impl(daemon_factory, remote_factory, fastboot_factory, is_experiment, c).await,
+ {{suite_subcommand_lib}}::Subcommand::{{plugin.enum}}(c) => {{plugin.lib}}_suite::ffx_plugin_impl(injector, c).await,
{% endfor %}
},
- None => {{execution_lib}}::ffx_plugin_impl(daemon_factory, remote_factory, fastboot_factory, is_experiment, cmd).await
+ None => {{execution_lib}}::ffx_plugin_impl(injector, cmd).await
}
{% else %}
- {{execution_lib}}::ffx_plugin_impl(daemon_factory, remote_factory, fastboot_factory, is_experiment, cmd).await
+ {{execution_lib}}::ffx_plugin_impl(injector, cmd).await
{% endif %}
{% else %}
{% if includes_subcommands %}
match cmd.subcommand {
{% for plugin in plugins %}
- {{suite_subcommand_lib}}::Subcommand::{{plugin.enum}}(c) => {{plugin.lib}}_suite::ffx_plugin_impl(daemon_factory, remote_factory, fastboot_factory, is_experiment, c).await,
+ {{suite_subcommand_lib}}::Subcommand::{{plugin.enum}}(c) => {{plugin.lib}}_suite::ffx_plugin_impl(injector, c).await,
{% endfor %}
}
{% else %}
diff --git a/src/developer/ffx/core/macro/src/impl.rs b/src/developer/ffx/core/macro/src/impl.rs
index 3428a47..7d6d4e6 100644
--- a/src/developer/ffx/core/macro/src/impl.rs
+++ b/src/developer/ffx/core/macro/src/impl.rs
@@ -277,7 +277,7 @@
let output_fut = Ident::new(&format!("{}_fut", factory_name), Span::call_site());
let output_fut_res =
Ident::new(&format!("{}_fut_res", factory_name), Span::call_site());
- let implementation = quote! { let #output_fut = #factory_name(); };
+ let implementation = quote! { let #output_fut = injector.#factory_name(); };
let testing =
generate_fake_test_proxy_method(pat_ident.ident.clone(), proxy_type_path);
let arg = match proxy_wrapper_type {
@@ -371,7 +371,7 @@
async {
// This needs to be a block in order for this `use` to avoid conflicting with a plugins own `use` for this trait.
use futures::TryFutureExt;
- remote_factory().await?
+ injector.remote_factory().await?
.connect(#selector, #server_end.into_channel())
.map_ok_or_else(|e| Result::<(), anyhow::Error>::Err(anyhow::anyhow!(e)), |fidl_result| {
fidl_result
@@ -425,10 +425,7 @@
} = parse_arguments(input.sig.inputs.clone(), &proxies)?;
let mut outer_args: Punctuated<_, Token!(,)> = Punctuated::new();
- outer_args.push(quote! {daemon_factory: D});
- outer_args.push(quote! {remote_factory: R});
- outer_args.push(quote! {fastboot_factory: F});
- outer_args.push(quote! {is_experiment: E});
+ outer_args.push(quote! {injector: I});
outer_args.push(quote! {#cmd});
let implementation = if proxies_to_generate.len() > 0 {
@@ -446,7 +443,7 @@
let gated_impl = if let Some(key) = proxies.experiment_key {
quote! {
- if is_experiment(#key).await {
+ if injector.is_experiment(#key).await {
#implementation
} else {
println!("This is an experimental subcommand. To enable this subcommand run 'ffx config set {} true'", #key);
@@ -459,27 +456,7 @@
let res = quote! {
#input
- pub async fn ffx_plugin_impl<D, R, DFut, RFut, E, EFut, F, FFut>(
- #outer_args
- ) -> anyhow::Result<()>
- where
- D: Fn() -> DFut,
- DFut: std::future::Future<
- Output = anyhow::Result<fidl_fuchsia_developer_bridge::DaemonProxy>,
- >,
- R: Fn() -> RFut,
- RFut: std::future::Future<
- Output = anyhow::Result<
- fidl_fuchsia_developer_remotecontrol::RemoteControlProxy
- >,
- >,
- E: Fn(&'static str) -> EFut,
- EFut: std::future::Future<Output = bool>,
- F: Fn() -> FFut,
- FFut: std::future::Future<
- Output = anyhow::Result<fidl_fuchsia_developer_bridge::FastbootProxy>,
- >,
- {
+ pub async fn ffx_plugin_impl<I: ffx_core::Injector>(#outer_args) -> anyhow::Result<()> {
#gated_impl
}
diff --git a/src/developer/ffx/core/src/lib.rs b/src/developer/ffx/core/src/lib.rs
index 0bd6875..1dede9a 100644
--- a/src/developer/ffx/core/src/lib.rs
+++ b/src/developer/ffx/core/src/lib.rs
@@ -6,6 +6,9 @@
use {
anyhow::Result,
+ async_trait::async_trait,
+ fidl_fuchsia_developer_bridge::{DaemonProxy, FastbootProxy},
+ fidl_fuchsia_developer_remotecontrol::RemoteControlProxy,
futures::stream::{FuturesUnordered, StreamExt, TryStream},
futures::{future::FusedFuture, Future},
pin_project::pin_project,
@@ -19,6 +22,14 @@
pub use ffx_build_version::build_info;
+#[async_trait]
+pub trait Injector {
+ async fn daemon_factory(&self) -> Result<DaemonProxy>;
+ async fn remote_factory(&self) -> Result<RemoteControlProxy>;
+ async fn fastboot_factory(&self) -> Result<FastbootProxy>;
+ async fn is_experiment(&self, key: &str) -> bool;
+}
+
// Error type for wrapping errors known to an `ffx` command and whose occurrence should
// not a priori be considered a bug in ffx.
// TODO(fxbug.dev/57592): consider extending this to allow custom types from plugins.
diff --git a/src/developer/ffx/src/main.rs b/src/developer/ffx/src/main.rs
index b798618..020e80f 100644
--- a/src/developer/ffx/src/main.rs
+++ b/src/developer/ffx/src/main.rs
@@ -6,8 +6,9 @@
analytics::{add_crash_event, get_notice},
anyhow::{anyhow, Context, Result},
async_once::Once,
+ async_trait::async_trait,
ffx_core::metrics::{add_fx_launch_event, init_metrics_svc},
- ffx_core::{ffx_bail, ffx_error, FfxError},
+ ffx_core::{ffx_bail, ffx_error, FfxError, Injector},
ffx_daemon::{get_daemon_proxy_single_link, is_daemon_running},
ffx_lib_args::{from_env, Ffx},
ffx_lib_sub_command::Subcommand,
@@ -16,8 +17,8 @@
fidl_fuchsia_developer_remotecontrol::{RemoteControlMarker, RemoteControlProxy},
fuchsia_async::TimeoutExt,
futures::{Future, FutureExt},
- lazy_static::lazy_static,
ring::digest::{Context as ShaContext, Digest, SHA256},
+ std::default::Default,
std::error::Error,
std::fs::File,
std::io::{BufReader, Read},
@@ -80,14 +81,78 @@
const DAEMON_CONNECTION_ISSUE: &str = "\
Timed out waiting on the Daemon.\nRun `ffx doctor` for further diagnostics";
-lazy_static! {
- static ref DAEMON_ONCE: Once<DaemonProxy> = Once::new();
+struct Injection {
+ daemon_once: Once<DaemonProxy>,
}
-// This could get called multiple times by the plugin system via multiple threads - so make sure
-// the spawning only happens one thread at a time.
-async fn get_daemon_proxy() -> Result<DaemonProxy> {
- DAEMON_ONCE.get_or_try_init(init_daemon_proxy()).await.map(|proxy| proxy.clone())
+impl Default for Injection {
+ fn default() -> Self {
+ Self { daemon_once: Once::new() }
+ }
+}
+
+#[async_trait]
+impl Injector for Injection {
+ // This could get called multiple times by the plugin system via multiple threads - so make sure
+ // the spawning only happens one thread at a time.
+ async fn daemon_factory(&self) -> Result<DaemonProxy> {
+ self.daemon_once.get_or_try_init(init_daemon_proxy()).await.map(|proxy| proxy.clone())
+ }
+
+ async fn fastboot_factory(&self) -> Result<FastbootProxy> {
+ let daemon_proxy = self.daemon_factory().await?;
+ let (fastboot_proxy, fastboot_server_end) = create_proxy::<FastbootMarker>()?;
+ let app: Ffx = argh::from_env();
+ let result = timeout(
+ proxy_timeout().await?,
+ daemon_proxy.get_fastboot(
+ app.target().await?.as_ref().map(|s| s.as_str()),
+ fastboot_server_end,
+ ),
+ )
+ .await
+ .context("timeout")?
+ .context("connecting to Fastboot")?;
+
+ match result {
+ Ok(_) => Ok(fastboot_proxy),
+ Err(DaemonError::NonFastbootDevice) => Err(ffx_error!(NON_FASTBOOT_MSG).into()),
+ Err(DaemonError::TargetAmbiguous) => Err(ffx_error!(TARGET_AMBIGUOUS_MSG).into()),
+ Err(DaemonError::TargetNotFound) => Err(ffx_error!(TARGET_NOT_FOUND_MSG).into()),
+ Err(DaemonError::TargetCacheError) => Err(ffx_error!(TARGET_FAILURE_MSG).into()),
+ Err(e) => Err(anyhow!("unexpected failure connecting to Fastboot: {:?}", e)),
+ }
+ }
+
+ async fn remote_factory(&self) -> Result<RemoteControlProxy> {
+ let daemon_proxy = self.daemon_factory().await?;
+ let (remote_proxy, remote_server_end) = create_proxy::<RemoteControlMarker>()?;
+ let app: Ffx = argh::from_env();
+
+ let result = timeout(
+ proxy_timeout().await?,
+ daemon_proxy.get_remote_control(
+ app.target().await?.as_ref().map(|s| s.as_str()),
+ remote_server_end,
+ ),
+ )
+ .await
+ .context("timeout")?
+ .context("connecting to target via daemon")?;
+
+ match result {
+ Ok(_) => Ok(remote_proxy),
+ Err(DaemonError::TargetAmbiguous) => Err(ffx_error!(TARGET_AMBIGUOUS_MSG).into()),
+ Err(DaemonError::TargetNotFound) => Err(ffx_error!(TARGET_NOT_FOUND_MSG).into()),
+ Err(DaemonError::TargetCacheError) => Err(ffx_error!(TARGET_FAILURE_MSG).into()),
+ Err(DaemonError::TargetInFastboot) => Err(ffx_error!(TARGET_IN_FASTBOOT).into()),
+ Err(e) => Err(anyhow!("unexpected failure connecting to RCS: {:?}", e)),
+ }
+ }
+
+ async fn is_experiment(&self, key: &str) -> bool {
+ ffx_config::get(key).await.unwrap_or(false)
+ }
}
async fn init_daemon_proxy() -> Result<DaemonProxy> {
@@ -185,59 +250,6 @@
}
}
-async fn get_fastboot_proxy() -> Result<FastbootProxy> {
- let daemon_proxy = get_daemon_proxy().await?;
- let (fastboot_proxy, fastboot_server_end) = create_proxy::<FastbootMarker>()?;
- let app: Ffx = argh::from_env();
- let result = timeout(
- proxy_timeout().await?,
- daemon_proxy
- .get_fastboot(app.target().await?.as_ref().map(|s| s.as_str()), fastboot_server_end),
- )
- .await
- .context("timeout")?
- .context("connecting to Fastboot")?;
-
- match result {
- Ok(_) => Ok(fastboot_proxy),
- Err(DaemonError::NonFastbootDevice) => Err(ffx_error!(NON_FASTBOOT_MSG).into()),
- Err(DaemonError::TargetAmbiguous) => Err(ffx_error!(TARGET_AMBIGUOUS_MSG).into()),
- Err(DaemonError::TargetNotFound) => Err(ffx_error!(TARGET_NOT_FOUND_MSG).into()),
- Err(DaemonError::TargetCacheError) => Err(ffx_error!(TARGET_FAILURE_MSG).into()),
- Err(e) => Err(anyhow!("unexpected failure connecting to Fastboot: {:?}", e)),
- }
-}
-
-async fn get_remote_proxy() -> Result<RemoteControlProxy> {
- let daemon_proxy = get_daemon_proxy().await?;
- let (remote_proxy, remote_server_end) = create_proxy::<RemoteControlMarker>()?;
- let app: Ffx = argh::from_env();
-
- let result = timeout(
- proxy_timeout().await?,
- daemon_proxy.get_remote_control(
- app.target().await?.as_ref().map(|s| s.as_str()),
- remote_server_end,
- ),
- )
- .await
- .context("timeout")?
- .context("connecting to target via daemon")?;
-
- match result {
- Ok(_) => Ok(remote_proxy),
- Err(DaemonError::TargetAmbiguous) => Err(ffx_error!(TARGET_AMBIGUOUS_MSG).into()),
- Err(DaemonError::TargetNotFound) => Err(ffx_error!(TARGET_NOT_FOUND_MSG).into()),
- Err(DaemonError::TargetCacheError) => Err(ffx_error!(TARGET_FAILURE_MSG).into()),
- Err(DaemonError::TargetInFastboot) => Err(ffx_error!(TARGET_IN_FASTBOOT).into()),
- Err(e) => Err(anyhow!("unexpected failure connecting to RCS: {:?}", e)),
- }
-}
-
-async fn is_experiment_subcommand_on(key: &'static str) -> bool {
- ffx_config::get(key).await.unwrap_or(false)
-}
-
fn is_daemon(subcommand: &Option<Subcommand>) -> bool {
if let Some(Subcommand::FfxDaemonPlugin(ffx_daemon_plugin_args::DaemonCommand {
subcommand: ffx_daemon_plugin_sub_command::Subcommand::FfxDaemonStart(_),
@@ -316,14 +328,7 @@
});
let command_start = Instant::now();
- let res = ffx_lib_suite::ffx_plugin_impl(
- get_daemon_proxy,
- get_remote_proxy,
- get_fastboot_proxy,
- is_experiment_subcommand_on,
- app,
- )
- .await;
+ let res = ffx_lib_suite::ffx_plugin_impl(Injection::default(), app).await;
let command_done = Instant::now();
log::info!("Command completed. Success: {}", res.is_ok());