diff --git a/includes/install.inc b/includes/install.inc index d68185e9145..2ce311ee59f 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -301,6 +301,24 @@ function drupal_verify_profile($profile, $locale) { * The modules to install. */ function drupal_install_modules($module_list = array()) { + $files = module_rebuild_cache(); + $module_list = array_flip(array_values($module_list)); + do { + $moved = FALSE; + foreach ($module_list as $module => $weight) { + $file = $files[$module]; + if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { + foreach ($file->info['dependencies'] as $dependency) { + if (isset($module_list[$dependency]) && $module_list[$module] < $module_list[$dependency] +1) { + $module_list[$module] = $module_list[$dependency] +1; + $moved = TRUE; + } + } + } + } + } while ($moved); + asort($module_list); + $module_list = array_keys($module_list); array_filter($module_list, '_drupal_install_module'); module_enable($module_list); } diff --git a/includes/module.inc b/includes/module.inc index c249a417589..651680b3b23 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -147,30 +147,74 @@ function module_rebuild_cache() { db_query("INSERT INTO {system} (name, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, serialize($files[$filename]->info), 'module', $file->filename, 0, 0, $bootstrap); } } - $files = _module_build_dependents($files); + $files = _module_build_dependencies($files); return $files; } /** - * Find dependents; modules that are required by other modules. - * Adds an array of dependents to the $file->info array. + * Find dependencies any level deep and fill in dependents information too. * + * If module A depends on B which in turn depends on C then this function will + * add C to the list of modules A depends on. This will be repeated until + * module A has a list of all modules it depends on. If it depends on itself, + * called a circular dependency, that's marked by adding a nonexistent module, + * called -circular- to this list of modules. Because this does not exist, + * it'll be impossible to switch module A on. + * + * Also we fill in a dependents array in $file->info. Using the names above, + * the dependents array of module B lists A. + * + * @param $files + * The array of filesystem objects used to rebuild the cache. * @return - * The list of files array with dependents added where applicable. + * The same array with dependencies and dependents added where applicable. */ -function _module_build_dependents($files) { - foreach ($files as $filename => $file) { - if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { - foreach ($file->info['dependencies'] as $dependency) { - if (!empty($files[$dependency]) && is_array($files[$dependency]->info)) { - if (!isset($files[$dependency]->info['dependents'])) { - $files[$dependency]->info['dependents'] = array(); +function _module_build_dependencies($files) { + do { + $new_dependency = FALSE; + foreach ($files as $filename => $file) { + // We will modify this object (module A, see doxygen for module A, B, C). + $file = &$files[$filename]; + if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { + foreach ($file->info['dependencies'] as $dependency_name) { + // This is a nonexistent module. + if ($dependency_name == '-circular-') { + continue; + } + // $dependency_name is module B (again, see doxygen). + $files[$dependency_name]->info['dependents'][$filename] = $filename; + $dependency = $files[$dependency_name]; + if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) { + // Let's find possible C modules. + foreach ($dependency->info['dependencies'] as $candidate) { + if (array_search($candidate, $file->info['dependencies']) === FALSE) { + // Is this a circular dependency? + if ($candidate == $filename) { + // As a module name can not contain dashes, this makes + // impossible to switch on the module. + $candidate = '-circular-'; + // Do not display the message or add -circular- more than once. + if (array_search($candidate, $file->info['dependencies']) !== FALSE) { + continue; + } + drupal_set_message(t('%module is part of a circular dependency. This is not supported and you will not be able to switch it on.', array('%module' => $file->info['name'])), 'error'); + } + else { + // We added a new dependency to module A. The next loop will + // be able to use this as "B module" thus finding even + // deeper dependencies. + $new_dependency = TRUE; + } + $file->info['dependencies'][] = $candidate; + } + } } - $files[$dependency]->info['dependents'][] = $filename; } } + // Don't forget to break the reference. + unset($file); } - } + } while ($new_dependency); return $files; } diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 368056a2b9c..a81f36f3da0 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -535,6 +535,12 @@ function system_theme_settings_submit($form, &$form_state) { * Recursively check compatability */ function _system_is_incompatible(&$incompatible, $files, $file) { + static $seen; + // We need to protect ourselves in case of a circular dependency. + if (isset($seen[$file->name])) { + return isset($incompatible[$file->name]); + } + $seen[$file->name] = TRUE; if (isset($incompatible[$file->name])) { return TRUE; }