diff --git a/docs/source/manual/openfpga_shell/launch_openfpga_shell.rst b/docs/source/manual/openfpga_shell/launch_openfpga_shell.rst index 3ef6e9fb5..f03819fe6 100644 --- a/docs/source/manual/openfpga_shell/launch_openfpga_shell.rst +++ b/docs/source/manual/openfpga_shell/launch_openfpga_shell.rst @@ -15,6 +15,13 @@ To launch OpenFPGA shell, users can choose two modes. Launch OpenFPGA in script mode where users write commands in scripts and FPGA will execute them +.. option:: --batch_execution or -batch + + Execute OpenFPGA script in batch mode. This option is only valid for script mode. + + - If in batch mode, OpenFPGA will abort immediately when fatal errors occurred. + - If not in batch mode, OpenFPGA will enter interactive mode when fatal errors occurred. + .. option:: --help or -h Show the help desk diff --git a/libopenfpga/libopenfpgashell/src/shell.h b/libopenfpga/libopenfpgashell/src/shell.h index 67dd4356d..7f8e3a142 100644 --- a/libopenfpga/libopenfpgashell/src/shell.h +++ b/libopenfpga/libopenfpgashell/src/shell.h @@ -143,11 +143,17 @@ class Shell { /* Start the interactive mode, where users will type-in command by command */ void run_interactive_mode(T& context, const bool& quiet_mode = false); /* Start the script mode, where users provide a file which includes all the commands to run */ - void run_script_mode(const char* script_file_name, T& context); + void run_script_mode(const char* script_file_name, + T& context, + const bool& batch_mode = false); /* Print all the commands by their classes. This is actually the help desk */ void print_commands() const; + /* Find the exit code (assume quit shell now) */ + int exit_code() const; + /* Show statistics of errors during command execution */ + int execution_errors() const; /* Quit the shell */ - void exit() const; + void exit(const int& init_err = 0) const; private: /* Private executors */ /* 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 diff --git a/libopenfpga/libopenfpgashell/src/shell.tpp b/libopenfpga/libopenfpgashell/src/shell.tpp index 7c0ff7ce6..3b608f135 100644 --- a/libopenfpga/libopenfpgashell/src/shell.tpp +++ b/libopenfpga/libopenfpgashell/src/shell.tpp @@ -272,7 +272,9 @@ void Shell::run_interactive_mode(T& context, const bool& quiet_mode) { } template -void Shell::run_script_mode(const char* script_file_name, T& context) { +void Shell::run_script_mode(const char* script_file_name, + T& context, + const bool& batch_mode) { time_start_ = std::clock(); @@ -356,17 +358,28 @@ void Shell::run_script_mode(const char* script_file_name, T& context) { /* Empty the line ready to start a new line */ cmd_line.clear(); - /* Check the execution status of the command, if fatal error happened, we should abort immediately */ + /* Check the execution status of the command, + * if fatal error happened, we should abort immediately + */ if (CMD_EXEC_FATAL_ERROR == status) { - VTR_LOG("Fatal error occurred!\nAbort and enter interactive mode\n"); + VTR_LOG("Fatal error occurred!\n"); + /* If in the batch mode, we will exit with errors */ + VTR_LOGV(batch_mode, "OpenFPGA Abort\n"); + if (batch_mode) { + exit(CMD_EXEC_FATAL_ERROR); + } + /* If not in the batch mode, we will got to interactive mode */ + VTR_LOGV(!batch_mode, "Enter interactive mode\n"); break; } } } fp.close(); - /* Return to interactive mode, stay tuned */ - run_interactive_mode(context, true); + /* If not in batch mode, switch to interactive mode, stay tuned */ + if (!batch_mode) { + run_interactive_mode(context, true); + } } template @@ -392,7 +405,7 @@ void Shell::print_commands() const { } template -void Shell::exit() const { +int Shell::exit_code() const { /* Check all the command status, if we see fatal errors or minor errors, we drop an error code */ int exit_code = 0; for (const int& status : command_status_) { @@ -403,23 +416,41 @@ void Shell::exit() const { } } + return exit_code; +} + +template +int Shell::execution_errors() const { /* Show error message if we detect any errors */ int num_err = 0; - if (0 != exit_code) { - VTR_LOG("\n"); - for (const ShellCommandId& cmd : commands()) { - if (command_status_[cmd] == CMD_EXEC_FATAL_ERROR) { - VTR_LOG_ERROR("Command '%s' execution has fatal errors\n", - commands_[cmd].name().c_str()); - num_err++; - } - - if (command_status_[cmd] == CMD_EXEC_MINOR_ERROR) { - VTR_LOG_ERROR("Command '%s' execution has minor errors\n", - commands_[cmd].name().c_str()); - num_err++; - } + + for (const ShellCommandId& cmd : commands()) { + if (command_status_[cmd] == CMD_EXEC_FATAL_ERROR) { + VTR_LOG_ERROR("Command '%s' execution has fatal errors\n", + commands_[cmd].name().c_str()); + num_err++; } + + if (command_status_[cmd] == CMD_EXEC_MINOR_ERROR) { + VTR_LOG_ERROR("Command '%s' execution has minor errors\n", + commands_[cmd].name().c_str()); + num_err++; + } + } + + return num_err; +} + +template +void Shell::exit(const int& init_err) const { + /* Check all the command status, if we see fatal errors or minor errors, we drop an error code */ + int shell_exit_code = exit_code() | init_err; + + /* Show error message if we detect any errors */ + int num_err = init_err; + if (CMD_EXEC_SUCCESS != shell_exit_code) { + VTR_LOG("\n"); + num_err += execution_errors(); } VTR_LOG("\nFinish execution with %d errors\n", @@ -431,7 +462,7 @@ void Shell::exit() const { VTR_LOG("\nThank you for using %s!\n", name().c_str()); - std::exit(exit_code); + std::exit(shell_exit_code); } /************************************************************************ @@ -460,6 +491,7 @@ int Shell::execute_command(const char* cmd_line, commands_[dep_cmd].name().c_str(), commands_[cmd_id].name().c_str()); /* Echo the command help desk */ print_command_options(commands_[cmd_id]); + command_status_[cmd_id] = CMD_EXEC_FATAL_ERROR; return CMD_EXEC_FATAL_ERROR; } } @@ -494,6 +526,7 @@ int Shell::execute_command(const char* cmd_line, if (false == parse_command(tokens, commands_[cmd_id], command_contexts_[cmd_id])) { /* Echo the command */ print_command_options(commands_[cmd_id]); + command_status_[cmd_id] = CMD_EXEC_FATAL_ERROR; return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/main.cpp b/openfpga/src/main.cpp index d19b35944..3c2372c2a 100644 --- a/openfpga/src/main.cpp +++ b/openfpga/src/main.cpp @@ -29,9 +29,10 @@ int main(int argc, char** argv) { /* Create the command to launch shell in different modes */ openfpga::Command start_cmd("OpenFPGA"); - /* Add two options: + /* Add options: * '--interactive', -i': launch the interactive mode * '--file', -f': launch the script mode + * '--batch_execution': execute the script in batch mode. Will exit immediately when fatal errors occurred */ openfpga::CommandOptionId opt_interactive = start_cmd.add_option("interactive", false, "Launch OpenFPGA in interactive mode"); start_cmd.set_option_short_name(opt_interactive, "i"); @@ -40,6 +41,9 @@ int main(int argc, char** argv) { start_cmd.set_option_require_value(opt_script_mode, openfpga::OPT_STRING); start_cmd.set_option_short_name(opt_script_mode, "f"); + openfpga::CommandOptionId opt_batch_exec = start_cmd.add_option("batch_execution", false, "Launch OpenFPGA in batch mode when running scripts"); + start_cmd.set_option_short_name(opt_batch_exec, "batch"); + openfpga::CommandOptionId opt_help = start_cmd.add_option("help", false, "Help desk"); start_cmd.set_option_short_name(opt_help, "h"); @@ -95,17 +99,21 @@ int main(int argc, char** argv) { if (true == start_cmd_context.option_enable(start_cmd, opt_interactive)) { shell.run_interactive_mode(openfpga_context); - return 0; + return shell.exit_code(); } if (true == start_cmd_context.option_enable(start_cmd, opt_script_mode)) { shell.run_script_mode(start_cmd_context.option_value(start_cmd, opt_script_mode).c_str(), - openfpga_context); - return 0; + openfpga_context, + start_cmd_context.option_enable(start_cmd, opt_batch_exec)); + return shell.exit_code(); } /* Reach here there is something wrong, show the help desk */ openfpga::print_command_options(start_cmd); } - return 0; + /* Reach here, it means shell execution has critical errors. + * Return a code with fatal errors + */ + return 1; }