diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 5feae341b7..8c843e9da1 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -76,6 +76,79 @@ upcase_string(const string &str) { } */ +/** + * Finds a dependency cycle between the given dependency mapping, starting at + * the node that is already placed in the given cycle vector. + */ +static bool find_dependency_cycle(vector_string &cycle, map > &dependencies) { + assert(!cycle.empty()); + + const set &deps = dependencies[cycle.back()]; + for (auto it = deps.begin(); it != deps.end(); ++it) { + auto it2 = std::find(cycle.begin(), cycle.end(), *it); + if (it2 != cycle.end()) { + // Chop off the part of the chain that is not relevant. + cycle.erase(cycle.begin(), it2); + cycle.push_back(*it); + return true; + } + + // Recurse. + cycle.push_back(*it); + if (find_dependency_cycle(cycle, dependencies)) { + return true; + } + cycle.pop_back(); + } + + return false; +} + +/** + * Given that a direct link has been established between the two libraries, + * finds the two types that make up this relationship and prints out the + * nature of their dependency. + */ +static bool print_dependent_types(const string &lib1, const string &lib2) { + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); + if (interrogate_type_has_module_name(thetype) && + interrogate_type_has_library_name(thetype) && + lib1 == interrogate_type_library_name(thetype) && + module_name == interrogate_type_module_name(thetype)) { + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype) && + interrogate_type_library_name(basetype) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") inherits from " + << interrogate_type_scoped_name(basetype) << " (" << lib2 << ")\n"; + return true; + } + } + + // It also counts if this is a typedef pointing to another type. + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped) && + interrogate_type_library_name(wrapped) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") is a typedef to " + << interrogate_type_scoped_name(wrapped) << " (" << lib2 << ")\n"; + } + } + } + } + return false; +} + int write_python_table_native(ostream &out) { out << "\n#include \"dtoolbase.h\"\n" << "#include \"interrogate_request.h\"\n\n" @@ -83,7 +156,7 @@ int write_python_table_native(ostream &out) { int count = 0; - vector_string libraries; + map > dependencies; // out << "extern \"C\" {\n"; @@ -99,23 +172,112 @@ int write_python_table_native(ostream &out) { // name add it to set of libraries if (interrogate_function_has_library_name(function_index)) { string library_name = interrogate_function_library_name(function_index); - if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { - libraries.push_back(library_name); - } + dependencies[library_name]; } // } } - for (int ti = 0; ti < interrogate_number_of_types(); ti++) { - TypeIndex thetype = interrogate_get_type(ti); + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); if (interrogate_type_has_module_name(thetype) && module_name == interrogate_type_module_name(thetype)) { if (interrogate_type_has_library_name(thetype)) { string library_name = interrogate_type_library_name(thetype); + set &deps = dependencies[library_name]; + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype)) { + string baselib = interrogate_type_library_name(basetype); + if (baselib != library_name) { + deps.insert(move(baselib)); + } + } + } + + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped)) { + string wrappedlib = interrogate_type_library_name(wrapped); + if (wrappedlib != library_name) { + deps.insert(move(wrappedlib)); + } + } + } + } + } + } + + // Now add the libraries in their proper ordering, based on dependencies. + vector_string libraries; + while (libraries.size() < dependencies.size()) { + // We have this check to make sure we don't enter an infinite loop. + bool added_any = false; + + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + + // Remove the dependencies that have already been added from the deps. + if (!deps.empty()) { + for (auto li = libraries.begin(); li != libraries.end(); ++li) { + deps.erase(*li); + } + } + + if (deps.empty()) { + // OK, no remaining dependencies, so we can add this. if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { libraries.push_back(library_name); + added_any = true; } } } + + if (!added_any) { + // Oh dear, we must have hit a circular dependency. Go through the + // remaining libraries to figure it out and print it. + cerr << "Circular dependency between libraries detected:\n"; + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + if (deps.empty()) { + continue; + } + + // But since it does indicate a potential architectural flaw, we do + // want to let the user know about this. + vector_string cycle; + cycle.push_back(library_name); + if (!find_dependency_cycle(cycle, dependencies)) { + continue; + } + assert(cycle.size() >= 2); + + // Show the cycle of library dependencies. + auto ci = cycle.begin(); + cerr << " " << *ci; + for (++ci; ci != cycle.end(); ++ci) { + cerr << " -> " << *ci; + } + cerr << "\n"; + + // Now print out the actual types that make up the cycle. + ci = cycle.begin(); + string prev = *ci; + for (++ci; ci != cycle.end(); ++ci) { + print_dependent_types(prev, *ci); + prev = *ci; + } + + // We have to arbitrarily break one of the dependencies in order to be + // able to proceed. Break the first dependency. + dependencies[cycle[0]].erase(cycle[1]); + } + } } vector_string::const_iterator ii;