PearPackageTask.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. /*
  3. * $Id: PearPackageTask.php 905 2010-10-05 16:28:03Z mrook $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. require_once 'phing/tasks/system/MatchingTask.php';
  22. include_once 'phing/types/FileSet.php';
  23. /**
  24. * A task to create PEAR package.xml file.
  25. *
  26. * This class uses the PEAR_PackageFileMaintainer class to perform the work.
  27. *
  28. * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o
  29. * requiring changes to this class. We've accomplished this by having generic <option> and <mapping>
  30. * nested elements. All options are set using PEAR_PackageFileMaintainer::setOptions().
  31. *
  32. * The <option> tag is used to set a simple option value.
  33. * <code>
  34. * <option name="option_name" value="option_value"/>
  35. * or <option name="option_name">option_value</option>
  36. * </code>
  37. *
  38. * The <mapping> tag represents a complex data type. You can use nested <element> (and nested <element> with
  39. * <element> tags) to represent the full complexity of the structure. Bear in mind that what you are creating
  40. * will be mapped to an associative array that will be passed in via PEAR_PackageFileMaintainer::setOptions().
  41. * <code>
  42. * <mapping name="option_name">
  43. * <element key="key_name" value="key_val"/>
  44. * <element key="key_name" value="key_val"/>
  45. * </mapping>
  46. * </code>
  47. *
  48. * Here's an over-simple example of how this could be used:
  49. * <code>
  50. * <pearpkg name="phing" dir="${build.src.dir}" destFile="${build.base.dir}/package.xml">
  51. * <fileset>
  52. * <include name="**"/>
  53. * </fileset>
  54. * <option name="notes">Sample release notes here.</option>
  55. * <option name="description">Package description</option>
  56. * <option name="summary">Short description</option>
  57. * <option name="version" value="2.0.0b1"/>
  58. * <option name="state" value="beta"/>
  59. * <mapping name="maintainers">
  60. * <element>
  61. * <element key="handle" value="hlellelid"/>
  62. * <element key="name" value="Hans"/>
  63. * <element key="email" value="hans@xmpl.org"/>
  64. * <element key="role" value="lead"/>
  65. * </element>
  66. * </mapping>
  67. * </pearpkg>
  68. * </code>
  69. *
  70. * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to
  71. * see a more complete example of how to call this script.
  72. *
  73. * @author Hans Lellelid <hans@xmpl.org>
  74. * @package phing.tasks.ext
  75. * @version $Id: PearPackageTask.php 905 2010-10-05 16:28:03Z mrook $
  76. */
  77. class PearPackageTask extends MatchingTask {
  78. /** */
  79. protected $package;
  80. /** Base directory for reading files. */
  81. protected $dir;
  82. /** Package file */
  83. private $packageFile;
  84. /** @var array FileSet[] */
  85. private $filesets = array();
  86. /** @var PEAR_PackageFileManager */
  87. protected $pkg;
  88. private $preparedOptions = array();
  89. /** @var array PearPkgOption[] */
  90. protected $options = array();
  91. /** Nested <mapping> (complex options) types. */
  92. protected $mappings = array();
  93. public function init() {
  94. include_once 'PEAR/PackageFileManager.php';
  95. if (!class_exists('PEAR_PackageFileManager')) {
  96. throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml file.");
  97. }
  98. }
  99. /**
  100. * Sets PEAR package.xml options, based on class properties.
  101. * @return void
  102. */
  103. protected function setOptions() {
  104. // 1) first prepare/populate options
  105. $this->populateOptions();
  106. // 2) make any final adjustments (this could move into populateOptions() also)
  107. // default PEAR basedir would be the name of the package (e.g."phing")
  108. if (!isset($this->preparedOptions['baseinstalldir'])) {
  109. $this->preparedOptions['baseinstalldir'] = $this->package;
  110. }
  111. // unless filelistgenerator has been overridden, we use Phing FileSet generator
  112. if (!isset($this->preparedOptions['filelistgenerator'])) {
  113. if (empty($this->filesets)) {
  114. throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml");
  115. }
  116. $this->preparedOptions['filelistgenerator'] = 'Fileset';
  117. $this->preparedOptions['usergeneratordir'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearpackage';
  118. // Some PHING-specific options needed by our Fileset reader
  119. $this->preparedOptions['phing_project'] = $this->project;
  120. $this->preparedOptions['phing_filesets'] = $this->filesets;
  121. } elseif ($this->preparedOptions['filelistgeneragor'] != 'Fileset' && !empty($this->filesets)) {
  122. throw new BuildException("You cannot use <fileset> element if you have specified the \"filelistgenerator\" option.");
  123. }
  124. // 3) Set the options
  125. // No need for excessive validation here, since the PEAR class will do its own
  126. // validation & return errors
  127. $e = $this->pkg->setOptions($this->preparedOptions);
  128. if (PEAR::isError($e)) {
  129. throw new BuildException("Unable to set options.", new Exception($e->getMessage()));
  130. }
  131. }
  132. /**
  133. * Fixes the boolean in optional dependencies
  134. */
  135. private function fixDeps($deps)
  136. {
  137. foreach (array_keys($deps) as $dep)
  138. {
  139. if (isset($deps[$dep]['optional']) && $deps[$dep]['optional'])
  140. {
  141. $deps[$dep]['optional'] = "yes";
  142. }
  143. }
  144. return $deps;
  145. }
  146. /**
  147. * Adds the options that are set via attributes and the nested tags to the options array.
  148. */
  149. private function populateOptions() {
  150. // These values could be overridden if explicitly defined using nested tags
  151. $this->preparedOptions['package'] = $this->package;
  152. $this->preparedOptions['packagedirectory'] = $this->dir->getAbsolutePath();
  153. if ($this->packageFile !== null) {
  154. // create one w/ full path
  155. $f = new PhingFile($this->packageFile->getAbsolutePath());
  156. $this->preparedOptions['packagefile'] = $f->getName();
  157. // must end in trailing slash
  158. $this->preparedOptions['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR;
  159. $this->log("Creating package file: " . $f->__toString(), Project::MSG_INFO);
  160. } else {
  161. $this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO);
  162. }
  163. // converts option objects and mapping objects into
  164. // key => value options that can be passed to PEAR_PackageFileManager
  165. foreach($this->options as $opt) {
  166. $this->preparedOptions[ $opt->getName() ] = $opt->getValue(); //no arrays yet. preg_split('/\s*,\s*/', $opt->getValue());
  167. }
  168. foreach($this->mappings as $map) {
  169. $value = $map->getValue(); // getValue returns complex value
  170. if ($map->getName() == 'deps')
  171. {
  172. $value = $this->fixDeps($value);
  173. }
  174. $this->preparedOptions[ $map->getName() ] = $value;
  175. }
  176. }
  177. /**
  178. * Main entry point.
  179. * @return void
  180. */
  181. public function main() {
  182. if ($this->dir === null) {
  183. throw new BuildException("You must specify the \"dir\" attribute for PEAR package task.");
  184. }
  185. if ($this->package === null) {
  186. throw new BuildException("You must specify the \"name\" attribute for PEAR package task.");
  187. }
  188. $this->pkg = new PEAR_PackageFileManager();
  189. $this->setOptions();
  190. $e = $this->pkg->writePackageFile();
  191. if (PEAR::isError($e)) {
  192. throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
  193. }
  194. }
  195. /**
  196. * Used by the PEAR_PackageFileManager_PhingFileSet lister.
  197. * @return array FileSet[]
  198. */
  199. public function getFileSets() {
  200. return $this->filesets;
  201. }
  202. // -------------------------------
  203. // Set properties from XML
  204. // -------------------------------
  205. /**
  206. * Nested creator, creates a FileSet for this task
  207. *
  208. * @return FileSet The created fileset object
  209. */
  210. function createFileSet() {
  211. $num = array_push($this->filesets, new FileSet());
  212. return $this->filesets[$num-1];
  213. }
  214. /**
  215. * Set "package" property from XML.
  216. * @see setName()
  217. * @param string $v
  218. * @return void
  219. */
  220. public function setPackage($v) {
  221. $this->package = $v;
  222. }
  223. /**
  224. * Sets "dir" property from XML.
  225. * @param PhingFile $f
  226. * @return void
  227. */
  228. public function setDir(PhingFile $f) {
  229. $this->dir = $f;
  230. }
  231. /**
  232. * Sets "name" property from XML.
  233. * @param string $v
  234. * @return void
  235. */
  236. public function setName($v) {
  237. $this->package = $v;
  238. }
  239. /**
  240. * Sets the file to use for generated package.xml
  241. */
  242. public function setDestFile(PhingFile $f) {
  243. $this->packageFile = $f;
  244. }
  245. /**
  246. * Handles nested generic <option> elements.
  247. */
  248. function createOption() {
  249. $o = new PearPkgOption();
  250. $this->options[] = $o;
  251. return $o;
  252. }
  253. /**
  254. * Handles nested generic <option> elements.
  255. */
  256. function createMapping() {
  257. $o = new PearPkgMapping();
  258. $this->mappings[] = $o;
  259. return $o;
  260. }
  261. }
  262. /**
  263. * Generic option class is used for non-complex options.
  264. *
  265. * @package phing.tasks.ext
  266. */
  267. class PearPkgOption {
  268. private $name;
  269. private $value;
  270. public function setName($v) { $this->name = $v; }
  271. public function getName() { return $this->name; }
  272. public function setValue($v) { $this->value = $v; }
  273. public function getValue() { return $this->value; }
  274. public function addText($txt) { $this->value = trim($txt); }
  275. }
  276. /**
  277. * Handles complex options <mapping> elements which are hashes (assoc arrays).
  278. *
  279. * @package phing.tasks.ext
  280. */
  281. class PearPkgMapping {
  282. private $name;
  283. private $elements = array();
  284. public function setName($v) {
  285. $this->name = $v;
  286. }
  287. public function getName() {
  288. return $this->name;
  289. }
  290. public function createElement() {
  291. $e = new PearPkgMappingElement();
  292. $this->elements[] = $e;
  293. return $e;
  294. }
  295. public function getElements() {
  296. return $this->elements;
  297. }
  298. /**
  299. * Returns the PHP hash or array of hashes (etc.) that this mapping represents.
  300. * @return array
  301. */
  302. public function getValue() {
  303. $value = array();
  304. foreach($this->getElements() as $el) {
  305. if ($el->getKey() !== null) {
  306. $value[ $el->getKey() ] = $el->getValue();
  307. } else {
  308. $value[] = $el->getValue();
  309. }
  310. }
  311. return $value;
  312. }
  313. }
  314. /**
  315. * Sub-element of <mapping>.
  316. *
  317. * @package phing.tasks.ext
  318. */
  319. class PearPkgMappingElement {
  320. private $key;
  321. private $value;
  322. private $elements = array();
  323. public function setKey($v) {
  324. $this->key = $v;
  325. }
  326. public function getKey() {
  327. return $this->key;
  328. }
  329. public function setValue($v) {
  330. $this->value = $v;
  331. }
  332. /**
  333. * Returns either the simple value or
  334. * the calculated value (array) of nested elements.
  335. * @return mixed
  336. */
  337. public function getValue() {
  338. if (!empty($this->elements)) {
  339. $value = array();
  340. foreach($this->elements as $el) {
  341. if ($el->getKey() !== null) {
  342. $value[ $el->getKey() ] = $el->getValue();
  343. } else {
  344. $value[] = $el->getValue();
  345. }
  346. }
  347. return $value;
  348. } else {
  349. return $this->value;
  350. }
  351. }
  352. /**
  353. * Handles nested <element> tags.
  354. */
  355. public function createElement() {
  356. $e = new PearPkgMappingElement();
  357. $this->elements[] = $e;
  358. return $e;
  359. }
  360. }