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: ZipTask.php 240 2007-09-25 20:43:37Z 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
 
22
require_once 'phing/tasks/system/MatchingTask.php';
23
include_once 'phing/util/SourceFileScanner.php';
24
include_once 'phing/mappers/MergeMapper.php';
25
include_once 'phing/util/StringHelper.php';
26
include_once 'phing/lib/Zip.php';
27
 
28
/**
29
 * Creates a zip archive using PEAR Archive_Zip (which is presently unreleased
30
 * and included with Phing).
31
 *
32
 * @author    Michiel Rook <michiel.rook@gmail.com>
33
 * @version   $Revision: 1.2 $
34
 * @package   phing.tasks.ext
35
 * @since     2.1.0
36
 */
37
class ZipTask extends MatchingTask {
38
 
39
	/**
40
	 * @var PhingFile
41
	 */
42
    private $zipFile;
43
 
44
    /**
45
     * @var PhingFile
46
     */
47
    private $baseDir;
48
 
49
    /**
50
     * Whether to include empty dirs in the archive.
51
     */
52
    private $includeEmpty = true;
53
 
54
    private $filesets = array();
55
    private $fileSetFiles = array();
56
 
57
    /**
58
     * Add a new fileset.
59
     * @return FileSet
60
     */
61
    public function createFileSet() {
62
        $this->fileset = new ZipFileSet();
63
        $this->filesets[] = $this->fileset;
64
        return $this->fileset;
65
    }
66
 
67
    /**
68
     * Set is the name/location of where to create the zip file.
69
     * @param PhingFile $destFile The output of the zip
70
     */
71
    public function setDestFile(PhingFile $destFile) {
72
        $this->zipFile = $destFile;
73
    }
74
 
75
    /**
76
     * This is the base directory to look in for things to zip.
77
     * @param PhingFile $baseDir
78
     */
79
    public function setBasedir(PhingFile $baseDir) {
80
        $this->baseDir = $baseDir;
81
    }
82
 
83
    /**
84
     * Set the include empty dirs flag.
85
     * @param  boolean  Flag if empty dirs should be tarred too
86
     * @return void
87
     * @access public
88
     */
89
    function setIncludeEmptyDirs($bool) {
90
        $this->includeEmpty = (boolean) $bool;
91
    }
92
 
93
    /**
94
     * do the work
95
     * @throws BuildException
96
     */
97
    public function main() {
98
 
99
        if ($this->zipFile === null) {
100
            throw new BuildException("zipfile attribute must be set!", $this->getLocation());
101
        }
102
 
103
        if ($this->zipFile->exists() && $this->zipFile->isDirectory()) {
104
            throw new BuildException("zipfile is a directory!", $this->getLocation());
105
        }
106
 
107
        if ($this->zipFile->exists() && !$this->zipFile->canWrite()) {
108
            throw new BuildException("Can not write to the specified zipfile!", $this->getLocation());
109
        }
110
 
111
        // shouldn't need to clone, since the entries in filesets
112
        // themselves won't be modified -- only elements will be added
113
        $savedFileSets = $this->filesets;
114
 
115
        try {
116
            if ($this->baseDir !== null) {
117
                if (!$this->baseDir->exists()) {
118
                    throw new BuildException("basedir does not exist!", $this->getLocation());
119
                }
120
 
121
                if (empty($this->filesets))
122
                {
123
	                // add the main fileset to the list of filesets to process.
124
	                $mainFileSet = new ZipFileSet($this->fileset);
125
	                $mainFileSet->setDir($this->baseDir);
126
	                $this->filesets[] = $mainFileSet;
127
                }
128
            }
129
 
130
            if (empty($this->filesets)) {
131
                throw new BuildException("You must supply either a basedir "
132
                                         . "attribute or some nested filesets.",
133
                                         $this->getLocation());
134
            }
135
 
136
            // check if zip is out of date with respect to each
137
            // fileset
138
            $upToDate = true;
139
            foreach($this->filesets as $fs) {
140
            	$files = $fs->getFiles($this->project, $this->includeEmpty);
141
                if (!$this->archiveIsUpToDate($files, $fs->getDir($this->project))) {
142
                    $upToDate = false;
143
                }
144
                for ($i=0, $fcount=count($files); $i < $fcount; $i++) {
145
                    if ($this->zipFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) {
146
                        throw new BuildException("A zip file cannot include itself", $this->getLocation());
147
                    }
148
                }
149
            }
150
 
151
            if ($upToDate) {
152
                $this->log("Nothing to do: " . $this->zipFile->__toString() . " is up to date.", Project::MSG_INFO);
153
                return;
154
            }
155
 
156
            $this->log("Building zip: " . $this->zipFile->__toString(), Project::MSG_INFO);
157
 
158
            $zip = new Archive_Zip($this->zipFile->getAbsolutePath());
159
 
160
            foreach($this->filesets as $fs) {
161
 
162
            	$files = $fs->getFiles($this->project, $this->includeEmpty);
163
 
164
                $fsBasedir = (null != $this->baseDir) ? $this->baseDir :
165
									$fs->getDir($this->project);
166
 
167
                $filesToZip = array();
168
                for ($i=0, $fcount=count($files); $i < $fcount; $i++) {
169
                    $f = new PhingFile($fsBasedir, $files[$i]);
170
                    $filesToZip[] = $f->getAbsolutePath();
171
                    $this->log("Adding " . $f->getPath() . " to archive.", Project::MSG_VERBOSE);
172
                }
173
                $zip->add($filesToZip, array('remove_path' => $fsBasedir->getCanonicalPath()));
174
            }
175
 
176
 
177
        } catch (IOException $ioe) {
178
                $msg = "Problem creating ZIP: " . $ioe->getMessage();
179
                $this->filesets = $savedFileSets;
180
                throw new BuildException($msg, $ioe, $this->getLocation());
181
        }
182
 
183
        $this->filesets = $savedFileSets;
184
    }
185
 
186
    /**
187
     * @param array $files array of filenames
188
     * @param PhingFile $dir
189
     * @return boolean
190
     */
191
    protected function archiveIsUpToDate($files, $dir) {
192
        $sfs = new SourceFileScanner($this);
193
        $mm = new MergeMapper();
194
        $mm->setTo($this->zipFile->getAbsolutePath());
195
        return count($sfs->restrict($files, $dir, null, $mm)) == 0;
196
    }
197
 
198
}
199
 
200
 
201
 
202
 
203
/**
204
 * This is a FileSet with the to specify permissions.
205
 *
206
 * Permissions are currently not implemented by PEAR Archive_Tar,
207
 * but hopefully they will be in the future.
208
 *
209
 */
210
class ZipFileSet extends FileSet {
211
 
212
    private $files = null;
213
 
214
    /**
215
     *  Get a list of files and directories specified in the fileset.
216
     *  @return array a list of file and directory names, relative to
217
     *    the baseDir for the project.
218
     */
219
    public function getFiles(Project $p, $includeEmpty = true) {
220
 
221
        if ($this->files === null) {
222
 
223
            $ds = $this->getDirectoryScanner($p);
224
            $this->files = $ds->getIncludedFiles();
225
 
226
            if ($includeEmpty) {
227
 
228
	            // first any empty directories that will not be implicitly added by any of the files
229
				$implicitDirs = array();
230
				foreach($this->files as $file) {
231
					$implicitDirs[] = dirname($file);
232
				}
233
 
234
				$incDirs = $ds->getIncludedDirectories();
235
 
236
				// we'll need to add to that list of implicit dirs any directories
237
				// that contain other *directories* (and not files), since otherwise
238
				// we get duplicate directories in the resulting tar
239
				foreach($incDirs as $dir) {
240
					foreach($incDirs as $dircheck) {
241
						if (!empty($dir) && $dir == dirname($dircheck)) {
242
							$implicitDirs[] = $dir;
243
						}
244
					}
245
				}
246
 
247
				$implicitDirs = array_unique($implicitDirs);
248
 
249
				// Now add any empty dirs (dirs not covered by the implicit dirs)
250
				// to the files array.
251
 
252
				foreach($incDirs as $dir) { // we cannot simply use array_diff() since we want to disregard empty/. dirs
253
					if ($dir != "" && $dir != "." && !in_array($dir, $implicitDirs)) {
254
						// it's an empty dir, so we'll add it.
255
						$this->files[] = $dir;
256
					}
257
				}
258
			} // if $includeEmpty
259
 
260
        } // if ($this->files===null)
261
 
262
        return $this->files;
263
    }
264
 
265
}