summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pear/PEAR/Command/Install.php51
-rw-r--r--pear/PEAR/Common.php169
-rw-r--r--pear/PEAR/Installer.php297
-rw-r--r--pear/package-PEAR.xml14
4 files changed, 474 insertions, 57 deletions
diff --git a/pear/PEAR/Command/Install.php b/pear/PEAR/Command/Install.php
index 8026a7bfb1..8d7f8d9f3d 100644
--- a/pear/PEAR/Command/Install.php
+++ b/pear/PEAR/Command/Install.php
@@ -69,6 +69,14 @@ class PEAR_Command_Install extends PEAR_Command_Common
'ignore-errors' => array(
'doc' => 'force install even if there were errors',
),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
),
'doc' => '<package> ...
Installs one or more PEAR packages. You can specify a package to
@@ -123,6 +131,14 @@ four ways of specifying packages.
'ignore-errors' => array(
'doc' => 'force install even if there were errors',
),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
),
'doc' => '<package> ...
Upgrades one or more PEAR packages. See documentation for the
@@ -273,9 +289,20 @@ package if needed.
$this->ui->outputData(array('data' => "Will upgrade $package"), $command);
}
}
- foreach ($params as $pkg) {
- $bn = basename($pkg);
- $info = $this->installer->install($pkg, $options, $this->config);
+ $errors = array();
+ $downloaded = array();
+ $this->installer->download($params, $options, $this->config, $downloaded,
+ $errors);
+ if (count($errors)) {
+ $err['data'] = array($errors);
+ $err['headline'] = 'Install Errors';
+ $this->ui->outputData($err);
+ return $this->raiseError("$command failed");
+ }
+ $this->installer->sortPkgDeps($downloaded);
+ foreach ($downloaded as $pkg) {
+ $bn = basename($pkg['file']);
+ $info = $this->installer->install($pkg['file'], $options, $this->config);
if (is_array($info)) {
if ($this->config->get('verbose') > 0) {
$label = "$info[package] $info[version]";
@@ -303,6 +330,24 @@ package if needed.
if (sizeof($params) < 1) {
return $this->raiseError("Please supply the package(s) you want to uninstall");
}
+ include_once 'PEAR/Registry.php';
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $newparams = array();
+ $badparams = array();
+ foreach ($params as $pkg) {
+ $info = $reg->packageInfo($pkg);
+ if ($info === null) {
+ $badparams[] = $pkg;
+ } else {
+ $newparams[] = $info;
+ }
+ }
+ PEAR_Common::sortPkgDeps($newparams, true);
+ $params = array();
+ foreach($newparams as $info) {
+ $params[] = $info['info']['package'];
+ }
+ $params = array_merge($params, $badparams);
foreach ($params as $pkg) {
if ($this->installer->uninstall($pkg, $options)) {
if ($this->config->get('verbose') > 0) {
diff --git a/pear/PEAR/Common.php b/pear/PEAR/Common.php
index 8f75a86085..2b416e020d 100644
--- a/pear/PEAR/Common.php
+++ b/pear/PEAR/Common.php
@@ -699,7 +699,7 @@ class PEAR_Common extends PEAR
break;
}
}
- $tmpdir = System::mkTemp('-d pear');
+ $tmpdir = System::mkTemp(array('-d', 'pear'));
$this->addTempFile($tmpdir);
if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
return $this->raiseError('could not extract the package.xml file');
@@ -1715,6 +1715,173 @@ class PEAR_Common extends PEAR
}
// }}}
+ // {{{ sortPkgDeps()
+
+ /**
+ * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+ *
+ * It also removes duplicate dependencies
+ * @param array
+ * @param boolean Sort packages in reverse order if true
+ * @return array array of array(packagefilename, package.xml contents)
+ */
+ function sortPkgDeps(&$packages, $uninstall = false)
+ {
+ $ret = array();
+ if ($uninstall) {
+ foreach($packages as $packageinfo) {
+ $ret[] = array('info' => $packageinfo);
+ }
+ } else {
+ foreach($packages as $packagefile) {
+ if (!is_array($packagefile)) {
+ $ret[] = array('file' => $packagefile,
+ 'info' => $a = $this->infoFromAny($packagefile),
+ 'pkg' => $a['package']);
+ } else {
+ $ret[] = $packagefile;
+ }
+ }
+ }
+ $checkdupes = array();
+ $newret = array();
+ foreach($ret as $i => $p) {
+ if (!isset($checkdupes[$p['info']['package']])) {
+ $checkdupes[$p['info']['package']][] = $i;
+ $newret[] = $p;
+ }
+ }
+
+ $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
+ usort($newret, array('PEAR_Common', $func));
+ $packages = $newret;
+ }
+
+ // }}}
+ // {{{ _sortPkgDeps()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are installed first
+ *
+ * This is a crude compare, real dependency checking is done on install.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependent package first, and
+ * installation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDeps($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = PEAR_Common::_getPkgDeps($p1);
+ $p2deps = PEAR_Common::_getPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return -1; // put package 1 first
+ }
+ if (in_array($p2name, $p1deps)) {
+ return 1; // put package 2 first
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _sortPkgDepsRev()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are uninstalled last
+ *
+ * This is a crude compare, real dependency checking is done on uninstall.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependency first, and
+ * uninstallation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDepsRev($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = PEAR_Common::_getRevPkgDeps($p1);
+ $p2deps = PEAR_Common::_getRevPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return 1; // put package 1 last
+ }
+ if (in_array($p2name, $p1deps)) {
+ return -1; // put package 2 last
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array of package dependency names
+ * @access private
+ */
+ function _getPkgDeps($p)
+ {
+ if (!isset($p['info']['releases'])) {
+ return array();
+ }
+ $rel = array_shift($p['info']['releases']);
+ if (!isset($rel['deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($rel['deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _getRevPkgDeps()
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @access private
+ */
+ function _getRevPkgDeps($p)
+ {
+ if (!isset($p['info']['release_deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($p['info']['release_deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
}
?>
diff --git a/pear/PEAR/Installer.php b/pear/PEAR/Installer.php
index 6fb653a370..4b66e2b4fc 100644
--- a/pear/PEAR/Installer.php
+++ b/pear/PEAR/Installer.php
@@ -464,7 +464,235 @@ class PEAR_Installer extends PEAR_Common
}
// }}}
+ // {{{ _downloadFile()
+ function _downloadFile($pkgfile, &$config, $options, &$errors)
+ {
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($this->validPackageName($pkgfile)) {
+ if ($this->registry->packageExists($pkgfile)) {
+ if (empty($options['upgrade']) && empty($options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
+ }
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ $errors[] = "Could not open the package file: $pkgfile";
+ } else {
+ $errors[] = "No package file given";
+ }
+ return;
+ }
+ }
+
+ // Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $this->log(2, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+ $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ if (PEAR::isError($file)) {
+ return $this->raiseError($file);
+ }
+ $pkgfile = $file;
+ }
+ return $pkgfile;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ * @param PEAR_Config
+ * @param array options from the command line
+ * @param array this is the array that will be populated with packages to
+ * install. Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @param array this will be populated with any error messages
+ * @param false private recursion variable
+ * @param false private recursion variable
+ * @param false private recursion variable
+ */
+ function download($packages, $options, &$config, &$installpackages,
+ &$errors, $installed = false, $willinstall = false, $state = false)
+ {
+ // recognized options:
+ // - onlyreqdeps : install all required dependencies as well
+ // - alldeps : install all dependencies, including optional
+ //
+ if (!$willinstall) {
+ $willinstall = array();
+ }
+ $mywillinstall = array();
+ $php_dir = $config->get('php_dir');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $options['installroot']);
+ $this->installroot = $options['installroot'];
+ } else {
+ $this->installroot = '';
+ }
+ $this->registry = &new PEAR_Registry($php_dir);
+
+ // download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ if ($this->validPackageName($pkgfile) && !isset($options['upgrade'])) {
+ if ($this->registry->packageExists($pkgfile)) {
+ // ignore dependencies that are installed unless we are upgrading
+ continue;
+ }
+ }
+ $pkgfile = $this->_downloadFile($pkgfile, $config, $options, $errors);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if (isset($options['alldeps']) || isset($options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $installpackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ }
+
+ // extract dependencies from downloaded files and then download them
+ // if necessary
+ if (isset($options['alldeps']) || isset($options['onlyreqdeps'])) {
+ $reg = new PEAR_Registry($config->get('php_dir'));
+ if (!$installed) {
+ $state = $config->get('preferred_state');
+ $installed = $reg->listPackages();
+ array_walk($installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $installed = array_flip($installed);
+ }
+ include_once "PEAR/Remote.php";
+ $remote = new PEAR_Remote($config);
+ $deppackages = array();
+ // construct the list of dependencies for each file
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ continue;
+ }
+ foreach($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ if (!isset($options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package $package optional dependency $info[name]");
+ continue;
+ }
+ // get releases
+ $releases = $remote->call('package.info', $info['name'], 'releases');
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($installed[strtolower($info['name'])])) {
+ $errors[] = "Package $package dependency $info[name] ".
+ "has no releases";
+ }
+ continue;
+ }
+ $found = false;
+ $save = $releases;
+ while(count($releases) && !$found) {
+ if (!empty($state) && $state != 'any') {
+ list($release_version,$release) = each($releases);
+ if ($state != $release['state'] &&
+ !in_array($release['state'],
+ $this->betterStates($state))) {
+ // drop this release - it ain't stable enough
+ array_shift($releases);
+ } else {
+ $found = true;
+ }
+ } else {
+ $found = true;
+ }
+ }
+ if (!count($releases) && !$found) {
+ $get = array();
+ foreach($save as $release) {
+ $get = array_merge($get,
+ $this->betterStates($release['state'], true));
+ }
+ $savestate = array_shift($get);
+ $errors[] = "Release for $package dependency $info[name] " .
+ "has state '$savestate', requires $state";
+ continue;
+ }
+ if (in_array(strtolower($info['name']), $willinstall) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ continue;
+ }
+ if (!isset($installed[strtolower($info['name'])])) {
+ // skip upgrade check for packages we don't have installed
+ $deppackages[] = $info['name'];
+ continue;
+ }
+
+ // see if a dependency must be upgraded
+ $inst_version = $reg->packageInfo($info['name'], 'version');
+ if (!isset($info['version'])) {
+ // this is a rel='has' dependency, check against latest
+ if (version_compare($release_version, $inst_version, 'le')) {
+ continue;
+ } else {
+ $deppackages[] = $info['name'];
+ continue;
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ continue;
+ }
+ $deppackages[] = $info['name'];
+ } // foreach($alldeps
+ } // foreach($willinstall
+
+ if (count($deppackages)) {
+ // check dependencies' dependencies
+ // combine the list of packages to install
+ $temppack = array();
+ foreach($installpackages as $p) {
+ $temppack[] = strtolower($p['info']['package']);
+ }
+ foreach($deppackages as $pack) {
+ $temppack[] = strtolower($pack);
+ }
+ $willinstall = array_merge($willinstall, $temppack);
+ $this->download($deppackages, $options, $config, $installpackages,
+ $errors, $installed, $willinstall, $state);
+ }
+ } // if --alldeps or --onlyreqdeps
+ }
+ // }}}
// {{{ install()
/**
@@ -497,42 +725,6 @@ class PEAR_Installer extends PEAR_Common
$need_download = false;
// ==> XXX should be removed later on
$flag_old_format = false;
- if (preg_match('#^(http|ftp)://#', $pkgfile)) {
- $need_download = true;
- } elseif (!@is_file($pkgfile)) {
- if ($this->validPackageName($pkgfile)) {
- if ($this->registry->packageExists($pkgfile) &&
- empty($options['upgrade']) && empty($options['force']))
- {
- return $this->raiseError("$pkgfile already installed");
- }
- $pkgfile = $this->getPackageDownloadUrl($pkgfile);
- $need_download = true;
- } else {
- if (strlen($pkgfile)) {
- return $this->raiseError("Could not open the package file: $pkgfile");
- } else {
- return $this->raiseError("No package file given");
- }
- }
- }
-
- // Download package -----------------------------------------------
- if ($need_download) {
- $downloaddir = $this->config->get('download_dir');
- if (empty($downloaddir)) {
- if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
- return $downloaddir;
- }
- $this->log(2, '+ tmp dir created at ' . $downloaddir);
- }
- $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
- $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
- if (PEAR::isError($file)) {
- return $this->raiseError($file);
- }
- $pkgfile = $file;
- }
if (substr($pkgfile, -4) == '.xml') {
$descfile = $pkgfile;
@@ -644,19 +836,21 @@ class PEAR_Installer extends PEAR_Common
}
} else {
// checks to do only when upgrading packages
- if (!$this->registry->packageExists($pkgname)) {
+/* if (!$this->registry->packageExists($pkgname)) {
return $this->raiseError("$pkgname not installed");
- }
- $v1 = $this->registry->packageInfo($pkgname, 'version');
- $v2 = $pkginfo['version'];
- $cmp = version_compare("$v1", "$v2", 'gt');
- if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
- return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
- }
- if (empty($options['register-only'])) {
- // when upgrading, remove old release's files first:
- if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
- return $this->raiseError($err);
+ }*/
+ if ($this->registry->packageExists($pkgname)) {
+ $v1 = $this->registry->packageInfo($pkgname, 'version');
+ $v2 = $pkginfo['version'];
+ $cmp = version_compare("$v1", "$v2", 'gt');
+ if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
+ return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
+ }
+ if (empty($options['register-only'])) {
+ // when upgrading, remove old release's files first:
+ if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
+ return $this->raiseError($err);
+ }
}
}
}
@@ -750,7 +944,12 @@ class PEAR_Installer extends PEAR_Common
}
$ret = $this->registry->addPackage($pkgname, $pkginfo);
} else {
- $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
+ // new: upgrade installs a package if it isn't installed
+ if (!$this->registry->packageExists($pkgname)) {
+ $ret = $this->registry->addPackage($pkgname, $pkginfo);
+ } else {
+ $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
+ }
}
if (!$ret) {
return null;
diff --git a/pear/package-PEAR.xml b/pear/package-PEAR.xml
index 37fc9111b1..d2a1275f6c 100644
--- a/pear/package-PEAR.xml
+++ b/pear/package-PEAR.xml
@@ -37,15 +37,15 @@
</maintainer>
<maintainer>
<user>cellog</user>
- <role>helper</role>
+ <role>developer</role>
<name>Greg Beaver</name>
<email>cellog@php.net</email>
</maintainer>
</maintainers>
<release>
- <version>1.2.2</version>
- <date>2003-08-25</date>
- <state>stable</state>
+ <version>1.3b1</version>
+ <date>2003-08-27</date>
+ <state>alpha</state>
<notes>
* Fixed #25131 - OS_Guess warnings on empty lines from
popen(&quot;/usr/bin/cpp $tmpfile&quot;, &quot;r&quot;);
@@ -54,6 +54,12 @@
* Added ability to use a static method callback for error-handling, and removed
use of inadvisable @ in setErrorHandling (Greg)
* Added dependency on XML_RPC, and optional dependency on xmlrpc extension (Greg)
+* Added --alldeps and --onlyreqdeps options to pear install/pear upgrade (Greg)
+* Sorting of installation/uninstallation so package order on the command-line is
+ insignificant (fixes upgrade-all if every package is installed) (Greg)
+* pear upgrade will now install if the package is not installed (necessary for
+ pear upgrade --alldeps, as installation is often necessary for new
+ dependencies) (Greg)
</notes>
<provides type="class" name="OS_Guess" />
<provides type="class" name="System" />