// This file is part of Substrate. // Copyright (C) 2017-2021 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. #![warn(missing_docs)] #![warn(unused_extern_crates)] #![warn(unused_imports)] pub mod arg_enums; mod commands; mod config; mod error; mod params; mod runner; pub use arg_enums::*; pub use commands::*; pub use config::*; pub use error::*; pub use params::*; pub use runner::*; pub use sc_service::{ChainSpec, Role}; use sc_service::{Configuration, TaskExecutor}; pub use sc_tracing::logging::LoggerBuilder; pub use sp_version::RuntimeVersion; use std::io::Write; pub use structopt; use structopt::{ clap::{self, AppSettings}, StructOpt, }; /// Substrate client CLI /// /// This trait needs to be defined on the root structopt 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. /// /// StructOpt must not be in scope to use from_args (or the similar methods). This trait provides /// its own implementation that will fill the necessary field based on the trait's functions. pub trait SubstrateCli: Sized { /// Implementation name. fn impl_name() -> String; /// Implementation version. /// /// By default this will look like this: 2.0.0-b950f731c-x86_64-linux-gnu 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 /// `structopt`'s `from_iter()` except that it takes a `VersionInfo` argument to provide the name of /// the application, author, "about" and version. It will also set `AppSettings::GlobalVersion`. /// /// To allow running the node without subcommand, tt also sets a few more settings: /// `AppSettings::ArgsNegateSubcommands` and `AppSettings::SubcommandsNegateReqs`. /// /// Gets the struct from the command line arguments. Print the /// error message and quit the program in case of failure. fn from_args() -> Self where Self: StructOpt + Sized, { ::from_iter(&mut std::env::args_os()) } /// Helper function used to parse the command line arguments. This is the equivalent of /// `structopt`'s `from_iter()` except that it takes a `VersionInfo` argument to provide the name of /// the application, author, "about" and version. It will also set `AppSettings::GlobalVersion`. /// /// To allow running the node without subcommand, it also sets a few more settings: /// `AppSettings::ArgsNegateSubcommands` and `AppSettings::SubcommandsNegateReqs`. /// /// Gets the struct from any iterator such as a `Vec` of your making. /// Print the error message and quit the program in case of failure. fn from_iter(iter: I) -> Self where Self: StructOpt + Sized, I: IntoIterator, I::Item: Into + Clone, { let app = ::clap(); let mut full_version = Self::impl_version(); full_version.push_str("\n"); let name = Self::executable_name(); let author = Self::author(); let about = Self::description(); let app = app .name(name) .author(author.as_str()) .about(about.as_str()) .version(full_version.as_str()) .settings(&[ AppSettings::GlobalVersion, AppSettings::ArgsNegateSubcommands, AppSettings::SubcommandsNegateReqs, AppSettings::ColoredHelp, ]); let matches = match app.get_matches_from_safe(iter) { Ok(matches) => matches, Err(mut e) => { // To support pipes, we can not use `writeln!` as any error // results in a "broken pipe" error. // // Instead we write directly to `stdout` and ignore any error // as we exit afterwards anyway. e.message.extend("\n".chars()); if e.use_stderr() { let _ = std::io::stderr().write_all(e.message.as_bytes()); std::process::exit(1); } else { let _ = std::io::stdout().write_all(e.message.as_bytes()); std::process::exit(0); } } }; ::from_clap(&matches) } /// Helper function used to parse the command line arguments. This is the equivalent of /// `structopt`'s `from_iter()` except that it takes a `VersionInfo` argument to provide the name of /// the application, author, "about" and version. It will also set `AppSettings::GlobalVersion`. /// /// To allow running the node without subcommand, it also sets a few more settings: /// `AppSettings::ArgsNegateSubcommands` and `AppSettings::SubcommandsNegateReqs`. /// /// Gets the struct from any iterator such as a `Vec` of your making. /// 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::ErrorKind::HelpDisplayed`] or [`clap::ErrorKind::VersionDisplayed`] respectively. /// You must call [`clap::Error::exit`] or perform a [`std::process::exit`]. fn try_from_iter(iter: I) -> clap::Result where Self: StructOpt + Sized, I: IntoIterator, I::Item: Into + Clone, { let app = ::clap(); let mut full_version = Self::impl_version(); full_version.push_str("\n"); let name = Self::executable_name(); let author = Self::author(); let about = Self::description(); let app = app .name(name) .author(author.as_str()) .about(about.as_str()) .version(full_version.as_str()); let matches = app.get_matches_from_safe(iter)?; Ok(::from_clap(&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, task_executor: TaskExecutor, ) -> error::Result { command.create_configuration(self, task_executor) } /// Create a runner for the command provided in argument. This will create a Configuration and /// a tokio runtime fn create_runner(&self, command: &T) -> error::Result> { command.init::()?; Runner::new(self, command) } /// Native runtime version. fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion; }