[engine] now developers can write their superset command based on other commands through openfpga shell

This commit is contained in:
tangxifan 2023-01-01 10:10:09 -08:00
parent cfedc547d9
commit 8d947c7bdb
3 changed files with 104 additions and 43 deletions

View File

@ -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<int(int, char**)> exec_func);
/* Wrapper function, which calls other command thru shell's APIs */
void set_command_execute_function(const ShellCommandId& cmd_id,
std::function<int(Shell<T>&, T&, const Command&, const CommandContext&)> exec_func);
void set_command_dependency(
const ShellCommandId& cmd_id,
const std::vector<ShellCommandId>& 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<ShellCommandId, std::function<int(int, char**)>>
command_macro_execute_functions_;
vtr::vector<ShellCommandId, std::function<int(Shell<T>&, 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 */

View File

@ -29,8 +29,6 @@ template<class T>
Shell<T>::Shell() {
name_ = std::string("shell_no_name");
time_start_ = 0;
COMMAND_NAME_SOURCE_ = "source";
COMMAND_NAME_EXEC_ = "exec";
}
/************************************************************************
@ -119,16 +117,6 @@ void Shell<T>::add_title(const char* title) {
/* Add a command with it description */
template<class T>
ShellCommandId Shell<T>::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<std::string, ShellCommandId>::const_iterator name_it = command_name2ids_.find(std::string(cmd.name()));
if (name_it != command_name2ids_.end()) {
@ -148,6 +136,7 @@ ShellCommandId Shell<T>::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<T>::set_command_execute_function(const ShellCommandId& cmd_id,
command_macro_execute_functions_[cmd_id] = exec_func;
}
template<class T>
void Shell<T>::set_command_execute_function(const ShellCommandId& cmd_id,
std::function<int(Shell<T>&, 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<class T>
void Shell<T>::set_command_dependency(const ShellCommandId& cmd_id,
const std::vector<ShellCommandId>& dependent_cmds) {
@ -488,35 +485,11 @@ void Shell<T>::exit(const int& init_err) const {
***********************************************************************/
template <class T>
int Shell<T>::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<std::string> 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<T>::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;

View File

@ -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<OpenfpgaContext>& 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<OpenfpgaContext>& shell,
const ShellCommandClassId& cmd_class_id,
const std::vector<ShellCommandId>& 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<OpenfpgaContext>& 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<OpenfpgaContext>& 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<ShellCommandId>());
}
} /* end namespace openfpga */