Skip to content

Commit

Permalink
Merge pull request #492 from metacall/fix/cli-full-refactor
Browse files Browse the repository at this point in the history
Fix/cli full refactor
  • Loading branch information
viferga authored Mar 1, 2024
2 parents c785106 + 0ca3ba7 commit e8a3cf0
Show file tree
Hide file tree
Showing 30 changed files with 687 additions and 170 deletions.
30 changes: 23 additions & 7 deletions source/cli/metacallcli/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Check if this loader is enabled
if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE)
message(WARNING "The Extension and NodeJS loaders are a dependency of the CLI, in order to compile the CLI, enable them with -DOPTION_BUILD_LOADERS_EXT=ON -DOPTION_BUILD_LOADERS_NODE=ON")
return()
endif()

#
# Executable name and options
#
Expand Down Expand Up @@ -149,23 +155,33 @@ target_link_libraries(${target}
#

add_loader_dependencies(${target}
node_loader
py_loader
rb_loader
cs_loader
jsm_loader
js_loader
mock_loader
c_loader
cob_loader
cs_loader
ext_loader
file_loader
java_loader
mock_loader
py_loader
rb_loader
rs_loader
rpc_loader
ts_loader
wasm_loader
)

add_dependencies(${target}
node_loader
cli_repl_plugin
cli_core_plugin
)

if(TARGET cli_cmd_plugin)
add_dependencies(${target}
cli_cmd_plugin
)
endif()

#
# Deployment
#
Expand Down
18 changes: 15 additions & 3 deletions source/cli/metacallcli/include/metacallcli/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ class application
*/
void repl();

/**
* @brief
* Initialize the CMD
*
* @param[in] arguments
* Vector of strings containing all the arguments from argv
*
* @return
* Return true if the load was successful, false otherwise
*/
bool cmd(std::vector<std::string> &arguments);

/**
* @brief
* Fallback argument parser
Expand Down Expand Up @@ -118,9 +130,9 @@ class application
private:
/* -- Private Member Data -- */

void *plugin_cli_handle; /**< Handle containing all loaded plugins for CLI */
void *plugin_repl_handle; /**< Handle containing all loaded plugins for REPL */
std::vector<std::string> arguments; /**< Vector containing a list of arguments */
void *plugin_cli_handle; /**< Handle containing all loaded plugins for CLI */
void *plugin_repl_handle; /**< Handle containing all loaded plugins for REPL */
void *plugin_cmd_handle; /**< Handle containing all loaded plugins for CMD */
};

} /* namespace metacallcli */
Expand Down
119 changes: 89 additions & 30 deletions source/cli/metacallcli/source/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ static bool exit_condition = true;

void application::repl()
{
/* Initialize CLI plugin */
if (!load_path("cli", &plugin_cli_handle))
/* Initialize REPL plugins */
if (!load_path("repl", &plugin_repl_handle))
{
/* Do not enter into the main loop */
exit_condition = true;
Expand All @@ -67,7 +67,7 @@ void application::repl()
return NULL;
};

int result = metacall_register_loaderv(metacall_loader("ext"), plugin_cli_handle, "exit", exit, METACALL_INVALID, 0, NULL);
int result = metacall_register_loaderv(metacall_loader("ext"), plugin_repl_handle, "exit", exit, METACALL_INVALID, 0, NULL);

if (result != 0)
{
Expand All @@ -79,13 +79,86 @@ void application::repl()
exit_condition = false;
}

void *ret = metacallhv_s(plugin_repl_handle, "initialize", metacall_null_args, 0);
std::string plugin_path(metacall_plugin_path());

void *args[] = {
metacall_value_create_string(plugin_path.c_str(), plugin_path.length())
};

void *ret = metacallhv_s(plugin_cli_handle, "repl_initialize", args, sizeof(args) / sizeof(args[0]));

metacall_value_destroy(args[0]);

check_for_exception(ret);
}

bool application::cmd(std::vector<std::string> &arguments)
{
/* Get the command parsing function */
void *command_parse_func = metacall_handle_function(plugin_cli_handle, "command_parse");

if (command_parse_func == NULL)
{
return false;
}

/* Initialize CMD plugins */
if (!load_path("cmd", &plugin_cmd_handle))
{
return false;
}

/* Convert all arguments into metacall value strings */
std::vector<void *> arguments_values;
arguments_values.reserve(arguments.size());

for (const std::string &str_argument : arguments)
{
arguments_values.push_back(metacall_value_create_string(str_argument.c_str(), str_argument.length()));
}

/* Parse the arguments with the CMD plugin command parse function */
void *ret = metacallhv_s(plugin_cmd_handle, "command_parse", arguments_values.data(), arguments_values.size());

/* Destroy all the command parse values */
for (void *value_argument : arguments_values)
{
metacall_value_destroy(value_argument);
}

/* Check for correct result of command parse */
if (metacall_value_id(ret) != METACALL_ARRAY)
{
check_for_exception(ret);
return false;
}

/* Get the argument map and the positional array */
void **ret_array = metacall_value_to_array(ret);
void **command_map = metacall_value_to_map(ret_array[0]);
size_t command_size = metacall_value_count(ret_array[0]);
void **positional_array = metacall_value_to_map(ret_array[1]);
size_t positional_size = metacall_value_count(ret_array[1]);

/* Execute the positional arguments */
/* TODO: ... */

/* Note: If it has zero positional arguments, we should also run the repl, for example:
* $ metacall --some-option --another-option
*/
if (positional_size == 0)
{
/* Initialize the REPL */
repl();
}

metacall_value_destroy(ret);

return true;
}

application::application(int argc, char *argv[]) :
plugin_cli_handle(NULL), plugin_repl_handle(NULL)
plugin_cli_handle(NULL), plugin_repl_handle(NULL), plugin_cmd_handle(NULL)
{
/* Initialize MetaCall */
if (metacall_initialize() != 0)
Expand All @@ -100,8 +173,8 @@ application::application(int argc, char *argv[]) :
/* Print MetaCall information */
metacall_print_info();

/* Initialize REPL plugin */
if (!load_path("repl", &plugin_repl_handle))
/* Initialize CLI internal plugins */
if (!load_path("internal", &plugin_cli_handle))
{
/* Do not enter into the main loop */
exit_condition = true;
Expand All @@ -115,9 +188,10 @@ application::application(int argc, char *argv[]) :
}
else
{
void *arguments_parse_func = metacall_handle_function(plugin_repl_handle, "arguments_parse");
std::vector<std::string> arguments(argv + 1, argv + argc);

if (arguments_parse_func == NULL)
/* Launch the CMD (parse arguments) */
if (!cmd(arguments))
{
std::cout << "Warning: CLI Arguments Parser was not loaded, "
"using fallback argument parser with positional arguments only. "
Expand All @@ -128,25 +202,8 @@ application::application(int argc, char *argv[]) :

/* Use fallback parser, it can execute files but does not support command line arguments as options (i.e: -h, --help) */
/* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */
std::vector<std::string> arguments(argv + 1, argv + argc);

arguments_parse_fallback(arguments);
}
else
{
/* TODO: Implement a new plugin for parsing command line options */
std::cout << "TODO: CLI Arguments Parser Plugin is not implemented yet" << std::endl;

/* Note: If it has zero positional arguments, we should also run the repl, for example:
* $ metacall --some-option --another-option --yeet
*/
// TODO:
// if (positional_arguments_size == 0)
// {
// /* Initialize the REPL */
// repl();
// }
}

exit_condition = true;
}
Expand Down Expand Up @@ -244,6 +301,7 @@ bool application::load_path(const char *path, void **handle)

/* Define the cli plugin path as string (core plugin path plus the subpath) */
fs::path plugin_cli_path(plugin_path);
plugin_cli_path /= "cli";
plugin_cli_path /= path;
std::string plugin_cli_path_str(plugin_cli_path.string());

Expand Down Expand Up @@ -275,12 +333,13 @@ bool application::load_path(const char *path, void **handle)
}

metacall_value_destroy(ret);

return true;
}

void application::run()
{
void *evaluate_func = metacall_handle_function(plugin_repl_handle, "evaluate");
void *evaluate_func = metacall_handle_function(plugin_cli_handle, "repl_evaluate");

while (exit_condition != true)
{
Expand Down Expand Up @@ -373,9 +432,9 @@ void application::run()
}

/* Close REPL */
if (plugin_repl_handle != NULL)
if (plugin_cli_handle != NULL)
{
void *ret = metacallhv_s(plugin_repl_handle, "close", metacall_null_args, 0);
void *ret = metacallhv_s(plugin_cli_handle, "repl_close", metacall_null_args, 0);

check_for_exception(ret);
}
Expand All @@ -394,7 +453,7 @@ void *application::execute(void *tokens)
void **tokens_array = metacall_value_to_array(tokens);
void *key = tokens_array[0];

return metacallhv_s(plugin_cli_handle, metacall_value_to_string(key), &tokens_array[1], size - 1);
return metacallhv_s(plugin_repl_handle, metacall_value_to_string(key), &tokens_array[1], size - 1);
}
}

Expand Down
12 changes: 11 additions & 1 deletion source/cli/plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,15 @@ if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_
endif()

# Extension sub-projects
add_subdirectory(cli_core_plugin)
add_subdirectory(cli_repl_plugin)
add_subdirectory(cli_cmd_plugin)
add_subdirectory(cli_core_plugin)
add_subdirectory(cli_sandbox_plugin)

# Generate output directories for CLI plugins
execute_process(
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/internal"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/cmd"
)
82 changes: 82 additions & 0 deletions source/cli/plugins/cli_cmd_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Check if this loader is enabled
if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE)
return()
endif()

find_package(NodeJS)

if(NOT NodeJS_FOUND)
message(SEND_ERROR "NodeJS libraries not found")
return()
endif()

#
# Plugin name and options
#

# Target name
set(target cli_cmd_plugin)

# Exit here if required dependencies are not met
message(STATUS "Plugin ${target}")

# NodeJS added util.parseArgs([config]) in versions v18.3.0, v16.17.0
# Check for compatibility, otherwise use fallback command parser in the CLI
if(NOT (NodeJS_VERSION VERSION_GREATER_EQUAL "18.3.0" OR (NodeJS_VERSION_MAJOR LESS 18 AND NodeJS_VERSION VERSION_GREATER_EQUAL "16.17.0")))
message(WARNING "NodeJS version ${NodeJS_VERSION} does not support ${target}, at least v18.3.0 or v16.17.0 are required, using fallback command parser")
return()
endif()

#
# Source
#

set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source")

#
# Destination
#

set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/cli/internal/${target}")

#
# Project Target
#

add_custom_target(${target} ALL
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json
COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_cmd_plugin.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_cmd_plugin.js
)

#
# Target Properties
#

set_target_properties(${target}
PROPERTIES
FOLDER "${IDE_FOLDER}"
)

#
# Dependencies
#

add_dependencies(${target}
plugin_extension
node_loader
)

#
# Define test
#

# Check if tests are enabled
if(NOT OPTION_BUILD_TESTS)
return()
endif()

add_test(NAME ${target}
COMMAND ${NodeJS_EXECUTABLE} test.js
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source
)
Loading

0 comments on commit e8a3cf0

Please sign in to comment.