428 lines
13 KiB
PHP
428 lines
13 KiB
PHP
|
<?php
|
||
|
/*
|
||
|
* $Id: PearPackageTask.php 905 2010-10-05 16:28:03Z mrook $
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* This software consists of voluntary contributions made by many individuals
|
||
|
* and is licensed under the LGPL. For more information please see
|
||
|
* <http://phing.info>.
|
||
|
*/
|
||
|
|
||
|
require_once 'phing/tasks/system/MatchingTask.php';
|
||
|
include_once 'phing/types/FileSet.php';
|
||
|
|
||
|
/**
|
||
|
* A task to create PEAR package.xml file.
|
||
|
*
|
||
|
* This class uses the PEAR_PackageFileMaintainer class to perform the work.
|
||
|
*
|
||
|
* This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o
|
||
|
* requiring changes to this class. We've accomplished this by having generic <option> and <mapping>
|
||
|
* nested elements. All options are set using PEAR_PackageFileMaintainer::setOptions().
|
||
|
*
|
||
|
* The <option> tag is used to set a simple option value.
|
||
|
* <code>
|
||
|
* <option name="option_name" value="option_value"/>
|
||
|
* or <option name="option_name">option_value</option>
|
||
|
* </code>
|
||
|
*
|
||
|
* The <mapping> tag represents a complex data type. You can use nested <element> (and nested <element> with
|
||
|
* <element> tags) to represent the full complexity of the structure. Bear in mind that what you are creating
|
||
|
* will be mapped to an associative array that will be passed in via PEAR_PackageFileMaintainer::setOptions().
|
||
|
* <code>
|
||
|
* <mapping name="option_name">
|
||
|
* <element key="key_name" value="key_val"/>
|
||
|
* <element key="key_name" value="key_val"/>
|
||
|
* </mapping>
|
||
|
* </code>
|
||
|
*
|
||
|
* Here's an over-simple example of how this could be used:
|
||
|
* <code>
|
||
|
* <pearpkg name="phing" dir="${build.src.dir}" destFile="${build.base.dir}/package.xml">
|
||
|
* <fileset>
|
||
|
* <include name="**"/>
|
||
|
* </fileset>
|
||
|
* <option name="notes">Sample release notes here.</option>
|
||
|
* <option name="description">Package description</option>
|
||
|
* <option name="summary">Short description</option>
|
||
|
* <option name="version" value="2.0.0b1"/>
|
||
|
* <option name="state" value="beta"/>
|
||
|
* <mapping name="maintainers">
|
||
|
* <element>
|
||
|
* <element key="handle" value="hlellelid"/>
|
||
|
* <element key="name" value="Hans"/>
|
||
|
* <element key="email" value="hans@xmpl.org"/>
|
||
|
* <element key="role" value="lead"/>
|
||
|
* </element>
|
||
|
* </mapping>
|
||
|
* </pearpkg>
|
||
|
* </code>
|
||
|
*
|
||
|
* Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to
|
||
|
* see a more complete example of how to call this script.
|
||
|
*
|
||
|
* @author Hans Lellelid <hans@xmpl.org>
|
||
|
* @package phing.tasks.ext
|
||
|
* @version $Id: PearPackageTask.php 905 2010-10-05 16:28:03Z mrook $
|
||
|
*/
|
||
|
class PearPackageTask extends MatchingTask {
|
||
|
|
||
|
/** */
|
||
|
protected $package;
|
||
|
|
||
|
/** Base directory for reading files. */
|
||
|
protected $dir;
|
||
|
|
||
|
/** Package file */
|
||
|
private $packageFile;
|
||
|
|
||
|
/** @var array FileSet[] */
|
||
|
private $filesets = array();
|
||
|
|
||
|
/** @var PEAR_PackageFileManager */
|
||
|
protected $pkg;
|
||
|
|
||
|
private $preparedOptions = array();
|
||
|
|
||
|
/** @var array PearPkgOption[] */
|
||
|
protected $options = array();
|
||
|
|
||
|
/** Nested <mapping> (complex options) types. */
|
||
|
protected $mappings = array();
|
||
|
|
||
|
public function init() {
|
||
|
include_once 'PEAR/PackageFileManager.php';
|
||
|
if (!class_exists('PEAR_PackageFileManager')) {
|
||
|
throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml file.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets PEAR package.xml options, based on class properties.
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function setOptions() {
|
||
|
|
||
|
// 1) first prepare/populate options
|
||
|
$this->populateOptions();
|
||
|
|
||
|
// 2) make any final adjustments (this could move into populateOptions() also)
|
||
|
|
||
|
// default PEAR basedir would be the name of the package (e.g."phing")
|
||
|
if (!isset($this->preparedOptions['baseinstalldir'])) {
|
||
|
$this->preparedOptions['baseinstalldir'] = $this->package;
|
||
|
}
|
||
|
|
||
|
// unless filelistgenerator has been overridden, we use Phing FileSet generator
|
||
|
if (!isset($this->preparedOptions['filelistgenerator'])) {
|
||
|
if (empty($this->filesets)) {
|
||
|
throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml");
|
||
|
}
|
||
|
$this->preparedOptions['filelistgenerator'] = 'Fileset';
|
||
|
$this->preparedOptions['usergeneratordir'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearpackage';
|
||
|
// Some PHING-specific options needed by our Fileset reader
|
||
|
$this->preparedOptions['phing_project'] = $this->project;
|
||
|
$this->preparedOptions['phing_filesets'] = $this->filesets;
|
||
|
} elseif ($this->preparedOptions['filelistgeneragor'] != 'Fileset' && !empty($this->filesets)) {
|
||
|
throw new BuildException("You cannot use <fileset> element if you have specified the \"filelistgenerator\" option.");
|
||
|
}
|
||
|
|
||
|
// 3) Set the options
|
||
|
|
||
|
// No need for excessive validation here, since the PEAR class will do its own
|
||
|
// validation & return errors
|
||
|
$e = $this->pkg->setOptions($this->preparedOptions);
|
||
|
|
||
|
if (PEAR::isError($e)) {
|
||
|
throw new BuildException("Unable to set options.", new Exception($e->getMessage()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fixes the boolean in optional dependencies
|
||
|
*/
|
||
|
private function fixDeps($deps)
|
||
|
{
|
||
|
foreach (array_keys($deps) as $dep)
|
||
|
{
|
||
|
if (isset($deps[$dep]['optional']) && $deps[$dep]['optional'])
|
||
|
{
|
||
|
$deps[$dep]['optional'] = "yes";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $deps;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds the options that are set via attributes and the nested tags to the options array.
|
||
|
*/
|
||
|
private function populateOptions() {
|
||
|
|
||
|
// These values could be overridden if explicitly defined using nested tags
|
||
|
$this->preparedOptions['package'] = $this->package;
|
||
|
$this->preparedOptions['packagedirectory'] = $this->dir->getAbsolutePath();
|
||
|
|
||
|
if ($this->packageFile !== null) {
|
||
|
// create one w/ full path
|
||
|
$f = new PhingFile($this->packageFile->getAbsolutePath());
|
||
|
$this->preparedOptions['packagefile'] = $f->getName();
|
||
|
// must end in trailing slash
|
||
|
$this->preparedOptions['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR;
|
||
|
$this->log("Creating package file: " . $f->__toString(), Project::MSG_INFO);
|
||
|
} else {
|
||
|
$this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO);
|
||
|
}
|
||
|
|
||
|
// converts option objects and mapping objects into
|
||
|
// key => value options that can be passed to PEAR_PackageFileManager
|
||
|
|
||
|
foreach($this->options as $opt) {
|
||
|
$this->preparedOptions[ $opt->getName() ] = $opt->getValue(); //no arrays yet. preg_split('/\s*,\s*/', $opt->getValue());
|
||
|
}
|
||
|
|
||
|
foreach($this->mappings as $map) {
|
||
|
$value = $map->getValue(); // getValue returns complex value
|
||
|
|
||
|
if ($map->getName() == 'deps')
|
||
|
{
|
||
|
$value = $this->fixDeps($value);
|
||
|
}
|
||
|
|
||
|
$this->preparedOptions[ $map->getName() ] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Main entry point.
|
||
|
* @return void
|
||
|
*/
|
||
|
public function main() {
|
||
|
|
||
|
if ($this->dir === null) {
|
||
|
throw new BuildException("You must specify the \"dir\" attribute for PEAR package task.");
|
||
|
}
|
||
|
|
||
|
if ($this->package === null) {
|
||
|
throw new BuildException("You must specify the \"name\" attribute for PEAR package task.");
|
||
|
}
|
||
|
|
||
|
$this->pkg = new PEAR_PackageFileManager();
|
||
|
|
||
|
$this->setOptions();
|
||
|
|
||
|
$e = $this->pkg->writePackageFile();
|
||
|
if (PEAR::isError($e)) {
|
||
|
throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by the PEAR_PackageFileManager_PhingFileSet lister.
|
||
|
* @return array FileSet[]
|
||
|
*/
|
||
|
public function getFileSets() {
|
||
|
return $this->filesets;
|
||
|
}
|
||
|
|
||
|
// -------------------------------
|
||
|
// Set properties from XML
|
||
|
// -------------------------------
|
||
|
|
||
|
/**
|
||
|
* Nested creator, creates a FileSet for this task
|
||
|
*
|
||
|
* @return FileSet The created fileset object
|
||
|
*/
|
||
|
function createFileSet() {
|
||
|
$num = array_push($this->filesets, new FileSet());
|
||
|
return $this->filesets[$num-1];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set "package" property from XML.
|
||
|
* @see setName()
|
||
|
* @param string $v
|
||
|
* @return void
|
||
|
*/
|
||
|
public function setPackage($v) {
|
||
|
$this->package = $v;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets "dir" property from XML.
|
||
|
* @param PhingFile $f
|
||
|
* @return void
|
||
|
*/
|
||
|
public function setDir(PhingFile $f) {
|
||
|
$this->dir = $f;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets "name" property from XML.
|
||
|
* @param string $v
|
||
|
* @return void
|
||
|
*/
|
||
|
public function setName($v) {
|
||
|
$this->package = $v;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the file to use for generated package.xml
|
||
|
*/
|
||
|
public function setDestFile(PhingFile $f) {
|
||
|
$this->packageFile = $f;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles nested generic <option> elements.
|
||
|
*/
|
||
|
function createOption() {
|
||
|
$o = new PearPkgOption();
|
||
|
$this->options[] = $o;
|
||
|
return $o;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles nested generic <option> elements.
|
||
|
*/
|
||
|
function createMapping() {
|
||
|
$o = new PearPkgMapping();
|
||
|
$this->mappings[] = $o;
|
||
|
return $o;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Generic option class is used for non-complex options.
|
||
|
*
|
||
|
* @package phing.tasks.ext
|
||
|
*/
|
||
|
class PearPkgOption {
|
||
|
|
||
|
private $name;
|
||
|
private $value;
|
||
|
|
||
|
public function setName($v) { $this->name = $v; }
|
||
|
public function getName() { return $this->name; }
|
||
|
|
||
|
public function setValue($v) { $this->value = $v; }
|
||
|
public function getValue() { return $this->value; }
|
||
|
public function addText($txt) { $this->value = trim($txt); }
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles complex options <mapping> elements which are hashes (assoc arrays).
|
||
|
*
|
||
|
* @package phing.tasks.ext
|
||
|
*/
|
||
|
class PearPkgMapping {
|
||
|
|
||
|
private $name;
|
||
|
private $elements = array();
|
||
|
|
||
|
public function setName($v) {
|
||
|
$this->name = $v;
|
||
|
}
|
||
|
|
||
|
public function getName() {
|
||
|
return $this->name;
|
||
|
}
|
||
|
|
||
|
public function createElement() {
|
||
|
$e = new PearPkgMappingElement();
|
||
|
$this->elements[] = $e;
|
||
|
return $e;
|
||
|
}
|
||
|
|
||
|
public function getElements() {
|
||
|
return $this->elements;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the PHP hash or array of hashes (etc.) that this mapping represents.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getValue() {
|
||
|
$value = array();
|
||
|
foreach($this->getElements() as $el) {
|
||
|
if ($el->getKey() !== null) {
|
||
|
$value[ $el->getKey() ] = $el->getValue();
|
||
|
} else {
|
||
|
$value[] = $el->getValue();
|
||
|
}
|
||
|
}
|
||
|
return $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sub-element of <mapping>.
|
||
|
*
|
||
|
* @package phing.tasks.ext
|
||
|
*/
|
||
|
class PearPkgMappingElement {
|
||
|
|
||
|
private $key;
|
||
|
private $value;
|
||
|
private $elements = array();
|
||
|
|
||
|
public function setKey($v) {
|
||
|
$this->key = $v;
|
||
|
}
|
||
|
|
||
|
public function getKey() {
|
||
|
return $this->key;
|
||
|
}
|
||
|
|
||
|
public function setValue($v) {
|
||
|
$this->value = $v;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns either the simple value or
|
||
|
* the calculated value (array) of nested elements.
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getValue() {
|
||
|
if (!empty($this->elements)) {
|
||
|
$value = array();
|
||
|
foreach($this->elements as $el) {
|
||
|
if ($el->getKey() !== null) {
|
||
|
$value[ $el->getKey() ] = $el->getValue();
|
||
|
} else {
|
||
|
$value[] = $el->getValue();
|
||
|
}
|
||
|
}
|
||
|
return $value;
|
||
|
} else {
|
||
|
return $this->value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles nested <element> tags.
|
||
|
*/
|
||
|
public function createElement() {
|
||
|
$e = new PearPkgMappingElement();
|
||
|
$this->elements[] = $e;
|
||
|
return $e;
|
||
|
}
|
||
|
|
||
|
}
|