diff --git a/libopenfpga/libopenfpgashell/CMakeLists.txt b/libopenfpga/libopenfpgashell/CMakeLists.txt index 2ee34a0ed..3f24c0a21 100644 --- a/libopenfpga/libopenfpgashell/CMakeLists.txt +++ b/libopenfpga/libopenfpgashell/CMakeLists.txt @@ -2,13 +2,18 @@ cmake_minimum_required(VERSION 3.9) project("libopenfpgashell") -file(GLOB_RECURSE EXEC_SOURCES test/test_command_parser.cpp) +# We need readline to compile +find_package(Readline REQUIRED) + +file(GLOB_RECURSE EXEC_TEST_SHELL test/test_shell.cpp) +file(GLOB_RECURSE EXEC_TEST_CMD test/test_command_parser.cpp) file(GLOB_RECURSE LIB_SOURCES src/*.cpp) file(GLOB_RECURSE LIB_HEADERS src/*.h) files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) #Remove test executable from library -list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES}) +list(REMOVE_ITEM LIB_SOURCES ${EXEC_TEST_SHELL}) +list(REMOVE_ITEM LIB_SOURCES ${EXEC_TEST_CMD}) #Create the library add_library(libopenfpgashell STATIC @@ -20,10 +25,14 @@ set_target_properties(libopenfpgashell PROPERTIES PREFIX "") #Avoid extra 'lib' #Specify link-time dependancies target_link_libraries(libopenfpgashell libopenfpgautil - libvtrutil) + libvtrutil + readline) #Create the test executable -add_executable(test_command_parser ${EXEC_SOURCES}) +add_executable(test_shell ${EXEC_TEST_SHELL}) +target_link_libraries(test_shell libopenfpgashell) + +add_executable(test_command_parser ${EXEC_TEST_CMD}) target_link_libraries(test_command_parser libopenfpgashell) #Supress IPO link warnings if IPO is enabled diff --git a/libopenfpga/libopenfpgashell/src/shell.h b/libopenfpga/libopenfpgashell/src/shell.h index 2301b0ed2..e1b910aef 100644 --- a/libopenfpga/libopenfpgashell/src/shell.h +++ b/libopenfpga/libopenfpgashell/src/shell.h @@ -68,11 +68,11 @@ class Shell { const CommandContext& command_context(const ShellCommandId& cmd_id) const; std::vector command_dependency(const ShellCommandId& cmd_id) const; public: /* Public mutators */ - void set_title(const char* title); + void add_title(const char* title); ShellCommandId add_command(const Command& cmd, const char* descr); - void add_command_execute_function(const ShellCommandId& cmd_id, - std::function exec_func); - void add_command_dependency(const ShellCommandId& cmd_id, + void set_command_execute_function(const ShellCommandId& cmd_id, + std::function exec_func); + void set_command_dependency(const ShellCommandId& cmd_id, const std::vector cmd_dependency); public: /* Public validators */ bool valid_command_id(const ShellCommandId& cmd_id) const; @@ -106,7 +106,7 @@ class Shell { vtr::vector command_description_; /* Function pointers to execute each command */ - vtr::vector> command_execute_functions_; + vtr::vector> command_execute_functions_; /* Dependency graph for different commands, * This helps the shell interface to check if a command need other commands to be run before its execution @@ -119,4 +119,7 @@ class Shell { } /* End namespace minshell */ +/* Include the implementation functions in the header file */ +#include "shell.tpp" + #endif diff --git a/libopenfpga/libopenfpgashell/src/shell.cpp b/libopenfpga/libopenfpgashell/src/shell.tpp similarity index 94% rename from libopenfpga/libopenfpgashell/src/shell.cpp rename to libopenfpga/libopenfpgashell/src/shell.tpp index 39e03d064..a989f5bc2 100644 --- a/libopenfpga/libopenfpgashell/src/shell.cpp +++ b/libopenfpga/libopenfpgashell/src/shell.tpp @@ -17,7 +17,6 @@ /* Headers from openfpgashell library */ #include "command_parser.h" #include "command_echo.h" -#include "shell.h" /* Begin namespace minishell */ namespace minishell { @@ -86,7 +85,7 @@ std::vector Shell::command_dependency(const ShellCommandId& c * Public mutators ***********************************************************************/ template -void Shell::set_title(const char* title) { +void Shell::add_title(const char* title) { title_ = std::string(title); } @@ -109,20 +108,20 @@ ShellCommandId Shell::add_command(const Command& cmd, const char* descr) { command_dependencies_.emplace_back(); /* Register the name in the name2id map */ - command_name2ids_[std::string(name)] = cmd.name(); + command_name2ids_[cmd.name()] = shell_cmd; return shell_cmd; } template -void Shell::add_command_execute_function(const ShellCommandId& cmd_id, - std::function exec_func) { +void Shell::set_command_execute_function(const ShellCommandId& cmd_id, + std::function exec_func) { VTR_ASSERT(true == valid_command_id(cmd_id)); command_execute_functions_[cmd_id] = exec_func; } template -void Shell::add_command_dependency(const ShellCommandId& cmd_id, +void Shell::set_command_dependency(const ShellCommandId& cmd_id, const std::vector dependent_cmds) { /* Validate the command id as well as each of the command dependency */ VTR_ASSERT(true == valid_command_id(cmd_id)); @@ -142,7 +141,7 @@ void Shell::run_interactive_mode(T& context) { /* Print the title of the shell */ if (!title().empty()) { - VTR_LOG("%s\n", title()); + VTR_LOG("%s\n", title().c_str()); } /* Wait for users input and execute the command */ @@ -169,7 +168,7 @@ void Shell::run_script_mode(const char* script_file_name, T& context) { /* Print the title of the shell */ if (!title().empty()) { - VTR_LOG("%s\n", title()); + VTR_LOG("%s\n", title().c_str()); } std::string line; @@ -232,7 +231,7 @@ void Shell::execute_command(const char* cmd_line, print_command_context(commands_[cmd_id], command_contexts_[cmd_id]); /* Execute the command! */ - command_execute_functions_[cmd_id](common_context, command_contexts_[cmd_id]); + command_execute_functions_[cmd_id](common_context, commands_[cmd_id], command_contexts_[cmd_id]); } /************************************************************************ diff --git a/libopenfpga/libopenfpgashell/test/test_shell.cpp b/libopenfpga/libopenfpgashell/test/test_shell.cpp new file mode 100644 index 000000000..ee2d3fbd9 --- /dev/null +++ b/libopenfpga/libopenfpgashell/test/test_shell.cpp @@ -0,0 +1,94 @@ +/******************************************************************** + * Test the shell interface by pre-defining simple commands + * like exit() and help() + *******************************************************************/ +#include "vtr_log.h" +#include "command_parser.h" +#include "command_echo.h" +#include "shell.h" + +using namespace minishell; + +class ShellContext { +}; + +static +void shell_cmd_help_executor(ShellContext& context, + const Command& cmd, + const CommandContext& cmd_context) { + VTR_LOG("Help desk:\n"); + VTR_LOG("Available commands:\n"); + VTR_LOG("help\texit\n"); +} + +static +void shell_cmd_exit_executor(ShellContext& context, + const Command& cmd, + const CommandContext& cmd_context) { + VTR_LOG("Thank you for using!\n"); + exit(1); +} + +int main(int argc, char** argv) { + /* Create the command to launch shell in different modes */ + Command start_cmd("test_shell"); + /* Add two options: + * '--interactive', -i': launch the interactive mode + * '--file', -f': launch the script mode + */ + CommandOptionId opt_interactive = start_cmd.add_option("interactive", false, "Launch the shell in interactive mode"); + start_cmd.set_option_short_name(opt_interactive, "i"); + + CommandOptionId opt_script_mode = start_cmd.add_option("file", false, "Launch the shell in script mode"); + start_cmd.set_option_require_value(opt_script_mode, OPT_STRING); + start_cmd.set_option_short_name(opt_script_mode, "f"); + + CommandOptionId opt_help = start_cmd.add_option("help", false, "Help desk"); + start_cmd.set_option_short_name(opt_help, "h"); + + /* Create a shell object + * Add two commands, which are + * 1. help + * 2. exit + */ + Shell shell("test_shell"); + shell.add_title("This is a simple test shell\nAuthor: Xifan Tang\n"); + + Command shell_cmd_help("help"); + ShellCommandId shell_cmd_help_id = shell.add_command(shell_cmd_help, "Launch help desk"); + shell.set_command_execute_function(shell_cmd_help_id, shell_cmd_help_executor); + + Command shell_cmd_exit("exit"); + ShellCommandId shell_cmd_exit_id = shell.add_command(shell_cmd_exit, "Exit the shell"); + shell.set_command_execute_function(shell_cmd_exit_id, shell_cmd_exit_executor); + + /* Create the data base for the shell */ + ShellContext shell_context; + + /* Parse the option, to avoid issues, we use the command name to replace the argv[0] */ + std::vector cmd_opts; + cmd_opts.push_back(start_cmd.name()); + for (int iarg = 1; iarg < argc; ++iarg) { + cmd_opts.push_back(std::string(argv[iarg])); + } + + CommandContext start_cmd_context(start_cmd); + if (false == parse_command(cmd_opts, start_cmd, start_cmd_context)) { + /* Parse fail: Echo the command */ + print_command_options(start_cmd); + } else { + /* Parse succeed. Start a shell */ + if (true == start_cmd_context.option_enable(start_cmd, opt_interactive)) { + shell.run_interactive_mode(shell_context); + return 0; + } + + 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(), + shell_context); + return 0; + } + } + + return 0; +}