| // 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:io'; |
| |
| import 'package:archive/archive.dart'; |
| import 'package:path/path.dart' as path; |
| |
| import 'asset.dart'; |
| import 'base/process.dart'; |
| |
| abstract class ZipBuilder { |
| factory ZipBuilder() { |
| if (exitsHappy(<String>['which', 'zip'])) { |
| return new _ZipToolBuilder(); |
| } else { |
| return new _ArchiveZipBuilder(); |
| } |
| } |
| |
| ZipBuilder._(); |
| |
| List<AssetBundleEntry> entries = <AssetBundleEntry>[]; |
| |
| void addEntry(AssetBundleEntry entry) => entries.add(entry); |
| |
| void createZip(File outFile, Directory zipBuildDir); |
| } |
| |
| class _ArchiveZipBuilder extends ZipBuilder { |
| _ArchiveZipBuilder() : super._(); |
| |
| @override |
| void createZip(File outFile, Directory zipBuildDir) { |
| Archive archive = new Archive(); |
| |
| for (AssetBundleEntry entry in entries) { |
| List<int> data = entry.contentsAsBytes(); |
| archive.addFile(new ArchiveFile.noCompress(entry.archivePath, data.length, data)); |
| } |
| |
| List<int> zipData = new ZipEncoder().encode(archive); |
| outFile.writeAsBytesSync(zipData); |
| } |
| } |
| |
| class _ZipToolBuilder extends ZipBuilder { |
| _ZipToolBuilder() : super._(); |
| |
| @override |
| void createZip(File outFile, Directory zipBuildDir) { |
| // If there are no assets, then create an empty zip file. |
| if (entries.isEmpty) { |
| List<int> zipData = new ZipEncoder().encode(new Archive()); |
| outFile.writeAsBytesSync(zipData); |
| return; |
| } |
| |
| if (outFile.existsSync()) |
| outFile.deleteSync(); |
| |
| if (zipBuildDir.existsSync()) |
| zipBuildDir.deleteSync(recursive: true); |
| zipBuildDir.createSync(recursive: true); |
| |
| for (AssetBundleEntry entry in entries) { |
| List<int> data = entry.contentsAsBytes(); |
| File file = new File(path.join(zipBuildDir.path, entry.archivePath)); |
| file.parent.createSync(recursive: true); |
| file.writeAsBytesSync(data); |
| } |
| |
| if (_getCompressedNames().isNotEmpty) { |
| runCheckedSync( |
| <String>['zip', '-q', outFile.absolute.path]..addAll(_getCompressedNames()), |
| workingDirectory: zipBuildDir.path |
| ); |
| } |
| |
| if (_getStoredNames().isNotEmpty) { |
| runCheckedSync( |
| <String>['zip', '-q', '-0', outFile.absolute.path]..addAll(_getStoredNames()), |
| workingDirectory: zipBuildDir.path |
| ); |
| } |
| } |
| |
| static const List<String> _kNoCompressFileExtensions = const <String>['.png', '.jpg']; |
| |
| bool isAssetCompressed(AssetBundleEntry entry) { |
| return !_kNoCompressFileExtensions.any( |
| (String extension) => entry.archivePath.endsWith(extension) |
| ); |
| } |
| |
| Iterable<String> _getCompressedNames() { |
| return entries |
| .where(isAssetCompressed) |
| .map((AssetBundleEntry entry) => entry.archivePath); |
| } |
| |
| Iterable<String> _getStoredNames() { |
| return entries |
| .where((AssetBundleEntry entry) => !isAssetCompressed(entry)) |
| .map((AssetBundleEntry entry) => entry.archivePath); |
| } |
| } |