Package.php 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  1. <?php
  2. /**
  3. * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
  4. * sign, makerpm, convert commands)
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * @category pear
  9. * @package PEAR
  10. * @author Stig Bakken <ssb@php.net>
  11. * @author Martin Jansen <mj@php.net>
  12. * @author Greg Beaver <cellog@php.net>
  13. * @copyright 1997-2009 The Authors
  14. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  15. * @version CVS: $Id: Package.php 313024 2011-07-06 19:51:24Z dufuz $
  16. * @link http://pear.php.net/package/PEAR
  17. * @since File available since Release 0.1
  18. */
  19. /**
  20. * base class
  21. */
  22. require_once 'PEAR/Command/Common.php';
  23. /**
  24. * PEAR commands for login/logout
  25. *
  26. * @category pear
  27. * @package PEAR
  28. * @author Stig Bakken <ssb@php.net>
  29. * @author Martin Jansen <mj@php.net>
  30. * @author Greg Beaver <cellog@php.net>
  31. * @copyright 1997-2009 The Authors
  32. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  33. * @version Release: @package_version@
  34. * @link http://pear.php.net/package/PEAR
  35. * @since Class available since Release 0.1
  36. */
  37. class PEAR_Command_Package extends PEAR_Command_Common
  38. {
  39. var $commands = array(
  40. 'package' => array(
  41. 'summary' => 'Build Package',
  42. 'function' => 'doPackage',
  43. 'shortcut' => 'p',
  44. 'options' => array(
  45. 'nocompress' => array(
  46. 'shortopt' => 'Z',
  47. 'doc' => 'Do not gzip the package file'
  48. ),
  49. 'showname' => array(
  50. 'shortopt' => 'n',
  51. 'doc' => 'Print the name of the packaged file.',
  52. ),
  53. ),
  54. 'doc' => '[descfile] [descfile2]
  55. Creates a PEAR package from its description file (usually called
  56. package.xml). If a second packagefile is passed in, then
  57. the packager will check to make sure that one is a package.xml
  58. version 1.0, and the other is a package.xml version 2.0. The
  59. package.xml version 1.0 will be saved as "package.xml" in the archive,
  60. and the other as "package2.xml" in the archive"
  61. '
  62. ),
  63. 'package-validate' => array(
  64. 'summary' => 'Validate Package Consistency',
  65. 'function' => 'doPackageValidate',
  66. 'shortcut' => 'pv',
  67. 'options' => array(),
  68. 'doc' => '
  69. ',
  70. ),
  71. 'cvsdiff' => array(
  72. 'summary' => 'Run a "cvs diff" for all files in a package',
  73. 'function' => 'doCvsDiff',
  74. 'shortcut' => 'cd',
  75. 'options' => array(
  76. 'quiet' => array(
  77. 'shortopt' => 'q',
  78. 'doc' => 'Be quiet',
  79. ),
  80. 'reallyquiet' => array(
  81. 'shortopt' => 'Q',
  82. 'doc' => 'Be really quiet',
  83. ),
  84. 'date' => array(
  85. 'shortopt' => 'D',
  86. 'doc' => 'Diff against revision of DATE',
  87. 'arg' => 'DATE',
  88. ),
  89. 'release' => array(
  90. 'shortopt' => 'R',
  91. 'doc' => 'Diff against tag for package release REL',
  92. 'arg' => 'REL',
  93. ),
  94. 'revision' => array(
  95. 'shortopt' => 'r',
  96. 'doc' => 'Diff against revision REV',
  97. 'arg' => 'REV',
  98. ),
  99. 'context' => array(
  100. 'shortopt' => 'c',
  101. 'doc' => 'Generate context diff',
  102. ),
  103. 'unified' => array(
  104. 'shortopt' => 'u',
  105. 'doc' => 'Generate unified diff',
  106. ),
  107. 'ignore-case' => array(
  108. 'shortopt' => 'i',
  109. 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
  110. ),
  111. 'ignore-whitespace' => array(
  112. 'shortopt' => 'b',
  113. 'doc' => 'Ignore changes in amount of white space',
  114. ),
  115. 'ignore-blank-lines' => array(
  116. 'shortopt' => 'B',
  117. 'doc' => 'Ignore changes that insert or delete blank lines',
  118. ),
  119. 'brief' => array(
  120. 'doc' => 'Report only whether the files differ, no details',
  121. ),
  122. 'dry-run' => array(
  123. 'shortopt' => 'n',
  124. 'doc' => 'Don\'t do anything, just pretend',
  125. ),
  126. ),
  127. 'doc' => '<package.xml>
  128. Compares all the files in a package. Without any options, this
  129. command will compare the current code with the last checked-in code.
  130. Using the -r or -R option you may compare the current code with that
  131. of a specific release.
  132. ',
  133. ),
  134. 'svntag' => array(
  135. 'summary' => 'Set SVN Release Tag',
  136. 'function' => 'doSvnTag',
  137. 'shortcut' => 'sv',
  138. 'options' => array(
  139. 'quiet' => array(
  140. 'shortopt' => 'q',
  141. 'doc' => 'Be quiet',
  142. ),
  143. 'slide' => array(
  144. 'shortopt' => 'F',
  145. 'doc' => 'Move (slide) tag if it exists',
  146. ),
  147. 'delete' => array(
  148. 'shortopt' => 'd',
  149. 'doc' => 'Remove tag',
  150. ),
  151. 'dry-run' => array(
  152. 'shortopt' => 'n',
  153. 'doc' => 'Don\'t do anything, just pretend',
  154. ),
  155. ),
  156. 'doc' => '<package.xml> [files...]
  157. Sets a SVN tag on all files in a package. Use this command after you have
  158. packaged a distribution tarball with the "package" command to tag what
  159. revisions of what files were in that release. If need to fix something
  160. after running svntag once, but before the tarball is released to the public,
  161. use the "slide" option to move the release tag.
  162. to include files (such as a second package.xml, or tests not included in the
  163. release), pass them as additional parameters.
  164. ',
  165. ),
  166. 'cvstag' => array(
  167. 'summary' => 'Set CVS Release Tag',
  168. 'function' => 'doCvsTag',
  169. 'shortcut' => 'ct',
  170. 'options' => array(
  171. 'quiet' => array(
  172. 'shortopt' => 'q',
  173. 'doc' => 'Be quiet',
  174. ),
  175. 'reallyquiet' => array(
  176. 'shortopt' => 'Q',
  177. 'doc' => 'Be really quiet',
  178. ),
  179. 'slide' => array(
  180. 'shortopt' => 'F',
  181. 'doc' => 'Move (slide) tag if it exists',
  182. ),
  183. 'delete' => array(
  184. 'shortopt' => 'd',
  185. 'doc' => 'Remove tag',
  186. ),
  187. 'dry-run' => array(
  188. 'shortopt' => 'n',
  189. 'doc' => 'Don\'t do anything, just pretend',
  190. ),
  191. ),
  192. 'doc' => '<package.xml> [files...]
  193. Sets a CVS tag on all files in a package. Use this command after you have
  194. packaged a distribution tarball with the "package" command to tag what
  195. revisions of what files were in that release. If need to fix something
  196. after running cvstag once, but before the tarball is released to the public,
  197. use the "slide" option to move the release tag.
  198. to include files (such as a second package.xml, or tests not included in the
  199. release), pass them as additional parameters.
  200. ',
  201. ),
  202. 'package-dependencies' => array(
  203. 'summary' => 'Show package dependencies',
  204. 'function' => 'doPackageDependencies',
  205. 'shortcut' => 'pd',
  206. 'options' => array(),
  207. 'doc' => '<package-file> or <package.xml> or <install-package-name>
  208. List all dependencies the package has.
  209. Can take a tgz / tar file, package.xml or a package name of an installed package.'
  210. ),
  211. 'sign' => array(
  212. 'summary' => 'Sign a package distribution file',
  213. 'function' => 'doSign',
  214. 'shortcut' => 'si',
  215. 'options' => array(
  216. 'verbose' => array(
  217. 'shortopt' => 'v',
  218. 'doc' => 'Display GnuPG output',
  219. ),
  220. ),
  221. 'doc' => '<package-file>
  222. Signs a package distribution (.tar or .tgz) file with GnuPG.',
  223. ),
  224. 'makerpm' => array(
  225. 'summary' => 'Builds an RPM spec file from a PEAR package',
  226. 'function' => 'doMakeRPM',
  227. 'shortcut' => 'rpm',
  228. 'options' => array(
  229. 'spec-template' => array(
  230. 'shortopt' => 't',
  231. 'arg' => 'FILE',
  232. 'doc' => 'Use FILE as RPM spec file template'
  233. ),
  234. 'rpm-pkgname' => array(
  235. 'shortopt' => 'p',
  236. 'arg' => 'FORMAT',
  237. 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
  238. by the PEAR package name, defaults to "PEAR::%s".',
  239. ),
  240. ),
  241. 'doc' => '<package-file>
  242. Creates an RPM .spec file for wrapping a PEAR package inside an RPM
  243. package. Intended to be used from the SPECS directory, with the PEAR
  244. package tarball in the SOURCES directory:
  245. $ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
  246. Wrote RPM spec file PEAR::Net_Geo-1.0.spec
  247. $ rpm -bb PEAR::Net_Socket-1.0.spec
  248. ...
  249. Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
  250. ',
  251. ),
  252. 'convert' => array(
  253. 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
  254. 'function' => 'doConvert',
  255. 'shortcut' => 'c2',
  256. 'options' => array(
  257. 'flat' => array(
  258. 'shortopt' => 'f',
  259. 'doc' => 'do not beautify the filelist.',
  260. ),
  261. ),
  262. 'doc' => '[descfile] [descfile2]
  263. Converts a package.xml in 1.0 format into a package.xml
  264. in 2.0 format. The new file will be named package2.xml by default,
  265. and package.xml will be used as the old file by default.
  266. This is not the most intelligent conversion, and should only be
  267. used for automated conversion or learning the format.
  268. '
  269. ),
  270. );
  271. var $output;
  272. /**
  273. * PEAR_Command_Package constructor.
  274. *
  275. * @access public
  276. */
  277. function PEAR_Command_Package(&$ui, &$config)
  278. {
  279. parent::PEAR_Command_Common($ui, $config);
  280. }
  281. function _displayValidationResults($err, $warn, $strict = false)
  282. {
  283. foreach ($err as $e) {
  284. $this->output .= "Error: $e\n";
  285. }
  286. foreach ($warn as $w) {
  287. $this->output .= "Warning: $w\n";
  288. }
  289. $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
  290. sizeof($err), sizeof($warn));
  291. if ($strict && count($err) > 0) {
  292. $this->output .= "Fix these errors and try again.";
  293. return false;
  294. }
  295. return true;
  296. }
  297. function &getPackager()
  298. {
  299. if (!class_exists('PEAR_Packager')) {
  300. require_once 'PEAR/Packager.php';
  301. }
  302. $a = &new PEAR_Packager;
  303. return $a;
  304. }
  305. function &getPackageFile($config, $debug = false)
  306. {
  307. if (!class_exists('PEAR_Common')) {
  308. require_once 'PEAR/Common.php';
  309. }
  310. if (!class_exists('PEAR_PackageFile')) {
  311. require_once 'PEAR/PackageFile.php';
  312. }
  313. $a = &new PEAR_PackageFile($config, $debug);
  314. $common = new PEAR_Common;
  315. $common->ui = $this->ui;
  316. $a->setLogger($common);
  317. return $a;
  318. }
  319. function doPackage($command, $options, $params)
  320. {
  321. $this->output = '';
  322. $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
  323. $pkg2 = isset($params[1]) ? $params[1] : null;
  324. if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) {
  325. $pkg2 = 'package2.xml';
  326. }
  327. $packager = &$this->getPackager();
  328. $compress = empty($options['nocompress']) ? true : false;
  329. $result = $packager->package($pkginfofile, $compress, $pkg2);
  330. if (PEAR::isError($result)) {
  331. return $this->raiseError($result);
  332. }
  333. // Don't want output, only the package file name just created
  334. if (isset($options['showname'])) {
  335. $this->output = $result;
  336. }
  337. if ($this->output) {
  338. $this->ui->outputData($this->output, $command);
  339. }
  340. return true;
  341. }
  342. function doPackageValidate($command, $options, $params)
  343. {
  344. $this->output = '';
  345. if (count($params) < 1) {
  346. $params[0] = 'package.xml';
  347. }
  348. $obj = &$this->getPackageFile($this->config, $this->_debug);
  349. $obj->rawReturn();
  350. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  351. $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
  352. if (PEAR::isError($info)) {
  353. $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
  354. } else {
  355. $archive = $info->getArchiveFile();
  356. $tar = &new Archive_Tar($archive);
  357. $tar->extract(dirname($info->getPackageFile()));
  358. $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
  359. $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
  360. basename($info->getPackageFile()));
  361. }
  362. PEAR::staticPopErrorHandling();
  363. if (PEAR::isError($info)) {
  364. return $this->raiseError($info);
  365. }
  366. $valid = false;
  367. if ($info->getPackagexmlVersion() == '2.0') {
  368. if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
  369. $info->flattenFileList();
  370. $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
  371. }
  372. } else {
  373. $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
  374. }
  375. $err = $warn = array();
  376. if ($errors = $info->getValidationWarnings()) {
  377. foreach ($errors as $error) {
  378. if ($error['level'] == 'warning') {
  379. $warn[] = $error['message'];
  380. } else {
  381. $err[] = $error['message'];
  382. }
  383. }
  384. }
  385. $this->_displayValidationResults($err, $warn);
  386. $this->ui->outputData($this->output, $command);
  387. return true;
  388. }
  389. function doSvnTag($command, $options, $params)
  390. {
  391. $this->output = '';
  392. $_cmd = $command;
  393. if (count($params) < 1) {
  394. $help = $this->getHelp($command);
  395. return $this->raiseError("$command: missing parameter: $help[0]");
  396. }
  397. $packageFile = realpath($params[0]);
  398. $dir = dirname($packageFile);
  399. $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
  400. $obj = &$this->getPackageFile($this->config, $this->_debug);
  401. $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
  402. if (PEAR::isError($info)) {
  403. return $this->raiseError($info);
  404. }
  405. $err = $warn = array();
  406. if (!$info->validate()) {
  407. foreach ($info->getValidationWarnings() as $error) {
  408. if ($error['level'] == 'warning') {
  409. $warn[] = $error['message'];
  410. } else {
  411. $err[] = $error['message'];
  412. }
  413. }
  414. }
  415. if (!$this->_displayValidationResults($err, $warn, true)) {
  416. $this->ui->outputData($this->output, $command);
  417. return $this->raiseError('SVN tag failed');
  418. }
  419. $version = $info->getVersion();
  420. $package = $info->getName();
  421. $svntag = "$package-$version";
  422. if (isset($options['delete'])) {
  423. return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
  424. }
  425. $path = $this->_svnFindPath($packageFile);
  426. // Check if there are any modified files
  427. $fp = popen('svn st --xml ' . dirname($packageFile), "r");
  428. $out = '';
  429. while ($line = fgets($fp, 1024)) {
  430. $out .= rtrim($line)."\n";
  431. }
  432. pclose($fp);
  433. if (!isset($options['quiet']) && strpos($out, 'item="modified"')) {
  434. $params = array(array(
  435. 'name' => 'modified',
  436. 'type' => 'yesno',
  437. 'default' => 'no',
  438. 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not commited, do you still want to tag ' . $version . '?',
  439. ));
  440. $answers = $this->ui->confirmDialog($params);
  441. if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) {
  442. return true;
  443. }
  444. }
  445. if (isset($options['slide'])) {
  446. $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
  447. }
  448. // Check if tag already exists
  449. $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag;
  450. $existsCommand = 'svn ls ' . $path['base'] . 'tags/';
  451. $fp = popen($existsCommand, "r");
  452. $out = '';
  453. while ($line = fgets($fp, 1024)) {
  454. $out .= rtrim($line)."\n";
  455. }
  456. pclose($fp);
  457. if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) {
  458. $this->ui->outputData($this->output, $command);
  459. return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.');
  460. } elseif (file_exists($path['local']['base'] . 'tags') === false) {
  461. return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags');
  462. } elseif (is_writeable($path['local']['base'] . 'tags') === false) {
  463. return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags');
  464. } else {
  465. $makeCommand = 'svn mkdir ' . $releaseTag;
  466. $this->output .= "+ $makeCommand\n";
  467. if (empty($options['dry-run'])) {
  468. // We need to create the tag dir.
  469. $fp = popen($makeCommand, "r");
  470. $out = '';
  471. while ($line = fgets($fp, 1024)) {
  472. $out .= rtrim($line)."\n";
  473. }
  474. pclose($fp);
  475. $this->output .= "$out\n";
  476. }
  477. }
  478. $command = 'svn';
  479. if (isset($options['quiet'])) {
  480. $command .= ' -q';
  481. }
  482. $command .= ' copy --parents ';
  483. $dir = dirname($packageFile);
  484. $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
  485. $files = array_keys($info->getFilelist());
  486. if (!in_array(basename($packageFile), $files)) {
  487. $files[] = basename($packageFile);
  488. }
  489. array_shift($params);
  490. if (count($params)) {
  491. // add in additional files to be tagged (package files and such)
  492. $files = array_merge($files, $params);
  493. }
  494. $commands = array();
  495. foreach ($files as $file) {
  496. if (!file_exists($file)) {
  497. $file = $dir . DIRECTORY_SEPARATOR . $file;
  498. }
  499. $commands[] = $command . ' ' . escapeshellarg($file) . ' ' .
  500. escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file);
  501. }
  502. $this->output .= implode("\n", $commands) . "\n";
  503. if (empty($options['dry-run'])) {
  504. foreach ($commands as $command) {
  505. $fp = popen($command, "r");
  506. while ($line = fgets($fp, 1024)) {
  507. $this->output .= rtrim($line)."\n";
  508. }
  509. pclose($fp);
  510. }
  511. }
  512. $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n";
  513. $this->output .= "+ $command\n";
  514. if (empty($options['dry-run'])) {
  515. $fp = popen($command, "r");
  516. while ($line = fgets($fp, 1024)) {
  517. $this->output .= rtrim($line)."\n";
  518. }
  519. pclose($fp);
  520. }
  521. $this->ui->outputData($this->output, $_cmd);
  522. return true;
  523. }
  524. function _svnFindPath($file)
  525. {
  526. $xml = '';
  527. $command = "svn info --xml $file";
  528. $fp = popen($command, "r");
  529. while ($line = fgets($fp, 1024)) {
  530. $xml .= rtrim($line)."\n";
  531. }
  532. pclose($fp);
  533. $url_tag = strpos($xml, '<url>');
  534. $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5));
  535. $path = array();
  536. $path['from'] = substr($url, 0, strrpos($url, '/'));
  537. $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1);
  538. // Figure out the local paths - see http://pear.php.net/bugs/17463
  539. $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR);
  540. if ($pos === false) {
  541. $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR);
  542. }
  543. $path['local']['base'] = substr($file, 0, $pos + 1);
  544. return $path;
  545. }
  546. function _svnRemoveTag($version, $package, $tag, $packageFile, $options)
  547. {
  548. $command = 'svn';
  549. if (isset($options['quiet'])) {
  550. $command .= ' -q';
  551. }
  552. $command .= ' remove';
  553. $command .= ' -m "Removing tag for the ' . $version . ' release."';
  554. $path = $this->_svnFindPath($packageFile);
  555. $command .= ' ' . $path['base'] . 'tags/' . $tag;
  556. if ($this->config->get('verbose') > 1) {
  557. $this->output .= "+ $command\n";
  558. }
  559. $this->output .= "+ $command\n";
  560. if (empty($options['dry-run'])) {
  561. $fp = popen($command, "r");
  562. while ($line = fgets($fp, 1024)) {
  563. $this->output .= rtrim($line)."\n";
  564. }
  565. pclose($fp);
  566. }
  567. $this->ui->outputData($this->output, $command);
  568. return true;
  569. }
  570. function doCvsTag($command, $options, $params)
  571. {
  572. $this->output = '';
  573. $_cmd = $command;
  574. if (count($params) < 1) {
  575. $help = $this->getHelp($command);
  576. return $this->raiseError("$command: missing parameter: $help[0]");
  577. }
  578. $packageFile = realpath($params[0]);
  579. $obj = &$this->getPackageFile($this->config, $this->_debug);
  580. $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
  581. if (PEAR::isError($info)) {
  582. return $this->raiseError($info);
  583. }
  584. $err = $warn = array();
  585. if (!$info->validate()) {
  586. foreach ($info->getValidationWarnings() as $error) {
  587. if ($error['level'] == 'warning') {
  588. $warn[] = $error['message'];
  589. } else {
  590. $err[] = $error['message'];
  591. }
  592. }
  593. }
  594. if (!$this->_displayValidationResults($err, $warn, true)) {
  595. $this->ui->outputData($this->output, $command);
  596. return $this->raiseError('CVS tag failed');
  597. }
  598. $version = $info->getVersion();
  599. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
  600. $cvstag = "RELEASE_$cvsversion";
  601. $files = array_keys($info->getFilelist());
  602. $command = 'cvs';
  603. if (isset($options['quiet'])) {
  604. $command .= ' -q';
  605. }
  606. if (isset($options['reallyquiet'])) {
  607. $command .= ' -Q';
  608. }
  609. $command .= ' tag';
  610. if (isset($options['slide'])) {
  611. $command .= ' -F';
  612. }
  613. if (isset($options['delete'])) {
  614. $command .= ' -d';
  615. }
  616. $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
  617. array_shift($params);
  618. if (count($params)) {
  619. // add in additional files to be tagged
  620. $files = array_merge($files, $params);
  621. }
  622. $dir = dirname($packageFile);
  623. $dir = substr($dir, strrpos($dir, '/') + 1);
  624. foreach ($files as $file) {
  625. if (!file_exists($file)) {
  626. $file = $dir . DIRECTORY_SEPARATOR . $file;
  627. }
  628. $command .= ' ' . escapeshellarg($file);
  629. }
  630. if ($this->config->get('verbose') > 1) {
  631. $this->output .= "+ $command\n";
  632. }
  633. $this->output .= "+ $command\n";
  634. if (empty($options['dry-run'])) {
  635. $fp = popen($command, "r");
  636. while ($line = fgets($fp, 1024)) {
  637. $this->output .= rtrim($line)."\n";
  638. }
  639. pclose($fp);
  640. }
  641. $this->ui->outputData($this->output, $_cmd);
  642. return true;
  643. }
  644. function doCvsDiff($command, $options, $params)
  645. {
  646. $this->output = '';
  647. if (sizeof($params) < 1) {
  648. $help = $this->getHelp($command);
  649. return $this->raiseError("$command: missing parameter: $help[0]");
  650. }
  651. $file = realpath($params[0]);
  652. $obj = &$this->getPackageFile($this->config, $this->_debug);
  653. $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL);
  654. if (PEAR::isError($info)) {
  655. return $this->raiseError($info);
  656. }
  657. $err = $warn = array();
  658. if (!$info->validate()) {
  659. foreach ($info->getValidationWarnings() as $error) {
  660. if ($error['level'] == 'warning') {
  661. $warn[] = $error['message'];
  662. } else {
  663. $err[] = $error['message'];
  664. }
  665. }
  666. }
  667. if (!$this->_displayValidationResults($err, $warn, true)) {
  668. $this->ui->outputData($this->output, $command);
  669. return $this->raiseError('CVS diff failed');
  670. }
  671. $info1 = $info->getFilelist();
  672. $files = $info1;
  673. $cmd = "cvs";
  674. if (isset($options['quiet'])) {
  675. $cmd .= ' -q';
  676. unset($options['quiet']);
  677. }
  678. if (isset($options['reallyquiet'])) {
  679. $cmd .= ' -Q';
  680. unset($options['reallyquiet']);
  681. }
  682. if (isset($options['release'])) {
  683. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
  684. $cvstag = "RELEASE_$cvsversion";
  685. $options['revision'] = $cvstag;
  686. unset($options['release']);
  687. }
  688. $execute = true;
  689. if (isset($options['dry-run'])) {
  690. $execute = false;
  691. unset($options['dry-run']);
  692. }
  693. $cmd .= ' diff';
  694. // the rest of the options are passed right on to "cvs diff"
  695. foreach ($options as $option => $optarg) {
  696. $arg = $short = false;
  697. if (isset($this->commands[$command]['options'][$option])) {
  698. $arg = $this->commands[$command]['options'][$option]['arg'];
  699. $short = $this->commands[$command]['options'][$option]['shortopt'];
  700. }
  701. $cmd .= $short ? " -$short" : " --$option";
  702. if ($arg && $optarg) {
  703. $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
  704. }
  705. }
  706. foreach ($files as $file) {
  707. $cmd .= ' ' . escapeshellarg($file['name']);
  708. }
  709. if ($this->config->get('verbose') > 1) {
  710. $this->output .= "+ $cmd\n";
  711. }
  712. if ($execute) {
  713. $fp = popen($cmd, "r");
  714. while ($line = fgets($fp, 1024)) {
  715. $this->output .= rtrim($line)."\n";
  716. }
  717. pclose($fp);
  718. }
  719. $this->ui->outputData($this->output, $command);
  720. return true;
  721. }
  722. function doPackageDependencies($command, $options, $params)
  723. {
  724. // $params[0] -> the PEAR package to list its information
  725. if (count($params) !== 1) {
  726. return $this->raiseError("bad parameter(s), try \"help $command\"");
  727. }
  728. $obj = &$this->getPackageFile($this->config, $this->_debug);
  729. if (is_file($params[0]) || strpos($params[0], '.xml') > 0) {
  730. $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
  731. } else {
  732. $reg = $this->config->getRegistry();
  733. $info = $obj->fromArray($reg->packageInfo($params[0]));
  734. }
  735. if (PEAR::isError($info)) {
  736. return $this->raiseError($info);
  737. }
  738. $deps = $info->getDeps();
  739. if (is_array($deps)) {
  740. if ($info->getPackagexmlVersion() == '1.0') {
  741. $data = array(
  742. 'caption' => 'Dependencies for pear/' . $info->getPackage(),
  743. 'border' => true,
  744. 'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
  745. );
  746. foreach ($deps as $d) {
  747. if (isset($d['optional'])) {
  748. if ($d['optional'] == 'yes') {
  749. $req = 'No';
  750. } else {
  751. $req = 'Yes';
  752. }
  753. } else {
  754. $req = 'Yes';
  755. }
  756. if (isset($this->_deps_rel_trans[$d['rel']])) {
  757. $rel = $this->_deps_rel_trans[$d['rel']];
  758. } else {
  759. $rel = $d['rel'];
  760. }
  761. if (isset($this->_deps_type_trans[$d['type']])) {
  762. $type = ucfirst($this->_deps_type_trans[$d['type']]);
  763. } else {
  764. $type = $d['type'];
  765. }
  766. if (isset($d['name'])) {
  767. $name = $d['name'];
  768. } else {
  769. $name = '';
  770. }
  771. if (isset($d['version'])) {
  772. $version = $d['version'];
  773. } else {
  774. $version = '';
  775. }
  776. $data['data'][] = array($req, $type, $name, $rel, $version);
  777. }
  778. } else { // package.xml 2.0 dependencies display
  779. require_once 'PEAR/Dependency2.php';
  780. $deps = $info->getDependencies();
  781. $reg = &$this->config->getRegistry();
  782. if (is_array($deps)) {
  783. $d = new PEAR_Dependency2($this->config, array(), '');
  784. $data = array(
  785. 'caption' => 'Dependencies for ' . $info->getPackage(),
  786. 'border' => true,
  787. 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
  788. );
  789. foreach ($deps as $type => $subd) {
  790. $req = ($type == 'required') ? 'Yes' : 'No';
  791. if ($type == 'group') {
  792. $group = $subd['attribs']['name'];
  793. } else {
  794. $group = '';
  795. }
  796. if (!isset($subd[0])) {
  797. $subd = array($subd);
  798. }
  799. foreach ($subd as $groupa) {
  800. foreach ($groupa as $deptype => $depinfo) {
  801. if ($deptype == 'attribs') {
  802. continue;
  803. }
  804. if ($deptype == 'pearinstaller') {
  805. $deptype = 'pear Installer';
  806. }
  807. if (!isset($depinfo[0])) {
  808. $depinfo = array($depinfo);
  809. }
  810. foreach ($depinfo as $inf) {
  811. $name = '';
  812. if (isset($inf['channel'])) {
  813. $alias = $reg->channelAlias($inf['channel']);
  814. if (!$alias) {
  815. $alias = '(channel?) ' .$inf['channel'];
  816. }
  817. $name = $alias . '/';
  818. }
  819. if (isset($inf['name'])) {
  820. $name .= $inf['name'];
  821. } elseif (isset($inf['pattern'])) {
  822. $name .= $inf['pattern'];
  823. } else {
  824. $name .= '';
  825. }
  826. if (isset($inf['uri'])) {
  827. $name .= ' [' . $inf['uri'] . ']';
  828. }
  829. if (isset($inf['conflicts'])) {
  830. $ver = 'conflicts';
  831. } else {
  832. $ver = $d->_getExtraString($inf);
  833. }
  834. $data['data'][] = array($req, ucfirst($deptype), $name,
  835. $ver, $group);
  836. }
  837. }
  838. }
  839. }
  840. }
  841. }
  842. $this->ui->outputData($data, $command);
  843. return true;
  844. }
  845. // Fallback
  846. $this->ui->outputData("This package does not have any dependencies.", $command);
  847. }
  848. function doSign($command, $options, $params)
  849. {
  850. // should move most of this code into PEAR_Packager
  851. // so it'll be easy to implement "pear package --sign"
  852. if (count($params) !== 1) {
  853. return $this->raiseError("bad parameter(s), try \"help $command\"");
  854. }
  855. require_once 'System.php';
  856. require_once 'Archive/Tar.php';
  857. if (!file_exists($params[0])) {
  858. return $this->raiseError("file does not exist: $params[0]");
  859. }
  860. $obj = $this->getPackageFile($this->config, $this->_debug);
  861. $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
  862. if (PEAR::isError($info)) {
  863. return $this->raiseError($info);
  864. }
  865. $tar = new Archive_Tar($params[0]);
  866. $tmpdir = $this->config->get('temp_dir');
  867. $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign');
  868. if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
  869. return $this->raiseError("failed to extract tar file");
  870. }
  871. if (file_exists("$tmpdir/package.sig")) {
  872. return $this->raiseError("package already signed");
  873. }
  874. $packagexml = 'package.xml';
  875. if (file_exists("$tmpdir/package2.xml")) {
  876. $packagexml = 'package2.xml';
  877. }
  878. if (file_exists("$tmpdir/package.sig")) {
  879. unlink("$tmpdir/package.sig");
  880. }
  881. if (!file_exists("$tmpdir/$packagexml")) {
  882. return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
  883. }
  884. $input = $this->ui->userDialog($command,
  885. array('GnuPG Passphrase'),
  886. array('password'));
  887. if (!isset($input[0])) {
  888. //use empty passphrase
  889. $input[0] = '';
  890. }
  891. $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
  892. $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
  893. if (!$gpg) {
  894. return $this->raiseError("gpg command failed");
  895. }
  896. fwrite($gpg, "$input[0]\n");
  897. if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
  898. return $this->raiseError("gpg sign failed");
  899. }
  900. if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
  901. return $this->raiseError('failed adding signature to file');
  902. }
  903. $this->ui->outputData("Package signed.", $command);
  904. return true;
  905. }
  906. /**
  907. * For unit testing purposes
  908. */
  909. function &getInstaller(&$ui)
  910. {
  911. if (!class_exists('PEAR_Installer')) {
  912. require_once 'PEAR/Installer.php';
  913. }
  914. $a = &new PEAR_Installer($ui);
  915. return $a;
  916. }
  917. /**
  918. * For unit testing purposes
  919. */
  920. function &getCommandPackaging(&$ui, &$config)
  921. {
  922. if (!class_exists('PEAR_Command_Packaging')) {
  923. if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
  924. fclose($fp);
  925. include_once 'PEAR/Command/Packaging.php';
  926. }
  927. }
  928. if (class_exists('PEAR_Command_Packaging')) {
  929. $a = &new PEAR_Command_Packaging($ui, $config);
  930. } else {
  931. $a = null;
  932. }
  933. return $a;
  934. }
  935. function doMakeRPM($command, $options, $params)
  936. {
  937. // Check to see if PEAR_Command_Packaging is installed, and
  938. // transparently switch to use the "make-rpm-spec" command from it
  939. // instead, if it does. Otherwise, continue to use the old version
  940. // of "makerpm" supplied with this package (PEAR).
  941. $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
  942. if ($packaging_cmd !== null) {
  943. $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
  944. 'newer "make-rpm-spec" command instead');
  945. return $packaging_cmd->run('make-rpm-spec', $options, $params);
  946. }
  947. $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
  948. 'improved version is available via "pear make-rpm-spec", which '.
  949. 'is available by installing PEAR_Command_Packaging');
  950. return true;
  951. }
  952. function doConvert($command, $options, $params)
  953. {
  954. $packagexml = isset($params[0]) ? $params[0] : 'package.xml';
  955. $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
  956. DIRECTORY_SEPARATOR . 'package2.xml';
  957. $pkg = &$this->getPackageFile($this->config, $this->_debug);
  958. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  959. $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
  960. PEAR::staticPopErrorHandling();
  961. if (PEAR::isError($pf)) {
  962. if (is_array($pf->getUserInfo())) {
  963. foreach ($pf->getUserInfo() as $warning) {
  964. $this->ui->outputData($warning['message']);
  965. }
  966. }
  967. return $this->raiseError($pf);
  968. }
  969. if (is_a($pf, 'PEAR_PackageFile_v2')) {
  970. $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
  971. return true;
  972. }
  973. $gen = &$pf->getDefaultGenerator();
  974. $newpf = &$gen->toV2();
  975. $newpf->setPackagefile($newpackagexml);
  976. $gen = &$newpf->getDefaultGenerator();
  977. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  978. $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
  979. $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml));
  980. PEAR::staticPopErrorHandling();
  981. if (PEAR::isError($saved)) {
  982. if (is_array($saved->getUserInfo())) {
  983. foreach ($saved->getUserInfo() as $warning) {
  984. $this->ui->outputData($warning['message']);
  985. }
  986. }
  987. $this->ui->outputData($saved->getMessage());
  988. return true;
  989. }
  990. $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
  991. return true;
  992. }
  993. }