Refactor: generalize deferred command line argument execution

Making arguments call into Python was impractical because Python
is only initialized for ARG_PASS_FINAL.

Replace "--command" specific logic with a general method of arguments
requesting to be executed once all sub-systems have been initialized.

Without this, the `main()` function needs hard coded logic to support
any time an argument needs to use Python internally.
This commit is contained in:
Campbell Barton
2024-04-05 11:24:07 +11:00
parent a00843d560
commit 204d282c75
3 changed files with 97 additions and 31 deletions

View File

@@ -37,7 +37,6 @@
/* Mostly initialization functions. */
#include "BKE_appdir.hh"
#include "BKE_blender.hh"
#include "BKE_blender_cli_command.hh"
#include "BKE_brush.hh"
#include "BKE_cachefile.hh"
#include "BKE_callbacks.hh"
@@ -122,6 +121,7 @@ ApplicationState app_state = []() {
app_state.signal.use_crash_handler = true;
app_state.signal.use_abort_handler = true;
app_state.exit_code_on_error.python = 0;
app_state.main_arg_deferred = nullptr;
return app_state;
}();
@@ -566,16 +566,9 @@ int main(int argc,
#ifndef WITH_PYTHON_MODULE
if (G.background) {
int exit_code;
if (app_state.command.argv) {
const char *id = app_state.command.argv[0];
if (STREQ(id, "help")) {
BKE_blender_cli_command_print_help();
exit_code = EXIT_SUCCESS;
}
else {
exit_code = BKE_blender_cli_command_exec(
C, id, app_state.command.argc - 1, app_state.command.argv + 1);
}
if (app_state.main_arg_deferred != nullptr) {
exit_code = main_arg_handle_deferred();
MEM_freeN(app_state.main_arg_deferred);
}
else {
exit_code = G.is_break ? EXIT_FAILURE : EXIT_SUCCESS;
@@ -584,6 +577,9 @@ int main(int argc,
WM_exit(C, exit_code);
}
else {
/* Not supported, although it could be made to work if needed. */
BLI_assert(app_state.main_arg_deferred == nullptr);
/* Shows the splash as needed. */
WM_init_splash_on_startup(C);

View File

@@ -35,6 +35,7 @@
# endif
# include "BKE_appdir.hh"
# include "BKE_blender_cli_command.hh"
# include "BKE_blender_version.h"
# include "BKE_blendfile.hh"
# include "BKE_context.hh"
@@ -420,6 +421,57 @@ fail:
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred Argument Handling
*
* Support executing an argument running instead of #WM_main which is deferred.
* Needed for arguments which are handled early but require sub-systems
* (Python in particular) * to be initialized.
* \{ */
struct BA_ArgCallback_Deferred {
BA_ArgCallback func;
int argc;
const char **argv;
void *data;
/** Return-code. */
int exit_code;
};
static bool main_arg_deferred_is_set()
{
return app_state.main_arg_deferred != nullptr;
}
static void main_arg_deferred_setup(BA_ArgCallback func, int argc, const char **argv, void *data)
{
BLI_assert(app_state.main_arg_deferred == nullptr);
BA_ArgCallback_Deferred *d = static_cast<BA_ArgCallback_Deferred *>(
MEM_callocN(sizeof(*d), __func__));
d->func = func;
d->argc = argc;
d->argv = argv;
d->data = data;
d->exit_code = 0;
app_state.main_arg_deferred = d;
}
static void main_arg_deferred_exit_code_set(int exit_code)
{
BA_ArgCallback_Deferred *d = app_state.main_arg_deferred;
BLI_assert(d != nullptr);
d->exit_code = exit_code;
}
int main_arg_handle_deferred()
{
BA_ArgCallback_Deferred *d = app_state.main_arg_deferred;
d->func(d->argc, d->argv, d->data);
return d->exit_code;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utilities Python Context Macro (#BPY_CTX_SETUP)
* \{ */
@@ -940,21 +992,34 @@ static const char arg_handle_command_set_doc[] =
"\tPass '--help' after the command to see its help text.\n"
"\n"
"\tThis implies '--background' mode.";
static int arg_handle_command_set(int argc, const char **argv, void * /*data*/)
static int arg_handle_command_set(int argc, const char **argv, void *data)
{
if (argc < 2) {
fprintf(stderr, "%s requires at least one argument\n", argv[0]);
exit(EXIT_FAILURE);
BLI_assert_unreachable();
if (!main_arg_deferred_is_set()) {
if (argc < 2) {
fprintf(stderr, "%s requires at least one argument\n", argv[0]);
exit(EXIT_FAILURE);
BLI_assert_unreachable();
}
/* Application "info" messages get in the way of command line output, suppress them. */
G.quiet = true;
background_mode_set();
main_arg_deferred_setup(arg_handle_command_set, argc, argv, data);
}
else {
bContext *C = static_cast<bContext *>(data);
const char *id = argv[1];
int exit_code;
if (STREQ(id, "help")) {
BKE_blender_cli_command_print_help();
exit_code = EXIT_SUCCESS;
}
else {
exit_code = BKE_blender_cli_command_exec(C, id, argc - 2, argv + 2);
}
main_arg_deferred_exit_code_set(exit_code);
}
/* Application "info" messages get in the way of command line output, suppress them. */
G.quiet = true;
background_mode_set();
app_state.command.argc = argc - 1;
app_state.command.argv = argv + 1;
/* Consume remaining arguments. */
return argc - 1;
@@ -2409,8 +2474,8 @@ void main_args_setup(bContext *C, bArgs *ba, bool all)
ba, nullptr, "--disable-abort-handler", CB(arg_handle_abort_handler_disable), nullptr);
BLI_args_add(ba, "-b", "--background", CB(arg_handle_background_mode_set), nullptr);
/* Command implies background mode. */
BLI_args_add(ba, "-c", "--command", CB(arg_handle_command_set), nullptr);
/* Command implies background mode (defers execution). */
BLI_args_add(ba, "-c", "--command", CB(arg_handle_command_set), C);
BLI_args_add(ba, "-a", nullptr, CB(arg_handle_playback_mode), nullptr);

View File

@@ -10,6 +10,7 @@
* Functionality for main() initialization.
*/
struct BA_ArgCallback_Deferred;
struct bArgs;
struct bContext;
@@ -33,6 +34,12 @@ void main_args_setup(struct bContext *C, struct bArgs *ba, bool all);
*/
int main_args_handle_load_file(int argc, const char **argv, void *data);
/**
* Handle an argument which requested deferred evaluation.
* Needed when arguments which evaluate early need Python to be initialized for example.
*/
int main_arg_handle_deferred();
/* `creator_signals.cc` */
void main_signal_setup(void);
@@ -53,12 +60,10 @@ struct ApplicationState {
unsigned char python;
} exit_code_on_error;
/** Storage for commands (see `--command` argument). */
struct {
int argc;
const char **argv;
} command;
/** Store the argument state for later handling. */
struct BA_ArgCallback_Deferred *main_arg_deferred;
};
extern struct ApplicationState app_state; /* `creator.cc` */
/**