[engine] now developers can write their superset command based on other commands through openfpga shell
This commit is contained in:
parent
cfedc547d9
commit
8d947c7bdb
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue