Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: PearPackageTask.php 144 2007-02-05 15:19:00Z hans $
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
 
22
require_once 'phing/tasks/system/MatchingTask.php';
23
include_once 'phing/types/FileSet.php';
24
 
25
/**
26
 * A task to create PEAR package.xml file.
27
 *
28
 * This class uses the PEAR_PackageFileMaintainer class to perform the work.
29
 *
30
 * This class is designed to be very flexible -- i.e. account for changes to the package.xml w/o
31
 * requiring changes to this class.  We've accomplished this by having generic <option> and <mapping>
32
 * nested elements.  All options are set using PEAR_PackageFileMaintainer::setOptions().
33
 *
34
 * The <option> tag is used to set a simple option value.
35
 * <code>
36
 * <option name="option_name" value="option_value"/>
37
 * or <option name="option_name">option_value</option>
38
 * </code>
39
 *
40
 * The <mapping> tag represents a complex data type.  You can use nested <element> (and nested <element> with
41
 * <element> tags) to represent the full complexity of the structure.  Bear in mind that what you are creating
42
 * will be mapped to an associative array that will be passed in via PEAR_PackageFileMaintainer::setOptions().
43
 * <code>
44
 * <mapping name="option_name">
45
 *  <element key="key_name" value="key_val"/>
46
 *  <element key="key_name" value="key_val"/>
47
 * </mapping>
48
 * </code>
49
 *
50
 * Here's an over-simple example of how this could be used:
51
 * <code>
52
 * <pearpkg name="phing" dir="${build.src.dir}" destFile="${build.base.dir}/package.xml">
53
 *  <fileset>
54
 *   <include name="**"/>
55
 *  </fileset>
56
 *  <option name="notes">Sample release notes here.</option>
57
 *  <option name="description">Package description</option>
58
 *  <option name="summary">Short description</option>
59
 *  <option name="version" value="2.0.0b1"/>
60
 *  <option name="state" value="beta"/>
61
 *  <mapping name="maintainers">
62
 *   <element>
63
 *    <element key="handle" value="hlellelid"/>
64
 *    <element key="name" value="Hans"/>
65
 *    <element key="email" value="hans@xmpl.org"/>
66
 *    <element key="role" value="lead"/>
67
 *   </element>
68
 *  </mapping>
69
 * </pearpkg>
70
 * </code>
71
 *
72
 * Look at the build.xml in the Phing base directory (assuming you have the full distro / CVS version of Phing) to
73
 * see a more complete example of how to call this script.
74
 *
75
 * @author   Hans Lellelid <hans@xmpl.org>
76
 * @package  phing.tasks.ext
77
 * @version  $Revision: 1.9 $
78
 */
79
class PearPackageTask extends MatchingTask {
80
 
81
    /** */
82
    protected $package;
83
 
84
    /** Base directory for reading files. */
85
    protected $dir;
86
 
87
    /** Package file */
88
    private $packageFile;
89
 
90
    /** @var array FileSet[] */
91
    private $filesets = array();
92
 
93
    /** @var PEAR_PackageFileManager */
94
    protected $pkg;
95
 
96
    private $preparedOptions = array();
97
 
98
    /** @var array PearPkgOption[] */
99
    protected $options = array();
100
 
101
    /** Nested <mapping> (complex options) types. */
102
    protected $mappings = array();
103
 
104
    public function init() {
105
        include_once 'PEAR/PackageFileManager.php';
106
        if (!class_exists('PEAR_PackageFileManager')) {
107
            throw new BuildException("You must have installed PEAR_PackageFileManager in order to create a PEAR package.xml file.");
108
        }
109
    }
110
 
111
    /**
112
     * Sets PEAR package.xml options, based on class properties.
113
     * @return void
114
     */
115
    protected function setOptions() {
116
 
117
        // 1) first prepare/populate options
118
        $this->populateOptions();
119
 
120
        // 2) make any final adjustments (this could move into populateOptions() also)
121
 
122
        // default PEAR basedir would be the name of the package (e.g."phing")
123
        if (!isset($this->preparedOptions['baseinstalldir'])) {
124
            $this->preparedOptions['baseinstalldir'] = $this->package;
125
        }
126
 
127
        // unless filelistgenerator has been overridden, we use Phing FileSet generator
128
        if (!isset($this->preparedOptions['filelistgenerator'])) {
129
            if (empty($this->filesets)) {
130
                throw new BuildException("You must use a <fileset> tag to specify the files to include in the package.xml");
131
            }
132
            $this->preparedOptions['filelistgenerator'] = 'Fileset';
133
            $this->preparedOptions['usergeneratordir'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pearpackage';
134
            // Some PHING-specific options needed by our Fileset reader
135
            $this->preparedOptions['phing_project'] = $this->project;
136
            $this->preparedOptions['phing_filesets'] = $this->filesets;
137
        } elseif ($this->preparedOptions['filelistgeneragor'] != 'Fileset' && !empty($this->filesets)) {
138
            throw new BuildException("You cannot use <fileset> element if you have specified the \"filelistgenerator\" option.");
139
        }
140
 
141
        // 3) Set the options
142
 
143
        // No need for excessive validation here, since the  PEAR class will do its own
144
        // validation & return errors
145
        $e = $this->pkg->setOptions($this->preparedOptions);
146
 
147
        if (PEAR::isError($e)) {
148
            throw new BuildException("Unable to set options.", new Exception($e->getMessage()));
149
        }
150
    }
151
 
152
    /**
153
     * Fixes the boolean in optional dependencies
154
     */
155
    private function fixDeps($deps)
156
    {
157
        foreach (array_keys($deps) as $dep)
158
        {
159
            if (isset($deps[$dep]['optional']) && $deps[$dep]['optional'])
160
            {
161
                $deps[$dep]['optional'] = "yes";
162
            }
163
        }
164
 
165
        return $deps;
166
    }
167
 
168
    /**
169
     * Adds the options that are set via attributes and the nested tags to the options array.
170
     */
171
    private function populateOptions() {
172
 
173
        // These values could be overridden if explicitly defined using nested tags
174
        $this->preparedOptions['package'] = $this->package;
175
        $this->preparedOptions['packagedirectory'] = $this->dir->getAbsolutePath();
176
 
177
        if ($this->packageFile !== null) {
178
            // create one w/ full path
179
            $f = new PhingFile($this->packageFile->getAbsolutePath());
180
            $this->preparedOptions['packagefile'] = $f->getName();
181
            // must end in trailing slash
182
            $this->preparedOptions['outputdirectory'] = $f->getParent() . DIRECTORY_SEPARATOR;
183
            $this->log("Creating package file: " . $f->__toString(), Project::MSG_INFO);
184
        } else {
185
            $this->log("Creating [default] package.xml file in base directory.", Project::MSG_INFO);
186
        }
187
 
188
        // converts option objects and mapping objects into
189
        // key => value options that can be passed to PEAR_PackageFileManager
190
 
191
        foreach($this->options as $opt) {
192
            $this->preparedOptions[ $opt->getName() ] = $opt->getValue(); //no arrays yet. preg_split('/\s*,\s*/', $opt->getValue());
193
        }
194
 
195
        foreach($this->mappings as $map) {
196
            $value = $map->getValue(); // getValue returns complex value
197
 
198
            if ($map->getName() == 'deps')
199
            {
200
                $value = $this->fixDeps($value);
201
            }
202
 
203
            $this->preparedOptions[ $map->getName() ] = $value;
204
        }
205
    }
206
 
207
    /**
208
     * Main entry point.
209
     * @return void
210
     */
211
    public function main() {
212
 
213
        if ($this->dir === null) {
214
            throw new BuildException("You must specify the \"dir\" attribute for PEAR package task.");
215
        }
216
 
217
        if ($this->package === null) {
218
            throw new BuildException("You must specify the \"name\" attribute for PEAR package task.");
219
        }
220
 
221
        $this->pkg = new PEAR_PackageFileManager();
222
 
223
        $this->setOptions();
224
 
225
        $e = $this->pkg->writePackageFile();
226
        if (PEAR::isError($e)) {
227
            throw new BuildException("Unable to write package file.", new Exception($e->getMessage()));
228
        }
229
 
230
    }
231
 
232
    /**
233
     * Used by the PEAR_PackageFileManager_PhingFileSet lister.
234
     * @return array FileSet[]
235
     */
236
    public function getFileSets() {
237
        return $this->filesets;
238
    }
239
 
240
    // -------------------------------
241
    // Set properties from XML
242
    // -------------------------------
243
 
244
    /**
245
     * Nested creator, creates a FileSet for this task
246
     *
247
     * @return FileSet The created fileset object
248
     */
249
    function createFileSet() {
250
        $num = array_push($this->filesets, new FileSet());
251
        return $this->filesets[$num-1];
252
    }
253
 
254
    /**
255
     * Set "package" property from XML.
256
     * @see setName()
257
     * @param string $v
258
     * @return void
259
     */
260
    public function setPackage($v) {
261
        $this->package = $v;
262
    }
263
 
264
    /**
265
     * Sets "dir" property from XML.
266
     * @param PhingFile $f
267
     * @return void
268
     */
269
    public function setDir(PhingFile $f) {
270
        $this->dir = $f;
271
    }
272
 
273
    /**
274
     * Sets "name" property from XML.
275
     * @param string $v
276
     * @return void
277
     */
278
    public function setName($v) {
279
        $this->package = $v;
280
    }
281
 
282
    /**
283
     * Sets the file to use for generated package.xml
284
     */
285
    public function setDestFile(PhingFile $f) {
286
        $this->packageFile = $f;
287
    }
288
 
289
    /**
290
     * Handles nested generic <option> elements.
291
     */
292
    function createOption() {
293
        $o = new PearPkgOption();
294
        $this->options[] = $o;
295
        return $o;
296
    }
297
 
298
    /**
299
     * Handles nested generic <option> elements.
300
     */
301
    function createMapping() {
302
        $o = new PearPkgMapping();
303
        $this->mappings[] = $o;
304
        return $o;
305
    }
306
}
307
 
308
 
309
 
310
/**
311
 * Generic option class is used for non-complex options.
312
 */
313
class PearPkgOption {
314
 
315
    private    $name;
316
    private $value;
317
 
318
    public function setName($v) { $this->name = $v; }
319
    public function getName() { return $this->name; }
320
 
321
    public function setValue($v) { $this->value = $v; }
322
    public function getValue() { return $this->value; }
323
    public function addText($txt) { $this->value = trim($txt); }
324
 
325
}
326
 
327
/**
328
 * Handles complex options <mapping> elements which are hashes (assoc arrays).
329
 */
330
class PearPkgMapping {
331
 
332
    private    $name;
333
    private $elements = array();
334
 
335
    public function setName($v) {
336
        $this->name = $v;
337
    }
338
 
339
    public function getName() {
340
        return $this->name;
341
    }
342
 
343
    public function createElement() {
344
        $e = new PearPkgMappingElement();
345
        $this->elements[] = $e;
346
        return $e;
347
    }
348
 
349
    public function    getElements() {
350
        return $this->elements;
351
    }
352
 
353
    /**
354
     * Returns the PHP hash or array of hashes (etc.) that this mapping represents.
355
     * @return array
356
     */
357
    public function getValue() {
358
        $value = array();
359
        foreach($this->getElements() as $el) {
360
            if ($el->getKey() !== null) {
361
                $value[ $el->getKey() ] = $el->getValue();
362
            } else {
363
                $value[] = $el->getValue();
364
            }
365
        }
366
        return $value;
367
    }
368
}
369
 
370
/**
371
 * Sub-element of <mapping>.
372
 */
373
class PearPkgMappingElement {
374
 
375
    private    $key;
376
    private $value;
377
    private $elements = array();
378
 
379
    public function setKey($v) {
380
        $this->key = $v;
381
    }
382
 
383
    public function getKey() {
384
        return $this->key;
385
    }
386
 
387
    public function setValue($v) {
388
        $this->value = $v;
389
    }
390
 
391
    /**
392
     * Returns either the simple value or
393
     * the calculated value (array) of nested elements.
394
     * @return mixed
395
     */
396
    public function getValue() {
397
        if (!empty($this->elements)) {
398
            $value = array();
399
            foreach($this->elements as $el) {
400
                if ($el->getKey() !== null) {
401
                    $value[ $el->getKey() ] = $el->getValue();
402
                } else {
403
                    $value[] = $el->getValue();
404
                }
405
            }
406
            return $value;
407
        } else  {
408
            return $this->value;
409
        }
410
    }
411
 
412
    /**
413
     * Handles nested <element> tags.
414
     */
415
    public function createElement() {
416
        $e = new PearPkgMappingElement();
417
        $this->elements[] = $e;
418
        return $e;
419
    }
420
 
421
}