// // Copyright (c) 2008, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 30 Sep 08 Brian Frank Creation // ** ** CreateZip is used to create a zip file from a directory on the file system. ** class CreateZip : Task { new make(BuildScript script) : super(script) { this.filter = |File f->Bool| { return true } } override Void run() { // basic sanity checking if (inDirs == null || inDirs.isEmpty) throw fatal("Not configured: CreateZip.inDirs") if (outFile == null) throw fatal("Not configured: CreateZip.outFile") // ensure outFile is not under inDir (although we do allow // outFile to be placed directly under inDir as convenience) inDirs.each |File inDir| { if (!inDir.isDir) throw fatal("Not a directory: $inDir") inPath := inDir.normalize.pathStr outPath := outFile.normalize.parent.pathStr if (outPath.startsWith(inPath) && inPath != outPath) throw fatal("Cannot set outFile under inDir: $outPath under $inPath") } // ensure prefixPath is formatted correctly if (pathPrefix != null) { if (pathPrefix.isPathAbs) throw fatal("Prefix path must not be absolute: $pathPrefix") if (!pathPrefix.isDir) throw fatal("Prefix path must be dir: $pathPrefix") } // zip it! log.info("CreateZip [$outFile]") out := Zip.write(outFile.out) try { inDirs.each |File inDir| { inDir.list.each |File f| { if (f.name == outFile.name) return uri := f.name.toUri if (pathPrefix != null) uri = pathPrefix + uri zip(out, f, uri.toStr) } } } catch (Err err) { throw fatal("Cannot create zip [$outFile]", err) } finally { out.close } } private Void zip(Zip out, File f, Str path) { if (!filter.call(f, path)) return if (f.isDir) { f.list.each |File sub| { zip(out, sub, path + "/" + sub.name) } } else { o := out.writeNext(path.toUri, f.modified) f.in.pipe(o) o.close } } ** Required output zip file to create File? outFile ** Required directories to zip up. The contents of these dirs are ** recursively zipped up with zip paths relative to this root ** directory. File[]? inDirs ** This function is called on each file under 'inDir'; if true ** returned it is included in the zip, if false then it is excluded. ** Returning false for a directory will skip recursing the entire ** directory. |File f, Str path->Bool| filter ** ** Specifies the top level directory inside the zip file ** prefixed to all the files. For example use 'acme/' to ** put everything inside the zip file inside a "acme" directory. ** The URI used must end with a slash. If null, then no path ** prefix is used. ** Uri? pathPrefix := null }