- Add module for generating component documentation.
- - Produce an HTML dependency graph.
-
- Add module for an initial empty PEAR template
- Add a commit module that automatically adds a changelog entry in
}
/**
+ * Returns the dependency list handler for a package.
+ *
+ * @return Components_Runner_Dependencies The dependency handler.
+ */
+ public function getRunnerDependencies()
+ {
+ return $this->getInstance('Components_Runner_Dependencies');
+ }
+
+ /**
* Returns the installer for a package.
*
* @return Components_Runner_Installer The installer.
--- /dev/null
+<?php
+/**
+ * Components_Helper_ListRun:: provides a utility that produces a dependency
+ * list and records what has already been listed.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+
+/**
+ * Components_Helper_ListRun:: provides a utility that produces a dependency
+ * list and records what has already been listed.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+class Components_Helper_ListRun
+{
+ /**
+ * The output handler.
+ *
+ * @param Component_Output
+ */
+ private $_output;
+
+ /**
+ * The list of dependencies already displayed.
+ *
+ * @var array
+ */
+ private $_displayed_dependencies = array();
+
+ /**
+ * The list of elements in case we are producing condensed output.
+ *
+ * @var array
+ */
+ private $_quiet_list = array();
+
+ /**
+ * Constructor.
+ *
+ * @param Component_Output $output The output handler.
+ */
+ public function __construct(Components_Output $output)
+ {
+ $this->_output = $output;
+ if ($this->_output->isVerbose()) {
+ $output->bold('List contains optional dependencies!');
+ } else {
+ $output->bold('List only contains required dependencies!');
+ }
+ $output->blue('Dependencies on PEAR itself are not displayed.');
+ $output->bold('');
+ }
+
+ /**
+ * List a Horde component as dependency.
+ *
+ * @param Components_Pear_Package $package The package that should be listed.
+ * @param int $level The current list level.
+ * @param string $parent Name of the parent element.
+ * @param boolean $reqired Is this a required element?
+ *
+ * @return boolean True in case listing should continue.
+ */
+ public function listHordePackage(
+ Components_Pear_Package $package,
+ $level,
+ $parent,
+ $required
+ ) {
+ if (!$this->_output->isVerbose() && !$required) {
+ return false;
+ }
+ $key = $package->getName() . '@pear.horde.org';
+ if (!$this->_output->isQuiet()) {
+ if (in_array($key, array_keys($this->_displayed_dependencies))) {
+ if (empty($this->_displayed_dependencies[$key])) {
+ $add = ' (RECURSION) ***STOP***';
+ } else {
+ $add = ' (ALREADY LISTED WITH '
+ . $this->_displayed_dependencies[$key] . ') ***STOP***';
+ }
+ } else {
+ $add = '';
+ }
+ $this->_output->green(
+ Horde_String::pad(
+ $this->_listLevel($level) . '|_'
+ . $package->getName(), 40
+ )
+ . Horde_String::pad(' [pear.horde.org]', 20)
+ . $add
+ );
+ if (in_array($key, array_keys($this->_displayed_dependencies))) {
+ return false;
+ } else {
+ $this->_displayed_dependencies[$key] = $parent;
+ return true;
+ }
+ } else {
+ $this->_quiet_list[$key] = array(
+ 'channel' => 'pear.horde.org',
+ 'name' => $package->getName(),
+ 'color' => 'green'
+ );
+ return true;
+ }
+ }
+
+ /**
+ * List an external package as dependency.
+ *
+ * @param array $dependency The dependency that should be listed.
+ * @param int $level The current list level.
+ *
+ * @return NULL
+ */
+ public function listExternalPackage(array $dependency, $level)
+ {
+ // Showing PEAR does not make much sense.
+ if ($dependency['name'] == 'PEAR'
+ && $dependency['channel'] == 'pear.php.net') {
+ return;
+ }
+
+ $key = $dependency['name'] . '@' . $dependency['channel'];
+ if (!$this->_output->isQuiet()) {
+ $this->_output->yellow(
+ Horde_String::pad(
+ $this->_listLevel($level) . '|_'
+ . $dependency['name'], 40
+ )
+ . Horde_String::pad(' [' . $dependency['channel'] . ']', 20)
+ . ' (EXTERNAL) ***STOP***'
+ );
+ } else {
+ $this->_quiet_list[$key] = array(
+ 'channel' => $dependency['channel'],
+ 'name' => $dependency['name'],
+ 'color' => 'yellow'
+ );
+ }
+ }
+
+ /**
+ * Wrap up the listing. This will produce a condensed list of packages in
+ * case quiet Output was requested.
+ *
+ * @return NULL
+ */
+ public function finish()
+ {
+ if (empty($this->_quiet_list)) {
+ return;
+ }
+ $channels = array();
+ $names = array();
+ $colors = array();
+ foreach ($this->_quiet_list as $key => $element) {
+ $channels[] = $element['channel'];
+ $names[] = $element['name'];
+ $colors[] = $element['color'];
+ }
+ array_multisort($channels, $names, $colors);
+ foreach ($names as $key => $name) {
+ $this->_output->$colors[$key](
+ Horde_String::pad($name, 20) .
+ Horde_String::pad('[' . $channels[$key] . ']', 20)
+ );
+ }
+ }
+
+ /**
+ * Produces an amount of whitespace depending on the specified level.
+ *
+ * @param int $level The level of indentation.
+ *
+ * @return string Whitespace.
+ */
+ private function _listLevel($level)
+ {
+ return str_repeat(' ', $level);
+ }
+}
\ No newline at end of file
/**
* Install the tree of packages into the specified environment.
*
- * @param string $package_file Path to the package file
- * representing the element
- * at the root of the
- * dependency tree.
+ * @param string $package_file Path to the package file representing the element
+ * at the root of the dependency tree.
+ *
* @return NULL
*/
public function installTreeInEnvironment($package_file) {
}
/**
+ * List the dependency tree for the specified root package element.
+ *
+ * @param string $package_file Path to the package file representing
+ * the element at the root of the
+ * dependency tree.
+ * @param Component_Output $output The output handler.
+ *
+ * @return NULL
+ */
+ public function listDependencyTree(
+ $package_file,
+ Components_Output $output
+ ) {
+ $run = new Components_Helper_ListRun($output);
+ $this->_getHordeChildElement($package_file)
+ ->listDependencies($run, 0);
+ $run->finish();
+ }
+
+ /**
* Get the children for an element.
*
* @param array $dependencies The dependencies of a package to be
. 'framework' . DIRECTORY_SEPARATOR . $dependency['name']
. DIRECTORY_SEPARATOR . 'package.xml';
}
- $children[] = $this->_getHordeChildElement($package_file);
+ $children[] = $this->_getHordeChildElement(
+ $package_file,
+ isset($dependency['optional']) && $dependency['optional'] == 'no'
+ );
}
return $children;
}
/**
* Return a Horde child element.
*
- * @param string $package_file Path to the package
- * file representing the
- * element at the root
- * of the dependency tree.
+ * @param string $package_file Path to the package file representing the
+ * element at the root of the dependency tree.
+ * @param boolean $required Is this a required element?
* @return NULL
*/
- private function _getHordeChildElement($package_file)
+ private function _getHordeChildElement($package_file, $required = true)
{
return new Components_Helper_Tree_Element(
$this->_factory->createPackageForEnvironment(
$package_file, $this->_environment
),
$package_file,
+ $required,
$this
);
}
private $_package_file;
/**
+ * Is this a required element?
+ *
+ * @var boolean
+ */
+ private $_required;
+
+ /**
* The parent tree for this child.
*
* @var Components_Helper_Tree
*
* @param Components_Pear_Package $package The root of the dependency tree.
* @param string $package_file The path to the package file.
+ * @param boolean $required Is this a required element?
* @param Components_Helper_Tree $tree The parent tree for this child.
*/
public function __construct(
Components_Pear_Package $package,
$package_file,
+ $required,
Components_Helper_Tree $tree
) {
$this->_package = $package;
$this->_package_file = $package_file;
+ $this->_required = $required;
$this->_tree = $tree;
}
}
$run->installHordePackageOnce($this->_package_file);
}
+
+ /**
+ * List the dependency tree for this package.
+ *
+ * @param Components_Helper_ListRun $run The current listing run.
+ * @param int $level The current list level.
+ * @param string $parent The name of the parent element.
+ *
+ * @return NULL
+ */
+ public function listDependencies(
+ Components_Helper_ListRun $run,
+ $level,
+ $parent = ''
+ ) {
+ if ($run->listHordePackage($this->_package, $level, $parent, $this->_required)) {
+ foreach ($this->_package->listAllExternalDependencies() as $dependency) {
+ $run->listExternalPackage($dependency, $level + 1);
+ }
+ foreach (
+ $this->_tree->getChildren(
+ $this->_package->listAllHordeDependencies()
+ ) as $child
+ ) {
+ $child->listDependencies($run, $level + 1, $this->_package->getName());
+ }
+ }
+ }
+
}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Components_Module_Dependencies:: generates a dependency listing for the
+ * specified package.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+
+/**
+ * Components_Module_Dependencies:: generates a dependency listing for the
+ * specified package.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+class Components_Module_Dependencies
+extends Components_Module_Base
+{
+ /**
+ * Return the title for the option group representing this module.
+ *
+ * @return string The group title.
+ */
+ public function getOptionGroupTitle()
+ {
+ return 'Package Dependencies';
+ }
+
+ /**
+ * Return the description for the option group representing this module.
+ *
+ * @return string The group description.
+ */
+ public function getOptionGroupDescription()
+ {
+ return 'This module generates a list of dependencies for the specified package';
+ }
+
+ /**
+ * Return the options for this module.
+ *
+ * @return array The group options.
+ */
+ public function getOptionGroupOptions()
+ {
+ return array(
+ new Horde_Argv_Option(
+ '-L',
+ '--list-deps',
+ array(
+ 'action' => 'store_true',
+ 'help' => 'generate a dependency listing'
+ )
+ ),
+ );
+ }
+
+ /**
+ * Determine if this module should act. Run all required actions if it has
+ * been instructed to do so.
+ *
+ * @return NULL
+ */
+ public function handle(Components_Config $config)
+ {
+ $options = $config->getOptions();
+ if (!empty($options['list_deps'])) {
+ $this->_dependencies->getRunnerDependencies()->run();
+ }
+ }
+}
$this->_nocolor = !empty($options['nocolor']);
}
+ public function bold($text)
+ {
+ if ($this->_nocolor) {
+ $this->_cli->writeln($text);
+ } else {
+ $this->_cli->writeln($this->_cli->bold($text));
+ }
+ }
+
+ public function blue($text)
+ {
+ if ($this->_nocolor) {
+ $this->_cli->writeln($text);
+ } else {
+ $this->_cli->writeln($this->_cli->blue($text));
+ }
+ }
+
+ public function green($text)
+ {
+ if ($this->_nocolor) {
+ $this->_cli->writeln($text);
+ } else {
+ $this->_cli->writeln($this->_cli->green($text));
+ }
+ }
+
+ public function yellow($text)
+ {
+ if ($this->_nocolor) {
+ $this->_cli->writeln($text);
+ } else {
+ $this->_cli->writeln($this->_cli->yellow($text));
+ }
+ }
+
public function ok($text)
{
if ($this->_quiet) {
return $type;
}
}
+
+ public function isVerbose()
+ {
+ return $this->_verbose;
+ }
+
+ public function isQuiet()
+ {
+ return $this->_quiet;
+ }
}
\ No newline at end of file
}
/**
+ * Create a tree helper for a specific PEAR environment..
+ *
+ * @param string $config_file The path to the configuration file.
+ * @param string $root_path The basic root path for Horde packages.
+ * @param array $options The application options
+ *
+ * @return Components_Helper_Tree The tree helper.
+ */
+ public function createSimpleTreeHelper( $root_path)
+ {
+ return new Components_Helper_Tree(
+ $this,
+ $this->_dependencies->createInstance('Components_Pear_InstallLocation'),
+ $root_path
+ );
+ }
+
+ /**
* Return the PEAR Package representation.
*
* @param string $package_xml_path Path to the package.xml file.
}
/**
+ * Return the name for this package.
+ *
+ * @return string The package name.
+ */
+ public function getName()
+ {
+ return $this->_getPackageFile()->getName();
+ }
+
+ /**
* Return the description for this package.
*
* @return string The package description.
--- /dev/null
+<?php
+/**
+ * Components_Runner_Dependencies:: lists a tree of dependencies.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+
+/**
+ * Components_Runner_Dependencies:: lists a tree of dependencies.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Components
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+class Components_Runner_Dependencies
+{
+ /**
+ * The configuration for the current job.
+ *
+ * @var Components_Config
+ */
+ private $_config;
+
+ /**
+ * The factory for PEAR dependencies.
+ *
+ * @var Components_Pear_Factory
+ */
+ private $_factory;
+
+ /**
+ * The output handler.
+ *
+ * @param Component_Output
+ */
+ private $_output;
+
+ /**
+ * Constructor.
+ *
+ * @param Components_Config $config The configuration for the current
+ * job.
+ * @param Components_Pear_Factory $factory The factory for PEAR
+ * dependencies.
+ * @param Component_Output $output The output handler.
+ */
+ public function __construct(
+ Components_Config $config,
+ Components_Pear_Factory $factory,
+ Components_Output $output
+ ) {
+ $this->_config = $config;
+ $this->_factory = $factory;
+ $this->_output = $output;
+ }
+
+ public function run()
+ {
+ $options = $this->_config->getOptions();
+ $arguments = $this->_config->getArguments();
+ $this->_factory
+ ->createSimpleTreeHelper(dirname(realpath($arguments[0])))
+ ->listDependencyTree(
+ realpath($arguments[0]) . DIRECTORY_SEPARATOR . 'package.xml',
+ $this->_output
+ );
+ }
+}
*/
class Components_Runner_Installer
{
- private $_run;
-
/**
* The configuration for the current job.
*
<channel>pear.horde.org</channel>
</package>
<package>
+ <name>Util</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
<name>Injector</name>
<channel>pear.horde.org</channel>
</package>
--- /dev/null
+<?php
+/**
+ * Test the Dependencies module.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Components
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../../../Autoload.php';
+
+/**
+ * Test the Dependencies module.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Components
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Components
+ */
+class Components_Integration_Components_Module_DependenciesTest
+extends Components_StoryTestCase
+{
+ /**
+ * @scenario
+ */
+ public function theDependenciesModuleAddsTheLOptionInTheHelpOutput()
+ {
+ $this->given('the default Components setup')
+ ->when('calling the package with the help option')
+ ->then('the help will contain the option', '-L,\s*--list-deps');
+ }
+
+ /**
+ * @scenario
+ */
+ public function theTheLOptionListThePackageDependenciesAsTree()
+ {
+ $this->given('the default Components setup')
+ ->when('calling the package with the list dependencies option and a path to a Horde framework component')
+ ->then('the non-Horde dependencies of the component will be listed')
+ ->and('the Horde dependencies of the component will be listed');
+ }
+
+ /**
+ * @scenario
+ */
+ public function theTheVerboseLOptionListThePackageDependenciesAsTree()
+ {
+ $this->given('the default Components setup')
+ ->when('calling the package with the verbose list dependencies option and a path to a Horde framework component')
+ ->then('the non-Horde dependencies of the component will be listed')
+ ->and('the Horde dependencies of the component will be listed');
+ }
+
+ /**
+ * @scenario
+ */
+ public function theTheQuietLOptionListThePackageDependenciesAsTree()
+ {
+ $this->given('the default Components setup')
+ ->when('calling the package with the quiet list dependencies option and a path to a Horde framework component')
+ ->then('the non-Horde dependencies of the component will be listed')
+ ->and('the Horde dependencies of the component will be listed');
+ }
+}
\ No newline at end of file
{
$this->given('the default Components setup')
->when('calling the package with the help option')
- ->then('the help will contain the "i" option.');
+ ->then('the help will contain the option', '-i\s*INSTALL,\s*--install=INSTALL');
}
/**
);
$world['output'] = $this->_callUnstrictComponents();
break;
+ case 'calling the package with the list dependencies option and a path to a Horde framework component':
+ $_SERVER['argv'] = array(
+ 'horde-components',
+ '--list-deps',
+ dirname(__FILE__) . '/fixture/framework/Install'
+ );
+ $world['output'] = $this->_callUnstrictComponents();
+ break;
+ case 'calling the package with the verbose list dependencies option and a path to a Horde framework component':
+ $_SERVER['argv'] = array(
+ 'horde-components',
+ '--verbose',
+ '--list-deps',
+ dirname(__FILE__) . '/fixture/framework/Install'
+ );
+ $world['output'] = $this->_callUnstrictComponents();
+ break;
+ case 'calling the package with the quiet list dependencies option and a path to a Horde framework component':
+ $_SERVER['argv'] = array(
+ 'horde-components',
+ '--quiet',
+ '--list-deps',
+ dirname(__FILE__) . '/fixture/framework/Install'
+ );
+ $world['output'] = $this->_callUnstrictComponents();
+ break;
default:
return $this->notImplemented($action);
}
$world['output']
);
break;
- case 'the help will contain the "i" option.':
- $this->assertRegExp(
- '/-i\s*INSTALL,\s*--install=INSTALL/',
- $world['output']
- );
- break;
case 'the help will contain the option':
$this->assertRegExp(
'/' . $arguments[0] . '/',
$world['output']
);
break;
+ case 'the non-Horde dependencies of the component will be listed':
+ $this->assertContains(
+ 'Console_Getopt',
+ $world['output']
+ );
+ break;
+ case 'the Horde dependencies of the component will be listed':
+ $this->assertContains(
+ 'Dependency',
+ $world['output']
+ );
+ break;
default:
return $this->notImplemented($action);
}