From dc9430c48039bda36c1ba052f00c20e554f6aef0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 7 Jun 2024 11:36:20 +1000 Subject: [PATCH] Extensions: support system repositories & BLENDER_SYSTEM_EXTENSIONS Support for "System" extensions as an alternative to the current "User" extensions repository. The purpose of this change is to support bundling extensions for offline work or in environments where users setting up thier own extensions isn't desirable, see #122512. Details: The default "System" repository on Linux will for example use: - `/usr/share/blender/4.2/extensions/{system}` For system installs. - `./4.2/extensions/{system}` For portable installs. - Blender's default startup now has a "System" repository which users or administrators may populate. - Repositories can select between User/System paths, setting a custom path overrides overrides this setting. - Add "BLENDER_SYSTEM_EXTENSIONS" (matching "BLENDER_LOCAL_EXTENSIONS"). Ref !122832 --- release/extensions/system/readme.txt | 7 +++++ scripts/startup/bl_ui/space_userpref.py | 4 +++ source/blender/blenkernel/BKE_appdir.hh | 3 +- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_preferences.h | 1 + source/blender/blenkernel/intern/appdir.cc | 12 +++++++ .../blender/blenkernel/intern/preferences.cc | 23 ++++++++++++-- .../blenloader/intern/versioning_userdef.cc | 4 +++ source/blender/makesdna/DNA_userdef_types.h | 16 ++++++++-- source/blender/makesrna/intern/rna_userdef.cc | 31 +++++++++++++++++++ source/blender/python/intern/bpy.cc | 3 +- source/creator/CMakeLists.txt | 7 +++++ source/creator/creator_args.cc | 9 ++++++ 13 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 release/extensions/system/readme.txt diff --git a/release/extensions/system/readme.txt b/release/extensions/system/readme.txt new file mode 100644 index 00000000000..44860437d98 --- /dev/null +++ b/release/extensions/system/readme.txt @@ -0,0 +1,7 @@ +System Extensions + +Extensions extracted into this directory will be available from the +default "System" repository. + +This allows extensions to be bundled with Blender outside of +user repositories. diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index f5d6b5f2dfb..8b63faec90a 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -2211,6 +2211,10 @@ class USERPREF_PT_extensions_repos(Panel): # valid UTF-8 which will raise a Python exception when passed in as text. sub.prop(active_repo, "directory", text="") + row = layout_panel.row() + row.active = not use_custom_directory + row.prop(active_repo, "source") + if active_repo.use_remote_url: row = layout_panel.row(align=True, heading="Authentication") row.prop(active_repo, "use_access_token") diff --git a/source/blender/blenkernel/BKE_appdir.hh b/source/blender/blenkernel/BKE_appdir.hh index 6002e3fcebc..7f70cf153a0 100644 --- a/source/blender/blenkernel/BKE_appdir.hh +++ b/source/blender/blenkernel/BKE_appdir.hh @@ -168,7 +168,8 @@ enum { /* system */ BLENDER_SYSTEM_DATAFILES = 52, BLENDER_SYSTEM_SCRIPTS = 53, - BLENDER_SYSTEM_PYTHON = 54, + BLENDER_SYSTEM_EXTENSIONS = 54, + BLENDER_SYSTEM_PYTHON = 55, }; /** For #BKE_appdir_folder_id_version only. */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 77289f10407..2d6b06b7987 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 55 +#define BLENDER_FILE_SUBVERSION 56 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index 14e311f5f1a..68d497f3313 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -90,6 +90,7 @@ bUserExtensionRepo *BKE_preferences_extension_repo_add(UserDef *userdef, void BKE_preferences_extension_repo_remove(UserDef *userdef, bUserExtensionRepo *repo); bUserExtensionRepo *BKE_preferences_extension_repo_add_default_remote(UserDef *userdef); bUserExtensionRepo *BKE_preferences_extension_repo_add_default_user(UserDef *userdef); +bUserExtensionRepo *BKE_preferences_extension_repo_add_default_system(UserDef *userdef); /** Create all default repositories, only use when repositories are empty. */ void BKE_preferences_extension_repo_add_defaults_all(UserDef *userdef); diff --git a/source/blender/blenkernel/intern/appdir.cc b/source/blender/blenkernel/intern/appdir.cc index 7b3cd188f0d..f3d86c4a58a 100644 --- a/source/blender/blenkernel/intern/appdir.cc +++ b/source/blender/blenkernel/intern/appdir.cc @@ -656,6 +656,18 @@ bool BKE_appdir_folder_id_ex(const int folder_id, } return false; + case BLENDER_SYSTEM_EXTENSIONS: + if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_EXTENSIONS")) { + break; + } + if (get_path_system(path, path_maxncpy, "extensions", subfolder)) { + break; + } + if (get_path_local(path, path_maxncpy, "extensions", subfolder)) { + break; + } + return false; + case BLENDER_SYSTEM_PYTHON: if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_PYTHON")) { break; diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index 91075bc903e..2633e9d62d2 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -216,11 +216,19 @@ bUserExtensionRepo *BKE_preferences_extension_repo_add_default_user(UserDef *use return repo; } +bUserExtensionRepo *BKE_preferences_extension_repo_add_default_system(UserDef *userdef) +{ + bUserExtensionRepo *repo = BKE_preferences_extension_repo_add(userdef, "System", "system", ""); + repo->source = USER_EXTENSION_REPO_SOURCE_SYSTEM; + return repo; +} + void BKE_preferences_extension_repo_add_defaults_all(UserDef *userdef) { BLI_assert(BLI_listbase_is_empty(&userdef->extension_repos)); BKE_preferences_extension_repo_add_default_remote(userdef); BKE_preferences_extension_repo_add_default_user(userdef); + BKE_preferences_extension_repo_add_default_system(userdef); } void BKE_preferences_extension_repo_name_set(UserDef *userdef, @@ -269,8 +277,19 @@ size_t BKE_preferences_extension_repo_dirpath_get(const bUserExtensionRepo *repo return BLI_strncpy_rlen(dirpath, repo->custom_dirpath, dirpath_maxncpy); } - std::optional path = BKE_appdir_folder_id_user_notest(BLENDER_USER_EXTENSIONS, - nullptr); + std::optional path = std::nullopt; + + switch (repo->source) { + case USER_EXTENSION_REPO_SOURCE_SYSTEM: { + path = BKE_appdir_folder_id(BLENDER_SYSTEM_EXTENSIONS, nullptr); + break; + } + default: { /* #USER_EXTENSION_REPO_SOURCE_USER. */ + path = BKE_appdir_folder_id_user_notest(BLENDER_USER_EXTENSIONS, nullptr); + break; + } + } + /* Highly unlikely to fail as the directory doesn't have to exist. */ if (!path) { dirpath[0] = '\0'; diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 11dbcfaac1f..8e0fcdd53e2 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -973,6 +973,10 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->sequencer_editor_flag |= USER_SEQ_ED_SIMPLE_TWEAKING; } + if (!USER_VERSION_ATLEAST(402, 56)) { + BKE_preferences_extension_repo_add_default_system(userdef); + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a USER_VERSION_ATLEAST check. diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e1ecea0a938..6da48d48330 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -649,8 +649,11 @@ typedef struct bUserExtensionRepo { char custom_dirpath[1024]; /* FILE_MAX */ char remote_url[1024]; /* FILE_MAX */ - int flag; - char _pad0[4]; + uint8_t flag; + /** The source location when the custom directory isn't used (#eUserExtensionRepo_Source).*/ + uint8_t source; + + char _pad0[6]; } bUserExtensionRepo; typedef enum eUserExtensionRepo_Flag { @@ -663,6 +666,15 @@ typedef enum eUserExtensionRepo_Flag { USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN = 1 << 5, } eUserExtensionRepo_Flag; +/** + * The source to use (User or System), only valid when the + * #USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY flag isn't set. + */ +typedef enum eUserExtensionRepo_Source { + USER_EXTENSION_REPO_SOURCE_USER = 0, + USER_EXTENSION_REPO_SOURCE_SYSTEM = 1, +} eUserExtensionRepo_Source; + typedef struct SolidLight { int flag; float smooth; diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 8168ea5b47d..f881345070f 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -439,6 +439,15 @@ static void rna_userdef_extension_repo_use_remote_url_set(PointerRNA *ptr, bool ptr, value, USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL); } +static void rna_userdef_extension_repo_source_set(PointerRNA *ptr, int value) +{ + Main *bmain = G.main; + bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + BKE_callback_exec_null(bmain, BKE_CB_EVT_EXTENSION_REPOS_UPDATE_PRE); + repo->source = value; + BKE_callback_exec_null(bmain, BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST); +} + static void rna_userdef_script_autoexec_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) @@ -6750,6 +6759,20 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + static const EnumPropertyItem source_type_items[] = { + {USER_EXTENSION_REPO_SOURCE_USER, + "USER", + 0, + "User", + "Repository managed by the user, stored in user directories"}, + {USER_EXTENSION_REPO_SOURCE_SYSTEM, + "SYSTEM", + 0, + "System", + "Read-only repository provided by the system"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + srna = RNA_def_struct(brna, "UserExtensionRepo", nullptr); RNA_def_struct_sdna(srna, "bUserExtensionRepo"); RNA_def_struct_ui_text( @@ -6801,6 +6824,14 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) "rna_userdef_extension_repo_access_token_length", "rna_userdef_extension_repo_access_token_set"); + prop = RNA_def_property(srna, "source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, source_type_items); + RNA_def_property_enum_funcs(prop, nullptr, "rna_userdef_extension_repo_source_set", nullptr); + RNA_def_property_ui_text( + prop, + "Source", + "Select if the repository is in a user managed or system provided directory"); + /* NOTE(@ideasman42): this is intended to be used by a package manger component * which is not yet integrated. */ prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/python/intern/bpy.cc b/source/blender/python/intern/bpy.cc index a2affd0f800..d23092d29e5 100644 --- a/source/blender/python/intern/bpy.cc +++ b/source/blender/python/intern/bpy.cc @@ -266,7 +266,7 @@ PyDoc_STRVAR( "\n" " Return a system resource path.\n" "\n" - " :arg type: string in ['DATAFILES', 'SCRIPTS', 'PYTHON'].\n" + " :arg type: string in ['DATAFILES', 'SCRIPTS', 'EXTENSIONS', 'PYTHON'].\n" " :type type: string\n" " :arg path: Optional subdirectory.\n" " :type path: string or bytes\n"); @@ -275,6 +275,7 @@ static PyObject *bpy_system_resource(PyObject * /*self*/, PyObject *args, PyObje const PyC_StringEnumItems type_items[] = { {BLENDER_SYSTEM_DATAFILES, "DATAFILES"}, {BLENDER_SYSTEM_SCRIPTS, "SCRIPTS"}, + {BLENDER_SYSTEM_EXTENSIONS, "EXTENSIONS"}, {BLENDER_SYSTEM_PYTHON, "PYTHON"}, {0, nullptr}, }; diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index d7033fd0ea2..b8d6a83ffc2 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1659,6 +1659,13 @@ if(DEFINED TARGETDIR_TEXT) ) endif() +# Create a system extensions directory (users or administrators may populate this). +# This only contains a `readme.txt` explaining it's purpose. +install( + DIRECTORY ${CMAKE_SOURCE_DIR}/release/extensions + DESTINATION ${TARGETDIR_VER} +) + # Install more files specified elsewhere. delayed_do_install(${TARGETDIR_VER}) diff --git a/source/creator/creator_args.cc b/source/creator/creator_args.cc index 95dcef1c733..03bdec798e1 100644 --- a/source/creator/creator_args.cc +++ b/source/creator/creator_args.cc @@ -853,6 +853,7 @@ static void print_help(bArgs *ba, bool all) PRINT("\n"); PRINT(" $BLENDER_SYSTEM_RESOURCES Replace default directory of all bundled resource files.\n"); PRINT(" $BLENDER_SYSTEM_SCRIPTS Directory to add more bundled scripts.\n"); + PRINT(" $BLENDER_SYSTEM_EXTENSIONS Directory for system extensions repository.\n"); PRINT(" $BLENDER_SYSTEM_DATAFILES Directory to replace bundled datafiles.\n"); PRINT(" $BLENDER_SYSTEM_PYTHON Directory to replace bundled Python libraries.\n"); @@ -1533,6 +1534,9 @@ static const char arg_handle_env_system_set_doc_scripts[] = static const char arg_handle_env_system_set_doc_python[] = "\n\t" "Set the " STRINGIFY_ARG(BLENDER_SYSTEM_PYTHON) " environment variable."; +static const char arg_handle_env_system_set_doc_extensions[] = + "\n\t" + "Set the " STRINGIFY_ARG(BLENDER_SYSTEM_EXTENSIONS) " environment variable."; static int arg_handle_env_system_set(int argc, const char **argv, void * /*data*/) { @@ -2542,6 +2546,11 @@ void main_args_setup(bContext *C, bArgs *ba, bool all) ba, nullptr, "--env-system-scripts", CB_EX(arg_handle_env_system_set, scripts), nullptr); BLI_args_add( ba, nullptr, "--env-system-python", CB_EX(arg_handle_env_system_set, python), nullptr); + BLI_args_add(ba, + nullptr, + "--env-system-extensions", + CB_EX(arg_handle_env_system_set, extensions), + nullptr); BLI_args_add(ba, "-t", "--threads", CB(arg_handle_threads_set), nullptr);