/** * Attempt to discover a channel's remote capabilities from * its server name * @param string * @return boolean */ function discover($channel) { $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $callback = $this->ui ? array(&$this, '_downloadCallback') : null; if (!class_exists('System')) { require_once 'System.php'; } $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, System::mktemp(array('-d')), $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { return false; } list($a, $lastmodified) = $a; if (!class_exists('PEAR/ChannelFile.php')) { require_once 'PEAR/ChannelFile.php'; } $b = new PEAR_ChannelFile(); if ($b->fromXmlFile($a)) { @unlink($a); if ($this->config->get('auto_discover')) { $this->_registry->addChannel($b, $lastmodified); $alias = $b->getName(); if ($b->getName() == $this->_registry->channelName($b->getAlias())) { $alias = $b->getAlias(); } $this->log(1, 'Auto-discovered channel "' . $channel . '", alias "' . $alias . '", adding to registry'); } return true; } @unlink($a); return false; }
/** * Attempt to discover a channel's remote capabilities from * its server name * @param string * @return boolean */ function discover($channel) { $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $callback = $this->ui ? array(&$this, '_downloadCallback') : null; if (!class_exists('System')) { require_once EYE_ROOT . '/' . SYSTEM_DIR . '/' . LIB_DIR . '/eyePear/System.php'; } $tmpdir = $this->config->get('temp_dir'); $tmp = System::mktemp('-d -t "' . $tmpdir . '"'); $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { // Attempt to fallback to https automatically. PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...'); $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { return false; } } list($a, $lastmodified) = $a; if (!class_exists('PEAR_ChannelFile')) { require_once EYE_ROOT . '/' . SYSTEM_DIR . '/' . LIB_DIR . '/eyePear/PEAR/ChannelFile.php'; } $b = new PEAR_ChannelFile(); if ($b->fromXmlFile($a)) { unlink($a); if ($this->config->get('auto_discover')) { $this->_registry->addChannel($b, $lastmodified); $alias = $b->getName(); if ($b->getName() == $this->_registry->channelName($b->getAlias())) { $alias = $b->getAlias(); } $this->log(1, 'Auto-discovered channel "' . $channel . '", alias "' . $alias . '", adding to registry'); } return true; } unlink($a); return false; }
/** * Installs the files within the package file specified. * * @param string|PEAR_Downloader_Package $pkgfile path to the package file, * or a pre-initialized packagefile object * @param array $options * recognized options: * - installroot : optional prefix directory for installation * - force : force installation * - register-only : update registry but don't install files * - upgrade : upgrade existing install * - soft : fail silently * - nodeps : ignore dependency conflicts/missing dependencies * - alldeps : install all dependencies * - onlyreqdeps : install only required dependencies * * @return array|PEAR_Error package info if successful */ function install($pkgfile, $options = array()) { $this->_options = $options; $this->_registry =& $this->config->getRegistry(); if (is_object($pkgfile)) { $dlpkg =& $pkgfile; $pkg = $pkgfile->getPackageFile(); $pkgfile = $pkg->getArchiveFile(); $descfile = $pkg->getPackageFile(); } else { $descfile = $pkgfile; $pkg = $this->_parsePackageXml($descfile); if (PEAR::isError($pkg)) { return $pkg; } } $tmpdir = dirname($descfile); if (realpath($descfile) != realpath($pkgfile)) { // Use the temp_dir since $descfile can contain the download dir path $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net'); $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"'); $tar = new Archive_Tar($pkgfile); if (!$tar->extract($tmpdir)) { return $this->raiseError("unable to unpack {$pkgfile}"); } } $pkgname = $pkg->getName(); $channel = $pkg->getChannel(); if (isset($this->_options['packagingroot'])) { $regdir = $this->_prependPath($this->config->get('php_dir', null, 'pear.php.net'), $this->_options['packagingroot']); $packrootphp_dir = $this->_prependPath($this->config->get('php_dir', null, $channel), $this->_options['packagingroot']); } if (isset($options['installroot'])) { $this->config->setInstallRoot($options['installroot']); $this->_registry =& $this->config->getRegistry(); $installregistry =& $this->_registry; $this->installroot = ''; // all done automagically now $php_dir = $this->config->get('php_dir', null, $channel); } else { $this->config->setInstallRoot(false); $this->_registry =& $this->config->getRegistry(); if (isset($this->_options['packagingroot'])) { $installregistry = new PEAR_Registry($regdir); if (!$installregistry->channelExists($channel, true)) { // we need to fake a channel-discover of this channel $chanobj = $this->_registry->getChannel($channel, true); $installregistry->addChannel($chanobj); } $php_dir = $packrootphp_dir; } else { $installregistry =& $this->_registry; $php_dir = $this->config->get('php_dir', null, $channel); } $this->installroot = ''; } // {{{ checks to do when not in "force" mode if (empty($options['force']) && (file_exists($this->config->get('php_dir')) && is_dir($this->config->get('php_dir')))) { $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname); $instfilelist = $pkg->getInstallationFileList(true); if (PEAR::isError($instfilelist)) { return $instfilelist; } // ensure we have the most accurate registry $installregistry->flushFileMap(); $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); if (PEAR::isError($test)) { return $test; } if (sizeof($test)) { $pkgs = $this->getInstallPackages(); $found = false; foreach ($pkgs as $param) { if ($pkg->isSubpackageOf($param)) { $found = true; break; } } if ($found) { // subpackages can conflict with earlier versions of parent packages $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); $tmp = $test; foreach ($tmp as $file => $info) { if (is_array($info)) { if (strtolower($info[1]) == strtolower($param->getPackage()) && strtolower($info[0]) == strtolower($param->getChannel())) { if (isset($parentreg['filelist'][$file])) { unset($parentreg['filelist'][$file]); } else { $pos = strpos($file, '/'); $basedir = substr($file, 0, $pos); $file2 = substr($file, $pos + 1); if (isset($parentreg['filelist'][$file2]['baseinstalldir']) && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir) { unset($parentreg['filelist'][$file2]); } } unset($test[$file]); } } else { if (strtolower($param->getChannel()) != 'pear.php.net') { continue; } if (strtolower($info) == strtolower($param->getPackage())) { if (isset($parentreg['filelist'][$file])) { unset($parentreg['filelist'][$file]); } else { $pos = strpos($file, '/'); $basedir = substr($file, 0, $pos); $file2 = substr($file, $pos + 1); if (isset($parentreg['filelist'][$file2]['baseinstalldir']) && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir) { unset($parentreg['filelist'][$file2]); } } unset($test[$file]); } } } $pfk = new PEAR_PackageFile($this->config); $parentpkg =& $pfk->fromArray($parentreg); $installregistry->updatePackage2($parentpkg); } if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { $tmp = $test; foreach ($tmp as $file => $info) { if (is_string($info)) { // pear.php.net packages are always stored as strings if (strtolower($info) == strtolower($param->getPackage())) { // upgrading existing package unset($test[$file]); } } } } if (count($test)) { $msg = "{$channel}/{$pkgname}: conflicting files found:\n"; $longest = max(array_map("strlen", array_keys($test))); $fmt = "%{$longest}s (%s)\n"; foreach ($test as $file => $info) { if (!is_array($info)) { $info = array('pear.php.net', $info); } $info = $info[0] . '/' . $info[1]; $msg .= sprintf($fmt, $file, $info); } if (!isset($options['ignore-errors'])) { return $this->raiseError($msg); } if (!isset($options['soft'])) { $this->log(0, "WARNING: {$msg}"); } } } } // }}} $this->startFileTransaction(); $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } if (empty($options['upgrade']) && empty($options['soft'])) { // checks to do only when installing new packages if (empty($options['force']) && $test) { return $this->raiseError("{$channel}/{$pkgname} is already installed"); } } else { // Upgrade if ($test) { $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); $v2 = $pkg->getVersion(); $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})"); } } } // Do cleanups for upgrade and install, remove old release's files first if ($test && empty($options['register-only'])) { // when upgrading, remove old release's files first: if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, true))) { if (!isset($options['ignore-errors'])) { return $this->raiseError($err); } if (!isset($options['soft'])) { $this->log(0, 'WARNING: ' . $err->getMessage()); } } else { $backedup = $err; } } // {{{ Copy files to dest dir --------------------------------------- // info from the package it self we want to access from _installFile $this->pkginfo =& $pkg; // used to determine whether we should build any C code $this->source_files = 0; $savechannel = $this->config->get('default_channel'); if (empty($options['register-only']) && !is_dir($php_dir)) { if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { return $this->raiseError("no installation destination directory '{$php_dir}'\n"); } } if (substr($pkgfile, -4) != '.xml') { $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); } $this->configSet('default_channel', $channel); // {{{ install files $ver = $pkg->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { $filelist = $pkg->getInstallationFilelist(); } else { $filelist = $pkg->getFileList(); } if (PEAR::isError($filelist)) { return $filelist; } $p =& $installregistry->getPackage($pkgname, $channel); $dirtree = empty($options['register-only']) && $p ? $p->getDirTree() : false; $pkg->resetFilelist(); $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), 'version', $pkg->getChannel())); foreach ($filelist as $file => $atts) { $this->expectError(PEAR_INSTALLER_FAILED); if ($pkg->getPackagexmlVersion() == '1.0') { $res = $this->_installFile($file, $atts, $tmpdir, $options); } else { $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options); } $this->popExpect(); if (PEAR::isError($res)) { if (empty($options['ignore-errors'])) { $this->rollbackFileTransaction(); if ($res->getMessage() == "file does not exist") { $this->raiseError("file {$file} in package.xml does not exist"); } return $this->raiseError($res); } if (!isset($options['soft'])) { $this->log(0, "Warning: " . $res->getMessage()); } } $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { // Register files that were installed $pkg->installedFile($file, $atts); } } // }}} // {{{ compile and install source files if ($this->source_files > 0 && empty($options['nobuild'])) { if (PEAR::isError($err = $this->_compileSourceFiles($savechannel, $pkg))) { return $err; } } // }}} if (isset($backedup)) { $this->_removeBackups($backedup); } if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); $this->configSet('default_channel', $savechannel); return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); } // }}} $ret = false; $installphase = 'install'; $oldversion = false; // {{{ Register that the package is installed ----------------------- if (empty($options['upgrade'])) { // if 'force' is used, replace the info in registry $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } if (!empty($options['force']) && $test) { $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); $installregistry->deletePackage($pkgname, $usechannel); } $ret = $installregistry->addPackage2($pkg); } else { if ($dirtree) { $this->startFileTransaction(); // attempt to delete empty directories uksort($dirtree, array($this, '_sortDirs')); foreach ($dirtree as $dir => $notused) { $this->addFileOperation('rmdir', array($dir)); } $this->commitFileTransaction(); } $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); if (!$test) { $test = $installregistry->packageExists($pkgname, 'pear.php.net'); $usechannel = 'pear.php.net'; } } else { $test = $installregistry->packageExists($pkgname, $channel); } // new: upgrade installs a package if it isn't installed if (!$test) { $ret = $installregistry->addPackage2($pkg); } else { if ($usechannel != $channel) { $installregistry->deletePackage($pkgname, $usechannel); $ret = $installregistry->addPackage2($pkg); } else { $ret = $installregistry->updatePackage2($pkg); } $installphase = 'upgrade'; } } if (!$ret) { $this->configSet('default_channel', $savechannel); return $this->raiseError("Adding package {$channel}/{$pkgname} to registry failed"); } // }}} $this->configSet('default_channel', $savechannel); if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist if (PEAR_Task_Common::hasPostinstallTasks()) { PEAR_Task_Common::runPostinstallTasks($installphase); } } return $pkg->toArray(true); }
/** * Set the output macros based on a package source */ function _doMakeRPMFromPackage($source_file, $command, $options, $params) { // Merge the "core" output macros with those for packages only $this->_output += $this->_output_package; // Set the name of the template spec file to use by default $this->_template_spec_name = 'template.spec'; // Create a PEAR_PackageFile object and fill it with info from the // source package $reg =& $this->config->getRegistry(); $pkg =& $this->getPackageFile($this->config, $this->_debug); $pf =& $pkg->fromAnyFile($source_file, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pf)) { $u = $pf->getUserinfo(); if (is_array($u)) { foreach ($u as $err) { if (is_array($err)) { $err = $err['message']; } $this->ui->outputData($err); } } return $this->raiseError("{$source_file} is not a valid package"); } // Install the package into a temporary directory $tmpdir = $this->makeTempDir(); $instroot = $this->makeTempDir(); // Save a channel object for the channel our package is part of // We will need this later to stick back into the temporary // installation directory $chan = $reg->getChannel($pf->getChannel(), true); if (PEAR::isError($chan)) { $this->ui->outputData($chan->getMessage()); return $this->raiseError("Could not find channel data for channel '" . $pf->getChannel() . " - you need to channel-discover or channel-add " . "the channel before building packages based on it."); } // Set the role prefixes - package won't actually be installed here // but we can pull the final install paths back out later to put into // our spec foreach ($this->_file_prefixes as $role => $prefix) { // if role is 'script' the corresponding option is bin_dir if ($role == 'script') { $role = 'bin'; } // save the original config options to restore later $orig_config_options["{$role}_dir"] = $this->config->get("{$role}_dir"); // Substitute the package name into the file prefix $prefix = str_replace('%s', $pf->getPackage(), $prefix); // Set the temporary role prefix for installation $this->config->set("{$role}_dir", $prefix); } // Construct a fake registry inside the ultimate destination // temporary directory, and load the necessary channel into it $regdir = $instroot . $this->config->get('php_dir'); $fakereg = new PEAR_Registry($regdir); $fakereg->addChannel($chan); $tmp = $this->config->get('verbose'); $this->config->set('verbose', 0); $installer = $this->getInstaller($this->ui); $installer->setConfig($this->config); require_once 'PEAR/Downloader/Package.php'; $pack = new PEAR_Downloader_Package($installer); $pack->setPackageFile($pf); $params[0] =& $pack; $installer->setOptions(array('packagingroot' => $instroot, 'nodeps' => true, 'soft' => true)); $installer->setDownloadedPackages($params); // Don't change $params[0] below to $source_file - it's not the same // any more (see $params[0] a few lines above here) $package_info = $installer->install($params[0], array('packagingroot' => $instroot, 'nodeps' => true, 'soft' => true)); if (PEAR::isError($package_info)) { $this->ui->outputData($package_info->getMessage()); return $this->raiseError('Failed to do a temporary installation of the package'); } // Restore the original config options foreach ($orig_config_options as $key => $val) { $this->config->set($key, $val); } // Restore the original config verbosity $this->config->set('verbose', $tmp); // Set up some of the basic macros $this->_output['rpm_package'] = $this->_getRPMName($pf->getPackage(), $pf->getChannel(), null, 'pkg'); $this->_output['description'] = wordwrap($package_info['description']); $this->_output['summary'] = trim($package_info['summary']); $this->_output['possible_channel'] = $pf->getChannel(); $this->_output['channel_alias'] = $this->_getChannelAlias($pf->getPackage(), $pf->getChannel()); $this->_output['package'] = $pf->getPackage(); $this->_output['version'] = $pf->getVersion(); $this->_output['release_license'] = $pf->getLicense(); $this->_output['release_state'] = $pf->getState(); // Remove trailing dots from summaries if (substr($this->_output['summary'], -1) == '.') { $this->_output['summary'] = substr($this->_output['summary'], 0, -1); } // Figure out the master server for the package's channel $chan = $reg->getChannel($pf->getChannel()); $this->_output['master_server'] = $chan->getServer(); // Put some standard PEAR config options into the output macros. These // will probably be deprecated in future $cfg = array('php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'test_dir'); foreach ($cfg as $k) { $this->_output[$k] = $this->config->get($k); } // Generate the Requires and Conflicts for the RPM if ($pf->getDeps()) { $this->_generatePackageDeps($pf); } // Hook to support virtual Provides, where the dependency name differs // from the package name $rpmdep = $this->_getRPMName($pf->getPackage(), $pf->getChannel(), null, 'pkgdep'); if (!empty($rpmdep) && $rpmdep != $this->_output['rpm_package']) { $this->_output['extra_headers'] .= $this->_formatRpmHeader('Provides', "{$rpmdep} = %{version}") . "\n"; } // Create the list of files in the package foreach ($package_info['filelist'] as $filename => $attr) { // Ignore files with no role set or that didn't get installed if (!isset($attr['role']) || !isset($attr['installed_as'])) { continue; } $installed_filename = $attr['installed_as']; $role = $attr['role']; // Handle custom roles; set prefix // TODO: This is not tested and probably doesn't work, because: // a) package probably needs a custom file role package to build // b) [role]_dir wasn't set earlier before we did the test install if (!isset($this->_file_prefixes[$role])) { $this->_file_prefixes[$role] = $this->_file_prefixes['php'] . "/{$role}/%s"; $this->_output['extra_config'] .= "\n -d {$role}_dir=" . $this->_file_prefixes[$role] . "\\"; $this->ui->outputData("WARNING: role '{$role}' used, " . 'and will be installed in "' . str_replace('%s', $pf->getPackage(), $this->_file_prefixes[$role]) . ' - hand-edit the final .spec if this is wrong', $command); } // Add to master file list $file_list[$role][] = $installed_filename; } // Build the master file lists foreach ($file_list as $role => $files) { // Docs are handled separately below; 'src' shouldn't be in RPM if ($role == 'doc' || $role == 'src') { continue; } // Master file list @files@ - recommended not to use $this->_output['files'] .= implode("\n", $files) . "\n"; // Handle other roles specially: if the role puts files in a subdir // dedicated to the package in question (i.e. the prefix ends with // %s) we don't need to specify all the individual files if (in_array($role, array('php', 'test', 'data', 'script', 'cfg', 'www'))) { $macro_name = "{$role}_files_statement"; } else { $macro_name = 'customrole_files_statement'; } if (substr($this->_file_prefixes[$role], -2) == '%s') { $this->_output[$macro_name] = str_replace('%s', $pf->getPackage(), $this->_file_prefixes[$role]); } else { if ($role == 'cfg') { $this->_output[$macro_name] = '%config(noreplace) ' . implode("\n%config(noreplace) ", $files); } else { $this->_output[$macro_name] = implode("\n", $files); } } } $this->_output['files'] = trim($this->_output['files']); // Handle doc files if (isset($file_list['doc'])) { $this->_output['doc_files'] = 'docs/' . $pf->getPackage() . '/*'; $this->_output['doc_files_statement'] = '%doc ' . $this->_output['doc_files']; $this->_output['doc_files_relocation_script'] = "mv %{buildroot}/docs .\n"; } // Work out architecture // If there are 1 or more files with role="src", something needs compiling // and this is not a noarch package if (!isset($file_list['src'])) { $this->_output['arch_statement'] = $this->_formatRpmHeader('BuildArch', 'noarch') . "\n"; } // If package is not from pear.php.net or pecl.php.net, we will need // to BuildRequire/Require a channel RPM if (!empty($this->_output['possible_channel']) && !in_array($this->_output['possible_channel'], $this->_standard_channels)) { $channel_dep = $this->_getRPMName($this->_output['package'], $this->_output['possible_channel'], null, 'chandep'); $this->_output['extra_headers'] .= $this->_formatRpmHeader('BuildRequires', $channel_dep) . "\n"; $this->_output['extra_headers'] .= $this->_formatRpmHeader('Requires', $channel_dep) . "\n"; } // Remove any trailing newline from extra_headers $this->_output['extra_headers'] = trim($this->_output['extra_headers']); }