From 8d947c7bdbe4c89c2a50f81e6c6f5928e32d71cc Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 1 Jan 2023 10:10:09 -0800 Subject: [PATCH] [engine] now developers can write their superset command based on other commands through openfpga shell --- libs/libopenfpgashell/src/shell.h | 14 ++--- libs/libopenfpgashell/src/shell.tpp | 50 +++++------------ openfpga/src/base/basic_command.cpp | 83 +++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 43 deletions(-) diff --git a/libs/libopenfpgashell/src/shell.h b/libs/libopenfpgashell/src/shell.h index 242b3b732..3075a8b75 100644 --- a/libs/libopenfpgashell/src/shell.h +++ b/libs/libopenfpgashell/src/shell.h @@ -71,6 +71,7 @@ class Shell { SHORT, BUILTIN, MACRO, + WRAPPER, NUM_EXEC_FUNC_TYPES }; @@ -154,6 +155,10 @@ class Shell { void set_command_execute_function(const ShellCommandId& cmd_id, std::function exec_func); + /* Wrapper function, which calls other command thru shell's APIs */ + void set_command_execute_function(const ShellCommandId& cmd_id, + std::function&, T&, const Command&, const CommandContext&)> exec_func); + void set_command_dependency( const ShellCommandId& cmd_id, const std::vector& cmd_dependency); @@ -181,8 +186,7 @@ class Shell { /* Execute a command, the command line is the user's input to launch a command * The common_context is the data structure to exchange data between commands */ - int execute_command(const char* cmd_line, T& common_context, - const bool& batch_mode = false); + int execute_command(const char* cmd_line, T& common_context); private: /* Internal data */ /* Name of the shell, this will appear in the interactive mode */ @@ -236,6 +240,8 @@ class Shell { command_builtin_execute_functions_; vtr::vector> command_macro_execute_functions_; + vtr::vector&, T&, const Command&, const CommandContext&)>> + command_wrapper_execute_functions_; /* Type of execute functions for each command. * This is supposed to be an internal data ONLY @@ -260,10 +266,6 @@ class Shell { /* Timer */ std::clock_t time_start_; - - /* Constants */ - std::string COMMAND_NAME_SOURCE_; - std::string COMMAND_NAME_EXEC_; }; } /* End namespace openfpga */ diff --git a/libs/libopenfpgashell/src/shell.tpp b/libs/libopenfpgashell/src/shell.tpp index 5c166a05a..abec58c46 100644 --- a/libs/libopenfpgashell/src/shell.tpp +++ b/libs/libopenfpgashell/src/shell.tpp @@ -29,8 +29,6 @@ template Shell::Shell() { name_ = std::string("shell_no_name"); time_start_ = 0; - COMMAND_NAME_SOURCE_ = "source"; - COMMAND_NAME_EXEC_ = "exec"; } /************************************************************************ @@ -119,16 +117,6 @@ void Shell::add_title(const char* title) { /* Add a command with it description */ template ShellCommandId Shell::add_command(const Command& cmd, const char* descr) { - /* Not allow to add a command whose name conflicts with pre-defined ones */ - if (cmd.name() == COMMAND_NAME_SOURCE_) { - VTR_LOG_WARN("Not allow to overwrite a built-in command: '%s'!\n", COMMAND_NAME_SOURCE_.c_str()); - return ShellCommandId::INVALID(); - } - if (cmd.name() == COMMAND_NAME_EXEC_) { - VTR_LOG_WARN("Not allow to overwrite a built-in command: '%s'!\n", COMMAND_NAME_EXEC_.c_str()); - return ShellCommandId::INVALID(); - } - /* Ensure that the name is unique in the command list */ std::map::const_iterator name_it = command_name2ids_.find(std::string(cmd.name())); if (name_it != command_name2ids_.end()) { @@ -148,6 +136,7 @@ ShellCommandId Shell::add_command(const Command& cmd, const char* descr) { command_short_const_execute_functions_.emplace_back(); command_short_execute_functions_.emplace_back(); command_builtin_execute_functions_.emplace_back(); + command_wrapper_execute_functions_.emplace_back(); command_macro_execute_functions_.emplace_back(); command_status_.push_back(CMD_EXEC_NONE); /* By default, the command should be marked as fatal error as it has been never executed */ command_dependencies_.emplace_back(); @@ -219,6 +208,14 @@ void Shell::set_command_execute_function(const ShellCommandId& cmd_id, command_macro_execute_functions_[cmd_id] = exec_func; } +template +void Shell::set_command_execute_function(const ShellCommandId& cmd_id, + std::function&, T&, const Command&, const CommandContext&)> exec_func) { + VTR_ASSERT(true == valid_command_id(cmd_id)); + command_execute_function_types_[cmd_id] = WRAPPER; + command_wrapper_execute_functions_[cmd_id] = exec_func; +} + template void Shell::set_command_dependency(const ShellCommandId& cmd_id, const std::vector& dependent_cmds) { @@ -488,35 +485,11 @@ void Shell::exit(const int& init_err) const { ***********************************************************************/ template int Shell::execute_command(const char* cmd_line, - T& common_context, - const bool& batch_mode) { + T& common_context) { /* Tokenize the line */ openfpga::StringToken tokenizer(cmd_line); std::vector tokens = tokenizer.split(" "); - /* "source" command has a higher priority than regular ones*/ - if (tokens[0] == COMMAND_NAME_SOURCE_) { - /* Expect only two tokens, second is the script name */ - if (2 != tokens.size()) { - VTR_LOG("'%s' command only accepts 1 argument!\n", COMMAND_NAME_SOURCE_.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - run_script_mode(tokens[1].c_str(), common_context, batch_mode); - } - - /* "exec" command has a higher priority than regular ones */ - if (tokens[0] == COMMAND_NAME_EXEC_) { - /* Expect only two tokens, second is the script name */ - if (2 != tokens.size()) { - VTR_LOG("'%s' command only accepts 1 argument!\n", COMMAND_NAME_EXEC_.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - StringToken cmd_line_tokenizer(tokens[1]); - cmd_line_tokenizer.ltrim(std::string(" ")); - std::string token_cmd_line = cmd_line_tokenizer.data(); - execute_command(token_cmd_line.c_str(), common_context, batch_mode); - } - /* Find if the command name is valid */ ShellCommandId cmd_id = command(tokens[0]); if (ShellCommandId::INVALID() == cmd_id) { @@ -577,6 +550,9 @@ int Shell::execute_command(const char* cmd_line, /* Execute the command depending on the type of function ! */ switch (command_execute_function_types_[cmd_id]) { + case WRAPPER: + command_status_[cmd_id] = command_wrapper_execute_functions_[cmd_id](*this, common_context, commands_[cmd_id], command_contexts_[cmd_id]); + break; case CONST_STANDARD: command_status_[cmd_id] = command_const_execute_functions_[cmd_id](common_context, commands_[cmd_id], command_contexts_[cmd_id]); break; diff --git a/openfpga/src/base/basic_command.cpp b/openfpga/src/base/basic_command.cpp index aa7f0d303..14d88fbcc 100644 --- a/openfpga/src/base/basic_command.cpp +++ b/openfpga/src/base/basic_command.cpp @@ -7,10 +7,88 @@ #include "basic_command.h" #include "openfpga_title.h" +#include "command_exit_codes.h" /* begin namespace openfpga */ namespace openfpga { +static int source_existing_command(Shell& shell, OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { + CommandOptionId opt_file = cmd.option("from_file"); + CommandOptionId opt_batch_mode = cmd.option("batch_mode"); + CommandOptionId opt_ss = cmd.option("command_stream"); + + bool is_cmd_file = cmd_context.option_enable(cmd, opt_file); + std::string cmd_ss = cmd_context.option_value(cmd, opt_ss); + + int status = CMD_EXEC_SUCCESS; + + /* If a file is specified, run script mode of the shell, otherwise, */ + if (is_cmd_file) { + shell.run_script_mode(cmd_ss.c_str(), openfpga_ctx, cmd_context.option_enable(cmd, opt_batch_mode)); + } else { + /* Split the string with ';' and run each command */ + /* Remove the space at the end of the line + * So that we can check easily if there is a continued line in the end + */ + StringToken cmd_ss_tokenizer(cmd_ss); + + for (std::string cmd_part : cmd_ss_tokenizer.split(";")) { + StringToken cmd_part_tokenizer(cmd_part); + cmd_part_tokenizer.rtrim(std::string(" ")); + std::string single_cmd_line = cmd_part_tokenizer.data(); + + if (!single_cmd_line.empty()) { + status = shell.execute_command(single_cmd_line.c_str(), openfpga_ctx); + + /* Check the execution status of the command, + * if fatal error happened, we should abort immediately + */ + if (CMD_EXEC_FATAL_ERROR == status) { + return CMD_EXEC_FATAL_ERROR; + } + } + } + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * - Add a command to Shell environment: repack + * - Add associated options + * - Add command dependency + *******************************************************************/ +static ShellCommandId add_openfpga_source_command( + openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("source"); + + /* Add an option '--command_stream' */ + CommandOptionId opt_cmdstream = shell_cmd.add_option( + "command_stream", true, "A string/file stream which contains the commands to be executed"); + shell_cmd.set_option_require_value(opt_cmdstream, openfpga::OPT_STRING); + + /* Add an option '--from_file' */ + shell_cmd.add_option("from_file", false, "Specify the command stream comes from a file"); + + /* Add an option '--batch_mode' */ + shell_cmd.add_option("batch_mode", false, "Enable batch mode when executing the script from a file (not a string)"); + + /* Add command 'repack' to the Shell */ + ShellCommandId shell_cmd_id = + shell.add_command(shell_cmd, "Source a string of commands or execute a script from a file"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, + source_existing_command); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + + void add_basic_commands(openfpga::Shell& shell) { /* Add a new class of commands */ ShellCommandClassId basic_cmd_class = shell.add_command_class("Basic"); @@ -40,6 +118,11 @@ void add_basic_commands(openfpga::Shell& shell) { shell.set_command_class(shell_cmd_help_id, basic_cmd_class); shell.set_command_execute_function(shell_cmd_help_id, [shell]() { shell.print_commands(); }); + + /* Add 'source' command which can run a set of commands */ + add_openfpga_source_command(shell, basic_cmd_class, + std::vector()); + } } /* end namespace openfpga */