#721 Unmet dependencies not detected

SVN:trunk[2739]
This commit is contained in:
Romain Quetiez
2013-05-16 08:54:50 +00:00
parent a194308486
commit a62c1946a6
4 changed files with 114 additions and 27 deletions

View File

@@ -23,6 +23,10 @@
* @license http://opensource.org/licenses/AGPL-3.0 * @license http://opensource.org/licenses/AGPL-3.0
*/ */
class MissingDependencyException extends Exception
{
}
class ModuleDiscovery class ModuleDiscovery
{ {
static $m_aModuleArgs = array( static $m_aModuleArgs = array(
@@ -106,13 +110,22 @@ class ModuleDiscovery
} }
} }
protected static function GetModules($oP = null) /**
*
* @param bool $bAbortOnMissingDependency ...
* @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted
*/
protected static function GetModules($bAbortOnMissingDependency = false, $aModulesToLoad = null)
{ {
// Order the modules to take into account their inter-dependencies // Order the modules to take into account their inter-dependencies
$aDependencies = array(); $aDependencies = array();
foreach(self::$m_aModules as $sId => $aModule) foreach(self::$m_aModules as $sId => $aModule)
{ {
$aDependencies[$sId] = $aModule['dependencies']; list($sModuleName, $sModuleVersion) = self::GetModuleName($sId);
if (is_null($aModulesToLoad) || in_array($sModuleName, $aModulesToLoad))
{
$aDependencies[$sId] = $aModule['dependencies'];
}
} }
ksort($aDependencies); ksort($aDependencies);
$aOrderedModules = array(); $aOrderedModules = array();
@@ -137,27 +150,16 @@ class ModuleDiscovery
} }
$iLoopCount++; $iLoopCount++;
} }
if (count($aDependencies) >0) if ($bAbortOnMissingDependency && count($aDependencies) > 0)
{ {
$sHtml = "<ul><b>Warning: the following modules have unmet dependencies, and have been ignored:</b>\n"; $aModuleDeps = array();
foreach($aDependencies as $sId => $aDeps) foreach($aDependencies as $sId => $aDeps)
{ {
$aModule = self::$m_aModules[$sId]; $aModule = self::$m_aModules[$sId];
$sHtml.= "<li>{$aModule['label']} (id: $sId), depends on: ".implode(', ', $aDeps)."</li>"; $aModuleDeps[] = "{$aModule['label']} (id: $sId) depends on ".implode(' + ', $aDeps);
}
$sHtml .= "</ul>\n";
if ($oP instanceof SetupPage)
{
$oP->warning($sHtml); // used in the context of the installation
}
elseif (class_exists('SetupPage'))
{
SetupPage::log_warning($sHtml); // used in the context of ?
}
else
{
echo $sHtml; // used in the context of the compiler
} }
$sMessage = "The following modules have unmet dependencies: ".implode(', ', $aModuleDeps);
throw new MissingDependencyException($sMessage);
} }
// Return the ordered list, so that the dependencies are met... // Return the ordered list, so that the dependencies are met...
$aResult = array(); $aResult = array();
@@ -207,9 +209,11 @@ class ModuleDiscovery
* Search (on the disk) for all defined iTop modules, load them and returns the list (as an array) * Search (on the disk) for all defined iTop modules, load them and returns the list (as an array)
* of the possible iTop modules to install * of the possible iTop modules to install
* @param aSearchDirs Array of directories to search (absolute paths) * @param aSearchDirs Array of directories to search (absolute paths)
* @param bool $bAbortOnMissingDependency ...
* @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted
* @return Hash A big array moduleID => ModuleData * @return Hash A big array moduleID => ModuleData
*/ */
public static function GetAvailableModules($aSearchDirs, $oP = null) public static function GetAvailableModules($aSearchDirs, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
{ {
if (self::$m_aSearchDirs != $aSearchDirs) if (self::$m_aSearchDirs != $aSearchDirs)
{ {
@@ -232,12 +236,12 @@ class ModuleDiscovery
clearstatcache(); clearstatcache();
self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir)); self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir));
} }
return self::GetModules($oP); return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
} }
else else
{ {
// Reuse the previous results // Reuse the previous results
return self::GetModules($oP); return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
} }
} }

View File

@@ -95,6 +95,8 @@ class RunTimeEnvironment
* *
* @param Config $oConfig Defines the target environment (DB) * @param Config $oConfig Defines the target environment (DB)
* @param mixed $modulesPath Either a single string or an array of absolute paths * @param mixed $modulesPath Either a single string or an array of absolute paths
* @param bool $bAbortOnMissingDependency ...
* @param hash $aModulesToLoad List of modules to search for, defaults to all if ommitted
* @return hash Array with the following format: * @return hash Array with the following format:
* array => * array =>
* 'iTop' => array( * 'iTop' => array(
@@ -118,7 +120,7 @@ class RunTimeEnvironment
* ) * )
* ) * )
*/ */
public function AnalyzeInstallation($oConfig, $modulesPath) public function AnalyzeInstallation($oConfig, $modulesPath, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
{ {
$aRes = array( $aRes = array(
ROOT_MODULE => array( ROOT_MODULE => array(
@@ -130,7 +132,7 @@ class RunTimeEnvironment
); );
$aDirs = is_array($modulesPath) ? $modulesPath : array($modulesPath); $aDirs = is_array($modulesPath) ? $modulesPath : array($modulesPath);
$aModules = ModuleDiscovery::GetAvailableModules($aDirs); $aModules = ModuleDiscovery::GetAvailableModules($aDirs, $bAbortOnMissingDependency, $aModulesToLoad);
foreach($aModules as $sModuleId => $aModuleInfo) foreach($aModules as $sModuleId => $aModuleInfo)
{ {
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId); list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);

View File

@@ -336,6 +336,33 @@ class SetupUtils
return $aResult; return $aResult;
} }
/**
* Check that the selected modules meet their dependencies
*/
static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules)
{
$aResult = array();
SetupPage::log('Info - CheckSelectedModules');
$aDirsToScan = array(APPROOT.$sSourceDir);
$sExtensionsPath = APPROOT.$sExtensionDir;
if (is_dir($sExtensionsPath))
{
// if the extensions dir exists, scan it for additional modules as well
$aDirsToScan[] = $sExtensionsPath;
}
require_once(APPROOT.'setup/modulediscovery.class.inc.php');
try
{
ModuleDiscovery::GetAvailableModules($aDirsToScan, true, $aSelectedModules);
}
catch(MissingDependencyException $e)
{
$aResult[] = new CheckResult(CheckResult::ERROR, $e->getMessage());
}
return $aResult;
}
/** /**
* Check that the backup could be executed * Check that the backup could be executed
* @param Page $oP The page used only for its 'log' method * @param Page $oP The page used only for its 'log' method
@@ -1008,7 +1035,12 @@ EOF
return $sHtml; return $sHtml;
} }
public static function AnalyzeInstallation($oWizard) /**
*
* @param bool $bAbortOnMissingDependency ...
* @param array $aModulesToLoad List of modules to search for, defaults to all if ommitted
*/
public static function AnalyzeInstallation($oWizard, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
{ {
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php'); require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
$oConfig = new Config(); $oConfig = new Config();
@@ -1048,7 +1080,7 @@ EOF
$aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from'); $aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from');
} }
$oProductionEnv = new RunTimeEnvironment(); $oProductionEnv = new RunTimeEnvironment();
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan); $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad);
return $aAvailableModules; return $aAvailableModules;
} }

View File

@@ -1095,6 +1095,16 @@ class WizStepModulesChoice extends WizardStep
protected function DisplayStep($oPage) protected function DisplayStep($oPage)
{ {
// Sanity check (not stopper, to let developpers go further...)
try
{
SetupUtils::AnalyzeInstallation($this->oWizard, true);
}
catch(MissingDependencyException $e)
{
$oPage->warning($e->getMessage());
}
$this->bUpgrade = ($this->oWizard->GetParameter('install_mode') != 'install'); $this->bUpgrade = ($this->oWizard->GetParameter('install_mode') != 'install');
$aStepInfo = $this->GetStepInfo(); $aStepInfo = $this->GetStepInfo();
$oPage->add_style("div.choice { margin: 0.5em;}"); $oPage->add_style("div.choice { margin: 0.5em;}");
@@ -1657,6 +1667,28 @@ EOF
*/ */
class WizStepSummary extends WizardStep class WizStepSummary extends WizardStep
{ {
protected $bDependencyCheck = null;
protected $sDependencyIssue = null;
protected function CheckDependencies()
{
if (is_null($this->bDependencyCheck))
{
$aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true);
$this->bDependencyCheck = true;
try
{
SetupUtils::AnalyzeInstallation($this->oWizard, true, $aSelectedModules);
}
catch(MissingDependencyException $e)
{
$this->bDependencyCheck = false;
$this->sDependencyIssue = $e->getMessage();
}
}
return $this->bDependencyCheck;
}
public function GetTitle() public function GetTitle()
{ {
$sMode = $this->oWizard->GetParameter('mode', 'install'); $sMode = $this->oWizard->GetParameter('mode', 'install');
@@ -1684,7 +1716,19 @@ class WizStepSummary extends WizardStep
{ {
return ' Install ! '; return ' Install ! ';
} }
public function CanMoveForward()
{
if ($this->CheckDependencies())
{
return true;
}
else
{
return false;
}
}
public function ProcessParams($bMoveForward = true) public function ProcessParams($bMoveForward = true)
{ {
return array('class' => 'WizStepDone', 'state' => ''); return array('class' => 'WizStepDone', 'state' => '');
@@ -1736,7 +1780,7 @@ class WizStepSummary extends WizardStep
} }
EOF EOF
); );
$aInstallParams = $this->BuildConfig(); $aInstallParams = $this->BuildConfig();
$sMode = $aInstallParams['mode']; $sMode = $aInstallParams['mode'];
@@ -1847,6 +1891,11 @@ EOF
$sJSONData = json_encode($aInstallParams); $sJSONData = json_encode($aInstallParams);
$oPage->add('<input type="hidden" id="installer_parameters" value="'.htmlentities($sJSONData, ENT_QUOTES, 'UTF-8').'"/>'); $oPage->add('<input type="hidden" id="installer_parameters" value="'.htmlentities($sJSONData, ENT_QUOTES, 'UTF-8').'"/>');
if (!$this->CheckDependencies())
{
$oPage->error($this->sDependencyIssue);
}
$oPage->add_ready_script( $oPage->add_ready_script(
<<<EOF <<<EOF
$("#params_summary div").addClass('closed'); $("#params_summary div").addClass('closed');