Core: Libraries: Fix library parenting when libraries are deleted.
So far, when deleting a library (either explicitely, or through e.g. relocation), its dependencies would get a `nullptr` parent, and therefore become 'directly used' libraries. This commit adds a new util to rebuild the libraries hieararchy, and calls it when a Library ID is deleted. NOTE: While logic is somewhat similar to what liboverride resync does to sort the libraries by indirect levels (`lib_override_libraries_index_define`), there are some key differences here, notably the fact that if a library has a valid `parent` pointer, it is not replaced, even if a 'better' parent (less indirect library) could be found.
This commit is contained in:
parent
dc2ec78f1d
commit
1dca5af712
|
@ -15,3 +15,9 @@ struct Library;
|
|||
struct Main;
|
||||
|
||||
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath);
|
||||
|
||||
/**
|
||||
* Rebuild the hierarchy of libraries, after e.g. deleting or relocating one, often some indirectly
|
||||
* linked libraries lose their 'parent' pointer, making them wrongly directly used ones.
|
||||
*/
|
||||
void BKE_library_main_rebuild_hierarchy(Main *bmain);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "BKE_lib_override.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
#include "BKE_material.h"
|
||||
|
@ -1916,6 +1917,8 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
|
|||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
|
||||
/* Resync overrides if needed. */
|
||||
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
|
||||
BlendFileReadReport report{};
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_override.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
|
||||
|
@ -213,9 +214,15 @@ void BKE_id_free_us(Main *bmain, void *idv) /* test users */
|
|||
}
|
||||
|
||||
if (id->us == 0) {
|
||||
const bool is_lib = GS(id->name) == ID_LI;
|
||||
|
||||
BKE_libblock_unlink(bmain, id, false, false);
|
||||
|
||||
BKE_id_free(bmain, id);
|
||||
|
||||
if (is_lib) {
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,6 +233,7 @@ static size_t id_delete(Main *bmain,
|
|||
const int tag = LIB_TAG_DOIT;
|
||||
ListBase *lbarray[INDEX_ID_MAX];
|
||||
int base_count, i;
|
||||
bool has_deleted_library = false;
|
||||
|
||||
/* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database,
|
||||
* and has already properly unlinked its other IDs usages.
|
||||
|
@ -400,6 +408,9 @@ static size_t id_delete(Main *bmain,
|
|||
ID_REAL_USERS(id),
|
||||
(id->tag & LIB_TAG_EXTRAUSER_SET) != 0 ? 1 : 0);
|
||||
}
|
||||
if (!has_deleted_library && GS(id->name) == ID_LI) {
|
||||
has_deleted_library = true;
|
||||
}
|
||||
id_free(bmain, id, free_flag, !do_tagged_deletion);
|
||||
++num_datablocks_deleted;
|
||||
}
|
||||
|
@ -409,6 +420,10 @@ static size_t id_delete(Main *bmain,
|
|||
BKE_layer_collection_resync_allow();
|
||||
BKE_main_collection_sync_remap(bmain);
|
||||
|
||||
if (has_deleted_library) {
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
}
|
||||
|
||||
bmain->is_memfile_undo_written = false;
|
||||
return num_datablocks_deleted;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -136,3 +138,158 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
|
|||
BLI_path_abs(lib->filepath_abs, blendfile_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void rebuild_hierarchy_best_parent_find(Main *bmain,
|
||||
blender::Set<Library *> &directly_used_libs,
|
||||
Library *lib)
|
||||
{
|
||||
BLI_assert(!directly_used_libs.contains(lib));
|
||||
|
||||
Library *best_parent_lib = nullptr;
|
||||
bool do_break = false;
|
||||
ListBase *lb;
|
||||
ID *id_iter;
|
||||
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
|
||||
if (!ID_IS_LINKED(id_iter) || id_iter->lib != lib) {
|
||||
continue;
|
||||
}
|
||||
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
|
||||
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
|
||||
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
|
||||
ID *from_id = item->id_pointer.from;
|
||||
if (!ID_IS_LINKED(from_id)) {
|
||||
BLI_assert_unreachable();
|
||||
continue;
|
||||
}
|
||||
Library *from_id_lib = from_id->lib;
|
||||
if (from_id_lib == lib) {
|
||||
continue;
|
||||
}
|
||||
if (directly_used_libs.contains(from_id_lib)) {
|
||||
/* Found the first best possible candidate, no need to search further. */
|
||||
BLI_assert(best_parent_lib == nullptr || best_parent_lib->temp_index > 0);
|
||||
best_parent_lib = from_id_lib;
|
||||
do_break = true;
|
||||
break;
|
||||
}
|
||||
if (!from_id_lib->parent) {
|
||||
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, from_id_lib);
|
||||
}
|
||||
if (!best_parent_lib || best_parent_lib->temp_index > from_id_lib->temp_index) {
|
||||
best_parent_lib = from_id_lib;
|
||||
if (best_parent_lib->temp_index == 0) {
|
||||
/* Found the first best possible candidate, no need to search further. */
|
||||
BLI_assert(directly_used_libs.contains(best_parent_lib));
|
||||
do_break = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (do_break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
if (do_break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_END;
|
||||
|
||||
/* NOTE: It may happen that no parent library is found, e.g. if after deleting a directly used
|
||||
* library, its indirect dependency is still around, but none of its linked IDs are used by local
|
||||
* data. */
|
||||
if (best_parent_lib) {
|
||||
lib->parent = best_parent_lib;
|
||||
lib->temp_index = best_parent_lib->temp_index + 1;
|
||||
}
|
||||
else {
|
||||
lib->parent = nullptr;
|
||||
lib->temp_index = 0;
|
||||
directly_used_libs.add(lib);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_library_main_rebuild_hierarchy(Main *bmain)
|
||||
{
|
||||
BKE_main_relations_create(bmain, 0);
|
||||
|
||||
/* Find all libraries with directly linked IDs (i.e. IDs used by local data). */
|
||||
blender::Set<Library *> directly_used_libs;
|
||||
ID *id_iter;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
|
||||
if (!ID_IS_LINKED(id_iter)) {
|
||||
continue;
|
||||
}
|
||||
id_iter->lib->temp_index = 0;
|
||||
if (directly_used_libs.contains(id_iter->lib)) {
|
||||
continue;
|
||||
}
|
||||
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
|
||||
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
|
||||
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
|
||||
if (!ID_IS_LINKED(item->id_pointer.from)) {
|
||||
directly_used_libs.add(id_iter->lib);
|
||||
id_iter->lib->parent = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
||||
/* A directly used library. */
|
||||
if (directly_used_libs.contains(lib_iter)) {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Assume existing parent is still valid, since it was not cleared in previous loop above.
|
||||
* Just compute 'hierarchy value' in temp index, if needed. */
|
||||
if (lib_iter->parent) {
|
||||
if (lib_iter->temp_index > 0) {
|
||||
continue;
|
||||
}
|
||||
blender::Vector<Library *> parent_libraries;
|
||||
for (Library *parent_lib_iter = lib_iter;
|
||||
parent_lib_iter && parent_lib_iter->temp_index == 0;
|
||||
parent_lib_iter = parent_lib_iter->parent)
|
||||
{
|
||||
parent_libraries.append(parent_lib_iter);
|
||||
}
|
||||
int parent_temp_index = parent_libraries.last()->temp_index + int(parent_libraries.size()) -
|
||||
1;
|
||||
for (Library *parent_lib_iter : parent_libraries) {
|
||||
BLI_assert(parent_lib_iter != parent_libraries.last() ||
|
||||
parent_lib_iter->temp_index == parent_temp_index);
|
||||
parent_lib_iter->temp_index = parent_temp_index--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise, it's an indirectly used library with no known parent, another loop is needed to
|
||||
* ansure all knwon hierarcy has valid indices when trying to find the best valid parent
|
||||
* library. */
|
||||
}
|
||||
|
||||
/* For all libraries known to be indirect, but without a known parent, find a best valid parent
|
||||
* (i.e. a 'most directly used' library). */
|
||||
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
||||
/* A directly used library. */
|
||||
if (directly_used_libs.contains(lib_iter)) {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lib_iter->parent) {
|
||||
BLI_assert(lib_iter->temp_index > 0);
|
||||
}
|
||||
else {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, lib_iter);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_main_relations_free(bmain);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue