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: CopyTask.php 235 2007-09-05 18:42:02Z 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/Task.php';
23
include_once 'phing/system/io/PhingFile.php';
24
include_once 'phing/util/FileUtils.php';
25
include_once 'phing/util/SourceFileScanner.php';
26
include_once 'phing/mappers/IdentityMapper.php';
27
include_once 'phing/mappers/FlattenMapper.php';
28
 
29
/**
30
 * A phing copy task.  Copies a file or directory to a new file
31
 * or directory.  Files are only copied if the source file is newer
32
 * than the destination file, or when the destination file does not
33
 * exist. It is possible to explictly overwrite existing files.
34
 *
35
 * @author   Andreas Aderhold, andi@binarycloud.com
36
 * @version  $Revision: 1.16 $ $Date: 2007-09-05 20:42:02 +0200 (Wed, 05 Sep 2007) $
37
 * @package  phing.tasks.system
38
 */
39
class CopyTask extends Task {
40
 
41
    protected $file          = null;   // the source file (from xml attribute)
42
    protected $destFile      = null;   // the destiantion file (from xml attribute)
43
    protected $destDir       = null;   // the destination dir (from xml attribute)
44
    protected $overwrite     = false;  // overwrite destination (from xml attribute)
45
    protected $preserveLMT   = true;   // sync timestamps (from xml attribute)
46
    protected $includeEmpty  = true;   // include empty dirs? (from XML)
47
    protected $flatten       = false;  // apply the FlattenMapper right way (from XML)
48
    protected $mapperElement = null;
49
 
50
    protected $fileCopyMap   = array(); // asoc array containing mapped file names
51
    protected $dirCopyMap    = array(); // asoc array containing mapped file names
52
    protected $completeDirMap= array(); // asoc array containing complete dir names
53
    protected $fileUtils     = null;    // a instance of fileutils
54
    protected $filesets      = array(); // all fileset objects assigned to this task
55
    protected $filterChains  = array(); // all filterchains objects assigned to this task
56
 
57
    protected $verbosity     = Project::MSG_VERBOSE;
58
 
59
    /**
60
     * Sets up this object internal stuff. i.e. the Fileutils instance
61
     *
62
     * @return object   The CopyTask instnace
63
     * @access public
64
     */
65
    function __construct() {
66
        $this->fileUtils = new FileUtils();
67
    }
68
 
69
    /**
70
     * Set the overwrite flag. IntrospectionHelper takes care of
71
     * booleans in set* methods so we can assume that the right
72
     * value (boolean primitive) is coming in here.
73
     *
74
     * @param  boolean  Overwrite the destination file(s) if it/they already exist
75
     * @return void
76
     * @access public
77
     */
78
    function setOverwrite($bool) {
79
        $this->overwrite = (boolean) $bool;
80
    }
81
 
82
    /**
83
     * Used to force listing of all names of copied files.
84
     * @param boolean $verbosity
85
     */
86
    function setVerbose($verbosity) {
87
        if ($verbosity) {
88
            $this->verbosity = Project::MSG_INFO;
89
        } else {
90
            $this->verbosity = Project::MSG_VERBOSE;
91
        }
92
    }
93
 
94
    /**
95
     * Set the preserve timestmap flag. IntrospectionHelper takes care of
96
     * booleans in set* methods so we can assume that the right
97
     * value (boolean primitive) is coming in here.
98
     *
99
     * @param  boolean  Preserve the timestamp on the destination file
100
     * @return void
101
     * @access public
102
     */
103
    function setTstamp($bool) {
104
        $this->preserveLMT = (boolean) $bool;
105
    }
106
 
107
 
108
    /**
109
     * Set the include empty dirs flag. IntrospectionHelper takes care of
110
     * booleans in set* methods so we can assume that the right
111
     * value (boolean primitive) is coming in here.
112
     *
113
     * @param  boolean  Flag if empty dirs should be cpoied too
114
     * @return void
115
     * @access public
116
     */
117
    function setIncludeEmptyDirs($bool) {
118
        $this->includeEmpty = (boolean) $bool;
119
    }
120
 
121
 
122
    /**
123
     * Set the file. We have to manually take care of the
124
     * type that is coming due to limited type support in php
125
     * in and convert it manually if neccessary.
126
     *
127
     * @param  string/object  The source file. Either a string or an PhingFile object
128
     * @return void
129
     * @access public
130
     */
131
    function setFile(PhingFile $file) {
132
        $this->file = $file;
133
    }
134
 
135
 
136
    /**
137
     * Set the toFile. We have to manually take care of the
138
     * type that is coming due to limited type support in php
139
     * in and convert it manually if neccessary.
140
     *
141
     * @param  string/object  The dest file. Either a string or an PhingFile object
142
     * @return void
143
     * @access public
144
     */
145
    function setTofile(PhingFile $file) {
146
        $this->destFile = $file;
147
    }
148
 
149
 
150
    /**
151
     * Set the toDir. We have to manually take care of the
152
     * type that is coming due to limited type support in php
153
     * in and convert it manually if neccessary.
154
     *
155
     * @param  string/object  The directory, either a string or an PhingFile object
156
     * @return void
157
     * @access public
158
     */
159
    function setTodir(PhingFile $dir) {
160
        $this->destDir = $dir;
161
    }
162
 
163
    /**
164
     * Nested creator, creates a FileSet for this task
165
     *
166
     * @access  public
167
     * @return  object  The created fileset object
168
     */
169
    function createFileSet() {
170
        $num = array_push($this->filesets, new FileSet());
171
        return $this->filesets[$num-1];
172
    }
173
 
174
    /**
175
     * Creates a filterchain
176
     *
177
     * @access public
178
     * @return  object  The created filterchain object
179
     */
180
    function createFilterChain() {
181
        $num = array_push($this->filterChains, new FilterChain($this->project));
182
        return $this->filterChains[$num-1];
183
    }
184
 
185
    /**
186
     * Nested creator, creates one Mapper for this task
187
     *
188
     * @access  public
189
     * @return  object  The created Mapper type object
190
     * @throws  BuildException
191
     */
192
    function createMapper() {
193
        if ($this->mapperElement !== null) {
194
            throw new BuildException("Cannot define more than one mapper", $this->location);
195
        }
196
        $this->mapperElement = new Mapper($this->project);
197
        return $this->mapperElement;
198
    }
199
 
200
    /**
201
     * The main entry point where everything gets in motion.
202
     *
203
     * @access  public
204
     * @return  true on success
205
     * @throws  BuildException
206
     */
207
    function main() {
208
 
209
        $this->validateAttributes();
210
 
211
        if ($this->file !== null) {
212
            if ($this->file->exists()) {
213
                if ($this->destFile === null) {
214
                    $this->destFile = new PhingFile($this->destDir, (string) $this->file->getName());
215
                }
216
                if ($this->overwrite === true || ($this->file->lastModified() > $this->destFile->lastModified())) {
217
                    $this->fileCopyMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();
218
                } else {
219
                    $this->log($this->file->getName()." omitted, is up to date");
220
                }
221
            } else {
222
                // terminate build
223
                throw new BuildException("Could not find file " . $this->file->__toString() . " to copy.");
224
            }
225
        }
226
 
227
        $project = $this->getProject();
228
 
229
        // process filesets
230
        foreach($this->filesets as $fs) {
231
            $ds = $fs->getDirectoryScanner($project);
232
            $fromDir  = $fs->getDir($project);
233
            $srcFiles = $ds->getIncludedFiles();
234
            $srcDirs  = $ds->getIncludedDirectories();
235
 
236
            if (!$this->flatten && $this->mapperElement === null)
237
            {
238
				$this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath();
239
			}
240
 
241
            $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
242
        }
243
 
244
        // go and copy the stuff
245
        $this->doWork();
246
 
247
        if ($this->destFile !== null) {
248
            $this->destDir = null;
249
        }
250
    }
251
 
252
    /**
253
     * Validates attributes coming in from XML
254
     *
255
     * @access  private
256
     * @return  void
257
     * @throws  BuildException
258
     */
259
    protected function validateAttributes() {
260
 
261
        if ($this->file === null && count($this->filesets) === 0) {
262
            throw new BuildException("CopyTask. Specify at least one source - a file or a fileset.");
263
        }
264
 
265
        if ($this->destFile !== null && $this->destDir !== null) {
266
            throw new BuildException("Only one of destfile and destdir may be set.");
267
        }
268
 
269
        if ($this->destFile === null && $this->destDir === null) {
270
            throw new BuildException("One of destfile or destdir must be set.");
271
        }
272
 
273
        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
274
            throw new BuildException("Use a fileset to copy directories.");
275
        }
276
 
277
        if ($this->destFile !== null && count($this->filesets) > 0) {
278
            throw new BuildException("Cannot concatenate multple files into a single file.");
279
        }
280
 
281
        if ($this->destFile !== null) {
282
            $this->destDir = new PhingFile($this->destFile->getParent());
283
        }
284
    }
285
 
286
    /**
287
     * Compares source files to destination files to see if they
288
     * should be copied.
289
     *
290
     * @access  private
291
     * @return  void
292
     */
293
    private function _scan(&$fromDir, &$toDir, &$files, &$dirs) {
294
        /* mappers should be generic, so we get the mappers here and
295
        pass them on to builMap. This method is not redundan like it seems */
296
        $mapper = null;
297
        if ($this->mapperElement !== null) {
298
            $mapper = $this->mapperElement->getImplementation();
299
        } else if ($this->flatten) {
300
            $mapper = new FlattenMapper();
301
        } else {
302
            $mapper = new IdentityMapper();
303
        }
304
        $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap);
305
        $this->buildMap($fromDir, $toDir, $dirs, $mapper, $this->dirCopyMap);
306
    }
307
 
308
    /**
309
     * Builds a map of filenames (from->to) that should be copied
310
     *
311
     * @access  private
312
     * @return  void
313
     */
314
    private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map) {
315
        $toCopy = null;
316
        if ($this->overwrite) {
317
            $v = array();
318
            foreach($names as $name) {
319
                $result = $mapper->main($name);
320
                if ($result !== null) {
321
                    $v[] = $name;
322
                }
323
            }
324
            $toCopy = $v;
325
        } else {
326
            $ds = new SourceFileScanner($this);
327
            $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper);
328
        }
329
 
330
        for ($i=0,$_i=count($toCopy); $i < $_i; $i++) {
331
            $src  = new PhingFile($fromDir, $toCopy[$i]);
332
            $mapped = $mapper->main($toCopy[$i]);
333
            $dest = new PhingFile($toDir, $mapped[0]);
334
            $map[$src->getAbsolutePath()] = $dest->getAbsolutePath();
335
        }
336
    }
337
 
338
 
339
    /**
340
     * Actually copies the files
341
     *
342
     * @access  private
343
     * @return  void
344
     * @throws  BuildException
345
     */
346
    protected function doWork() {
347
 
348
		// These "slots" allow filters to retrieve information about the currently-being-process files
349
		$fromSlot = $this->getRegisterSlot("currentFromFile");
350
		$fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename");
351
 
352
		$toSlot = $this->getRegisterSlot("currentToFile");
353
		$toBasenameSlot = $this->getRegisterSlot("currentToFile.basename");
354
 
355
        $mapSize = count($this->fileCopyMap);
356
        $total = $mapSize;
357
        if ($mapSize > 0) {
358
            $this->log("Copying ".$mapSize." file".(($mapSize) === 1 ? '' : 's')." to ". $this->destDir->getAbsolutePath());
359
            // walks the map and actually copies the files
360
            $count=0;
361
            foreach($this->fileCopyMap as $from => $to) {
362
                if ($from === $to) {
363
                    $this->log("Skipping self-copy of " . $from, $this->verbosity);
364
                    $total--;
365
                    continue;
366
                }
367
                $this->log("From ".$from." to ".$to, $this->verbosity);
368
                try { // try to copy file
369
 
370
					$fromFile = new PhingFile($from);
371
					$toFile = new PhingFile($to);
372
 
373
                    $fromSlot->setValue($fromFile->getPath());
374
					$fromBasenameSlot->setValue($fromFile->getName());
375
 
376
					$toSlot->setValue($toFile->getPath());
377
					$toBasenameSlot->setValue($toFile->getName());
378
 
379
                    $this->fileUtils->copyFile($fromFile, $toFile, $this->overwrite, $this->preserveLMT, $this->filterChains, $this->getProject());
380
 
381
                    $count++;
382
                } catch (IOException $ioe) {
383
                    $this->log("Failed to copy " . $from . " to " . $to . ": " . $ioe->getMessage(), Project::MSG_ERR);
384
                }
385
            }
386
        }
387
 
388
        // handle empty dirs if appropriate
389
        if ($this->includeEmpty) {
390
            $destdirs = array_values($this->dirCopyMap);
391
            $count = 0;
392
            foreach ($destdirs as $destdir) {
393
                $d = new PhingFile((string) $destdir);
394
                if (!$d->exists()) {
395
                    if (!$d->mkdirs()) {
396
                        $this->log("Unable to create directory " . $d->__toString(), Project::MSG_ERR);
397
                    } else {
398
                        $count++;
399
                    }
400
                }
401
            }
402
            if ($count > 0) {
403
                $this->log("Copied ".$count." empty director" . ($count == 1 ? "y" : "ies") . " to " . $this->destDir->getAbsolutePath());
404
            }
405
        }
406
    }
407
 
408
}