/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup creator */ #ifndef WITH_PYTHON_MODULE # include # include # include # include "MEM_guardedalloc.h" # include "CLG_log.h" # ifdef WIN32 # include "BLI_winstuff.h" # endif # include "BLI_args.h" # include "BLI_dynstr.h" # include "BLI_fileops.h" # include "BLI_listbase.h" # include "BLI_path_util.h" # include "BLI_string.h" # include "BLI_string_utf8.h" # include "BLI_system.h" # include "BLI_threads.h" # include "BLI_utildefines.h" # ifndef NDEBUG # include "BLI_mempool.h" # endif # include "BKE_appdir.hh" # include "BKE_blender_version.h" # include "BKE_blendfile.hh" # include "BKE_context.hh" # include "BKE_global.hh" # include "BKE_image_format.h" # include "BKE_lib_id.hh" # include "BKE_main.hh" # include "BKE_report.hh" # include "BKE_scene.hh" # include "BKE_sound.h" # include "GPU_context.h" # ifdef WITH_PYTHON # include "BPY_extern_python.h" # include "BPY_extern_run.h" # endif # include "RE_engine.h" # include "RE_pipeline.h" # include "WM_api.hh" # ifdef WITH_LIBMV # include "libmv-capi.h" # endif # ifdef WITH_CYCLES_LOGGING # include "CCL_api.h" # endif # include "DEG_depsgraph.hh" # include "WM_types.hh" # include "creator_intern.h" /* own include */ /* -------------------------------------------------------------------- */ /** \name Build Defines * \{ */ /** * Support extracting arguments for all platforms (for documentation purposes). * These names match the upper case defines. */ struct BuildDefs { bool win32; bool with_cycles; bool with_cycles_logging; bool with_ffmpeg; bool with_freestyle; bool with_libmv; bool with_ocio; bool with_renderdoc; bool with_xr_openxr; }; static void build_defs_init(BuildDefs *build_defs, bool force_all) { if (force_all) { bool *var_end = (bool *)(build_defs + 1); for (bool *var = (bool *)build_defs; var < var_end; var++) { *var = true; } return; } memset(build_defs, 0x0, sizeof(*build_defs)); # ifdef WIN32 build_defs->win32 = true; # endif # ifdef WITH_CYCLES build_defs->with_cycles = true; # endif # ifdef WITH_CYCLES_LOGGING build_defs->with_cycles_logging = true; # endif # ifdef WITH_FFMPEG build_defs->with_ffmpeg = true; # endif # ifdef WITH_FREESTYLE build_defs->with_freestyle = true; # endif # ifdef WITH_LIBMV build_defs->with_libmv = true; # endif # ifdef WITH_OCIO build_defs->with_ocio = true; # endif # ifdef WITH_RENDERDOC build_defs->with_renderdoc = true; # endif # ifdef WITH_XR_OPENXR build_defs->with_xr_openxr = true; # endif } /** \} */ /* -------------------------------------------------------------------- */ /** \name Utility String Parsing * \{ */ static bool parse_int_relative(const char *str, const char *str_end_test, int pos, int neg, int *r_value, const char **r_err_msg) { char *str_end = nullptr; long value; errno = 0; switch (*str) { case '+': value = pos + strtol(str + 1, &str_end, 10); break; case '-': value = (neg - strtol(str + 1, &str_end, 10)) + 1; break; default: value = strtol(str, &str_end, 10); break; } if (*str_end != '\0' && (str_end != str_end_test)) { static const char *msg = "not a number"; *r_err_msg = msg; return false; } if ((errno == ERANGE) || ((value < INT_MIN) || (value > INT_MAX))) { static const char *msg = "exceeds range"; *r_err_msg = msg; return false; } *r_value = int(value); return true; } static const char *parse_int_range_sep_search(const char *str, const char *str_end_test) { const char *str_end_range = nullptr; if (str_end_test) { str_end_range = static_cast(memchr(str, '.', (str_end_test - str) - 1)); if (str_end_range && (str_end_range[1] != '.')) { str_end_range = nullptr; } } else { str_end_range = strstr(str, ".."); if (str_end_range && (str_end_range[2] == '\0')) { str_end_range = nullptr; } } return str_end_range; } /** * Parse a number as a range, eg: `1..4`. * * The \a str_end_range argument is a result of #parse_int_range_sep_search. */ static bool parse_int_range_relative(const char *str, const char *str_end_range, const char *str_end_test, int pos, int neg, int r_value_range[2], const char **r_err_msg) { if (parse_int_relative(str, str_end_range, pos, neg, &r_value_range[0], r_err_msg) && parse_int_relative(str_end_range + 2, str_end_test, pos, neg, &r_value_range[1], r_err_msg)) { return true; } return false; } static bool parse_int_relative_clamp(const char *str, const char *str_end_test, int pos, int neg, int min, int max, int *r_value, const char **r_err_msg) { if (parse_int_relative(str, str_end_test, pos, neg, r_value, r_err_msg)) { CLAMP(*r_value, min, max); return true; } return false; } static bool parse_int_range_relative_clamp(const char *str, const char *str_end_range, const char *str_end_test, int pos, int neg, int min, int max, int r_value_range[2], const char **r_err_msg) { if (parse_int_range_relative( str, str_end_range, str_end_test, pos, neg, r_value_range, r_err_msg)) { CLAMP(r_value_range[0], min, max); CLAMP(r_value_range[1], min, max); return true; } return false; } /** * No clamping, fails with any number outside the range. */ static bool parse_int_strict_range(const char *str, const char *str_end_test, const int min, const int max, int *r_value, const char **r_err_msg) { char *str_end = nullptr; long value; errno = 0; value = strtol(str, &str_end, 10); if (*str_end != '\0' && (str_end != str_end_test)) { static const char *msg = "not a number"; *r_err_msg = msg; return false; } if ((errno == ERANGE) || ((value < min) || (value > max))) { static const char *msg = "exceeds range"; *r_err_msg = msg; return false; } *r_value = int(value); return true; } static bool parse_int(const char *str, const char *str_end_test, int *r_value, const char **r_err_msg) { return parse_int_strict_range(str, str_end_test, INT_MIN, INT_MAX, r_value, r_err_msg); } static bool parse_int_clamp(const char *str, const char *str_end_test, int min, int max, int *r_value, const char **r_err_msg) { if (parse_int(str, str_end_test, r_value, r_err_msg)) { CLAMP(*r_value, min, max); return true; } return false; } # if 0 /** * Version of #parse_int_relative_clamp * that parses a comma separated list of numbers. */ static int *parse_int_relative_clamp_n( const char *str, int pos, int neg, int min, int max, int *r_value_len, const char **r_err_msg) { const char sep = ','; int len = 1; for (int i = 0; str[i]; i++) { if (str[i] == sep) { len++; } } int *values = MEM_mallocN(sizeof(*values) * len, __func__); int i = 0; while (true) { const char *str_end = strchr(str, sep); if (ELEM(*str, sep, '\0')) { static const char *msg = "incorrect comma use"; *r_err_msg = msg; goto fail; } else if (parse_int_relative_clamp(str, str_end, pos, neg, min, max, &values[i], r_err_msg)) { i++; } else { goto fail; /* error message already set */ } if (str_end) { /* next */ str = str_end + 1; } else { /* finished */ break; } } *r_value_len = i; return values; fail: MEM_freeN(values); return nullptr; } # endif /** * Version of #parse_int_relative_clamp & #parse_int_range_relative_clamp * that parses a comma separated list of numbers. * * \note single values are evaluated as a range with matching start/end. */ static int (*parse_int_range_relative_clamp_n(const char *str, int pos, int neg, int min, int max, int *r_value_len, const char **r_err_msg))[2] { const char sep = ','; int len = 1; for (int i = 0; str[i]; i++) { if (str[i] == sep) { len++; } } int(*values)[2] = static_cast(MEM_mallocN(sizeof(*values) * len, __func__)); int i = 0; while (true) { const char *str_end_range; const char *str_end = strchr(str, sep); if (ELEM(*str, sep, '\0')) { static const char *msg = "incorrect comma use"; *r_err_msg = msg; goto fail; } else if ((str_end_range = parse_int_range_sep_search(str, str_end)) ? parse_int_range_relative_clamp( str, str_end_range, str_end, pos, neg, min, max, values[i], r_err_msg) : parse_int_relative_clamp( str, str_end, pos, neg, min, max, &values[i][0], r_err_msg)) { if (str_end_range == nullptr) { values[i][1] = values[i][0]; } i++; } else { goto fail; /* error message already set */ } if (str_end) { /* next */ str = str_end + 1; } else { /* finished */ break; } } *r_value_len = i; return values; fail: MEM_freeN(values); return nullptr; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Utilities Python Context Macro (#BPY_CTX_SETUP) * \{ */ # ifdef WITH_PYTHON struct BlendePyContextStore { wmWindowManager *wm; Scene *scene; wmWindow *win; bool has_win; }; static void arg_py_context_backup(bContext *C, BlendePyContextStore *c_py, const char *script_id) { c_py->wm = CTX_wm_manager(C); c_py->scene = CTX_data_scene(C); c_py->has_win = !BLI_listbase_is_empty(&c_py->wm->windows); if (c_py->has_win) { c_py->win = CTX_wm_window(C); CTX_wm_window_set(C, static_cast(c_py->wm->windows.first)); } else { c_py->win = nullptr; fprintf(stderr, "Python script \"%s\" " "running with missing context data.\n", script_id); } } static void arg_py_context_restore(bContext *C, BlendePyContextStore *c_py) { /* script may load a file, check old data is valid before using */ if (c_py->has_win) { if ((c_py->win == nullptr) || ((BLI_findindex(&G_MAIN->wm, c_py->wm) != -1) && (BLI_findindex(&c_py->wm->windows, c_py->win) != -1))) { CTX_wm_window_set(C, c_py->win); } } if ((c_py->scene == nullptr) || BLI_findindex(&G_MAIN->scenes, c_py->scene) != -1) { CTX_data_scene_set(C, c_py->scene); } } /* macro for context setup/reset */ # define BPY_CTX_SETUP(_cmd) \ { \ BlendePyContextStore py_c; \ arg_py_context_backup(C, &py_c, argv[1]); \ { \ _cmd; \ } \ arg_py_context_restore(C, &py_c); \ } \ ((void)0) # endif /* WITH_PYTHON */ /** \} */ /* -------------------------------------------------------------------- */ /** \name Handle Argument Callbacks * * \note Doc strings here are used in differently: * * - The `--help` message. * - The man page (for Unix systems), * see: `doc/manpage/blender.1.py` * - Parsed and extracted for the manual, * which converts our ad-hoc formatting to reStructuredText. * see: https://docs.blender.org/manual/en/dev/advanced/command_line.html * * \{ */ static void print_version_full() { printf("Blender %s\n", BKE_blender_version_string()); # ifdef BUILD_DATE printf("\tbuild date: %s\n", build_date); printf("\tbuild time: %s\n", build_time); printf("\tbuild commit date: %s\n", build_commit_date); printf("\tbuild commit time: %s\n", build_commit_time); printf("\tbuild hash: %s\n", build_hash); printf("\tbuild platform: %s\n", build_platform); printf("\tbuild type: %s\n", build_type); printf("\tbuild c flags: %s\n", build_cflags); printf("\tbuild c++ flags: %s\n", build_cxxflags); printf("\tbuild link flags: %s\n", build_linkflags); printf("\tbuild system: %s\n", build_system); # endif } static void print_version_short() { # ifdef BUILD_DATE /* NOTE: We include built time since sometimes we need to tell broken from * working built of the same hash. */ printf("Blender %s (hash %s built %s %s)\n", BKE_blender_version_string(), build_hash, build_date, build_time); # else printf("Blender %s\n", BKE_blender_version_string()); # endif } static const char arg_handle_print_version_doc[] = "\n\t" "Print Blender version and exit."; static int arg_handle_print_version(int /*argc*/, const char ** /*argv*/, void * /*data*/) { print_version_full(); exit(EXIT_SUCCESS); BLI_assert_unreachable(); return 0; } static void print_help(bArgs *ba, bool all) { BuildDefs defs; build_defs_init(&defs, all); /* All printing must go via `PRINT` macro. */ # define printf __ERROR__ # define PRINT(...) BLI_args_printf(ba, __VA_ARGS__) PRINT("Blender %s\n", BKE_blender_version_string()); PRINT("Usage: blender [args ...] [file] [args ...]\n"); PRINT("\n"); PRINT("Render Options:\n"); BLI_args_print_arg_doc(ba, "--background"); BLI_args_print_arg_doc(ba, "--render-anim"); BLI_args_print_arg_doc(ba, "--scene"); BLI_args_print_arg_doc(ba, "--render-frame"); BLI_args_print_arg_doc(ba, "--frame-start"); BLI_args_print_arg_doc(ba, "--frame-end"); BLI_args_print_arg_doc(ba, "--frame-jump"); BLI_args_print_arg_doc(ba, "--render-output"); BLI_args_print_arg_doc(ba, "--engine"); BLI_args_print_arg_doc(ba, "--threads"); if (defs.with_cycles) { PRINT("Cycles Render Options:\n"); PRINT("\tCycles add-on options must be specified following a double dash.\n"); PRINT("\n"); PRINT("--cycles-device \n"); PRINT("\tSet the device used for rendering.\n"); PRINT("\tValid options are: 'CPU' 'CUDA' 'OPTIX' 'HIP' 'ONEAPI' 'METAL'.\n"); PRINT("\n"); PRINT("\tAppend +CPU to a GPU device to render on both CPU and GPU.\n"); PRINT("\n"); PRINT("\tExample:\n"); PRINT("\t# blender -b file.blend -f 20 -- --cycles-device OPTIX\n"); PRINT("--cycles-print-stats\n"); PRINT("\tLog statistics about render memory and time usage.\n"); } PRINT("\n"); PRINT("Format Options:\n"); BLI_args_print_arg_doc(ba, "--render-format"); BLI_args_print_arg_doc(ba, "--use-extension"); PRINT("\n"); PRINT("Animation Playback Options:\n"); BLI_args_print_arg_doc(ba, "-a"); PRINT("\n"); PRINT("Window Options:\n"); BLI_args_print_arg_doc(ba, "--window-border"); BLI_args_print_arg_doc(ba, "--window-fullscreen"); BLI_args_print_arg_doc(ba, "--window-geometry"); BLI_args_print_arg_doc(ba, "--window-maximized"); BLI_args_print_arg_doc(ba, "--start-console"); BLI_args_print_arg_doc(ba, "--no-native-pixels"); BLI_args_print_arg_doc(ba, "--no-window-focus"); PRINT("\n"); PRINT("Python Options:\n"); BLI_args_print_arg_doc(ba, "--enable-autoexec"); BLI_args_print_arg_doc(ba, "--disable-autoexec"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--python"); BLI_args_print_arg_doc(ba, "--python-text"); BLI_args_print_arg_doc(ba, "--python-expr"); BLI_args_print_arg_doc(ba, "--python-console"); BLI_args_print_arg_doc(ba, "--python-exit-code"); BLI_args_print_arg_doc(ba, "--python-use-system-env"); BLI_args_print_arg_doc(ba, "--addons"); PRINT("\n"); PRINT("Logging Options:\n"); BLI_args_print_arg_doc(ba, "--log"); BLI_args_print_arg_doc(ba, "--log-level"); BLI_args_print_arg_doc(ba, "--log-show-basename"); BLI_args_print_arg_doc(ba, "--log-show-backtrace"); BLI_args_print_arg_doc(ba, "--log-show-timestamp"); BLI_args_print_arg_doc(ba, "--log-file"); PRINT("\n"); PRINT("Debug Options:\n"); BLI_args_print_arg_doc(ba, "--debug"); BLI_args_print_arg_doc(ba, "--debug-value"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--debug-events"); if (defs.with_ffmpeg) { BLI_args_print_arg_doc(ba, "--debug-ffmpeg"); } BLI_args_print_arg_doc(ba, "--debug-handlers"); if (defs.with_libmv) { BLI_args_print_arg_doc(ba, "--debug-libmv"); } if (defs.with_cycles_logging) { BLI_args_print_arg_doc(ba, "--debug-cycles"); } BLI_args_print_arg_doc(ba, "--debug-memory"); BLI_args_print_arg_doc(ba, "--debug-jobs"); BLI_args_print_arg_doc(ba, "--debug-python"); BLI_args_print_arg_doc(ba, "--debug-depsgraph"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-eval"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-build"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-tag"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-no-threads"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-time"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-pretty"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-uid"); BLI_args_print_arg_doc(ba, "--debug-ghost"); BLI_args_print_arg_doc(ba, "--debug-wintab"); BLI_args_print_arg_doc(ba, "--debug-gpu"); BLI_args_print_arg_doc(ba, "--debug-gpu-force-workarounds"); BLI_args_print_arg_doc(ba, "--debug-gpu-compile-shaders"); if (defs.with_renderdoc) { BLI_args_print_arg_doc(ba, "--debug-gpu-renderdoc"); } BLI_args_print_arg_doc(ba, "--debug-wm"); if (defs.with_xr_openxr) { BLI_args_print_arg_doc(ba, "--debug-xr"); BLI_args_print_arg_doc(ba, "--debug-xr-time"); } BLI_args_print_arg_doc(ba, "--debug-all"); BLI_args_print_arg_doc(ba, "--debug-io"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--debug-fpe"); BLI_args_print_arg_doc(ba, "--debug-exit-on-error"); if (defs.with_freestyle) { BLI_args_print_arg_doc(ba, "--debug-freestyle"); } BLI_args_print_arg_doc(ba, "--disable-crash-handler"); BLI_args_print_arg_doc(ba, "--disable-abort-handler"); BLI_args_print_arg_doc(ba, "--verbose"); PRINT("\n"); PRINT("GPU Options:\n"); BLI_args_print_arg_doc(ba, "--gpu-backend"); PRINT("\n"); PRINT("Misc Options:\n"); BLI_args_print_arg_doc(ba, "--open-last"); BLI_args_print_arg_doc(ba, "--app-template"); BLI_args_print_arg_doc(ba, "--factory-startup"); BLI_args_print_arg_doc(ba, "--enable-event-simulate"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--env-system-datafiles"); BLI_args_print_arg_doc(ba, "--env-system-scripts"); BLI_args_print_arg_doc(ba, "--env-system-python"); PRINT("\n"); BLI_args_print_arg_doc(ba, "-noaudio"); BLI_args_print_arg_doc(ba, "-setaudio"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--command"); PRINT("\n"); BLI_args_print_arg_doc(ba, "--help"); BLI_args_print_arg_doc(ba, "/?"); /* WIN32 only (ignored for non-win32) */ BLI_args_print_arg_doc(ba, "--register"); BLI_args_print_arg_doc(ba, "--register-allusers"); BLI_args_print_arg_doc(ba, "--unregister"); BLI_args_print_arg_doc(ba, "--unregister-allusers"); BLI_args_print_arg_doc(ba, "--version"); BLI_args_print_arg_doc(ba, "--"); // PRINT("\n"); // PRINT("Experimental Features:\n"); /* Other options _must_ be last (anything not handled will show here). * * Note that it's good practice for this to remain empty, * nevertheless print if any exist. */ if (BLI_args_has_other_doc(ba)) { PRINT("\n"); PRINT("Other Options:\n"); BLI_args_print_other_doc(ba); } PRINT("\n"); PRINT("Argument Parsing:\n"); PRINT("\tArguments must be separated by white space, eg:\n"); PRINT("\t# blender -ba test.blend\n"); PRINT("\t...will exit since '-ba' is an unknown argument.\n"); PRINT("\n"); PRINT("Argument Order:\n"); PRINT("\tArguments are executed in the order they are given. eg:\n"); PRINT("\t# blender --background test.blend --render-frame 1 --render-output \"/tmp\"\n"); PRINT( "\t...will not render to '/tmp' because '--render-frame 1' renders before the output path " "is set.\n"); PRINT("\t# blender --background --render-output /tmp test.blend --render-frame 1\n"); PRINT( "\t...will not render to '/tmp' because loading the blend-file overwrites the render output " "that was set.\n"); PRINT("\t# blender --background test.blend --render-output /tmp --render-frame 1\n"); PRINT("\t...works as expected.\n"); PRINT("\n"); PRINT("Environment Variables:\n"); PRINT(" $BLENDER_USER_RESOURCES Top level directory for user files.\n"); PRINT(" (other 'BLENDER_USER_*' variables override when set).\n"); PRINT(" $BLENDER_USER_CONFIG Directory for user configuration files.\n"); PRINT(" $BLENDER_USER_SCRIPTS Directory for user scripts.\n"); PRINT(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n"); PRINT("\n"); PRINT(" $BLENDER_SYSTEM_RESOURCES Top level directory for system files.\n"); PRINT(" (other 'BLENDER_SYSTEM_*' variables override when set).\n"); PRINT(" $BLENDER_SYSTEM_SCRIPTS Directory for system wide scripts.\n"); PRINT(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n"); PRINT(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); if (defs.with_ocio) { PRINT(" $OCIO Path to override the OpenColorIO configuration file.\n"); } if (defs.win32 || all) { PRINT(" $TEMP Store temporary files here (MS-Windows).\n"); } if (!defs.win32 || all) { /* NOTE: while `TMP` checked, don't include here as it's non-standard & may be removed. */ PRINT(" $TMPDIR Store temporary files here (UNIX Systems).\n"); } PRINT( " The path must reference an existing directory " "or it will be ignored.\n"); # undef printf # undef PRINT } ATTR_PRINTF_FORMAT(2, 0) static void help_print_ds_fn(void *ds_v, const char *format, va_list args) { DynStr *ds = static_cast(ds_v); BLI_dynstr_vappendf(ds, format, args); } static char *main_args_help_as_string(bool all) { DynStr *ds = BLI_dynstr_new(); { bArgs *ba = BLI_args_create(0, nullptr); main_args_setup(nullptr, ba, all); BLI_args_print_fn_set(ba, help_print_ds_fn, ds); print_help(ba, all); BLI_args_destroy(ba); } char *buf = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); return buf; } static const char arg_handle_print_help_doc[] = "\n\t" "Print this help text and exit."; static const char arg_handle_print_help_doc_win32[] = "\n\t" "Print this help text and exit (Windows only)."; static int arg_handle_print_help(int /*argc*/, const char ** /*argv*/, void *data) { bArgs *ba = (bArgs *)data; print_help(ba, false); exit(EXIT_SUCCESS); BLI_assert_unreachable(); return 0; } static const char arg_handle_arguments_end_doc[] = "\n\t" "End option processing, following arguments passed unchanged. Access via Python's " "'sys.argv'."; static int arg_handle_arguments_end(int /*argc*/, const char ** /*argv*/, void * /*data*/) { return -1; } /* only to give help message */ # ifdef WITH_PYTHON_SECURITY /* default */ # define PY_ENABLE_AUTO "" # define PY_DISABLE_AUTO ", (default)" # else # define PY_ENABLE_AUTO ", (default, non-standard compilation option)" # define PY_DISABLE_AUTO "" # endif static const char arg_handle_python_set_doc_enable[] = "\n\t" "Enable automatic Python script execution" PY_ENABLE_AUTO "."; static const char arg_handle_python_set_doc_disable[] = "\n\t" "Disable automatic Python script execution " "(Python-drivers & startup scripts)" PY_DISABLE_AUTO "."; # undef PY_ENABLE_AUTO # undef PY_DISABLE_AUTO static int arg_handle_python_set(int /*argc*/, const char ** /*argv*/, void *data) { if (bool(data)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC; } else { G.f &= ~G_FLAG_SCRIPT_AUTOEXEC; } G.f |= G_FLAG_SCRIPT_OVERRIDE_PREF; return 0; } static const char arg_handle_crash_handler_disable_doc[] = "\n\t" "Disable the crash handler."; static int arg_handle_crash_handler_disable(int /*argc*/, const char ** /*argv*/, void * /*data*/) { app_state.signal.use_crash_handler = false; return 0; } static const char arg_handle_abort_handler_disable_doc[] = "\n\t" "Disable the abort handler."; static int arg_handle_abort_handler_disable(int /*argc*/, const char ** /*argv*/, void * /*data*/) { app_state.signal.use_abort_handler = false; return 0; } static void clog_abort_on_error_callback(void *fp) { BLI_system_backtrace(static_cast(fp)); fflush(static_cast(fp)); abort(); } static const char arg_handle_debug_exit_on_error_doc[] = "\n\t" "Immediately exit when internal errors are detected."; static int arg_handle_debug_exit_on_error(int /*argc*/, const char ** /*argv*/, void * /*data*/) { MEM_enable_fail_on_memleak(); CLG_error_fn_set(clog_abort_on_error_callback); return 0; } /** Shared by `--background` & `--command`. */ static void background_mode_set() { G.background = true; /* Background Mode Defaults: * * In general background mode should strive to match the behavior of running * Blender inside a graphical session, any exception to this should have a well * justified reason and be noted in the doc-string. */ /* NOTE(@ideasman42): While there is no requirement for sound to be disabled in background-mode, * the use case for playing audio in background mode is enough of a special-case * that users who wish to do this can explicitly enable audio in background mode. * While the down sides for connecting to an audio device aren't terrible they include: * - Listing Blender as an active application which may output audio. * - Unnecessary overhead running an operation in background mode or ... * - Having to remember to include `-noaudio` with batch operations. * - A quiet but audible click when Blender starts & configures its audio device. */ BKE_sound_force_device("None"); } static const char arg_handle_background_mode_set_doc[] = "\n" "\tRun in background (often used for UI-less rendering).\n" "\n" "\tThe audio device is disabled in background-mode by default\n" "\tand can be re-enabled by passing in '-setaudo Default' afterwards."; static int arg_handle_background_mode_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { print_version_short(); background_mode_set(); return 0; } static const char arg_handle_command_set_doc[] = "\n" "\tRun a command which consumes all remaining arguments.\n" "\tUse '-c help' to list all other commands.\n" "\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*/) { 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(); app_state.command.argc = argc - 1; app_state.command.argv = argv + 1; /* Consume remaining arguments. */ return argc - 1; } static const char arg_handle_log_level_set_doc[] = "\n" "\tSet the logging verbosity level (higher for more details) defaults to 1,\n" "\tuse -1 to log all levels."; static int arg_handle_log_level_set(int argc, const char **argv, void * /*data*/) { const char *arg_id = "--log-level"; if (argc > 1) { const char *err_msg = nullptr; if (!parse_int_clamp(argv[1], nullptr, -1, INT_MAX, &G.log.level, &err_msg)) { fprintf(stderr, "\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]); } else { if (G.log.level == -1) { G.log.level = INT_MAX; } CLG_level_set(G.log.level); } return 1; } fprintf(stderr, "\nError: '%s' no args given.\n", arg_id); return 0; } static const char arg_handle_log_show_basename_set_doc[] = "\n\t" "Only show file name in output (not the leading path)."; static int arg_handle_log_show_basename_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { CLG_output_use_basename_set(true); return 0; } static const char arg_handle_log_show_backtrace_set_doc[] = "\n\t" "Show a back trace for each log message (debug builds only)."; static int arg_handle_log_show_backtrace_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { /* Ensure types don't become incompatible. */ void (*fn)(FILE *fp) = BLI_system_backtrace; CLG_backtrace_fn_set((void (*)(void *))fn); return 0; } static const char arg_handle_log_show_timestamp_set_doc[] = "\n\t" "Show a timestamp for each log message in seconds since start."; static int arg_handle_log_show_timestamp_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { CLG_output_use_timestamp_set(true); return 0; } static const char arg_handle_log_file_set_doc[] = "\n" "\tSet a file to output the log to."; static int arg_handle_log_file_set(int argc, const char **argv, void * /*data*/) { const char *arg_id = "--log-file"; if (argc > 1) { errno = 0; FILE *fp = BLI_fopen(argv[1], "w"); if (fp == nullptr) { const char *err_msg = errno ? strerror(errno) : "unknown"; fprintf(stderr, "\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]); } else { if (UNLIKELY(G.log.file != nullptr)) { fclose(static_cast(G.log.file)); } G.log.file = fp; CLG_output_set(G.log.file); } return 1; } fprintf(stderr, "\nError: '%s' no args given.\n", arg_id); return 0; } static const char arg_handle_log_set_doc[] = "\n" "\tEnable logging categories, taking a single comma separated argument.\n" "\tMultiple categories can be matched using a '.*' suffix,\n" "\tso '--log \"wm.*\"' logs every kind of window-manager message.\n" "\tSub-string can be matched using a '*' prefix and suffix,\n" "\tso '--log \"*undo*\"' logs every kind of undo-related message.\n" "\tUse \"^\" prefix to ignore, so '--log \"*,^wm.operator.*\"' logs all except for " "'wm.operators.*'\n" "\tUse \"*\" to log everything."; static int arg_handle_log_set(int argc, const char **argv, void * /*data*/) { const char *arg_id = "--log"; if (argc > 1) { const char *str_step = argv[1]; while (*str_step) { const char *str_step_end = strchr(str_step, ','); int str_step_len = str_step_end ? (str_step_end - str_step) : strlen(str_step); if (str_step[0] == '^') { CLG_type_filter_exclude(str_step + 1, str_step_len - 1); } else { CLG_type_filter_include(str_step, str_step_len); } if (str_step_end) { /* Typically only be one, but don't fail on multiple. */ while (*str_step_end == ',') { str_step_end++; } str_step = str_step_end; } else { break; } } return 1; } fprintf(stderr, "\nError: '%s' no args given.\n", arg_id); return 0; } static const char arg_handle_debug_mode_set_doc[] = "\n" "\tTurn debugging on.\n" "\n" "\t* Enables memory error detection\n" "\t* Disables mouse grab (to interact with a debugger in some cases)\n" "\t* Keeps Python's 'sys.stdin' rather than setting it to None"; static int arg_handle_debug_mode_set(int /*argc*/, const char ** /*argv*/, void *data) { G.debug |= G_DEBUG; /* std output printf's */ printf("Blender %s\n", BKE_blender_version_string()); MEM_set_memory_debug(); # ifndef NDEBUG BLI_mempool_set_memory_debug(); # endif # ifdef WITH_BUILDINFO printf("Build: %s %s %s %s\n", build_date, build_time, build_platform, build_type); # endif BLI_args_print(static_cast(data)); return 0; } static const char arg_handle_debug_mode_generic_set_doc_ffmpeg[] = "\n\t" "Enable debug messages from FFmpeg library."; static const char arg_handle_debug_mode_generic_set_doc_freestyle[] = "\n\t" "Enable debug messages for Freestyle."; static const char arg_handle_debug_mode_generic_set_doc_python[] = "\n\t" "Enable debug messages for Python."; static const char arg_handle_debug_mode_generic_set_doc_events[] = "\n\t" "Enable debug messages for the event system."; static const char arg_handle_debug_mode_generic_set_doc_handlers[] = "\n\t" "Enable debug messages for event handling."; static const char arg_handle_debug_mode_generic_set_doc_wm[] = "\n\t" "Enable debug messages for the window manager, shows all operators in search, shows " "keymap errors."; static const char arg_handle_debug_mode_generic_set_doc_ghost[] = "\n\t" "Enable debug messages for Ghost (Linux only)."; static const char arg_handle_debug_mode_generic_set_doc_wintab[] = "\n\t" "Enable debug messages for Wintab."; static const char arg_handle_debug_mode_generic_set_doc_xr[] = "\n\t" "Enable debug messages for virtual reality contexts.\n" "\tEnables the OpenXR API validation layer, (OpenXR) debug messages and general information " "prints."; static const char arg_handle_debug_mode_generic_set_doc_xr_time[] = "\n\t" "Enable debug messages for virtual reality frame rendering times."; static const char arg_handle_debug_mode_generic_set_doc_jobs[] = "\n\t" "Enable time profiling for background jobs."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph[] = "\n\t" "Enable all debug messages from dependency graph."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_build[] = "\n\t" "Enable debug messages from dependency graph related on graph construction."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_tag[] = "\n\t" "Enable debug messages from dependency graph related on tagging."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_time[] = "\n\t" "Enable debug messages from dependency graph related on timing."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_eval[] = "\n\t" "Enable debug messages from dependency graph related on evaluation."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_no_threads[] = "\n\t" "Switch dependency graph to a single threaded evaluation."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_pretty[] = "\n\t" "Enable colors for dependency graph debug messages."; static const char arg_handle_debug_mode_generic_set_doc_depsgraph_uid[] = "\n\t" "Verify validness of session-wide identifiers assigned to ID data-blocks."; static const char arg_handle_debug_mode_generic_set_doc_gpu_force_workarounds[] = "\n\t" "Enable workarounds for typical GPU issues and disable all GPU extensions."; static int arg_handle_debug_mode_generic_set(int /*argc*/, const char ** /*argv*/, void *data) { G.debug |= POINTER_AS_INT(data); return 0; } static const char arg_handle_debug_mode_io_doc[] = "\n\t" "Enable debug messages for I/O (Collada, ...)."; static int arg_handle_debug_mode_io(int /*argc*/, const char ** /*argv*/, void * /*data*/) { G.debug |= G_DEBUG_IO; return 0; } static const char arg_handle_debug_mode_all_doc[] = "\n\t" "Enable all debug messages."; static int arg_handle_debug_mode_all(int /*argc*/, const char ** /*argv*/, void * /*data*/) { G.debug |= G_DEBUG_ALL; # ifdef WITH_LIBMV libmv_startDebugLogging(); # endif # ifdef WITH_CYCLES_LOGGING CCL_start_debug_logging(); # endif return 0; } static const char arg_handle_debug_mode_libmv_doc[] = "\n\t" "Enable debug messages from libmv library."; static int arg_handle_debug_mode_libmv(int /*argc*/, const char ** /*argv*/, void * /*data*/) { # ifdef WITH_LIBMV libmv_startDebugLogging(); # endif return 0; } static const char arg_handle_debug_mode_cycles_doc[] = "\n\t" "Enable debug messages from Cycles."; static int arg_handle_debug_mode_cycles(int /*argc*/, const char ** /*argv*/, void * /*data*/) { # ifdef WITH_CYCLES_LOGGING CCL_start_debug_logging(); # endif return 0; } static const char arg_handle_debug_mode_memory_set_doc[] = "\n\t" "Enable fully guarded memory allocation and debugging."; static int arg_handle_debug_mode_memory_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { MEM_set_memory_debug(); return 0; } static const char arg_handle_debug_value_set_doc[] = "\n" "\tSet debug value of on startup."; static int arg_handle_debug_value_set(int argc, const char **argv, void * /*data*/) { const char *arg_id = "--debug-value"; if (argc > 1) { const char *err_msg = nullptr; int value; if (!parse_int(argv[1], nullptr, &value, &err_msg)) { fprintf(stderr, "\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]); return 1; } G.debug_value = value; return 1; } fprintf(stderr, "\nError: you must specify debug value to set.\n"); return 0; } static const char arg_handle_debug_gpu_set_doc[] = "\n" "\tEnable GPU debug context and information for OpenGL 4.3+."; static int arg_handle_debug_gpu_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { /* Also enable logging because that how gl errors are reported. */ const char *gpu_filter = "gpu.*"; CLG_type_filter_include(gpu_filter, strlen(gpu_filter)); G.debug |= G_DEBUG_GPU; return 0; } static const char arg_handle_debug_gpu_compile_shaders_set_doc[] = "\n" "\tCompile all statically defined shaders to test platform compatibility."; static int arg_handle_debug_gpu_compile_shaders_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { G.debug |= G_DEBUG_GPU_COMPILE_SHADERS; return 0; } static const char arg_handle_debug_gpu_renderdoc_set_doc[] = "\n" "\tEnable Renderdoc integration for GPU frame grabbing and debugging."; static int arg_handle_debug_gpu_renderdoc_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { # ifdef WITH_RENDERDOC G.debug |= G_DEBUG_GPU_RENDERDOC | G_DEBUG_GPU; # endif return 0; } static const char arg_handle_gpu_backend_set_doc_all[] = "\n" "\tForce to use a specific GPU backend. Valid options: " "'vulkan' (experimental), " "'metal', " "'opengl'."; static const char arg_handle_gpu_backend_set_doc[] = "\n" "\tForce to use a specific GPU backend. Valid options: " # ifdef WITH_OPENGL_BACKEND "'opengl'" # if defined(WITH_VULKAN_BACKEND) " or " # endif # endif # ifdef WITH_VULKAN_BACKEND "'vulkan' (experimental)" # endif # ifdef WITH_METAL_BACKEND "'metal'" # endif "."; static int arg_handle_gpu_backend_set(int argc, const char **argv, void * /*data*/) { if (argc == 0) { fprintf(stderr, "\nError: GPU backend must follow '--gpu-backend'.\n"); return 0; } const char *backends_supported[3] = {nullptr}; int backends_supported_num = 0; eGPUBackendType gpu_backend = GPU_BACKEND_NONE; /* NOLINTBEGIN: bugprone-assignment-in-if-condition */ if (false) { /* Just a dummy if to make the following ifdef blocks work. */ } # ifdef WITH_OPENGL_BACKEND else if (STREQ(argv[1], (backends_supported[backends_supported_num++] = "opengl"))) { gpu_backend = GPU_BACKEND_OPENGL; } # endif # ifdef WITH_VULKAN_BACKEND else if (STREQ(argv[1], (backends_supported[backends_supported_num++] = "vulkan"))) { gpu_backend = GPU_BACKEND_VULKAN; } # endif # ifdef WITH_METAL_BACKEND else if (STREQ(argv[1], (backends_supported[backends_supported_num++] = "metal"))) { gpu_backend = GPU_BACKEND_METAL; } # endif else { fprintf(stderr, "\nError: Unrecognized GPU backend for '--gpu-backend', expected one of ["); for (int i = 0; i < backends_supported_num; i++) { fprintf(stderr, (i + 1 != backends_supported_num) ? "%s, " : "%s", backends_supported[i]); } fprintf(stderr, "].\n"); return 0; } /* NOLINTEND: bugprone-assignment-in-if-condition */ GPU_backend_type_selection_set_override(gpu_backend); return 1; } static const char arg_handle_debug_fpe_set_doc[] = "\n\t" "Enable floating-point exceptions."; static int arg_handle_debug_fpe_set(int /*argc*/, const char ** /*argv*/, void * /*data*/) { main_signal_setup_fpe(); return 0; } static const char arg_handle_app_template_doc[] = "