From e6d66358965e292677f8c947e9e2f83f14e7c7b5 Mon Sep 17 00:00:00 2001 From: Sergej Geringer Date: Mon, 17 Apr 2023 17:51:45 +0200 Subject: [PATCH] main, CLI parsing: queue project files and CLI lua commands for joint execution this preserves order of CLI commands for lua execution. regrettably this also touches all parsing of CLI inputs, but should keep the logic sound. --- frontend/main/src/CLIConfigParsing.cpp | 322 +++++++++------------ frontend/main/src/main.cpp | 78 +++-- frontend/resources/include/RuntimeConfig.h | 13 +- 3 files changed, 183 insertions(+), 230 deletions(-) diff --git a/frontend/main/src/CLIConfigParsing.cpp b/frontend/main/src/CLIConfigParsing.cpp index 9d2c2eda1f..43a4382f78 100644 --- a/frontend/main/src/CLIConfigParsing.cpp +++ b/frontend/main/src/CLIConfigParsing.cpp @@ -64,9 +64,10 @@ std::pair megamol::frontend::handle_cli_and_con } // set delimiter ; in lua commands to newlines, so lua can actually execute - for (auto& character : config.cli_execute_lua_commands) { - if (character == ';') - character = '\n'; + for (auto& command : config.cli_execute_lua_commands) { + for (auto& character : command.contents) + if (command.source == RuntimeConfig::CliLuaRequest::Type::Raw && character == ';') + character = '\n'; } return {config, global_value_store}; @@ -140,165 +141,129 @@ static void files_exist(std::vector vec, std::string const& type) { // option handlers fill the config struct with passed options // this is a handler template -static void empty_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config){ - // config.option = parsed_options[option_name].as(); +static void empty_handler(cxxopts::KeyValue const& option, RuntimeConfig& config){ + // config.option = option.as(); }; -static void guishow_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.gui_show = parsed_options[option_name].as(); +static void guishow_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.gui_show = option.as(); }; -static void nogui_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.gui_show = !parsed_options[option_name].as(); +static void nogui_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.gui_show = !option.as(); }; -static void guiscale_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.gui_scale = parsed_options[option_name].as(); +static void guiscale_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.gui_scale = option.as(); }; -static void privacynote_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.screenshot_show_privacy_note = parsed_options[option_name].as(); +static void privacynote_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.screenshot_show_privacy_note = option.as(); }; -static void versionnote_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.show_version_note = parsed_options[option_name].as(); +static void versionnote_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.show_version_note = option.as(); }; -static void profile_log_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.profiling_output_file = parsed_options[option_name].as(); +static void profile_log_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.profiling_output_file = option.as(); } -static void profile_log_autostart_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.autostart_profiling = !parsed_options[option_name].as(); +static void profile_log_autostart_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.autostart_profiling = !option.as(); } -static void remote_head_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_headnode = parsed_options[option_name].as(); +static void remote_head_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_headnode = option.as(); }; -static void remote_render_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_rendernode = parsed_options[option_name].as(); +static void remote_render_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_rendernode = option.as(); }; -static void remote_mpirender_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_mpirendernode = parsed_options[option_name].as(); +static void remote_mpirender_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_mpirendernode = option.as(); }; -static void remote_mpirank_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_mpi_broadcast_rank = parsed_options[option_name].as(); +static void remote_mpirank_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_mpi_broadcast_rank = option.as(); }; -static void remote_zmqtarget_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_headnode_zmq_target_address = parsed_options[option_name].as(); +static void remote_zmqtarget_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_headnode_zmq_target_address = option.as(); }; -static void remote_zmqsource_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_rendernode_zmq_source_address = parsed_options[option_name].as(); +static void remote_zmqsource_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_rendernode_zmq_source_address = option.as(); }; -static void remote_head_broadcast_quit_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_headnode_broadcast_quit = parsed_options[option_name].as(); +static void remote_head_broadcast_quit_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_headnode_broadcast_quit = option.as(); }; -static void remote_head_broadcast_project_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_headnode_broadcast_initial_project = parsed_options[option_name].as(); +static void remote_head_broadcast_project_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_headnode_broadcast_initial_project = option.as(); }; -static void remote_head_connect_at_start_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.remote_headnode_connect_on_start = parsed_options[option_name].as(); +static void remote_head_connect_at_start_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.remote_headnode_connect_on_start = option.as(); }; -static void config_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config){ +static void config_handler(cxxopts::KeyValue const& option, RuntimeConfig& config){ // is already done by first CLI pass which checks config files before running them through Lua }; -static void appdir_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto appdir = parsed_options[option_name].as(); +static void appdir_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto appdir = option.as(); files_exist({appdir}, "Application directory"); config.application_directory = appdir; }; -static void resourcedir_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto v = parsed_options[option_name].as>(); - files_exist(v, "Resource directory"); +static void resourcedir_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto v = option.as(); + files_exist({v}, "Resource directory"); - config.resource_directories.insert(config.resource_directories.end(), v.begin(), v.end()); + config.resource_directories.push_back(v); }; -static void shaderdir_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto v = parsed_options[option_name].as>(); - files_exist(v, "Shader directory"); +static void shaderdir_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto v = option.as(); + files_exist({v}, "Shader directory"); - config.shader_directories.insert(config.shader_directories.end(), v.begin(), v.end()); + config.shader_directories.push_back(v); }; -static void logfile_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.log_file = parsed_options[option_name].as(); +static void logfile_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.log_file = option.as(); }; static const std::string accepted_log_level_strings = "('error', 'warn', 'warning', 'info', 'none', 'null', 'zero', 'all', '*')"; -static void loglevel_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.log_level = - megamol::core::utility::log::Log::ParseLevelAttribute(parsed_options[option_name].as()); +static void loglevel_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.log_level = megamol::core::utility::log::Log::ParseLevelAttribute(option.as()); }; -static void echolevel_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.echo_level = - megamol::core::utility::log::Log::ParseLevelAttribute(parsed_options[option_name].as()); +static void echolevel_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.echo_level = megamol::core::utility::log::Log::ParseLevelAttribute(option.as()); }; -static void project_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto v = parsed_options[option_name].as>(); - while (v.size() > 1) { - v.front() += "," + v[1]; - v.pop_back(); - } - files_exist(v, "Project file"); +static void project_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto v = option.as(); + files_exist({v}, "Project file"); - config.project_files.insert(config.project_files.end(), v.begin(), v.end()); + config.project_files.push_back(v); + config.cli_execute_lua_commands.push_back({RuntimeConfig::CliLuaRequest::Type::File, v}); }; -static void execute_lua_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto commands = parsed_options[option_name].as>(); - - for (auto& cmd : commands) { - config.cli_execute_lua_commands += cmd + ";"; - } +static void execute_lua_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto command = option.as(); + config.cli_execute_lua_commands.push_back({RuntimeConfig::CliLuaRequest::Type::Raw, command + ";"}); }; -static void param_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto strings = parsed_options[option_name].as>(); - std::string cmds; +static void param_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto string = option.as(); std::regex param_value("(.+)=(.+)"); @@ -308,47 +273,37 @@ static void param_handler( auto param = "\"" + match[1].str() + "\""; auto value = "\"" + match[2].str() + "\""; - std::string cmd = "mmSetParamValue(" + param + "," + value + ")"; - cmds += cmd + ";"; + return "mmSetParamValue(" + param + "," + value + ")"; } else { exit("param option needs to be in the following format: param=value"); } }; - for (auto& paramstring : strings) { - handle_param(paramstring); - } + std::string cmd = handle_param(string); - // prepend param value changes before other CLI Lua commands - config.cli_execute_lua_commands = cmds + config.cli_execute_lua_commands; + config.cli_execute_lua_commands.push_back({RuntimeConfig::CliLuaRequest::Type::Raw, cmd}); }; -static void global_value_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto v = parsed_options[option_name].as>(); +static void global_value_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto key_value = option.as(); - for (auto& key_value : v) { - auto delimiter = key_value.find(':'); - if (delimiter == std::string::npos) - exit("Config Key-Value pair \"" + key_value + - "\" not valid. Needs colon (:) delimiter between key and value."); + auto delimiter = key_value.find(':'); + if (delimiter == std::string::npos) + exit("Config Key-Value pair \"" + key_value + "\" not valid. Needs colon (:) delimiter between key and value."); - auto key = key_value.substr(0, delimiter); - auto value = key_value.substr(delimiter + 1); + auto key = key_value.substr(0, delimiter); + auto value = key_value.substr(delimiter + 1); - config.global_values.push_back({key, value}); - } + config.global_values.push_back({key, value}); }; -static void host_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.lua_host_address = parsed_options[option_name].as(); +static void host_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.lua_host_address = option.as(); }; -static void opengl_context_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto string = parsed_options[option_name].as(); +static void opengl_context_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto string = option.as(); std::regex version("(\\d+).(\\d+)(core|compat)?"); std::smatch match; @@ -367,55 +322,44 @@ static void opengl_context_handler( } }; -static void khrdebug_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.opengl_khr_debug = parsed_options[option_name].as(); +static void khrdebug_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.opengl_khr_debug = option.as(); }; -static void vsync_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.opengl_vsync = parsed_options[option_name].as(); +static void vsync_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.opengl_vsync = option.as(); }; -static void no_opengl_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { +static void no_opengl_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { // User cannot overwrite default value when there is no openGL present #ifdef MEGAMOL_USE_OPENGL - config.no_opengl = parsed_options[option_name].as(); + config.no_opengl = option.as(); #endif }; -static void force_window_size_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.force_window_size = parsed_options[option_name].as(); +static void force_window_size_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.force_window_size = option.as(); }; -static void fullscreen_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.window_mode |= parsed_options[option_name].as() * RuntimeConfig::WindowMode::fullscreen; +static void fullscreen_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.window_mode |= option.as() * RuntimeConfig::WindowMode::fullscreen; }; -static void nodecoration_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.window_mode |= parsed_options[option_name].as() * RuntimeConfig::WindowMode::nodecoration; +static void nodecoration_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.window_mode |= option.as() * RuntimeConfig::WindowMode::nodecoration; }; -static void topmost_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.window_mode |= parsed_options[option_name].as() * RuntimeConfig::WindowMode::topmost; +static void topmost_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.window_mode |= option.as() * RuntimeConfig::WindowMode::topmost; }; -static void nocursor_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.window_mode |= parsed_options[option_name].as() * RuntimeConfig::WindowMode::nocursor; +static void nocursor_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.window_mode |= option.as() * RuntimeConfig::WindowMode::nocursor; }; -static void hidden_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.window_mode |= parsed_options[option_name].as() * RuntimeConfig::WindowMode::hidden; +static void hidden_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.window_mode |= option.as() * RuntimeConfig::WindowMode::hidden; }; -static void interactive_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - config.interactive = parsed_options[option_name].as(); +static void interactive_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + config.interactive = option.as(); }; -static void window_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto s = parsed_options[option_name].as(); +static void window_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto s = option.as(); // 'WIDTHxHEIGHT[+POSX+POSY]' // 'wxh+x+y' with optional '+x+y', e.g. 600x800+0+0 opens window in upper left corner std::regex geometry("(\\d+)x(\\d+)(?:\\+(\\d+)\\+(\\d+))?"); @@ -435,9 +379,8 @@ static void window_handler( } }; -static void framebuffer_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto string = parsed_options[option_name].as(); +static void framebuffer_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto string = option.as(); // WIDTHxHEIGHT std::regex geometry("(\\d+)x(\\d+)"); std::smatch match; @@ -450,9 +393,8 @@ static void framebuffer_handler( } }; -static void viewport_tile_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto string = parsed_options[option_name].as(); +static void viewport_tile_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto string = option.as(); // x,y;LWIDTHxLHEIGHT;GWIDTHxGHEIGHT std::regex geometry("(\\d+),(\\d+):(\\d+)x(\\d+):(\\d+)x(\\d+)"); std::smatch match; @@ -475,9 +417,8 @@ static void viewport_tile_handler( } }; -static void vr_service_handler( - std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config) { - auto string = parsed_options[option_name].as(); +static void vr_service_handler(cxxopts::KeyValue const& option, RuntimeConfig& config) { + auto string = option.as(); // --vr=[off|unitykolab] std::vector> options = { @@ -502,7 +443,7 @@ static void vr_service_handler( }; using OptionsListEntry = std::tuple, - std::function>; + std::function>; std::vector cli_options_list = { // config name option description type handler @@ -658,6 +599,33 @@ std::vector megamol::frontend::extract_config_file_paths(const int } } +static void handle_options_individually(cxxopts::ParseResult const& parsed_options, + std::vector const& cli_options_list, RuntimeConfig& config) { + auto find_handler = [&](std::string const& name) { + auto find_it = std::find_if(cli_options_list.begin(), cli_options_list.end(), [&](auto& option) { + auto option_name = loong(std::get<0>(option)); + return option_name == name; + }); + + if (find_it != cli_options_list.end()) { + auto& option = *find_it; + return std::get<3>(option); + } else { + std::cerr << "unknown option: " + name; + } + }; + + // go through provided CLI arguments in order of appearance + for (auto& option : parsed_options.arguments()) { + const auto& option_name = option.key(); + + if (parsed_options.count(option_name)) { + const auto& option_handler = find_handler(option_name); + option_handler(option, config); + } + } +} + #define add_option(X) (std::get<0>(X), std::get<1>(X), std::get<2>(X)) megamol::frontend_resources::RuntimeConfig megamol::frontend::handle_config( @@ -834,15 +802,8 @@ megamol::frontend_resources::RuntimeConfig megamol::frontend::handle_config( // actually process passed options try { auto parsed_options = options.parse(argc, argv.data()); - std::string res; - for (auto& option : cli_options_list) { - auto option_name = loong(std::get<0>(option)); - if (parsed_options.count(option_name)) { - auto& option_handler = std::get<3>(option); - option_handler(option_name, parsed_options, config); - } - } + handle_options_individually(parsed_options, cli_options_list, config); } catch (cxxopts::exceptions::exception ex) { exit(std::string(ex.what()) + "\nIn file: " + file); @@ -878,20 +839,13 @@ megamol::frontend_resources::RuntimeConfig megamol::frontend::handle_cli( // actually process passed options try { auto parsed_options = options.parse(argc, argv); - std::string res; if (parsed_options.count("help")) { std::cout << options.help({""}) << std::endl; std::exit(0); } - for (auto& option : cli_options_list) { - auto option_name = loong(std::get<0>(option)); - if (parsed_options.count(option_name)) { - auto& option_handler = std::get<3>(option); - option_handler(option_name, parsed_options, config); - } - } + handle_options_individually(parsed_options, cli_options_list, config); } catch (cxxopts::exceptions::exception ex) { exit(std::string(ex.what()) + "\n" + options.help({""})); diff --git a/frontend/main/src/main.cpp b/frontend/main/src/main.cpp index 141a046ae3..f666fc8928 100644 --- a/frontend/main/src/main.cpp +++ b/frontend/main/src/main.cpp @@ -6,6 +6,7 @@ #include "mmcore/MegaMolGraph.h" #include "mmcore/factories/PluginRegister.h" +#include "FrontendResourcesMap.h" #include "GlobalValueStore.h" #include "RuntimeConfig.h" @@ -322,54 +323,43 @@ int main(const int argc, const char** argv) { ret += 2; } - // load project files via lua - if (run_megamol && graph_resources_ok) - for (auto& file : config.project_files) { - // attention: the project files are only _queued_ for later lua execution here - // if a project file leads to errors on the file-level, we exit here, if not in interactive mode - if (!projectloader_service.load_file(file)) { - log_error("Project file \"" + file + "\" did not execute correctly"); + // queue CLI project files and raw lua + if (run_megamol && graph_resources_ok) { + const auto& frontend_resources_map = megamol::frontend_resources::FrontendResourcesMap{frontend_resources}; + const auto& execute_lua_deferred = + frontend_resources_map.get().execute_deferred_callback; + auto raw_lua_callback = [&](auto const& result) { + const bool cli_lua_ok = std::get<0>(result); + const std::string lua_result = std::get<1>(result); + + if (!cli_lua_ok) + log_error("Error in CLI Lua command: " + lua_result); + }; + for (auto& request : config.cli_execute_lua_commands) { + switch (request.source) { + case RuntimeConfig::CliLuaRequest::Type::File: { + // attention: the project files are only _queued_ for later lua execution here + // if a project file leads to errors on the file-level, we exit here, if not in interactive mode + auto& file = request.contents; + if (!projectloader_service.load_file(file)) { + log_error("Loading project file \"" + file + "\" produced error"); + run_megamol = false; + ret += 8; + } + } break; + case RuntimeConfig::CliLuaRequest::Type::Raw: + // queue Lua commands passed via CLI. this also include CLI param changes. + execute_lua_deferred(request.contents, "", raw_lua_callback); + break; + default: + log_warning("CLI Lua Commands: unknown type: " + std::to_string(static_cast(request.source))); run_megamol = false; ret += 4; - - // if interactive, continue to run MegaMol - if (config.interactive) { - log_warning("Interactive mode: start MegaMol anyway"); - run_megamol = true; - } - } - } - - // execute Lua commands passed via CLI - if (graph_resources_ok) - if (!config.cli_execute_lua_commands.empty()) { - - auto& resources = services.getProvidedResources(); - auto const& lua_execution = - std::find_if(resources.begin(), resources.end(), [](megamol::frontend::FrontendResource const& r) { - return r.getIdentifier() == std::string{"LuaScriptExecution"}; - }); - - const bool interactive = config.interactive; - auto callback = [&, interactive](auto const& result) { - bool cli_lua_ok = std::get<0>(result); - std::string lua_result = std::get<1>(result); - - if (!cli_lua_ok) { - log_error("Error in CLI Lua command: " + lua_result); - if (!interactive) { - lua_service_wrapper.setShutdown(true); - } - } - }; - - // attention: the CLI lua commands are only _queued_ for later lua execution here - // if the CLI scripts lead to lua errors, we exit the program, if not in interactive mode - if (lua_execution != resources.end()) { - lua_execution->getResource().execute_deferred_callback( - config.cli_execute_lua_commands, "", callback); + break; } } + // if interactive, continuing to run MegaMol is ensured by the Project or Lua service itself + } while (run_megamol) { // we run the lua queue here as a starting point to evaluate CLI-related lua inputs diff --git a/frontend/resources/include/RuntimeConfig.h b/frontend/resources/include/RuntimeConfig.h index 92e095fcf9..203387a18d 100644 --- a/frontend/resources/include/RuntimeConfig.h +++ b/frontend/resources/include/RuntimeConfig.h @@ -40,9 +40,18 @@ struct RuntimeConfig { megamol::core::utility::log::Log::log_level::info; // mmSetLogLevel megamol::core::utility::log::Log::log_level echo_level = megamol::core::utility::log::Log::log_level::info; // mmSetEchoLevel - std::vector project_files = {}; // NEW: mmLoadProject - project files are loaded after services are up std::vector global_values = {}; // use GlobalValueStore resource for access to global values! - std::string cli_execute_lua_commands; + std::vector project_files = {}; // mmLoadProject + + struct CliLuaRequest { + enum class Type { + File, + Raw, + }; + Type source; + std::string contents; + }; + std::vector cli_execute_lua_commands; // CLI project files and raw lua scripts, in the order as provided via CLI // detailed and service-specific configurations // every CLI option can be set via the config file using mmSetConfigValue