// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Substrate CLI library. //! //! To see a full list of commands available, see [`commands`]. #![warn(missing_docs)] #![warn(unused_extern_crates)] #![warn(unused_imports)] use clap::{CommandFactory, FromArgMatches, Parser}; use sc_service::Configuration; pub mod arg_enums; pub mod commands; mod config; mod error; mod params; mod runner; mod signals; pub use arg_enums::*; pub use clap; pub use commands::*; pub use config::*; pub use error::*; pub use params::*; pub use runner::*; pub use sc_service::{ChainSpec, Role}; pub use sc_tracing::logging::LoggerBuilder; pub use signals::Signals; pub use sp_version::RuntimeVersion; /// Substrate client CLI /// /// This trait needs to be implemented on the root CLI struct of the application. It will provide /// the implementation `name`, `version`, `executable name`, `description`, `author`, `support_url`, /// `copyright start year` and most importantly: how to load the chain spec. pub trait SubstrateCli: Sized { /// Implementation name. fn impl_name() -> String; /// Implementation version. /// /// By default this will look like this: /// /// `2.0.0-b950f731c` /// /// Where the hash is the short commit hash of the commit of in the Git repository. fn impl_version() -> String; /// Executable file name. /// /// Extracts the file name from `std::env::current_exe()`. /// Resorts to the env var `CARGO_PKG_NAME` in case of Error. fn executable_name() -> String { std::env::current_exe() .ok() .and_then(|e| e.file_name().map(|s| s.to_os_string())) .and_then(|w| w.into_string().ok()) .unwrap_or_else(|| env!("CARGO_PKG_NAME").into()) } /// Executable file description. fn description() -> String; /// Executable file author. fn author() -> String; /// Support URL. fn support_url() -> String; /// Copyright starting year (x-current year) fn copyright_start_year() -> i32; /// Chain spec factory fn load_spec(&self, id: &str) -> std::result::Result, String>; /// Helper function used to parse the command line arguments. This is the equivalent of /// [`clap::Parser::parse()`]. /// /// To allow running the node without subcommand, it also sets a few more settings: /// [`clap::Command::propagate_version`], [`clap::Command::args_conflicts_with_subcommands`], /// [`clap::Command::subcommand_negates_reqs`]. /// /// Creates `Self` from the command line arguments. Print the /// error message and quit the program in case of failure. fn from_args() -> Self where Self: Parser + Sized, { ::from_iter(&mut std::env::args_os()) } /// Helper function used to parse the command line arguments. This is the equivalent of /// [`clap::Parser::parse_from`]. /// /// To allow running the node without subcommand, it also sets a few more settings: /// [`clap::Command::propagate_version`], [`clap::Command::args_conflicts_with_subcommands`], /// [`clap::Command::subcommand_negates_reqs`]. /// /// Creates `Self` from any iterator over arguments. /// Print the error message and quit the program in case of failure. fn from_iter(iter: I) -> Self where Self: Parser + Sized, I: IntoIterator, I::Item: Into + Clone, { let app = ::command(); let mut full_version = Self::impl_version(); full_version.push('\n'); let name = Self::executable_name(); let author = Self::author(); let about = Self::description(); let app = app .name(name) .author(author) .about(about) .version(full_version) .propagate_version(true) .args_conflicts_with_subcommands(true) .subcommand_negates_reqs(true); let matches = app.try_get_matches_from(iter).unwrap_or_else(|e| e.exit()); ::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()) } /// Helper function used to parse the command line arguments. This is the equivalent of /// [`clap::Parser::try_parse_from`] /// /// To allow running the node without subcommand, it also sets a few more settings: /// [`clap::Command::propagate_version`], [`clap::Command::args_conflicts_with_subcommands`], /// [`clap::Command::subcommand_negates_reqs`]. /// /// Creates `Self` from any iterator over arguments. /// Print the error message and quit the program in case of failure. /// /// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are /// used. It will return a [`clap::Error`], where the [`clap::Error::kind`] is a /// [`clap::error::ErrorKind::DisplayHelp`] or [`clap::error::ErrorKind::DisplayVersion`] /// respectively. You must call [`clap::Error::exit`] or perform a [`std::process::exit`]. fn try_from_iter(iter: I) -> clap::error::Result where Self: Parser + Sized, I: IntoIterator, I::Item: Into + Clone, { let app = ::command(); let mut full_version = Self::impl_version(); full_version.push('\n'); let name = Self::executable_name(); let author = Self::author(); let about = Self::description(); let app = app.name(name).author(author).about(about).version(full_version); let matches = app.try_get_matches_from(iter)?; ::from_arg_matches(&matches) } /// Returns the client ID: `{impl_name}/v{impl_version}` fn client_id() -> String { format!("{}/v{}", Self::impl_name(), Self::impl_version()) } /// Only create a Configuration for the command provided in argument fn create_configuration, DVC: DefaultConfigurationValues>( &self, command: &T, tokio_handle: tokio::runtime::Handle, ) -> error::Result { command.create_configuration(self, tokio_handle) } /// Create a runner for the command provided in argument. This will create a Configuration and /// a tokio runtime fn create_runner, DVC: DefaultConfigurationValues>( &self, command: &T, ) -> error::Result> { let tokio_runtime = build_runtime()?; // `capture` needs to be called in a tokio context. // Also capture them as early as possible. let signals = tokio_runtime.block_on(async { Signals::capture() })?; let config = command.create_configuration(self, tokio_runtime.handle().clone())?; command.init(&Self::support_url(), &Self::impl_version(), |_, _| {}, &config)?; Runner::new(config, tokio_runtime, signals) } /// Create a runner for the command provided in argument. The `logger_hook` can be used to setup /// a custom profiler or update the logger configuration before it is initialized. /// /// Example: /// ``` /// use sc_tracing::{SpanDatum, TraceEvent}; /// struct TestProfiler; /// /// impl sc_tracing::TraceHandler for TestProfiler { /// fn handle_span(&self, sd: &SpanDatum) {} /// fn handle_event(&self, _event: &TraceEvent) {} /// }; /// /// fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration) -> () { /// |logger_builder, config| { /// logger_builder.with_custom_profiling(Box::new(TestProfiler{})); /// } /// } /// ``` fn create_runner_with_logger_hook( &self, command: &T, logger_hook: F, ) -> error::Result> where F: FnOnce(&mut LoggerBuilder, &Configuration), { let tokio_runtime = build_runtime()?; // `capture` needs to be called in a tokio context. // Also capture them as early as possible. let signals = tokio_runtime.block_on(async { Signals::capture() })?; let config = command.create_configuration(self, tokio_runtime.handle().clone())?; command.init(&Self::support_url(), &Self::impl_version(), logger_hook, &config)?; Runner::new(config, tokio_runtime, signals) } }