// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'package:archive/archive.dart'; import 'base/file_system.dart'; import 'base/process.dart'; import 'devfs.dart'; abstract class ZipBuilder { factory ZipBuilder() { if (exitsHappy(['which', 'zip'])) { return new _ZipToolBuilder(); } else { return new _ArchiveZipBuilder(); } } ZipBuilder._(); Map entries = {}; Future createZip(File outFile, Directory zipBuildDir); } class _ArchiveZipBuilder extends ZipBuilder { _ArchiveZipBuilder() : super._(); @override Future createZip(File outFile, Directory zipBuildDir) async { final Archive archive = new Archive(); final Completer finished = new Completer(); int count = entries.length; entries.forEach((String archivePath, DevFSContent content) { content.contentsAsBytes().then((List data) { archive.addFile(new ArchiveFile.noCompress(archivePath, data.length, data)); count -= 1; if (count == 0) finished.complete(); }); }); await finished.future; final List zipData = new ZipEncoder().encode(archive); await outFile.writeAsBytes(zipData); } } class _ZipToolBuilder extends ZipBuilder { _ZipToolBuilder() : super._(); @override Future createZip(File outFile, Directory zipBuildDir) async { // If there are no assets, then create an empty zip file. if (entries.isEmpty) { final List zipData = new ZipEncoder().encode(new Archive()); await outFile.writeAsBytes(zipData); return; } final File tmpFile = fs.file('${outFile.path}.tmp'); if (tmpFile.existsSync()) tmpFile.deleteSync(); if (zipBuildDir.existsSync()) zipBuildDir.deleteSync(recursive: true); zipBuildDir.createSync(recursive: true); final Completer finished = new Completer(); int count = entries.length; entries.forEach((String archivePath, DevFSContent content) { content.contentsAsBytes().then((List data) { final File file = fs.file(fs.path.join(zipBuildDir.path, archivePath)); file.parent.createSync(recursive: true); file.writeAsBytes(data).then((File value) { count -= 1; if (count == 0) finished.complete(); }); }); }); await finished.future; final Iterable compressedNames = _getCompressedNames(); if (compressedNames.isNotEmpty) { await runCheckedAsync( ['zip', '-q', tmpFile.absolute.path]..addAll(compressedNames), workingDirectory: zipBuildDir.path ); } final Iterable storedNames = _getStoredNames(); if (storedNames.isNotEmpty) { await runCheckedAsync( ['zip', '-q', '-0', tmpFile.absolute.path]..addAll(storedNames), workingDirectory: zipBuildDir.path ); } tmpFile.renameSync(outFile.absolute.path); } static const List _kNoCompressFileExtensions = const ['.png', '.jpg']; bool isAssetCompressed(String archivePath) { return !_kNoCompressFileExtensions.any( (String extension) => archivePath.endsWith(extension) ); } Iterable _getCompressedNames() => entries.keys.where(isAssetCompressed); Iterable _getStoredNames() => entries.keys .where((String archivePath) => !isAssetCompressed(archivePath)); }