Passed
Push — master ( 6595be...1b5282 )
by Siad
10:38
created

SubPhing::getBuildpath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 6
rs 10
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
class SubPhing extends Task
21
{
22
    use FileSetAware;
23
24
    /** @var Path */
25
    private $buildpath;
26
27
    private $phing;
28
    private $subTarget;
29
    private $phingfile = 'build.xml';
30
    private $genericphingfile;
31
    private $verbose = false;
32
    private $inheritAll = false;
33
    private $inheritRefs = false;
34
    private $failOnError = true;
35
    private $output;
36
37
    private $properties = [];
38
    private $references = [];
39
40
    /**
41
     * Assigns an Ant property to another.
42
     *
43
     * @param PropertyTask $to the destination property whose content is modified.
44
     * @param PropertyTask $from the source property whose content is copied.
45
     */
46
    private static function copyProperty(PropertyTask $to, PropertyTask $from)
47
    {
48
        $to->setName($from->getName());
49
50
        if ($from->getValue() !== null) {
0 ignored issues
show
introduced by
The condition $from->getValue() !== null is always true.
Loading history...
51
            $to->setValue($from->getValue());
52
        }
53
        if ($from->getFile() !== null) {
54
            $to->setFile($from->getFile());
55
        }
56
        if ($from->getPrefix() !== null) {
0 ignored issues
show
introduced by
The condition $from->getPrefix() !== null is always true.
Loading history...
57
            $to->setPrefix($from->getPrefix());
58
        }
59
        if ($from->getRefid() !== null) {
60
            $to->setRefid($from->getRefid());
61
        }
62
        if ($from->getEnvironment() !== null) {
0 ignored issues
show
introduced by
The condition $from->getEnvironment() !== null is always true.
Loading history...
63
            $to->setEnvironment($from->getEnvironment());
64
        }
65
    }
66
67
    /**
68
     * Runs the various sub-builds.
69
     */
70 2
    public function main()
71
    {
72 2
        $filenames = [];
73 2
        if ($this->buildpath !== null) {
74
            $filenames = $this->buildpath->listPaths();
75 2
        } elseif (count($this->filesets) > 0) {
76 2
            foreach ($this->filesets as $fileset) {
77 2
                foreach ($fileset as $filename) {
78
                    $filenames[] = $filename;
79
                }
80
            }
81
        } else {
82
            throw new BuildException("No buildpath specified");
83
        }
84 2
        $count = count($filenames);
85 2
        if ($count < 1) {
86 2
            $this->log('No sub-builds to iterate on', Project::MSG_WARN);
87 2
            return;
88
        }
89
90
        $buildException = null;
91
        foreach ($filenames as $filename) {
92
            $file = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $file is dead and can be removed.
Loading history...
93
            $subdirPath = null;
94
            $thrownException = null;
95
            try {
96
                $directory = null;
97
                $file = new PhingFile($filename);
98
                if ($file->isDirectory()) {
99
                    if ($this->verbose) {
100
                        $subdirPath = $file->getPath();
101
                        $this->log("Entering directory: " . $subdirPath . "\n", Project::MSG_INFO);
102
                    }
103
                    if ($this->genericphingfile !== null) {
104
                        $directory = $file;
105
                        $file = $this->genericphingfile;
106
                    } else {
107
                        $file = new PhingFile($file, $this->phingfile);
108
                    }
109
                }
110
                $this->execute($file, $directory);
111
                if ($this->verbose && $subdirPath !== null) {
112
                    $this->log('Leaving directory: ' . $subdirPath . "\n", Project::MSG_INFO);
113
                }
114
            } catch (RuntimeException $ex) {
115
                if (!$this->getProject()->isKeepGoingMode()) {
116
                    if ($this->verbose && $subdirPath !== null) {
117
                        $this->log('Leaving directory: ' . $subdirPath . "\n", Project::MSG_INFO);
118
                    }
119
                    throw $ex; // throw further
120
                }
121
                $thrownException = $ex;
122
            } catch (Throwable $ex) {
123
                if (!$this->getProject()->isKeepGoingMode()) {
124
                    if ($this->verbose && $subdirPath !== null) {
125
                        $this->log('Leaving directory: ' . $subdirPath . "\n", Project::MSG_INFO);
126
                    }
127
                    throw new BuildException($ex);
128
                }
129
                $thrownException = $ex;
130
            }
131
            if ($thrownException !== null) {
132
                if ($thrownException instanceof BuildException) {
133
                    $this->log("File '" . $file
134
                        . "' failed with message '"
135
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
136
                    // only the first build exception is reported
137
                    if ($buildException === null) {
138
                        $buildException = $thrownException;
139
                    }
140
                } else {
141
                    $this->log("Target '" . $file
142
                        . "' failed with message '"
143
                        . $thrownException->getMessage() . "'.", Project::MSG_ERR);
144
                    $this->log($thrownException->getTraceAsString(), Project::MSG_ERR);
145
                    if ($buildException === null) {
146
                        $buildException = new BuildException($thrownException);
147
                    }
148
                }
149
                if ($this->verbose && $subdirPath !== null) {
150
                    $this->log('Leaving directory: ' . $subdirPath . "\n", Project::MSG_INFO);
151
                }
152
            }
153
        }
154
        // check if one of the builds failed in keep going mode
155
        if ($buildException !== null) {
156
            throw $buildException;
157
        }
158
    }
159
160
    /**
161
     * Runs the given target on the provided build file.
162
     *
163
     * @param file the build file to execute
164
     * @param directory the directory of the current iteration
165
     * @throws BuildException is the file cannot be found, read, is
166
     *         a directory, or the target called failed, but only if
167
     *         <code>failOnError</code> is <code>true</code>. Otherwise,
168
     *         a warning log message is simply output.
169
     */
170
    private function execute(PhingFile $file, ?PhingFile $directory)
171
    {
172
        if (!$file->exists() || $file->isDirectory() || !$file->canRead()) {
173
            $msg = "Invalid file: " . $file;
174
            if ($this->failOnError) {
175
                throw new BuildException($msg);
176
            }
177
            $this->log($msg, Project::MSG_WARN);
178
            return;
179
        }
180
181
        $this->phing = $this->createPhingTask($directory);
182
        $phingfilename = $file->getAbsolutePath();
183
        $this->phing->setPhingfile($phingfilename);
184
        if ($this->subTarget !== null) {
185
            $this->phing->setTarget($this->subTarget);
186
        }
187
188
        try {
189
            if ($this->verbose) {
190
                $this->log("Executing: " . $phingfilename, Project::MSG_INFO);
191
            }
192
            $this->phing->main();
193
        } catch (BuildException $e) {
194
            if ($this->failOnError || $this->isHardError($e)) {
195
                throw $e;
196
            }
197
            $this->log("Failure for target '" . $this->subTarget
198
                . "' of: " . $phingfilename . "\n"
199
                . $e->getMessage(), Project::MSG_WARN);
200
        } catch (Throwable $e) {
201
            if ($this->failOnError || $this->isHardError($e)) {
202
                throw new BuildException($e);
203
            }
204
            $this->log(
205
                "Failure for target '" . $this->subTarget
206
                . "' of: " . $phingfilename . "\n"
207
                . $e,
208
                Project::MSG_WARN
209
            );
210
        } finally {
211
            $this->phing = null;
212
        }
213
    }
214
215
    /**
216
     * Creates the &lt;ant&gt; task configured to run a specific target.
217
     *
218
     * @param directory : if not null the directory where the build should run
0 ignored issues
show
Documentation Bug introduced by
The doc comment : if at position 0 could not be parsed: Unknown type name ':' at position 0 in : if.
Loading history...
219
     *
220
     * @return PhingTask the ant task, configured with the explicit properties and
221
     *         references necessary to run the sub-build.
222
     */
223
    private function createPhingTask(?PhingFile $directory): PhingTask
224
    {
225
        $phingTask = new PhingTask($this);
226
        $phingTask->setHaltOnFailure(true);
227
        $phingTask->init();
228
        if ($this->subTarget !== null && $this->subTarget !== '') {
229
            $phingTask->setTarget($this->subTarget);
230
        }
231
232
        if ($this->output !== null) {
233
            $phingTask->setOutput($this->output);
234
        }
235
236
        if ($directory !== null) {
237
            $phingTask->setDir($directory);
238
        } else {
239
            $phingTask->setUseNativeBasedir(true);
240
        }
241
242
        $phingTask->setInheritAll($this->inheritAll);
243
244
        foreach ($this->properties as $p) {
245
            self::copyProperty($phingTask->createProperty(), $p);
246
        }
247
248
        $phingTask->setInheritRefs($this->inheritRefs);
249
250
        foreach ($this->references as $reference) {
251
            $phingTask->addReference($reference);
252
        }
253
254
        foreach ($this->filesets as $fileset) {
255
            $phingTask->addFileSet($fileset);
256
        }
257
258
        return $phingTask;
259
    }
260
261
    /** whether we should even try to continue after this error */
262
    private function isHardError(Throwable $t)
263
    {
264
        if ($t instanceof BuildException) {
265
            return $this->isHardError($t->getPrevious());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->isHardError($t->getPrevious()) targeting SubPhing::isHardError() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
266
        }
267
    }
268
269
    /**
270
     * This method builds the file name to use in conjunction with directories.
271
     *
272
     * <p>Defaults to "build.xml".
273
     * If <code>genericantfile</code> is set, this attribute is ignored.</p>
274
     *
275
     * @param string $phingfile the short build file name. Defaults to "build.xml".
276
     */
277 1
    public function setPhingfile(string $phingfile)
278
    {
279 1
        $this->phingfile = $phingfile;
280 1
    }
281
282
    /**
283
     * This method builds a file path to use in conjunction with directories.
284
     *
285
     * <p>Use <code>genericantfile</code>, in order to run the same build file
286
     * with different basedirs.</p>
287
     * If this attribute is set, <code>antfile</code> is ignored.
288
     *
289
     * @param PhingFile $afile (path of the generic ant file, absolute or relative to
290
     *               project base directory)
291
     *
292
     */
293 2
    public function setGenericPhingfile(PhingFile $afile)
294
    {
295 2
        $this->genericphingfile = $afile;
296 2
    }
297
298
    /**
299
     * The target to call on the different sub-builds. Set to "" to execute
300
     * the default target.
301
     *
302
     * @param target the target
303
     */
304
    // REVISIT: Defaults to the target name that contains this task if not specified.
305
306
    /**
307
     * Sets whether to fail with a build exception on error, or go on.
308
     *
309
     * @param bool $failOnError the new value for this boolean flag.
310
     */
311 1
    public function setFailonerror(bool $failOnError)
312
    {
313 1
        $this->failOnError = $failOnError;
314 1
    }
315
316
    public function setTarget(string $target)
317
    {
318
        $this->subTarget = $target;
319
    }
320
321
    /**
322
     * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
323
     * The default value is "false".
324
     * @param bool $on true to enable verbose mode, false otherwise (default).
325
     */
326 1
    public function setVerbose(bool $on)
327
    {
328 1
        $this->verbose = $on;
329 1
    }
330
331
    /**
332
     * Corresponds to <code>&lt;ant&gt;</code>'s
333
     * <code>output</code> attribute.
334
     *
335
     * @param string $s the filename to write the output to.
336
     */
337
    public function setOutput(string $s)
338
    {
339
        $this->output = $s;
340
    }
341
342
    /**
343
     * Corresponds to <code>&lt;ant&gt;</code>'s
344
     * <code>inheritall</code> attribute.
345
     *
346
     * @param b the new value for this boolean flag.
347
     */
348 1
    public function setInheritall(bool $b)
349
    {
350 1
        $this->inheritAll = $b;
351 1
    }
352
353
    /**
354
     * Corresponds to <code>&lt;ant&gt;</code>'s
355
     * <code>inheritrefs</code> attribute.
356
     *
357
     * @param b the new value for this boolean flag.
358
     */
359 1
    public function setInheritrefs(bool $b)
360
    {
361 1
        $this->inheritRefs = $b;
362 1
    }
363
364
    /**
365
     * Property to pass to the new project.
366
     * The property is passed as a 'user property'
367
     */
368
    public function createProperty()
369
    {
370
        $p = new PropertyTask();
371
        $p->setUserProperty(true);
372
        $p->setTaskName('property');
373
        $this->properties[] = $p;
374
375
        return $p;
376
    }
377
378
    /**
379
     * Corresponds to <code>&lt;ant&gt;</code>'s
380
     * nested <code>&lt;reference&gt;</code> element.
381
     *
382
     * @param PhingReference $r the reference to pass on explicitly to the sub-build.
383
     */
384
    public function addReference(PhingReference $r)
385
    {
386
        $this->references[] = $r;
387
    }
388
389
    /**
390
     * Set the buildpath to be used to find sub-projects.
391
     *
392
     * @param Path $s an Ant Path object containing the buildpath.
393
     */
394
    public function setBuildpath(Path $s)
395
    {
396
        $this->getBuildpath()->append($s);
397
    }
398
399
    /**
400
     * Creates a nested build path, and add it to the implicit build path.
401
     *
402
     * @return Path the newly created nested build path.
403
     */
404
    public function createBuildpath(): Path
405
    {
406
        return $this->getBuildpath()->createPath();
407
    }
408
409
    /**
410
     * Gets the implicit build path, creating it if <code>null</code>.
411
     *
412
     * @return Path the implicit build path.
413
     */
414
    private function getBuildpath(): Path
415
    {
416
        if ($this->buildpath === null) {
417
            $this->buildpath = new Path($this->getProject());
418
        }
419
        return $this->buildpath;
420
    }
421
422
    /**
423
     * Creates a nested <code>&lt;buildpathelement&gt;</code>,
424
     * and add it to the implicit build path.
425
     *
426
     * @return PathElement the newly created nested build path element.
427
     */
428
    public function createBuildpathElement()
429
    {
430
        return $this->getBuildpath()->createPathElement();
431
    }
432
433
    /**
434
     * Buildpath to use, by reference.
435
     *
436
     * @param Reference $r a reference to an Ant Path object containing the buildpath.
437
     */
438
    public function setBuildpathRef(Reference $r)
439
    {
440
        $this->createBuildpath()->setRefid($r);
441
    }
442
}
443