improve startup time
This commit is contained in:
parent
3c7ede15b0
commit
4e10bf596c
@ -3,8 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:archive/archive.dart';
|
|
||||||
|
|
||||||
import 'src/flx.dart' as flx;
|
import 'src/flx.dart' as flx;
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ import 'src/flx.dart' as flx;
|
|||||||
/// pre-compiled snapshot.
|
/// pre-compiled snapshot.
|
||||||
Future<int> assembleFlx({
|
Future<int> assembleFlx({
|
||||||
Map manifestDescriptor: const {},
|
Map manifestDescriptor: const {},
|
||||||
ArchiveFile snapshotFile: null,
|
File snapshotFile: null,
|
||||||
String assetBasePath: flx.defaultAssetBasePath,
|
String assetBasePath: flx.defaultAssetBasePath,
|
||||||
String materialAssetBasePath: flx.defaultMaterialAssetBasePath,
|
String materialAssetBasePath: flx.defaultMaterialAssetBasePath,
|
||||||
String outputPath: flx.defaultFlxOutputPath,
|
String outputPath: flx.defaultFlxOutputPath,
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
import '../android/android_sdk.dart';
|
import '../android/android_sdk.dart';
|
||||||
@ -136,26 +135,16 @@ class AndroidDevice extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getSourceSha1(ApplicationPackage app) {
|
String _getSourceSha1(ApplicationPackage app) {
|
||||||
var sha1 = new SHA1();
|
File shaFile = new File('${app.localPath}.sha1');
|
||||||
var file = new File(app.localPath);
|
return shaFile.existsSync() ? shaFile.readAsStringSync() : '';
|
||||||
sha1.add(file.readAsBytesSync());
|
|
||||||
return CryptoUtils.bytesToHex(sha1.close());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get name => modelID;
|
String get name => modelID;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isAppInstalled(ApplicationPackage app) {
|
bool isAppInstalled(ApplicationPackage app) {
|
||||||
if (runCheckedSync(adbCommandForDevice(<String>['shell', 'pm', 'path', app.id])) == '') {
|
// Just check for the existence of the application SHA.
|
||||||
printTrace('TODO(iansf): move this log to the caller. ${app.name} is not on the device. Installing now...');
|
return _getDeviceApkSha1(app) == _getSourceSha1(app);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_getDeviceApkSha1(app) != _getSourceSha1(app)) {
|
|
||||||
printTrace(
|
|
||||||
'TODO(iansf): move this log to the caller. ${app.name} is out of date. Installing now...');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -179,7 +168,7 @@ class AndroidDevice extends Device {
|
|||||||
|
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
// Auto-bind to a port. Set up forwarding for that port. Emit a stdout
|
// Auto-bind to a port. Set up forwarding for that port. Emit a stdout
|
||||||
// message similar to the command-line VM, so that tools can parse the output.
|
// message similar to the command-line VM so that tools can parse the output.
|
||||||
// "Observatory listening on http://127.0.0.1:52111"
|
// "Observatory listening on http://127.0.0.1:52111"
|
||||||
port = await findAvailablePort();
|
port = await findAvailablePort();
|
||||||
}
|
}
|
||||||
@ -258,30 +247,26 @@ class AndroidDevice extends Device {
|
|||||||
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
|
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
flx.DirectoryResult buildResult = await flx.buildInTempDir(
|
String localBundlePath = await flx.buildFlx(
|
||||||
toolchain,
|
toolchain,
|
||||||
mainPath: mainPath
|
mainPath: mainPath
|
||||||
);
|
);
|
||||||
|
|
||||||
printTrace('Starting bundle for $this.');
|
printTrace('Starting bundle for $this.');
|
||||||
|
|
||||||
try {
|
if (await startBundle(
|
||||||
if (await startBundle(
|
package,
|
||||||
package,
|
localBundlePath,
|
||||||
buildResult.localBundlePath,
|
checked: checked,
|
||||||
checked: checked,
|
traceStartup: platformArgs['trace-startup'],
|
||||||
traceStartup: platformArgs['trace-startup'],
|
route: route,
|
||||||
route: route,
|
clearLogs: clearLogs,
|
||||||
clearLogs: clearLogs,
|
startPaused: startPaused,
|
||||||
startPaused: startPaused,
|
debugPort: debugPort
|
||||||
debugPort: debugPort
|
)) {
|
||||||
)) {
|
return true;
|
||||||
return true;
|
} else {
|
||||||
} else {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
buildResult.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,12 @@ Future<Process> runDetached(List<String> cmd) {
|
|||||||
|
|
||||||
/// Run cmd and return stdout.
|
/// Run cmd and return stdout.
|
||||||
/// Throws an error if cmd exits with a non-zero value.
|
/// Throws an error if cmd exits with a non-zero value.
|
||||||
String runCheckedSync(List<String> cmd, { String workingDirectory }) {
|
String runCheckedSync(List<String> cmd, {
|
||||||
return _runWithLoggingSync(cmd, workingDirectory: workingDirectory, checked: true, noisyErrors: true);
|
String workingDirectory, bool truncateCommand: false
|
||||||
|
}) {
|
||||||
|
return _runWithLoggingSync(
|
||||||
|
cmd, workingDirectory: workingDirectory, checked: true, noisyErrors: true, truncateCommand: truncateCommand
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run cmd and return stdout.
|
/// Run cmd and return stdout.
|
||||||
@ -91,9 +95,13 @@ bool exitsHappy(List<String> cli) {
|
|||||||
String _runWithLoggingSync(List<String> cmd, {
|
String _runWithLoggingSync(List<String> cmd, {
|
||||||
bool checked: false,
|
bool checked: false,
|
||||||
bool noisyErrors: false,
|
bool noisyErrors: false,
|
||||||
String workingDirectory
|
String workingDirectory,
|
||||||
|
bool truncateCommand: false
|
||||||
}) {
|
}) {
|
||||||
printTrace(cmd.join(' '));
|
String cmdText = cmd.join(' ');
|
||||||
|
if (truncateCommand && cmdText.length > 160)
|
||||||
|
cmdText = cmdText.substring(0, 160) + '…';
|
||||||
|
printTrace(cmdText);
|
||||||
ProcessResult results =
|
ProcessResult results =
|
||||||
Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList(), workingDirectory: workingDirectory);
|
Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList(), workingDirectory: workingDirectory);
|
||||||
if (results.exitCode != 0) {
|
if (results.exitCode != 0) {
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
|
String calculateSha(File file) {
|
||||||
|
SHA1 sha1 = new SHA1();
|
||||||
|
sha1.add(file.readAsBytesSync());
|
||||||
|
return CryptoUtils.bytesToHex(sha1.close());
|
||||||
|
}
|
||||||
|
|
||||||
/// A class to maintain a list of items, fire events when items are added or
|
/// A class to maintain a list of items, fire events when items are added or
|
||||||
/// removed, and calculate a diff of changes when a new list of items is
|
/// removed, and calculate a diff of changes when a new list of items is
|
||||||
|
@ -13,6 +13,7 @@ import '../artifacts.dart';
|
|||||||
import '../base/file_system.dart' show ensureDirectoryExists;
|
import '../base/file_system.dart' show ensureDirectoryExists;
|
||||||
import '../base/os.dart';
|
import '../base/os.dart';
|
||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
|
import '../base/utils.dart';
|
||||||
import '../build_configuration.dart';
|
import '../build_configuration.dart';
|
||||||
import '../device.dart';
|
import '../device.dart';
|
||||||
import '../flx.dart' as flx;
|
import '../flx.dart' as flx;
|
||||||
@ -296,6 +297,9 @@ int _buildApk(
|
|||||||
ensureDirectoryExists(finalApk.path);
|
ensureDirectoryExists(finalApk.path);
|
||||||
builder.align(unalignedApk, finalApk);
|
builder.align(unalignedApk, finalApk);
|
||||||
|
|
||||||
|
File apkShaFile = new File('$outputFile.sha1');
|
||||||
|
apkShaFile.writeAsStringSync(calculateSha(finalApk));
|
||||||
|
|
||||||
printStatus('Generated APK to ${finalApk.path}.');
|
printStatus('Generated APK to ${finalApk.path}.');
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -313,7 +317,7 @@ int _signApk(
|
|||||||
String keyPassword;
|
String keyPassword;
|
||||||
|
|
||||||
if (keystoreInfo == null) {
|
if (keystoreInfo == null) {
|
||||||
printError('Signing the APK using the debug keystore.');
|
printStatus('Warning: signing the APK using the debug keystore.');
|
||||||
keystore = components.debugKeystore;
|
keystore = components.debugKeystore;
|
||||||
keystorePassword = _kDebugKeystorePassword;
|
keystorePassword = _kDebugKeystorePassword;
|
||||||
keyAlias = _kDebugKeystoreKeyAlias;
|
keyAlias = _kDebugKeystoreKeyAlias;
|
||||||
@ -345,13 +349,14 @@ bool _needsRebuild(String apkPath, String manifest) {
|
|||||||
Iterable<FileStat> dependenciesStat = [
|
Iterable<FileStat> dependenciesStat = [
|
||||||
manifest,
|
manifest,
|
||||||
_kFlutterManifestPath,
|
_kFlutterManifestPath,
|
||||||
_kPackagesStatusPath
|
_kPackagesStatusPath,
|
||||||
|
'$apkPath.sha1'
|
||||||
].map((String path) => FileStat.statSync(path));
|
].map((String path) => FileStat.statSync(path));
|
||||||
|
|
||||||
if (apkStat.type == FileSystemEntityType.NOT_FOUND)
|
if (apkStat.type == FileSystemEntityType.NOT_FOUND)
|
||||||
return true;
|
return true;
|
||||||
for (FileStat dep in dependenciesStat) {
|
for (FileStat dep in dependenciesStat) {
|
||||||
if (dep.modified.isAfter(apkStat.modified))
|
if (dep.modified == null || dep.modified.isAfter(apkStat.modified))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -381,7 +386,7 @@ Future<int> buildAndroid({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!force && !_needsRebuild(outputFile, manifest)) {
|
if (!force && !_needsRebuild(outputFile, manifest)) {
|
||||||
printTrace('APK up to date. Skipping build step.');
|
printTrace('APK up to date; skipping build step.');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,13 +413,8 @@ Future<int> buildAndroid({
|
|||||||
String mainPath = findMainDartFile(target);
|
String mainPath = findMainDartFile(target);
|
||||||
|
|
||||||
// Build the FLX.
|
// Build the FLX.
|
||||||
flx.DirectoryResult buildResult = await flx.buildInTempDir(toolchain, mainPath: mainPath);
|
String localBundlePath = await flx.buildFlx(toolchain, mainPath: mainPath);
|
||||||
|
return _buildApk(components, localBundlePath, keystore, outputFile);
|
||||||
try {
|
|
||||||
return _buildApk(components, buildResult.localBundlePath, keystore, outputFile);
|
|
||||||
} finally {
|
|
||||||
buildResult.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:archive/archive.dart';
|
|
||||||
import 'package:flx/bundle.dart';
|
import 'package:flx/bundle.dart';
|
||||||
import 'package:flx/signing.dart';
|
import 'package:flx/signing.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
@ -16,6 +14,7 @@ import 'package:yaml/yaml.dart';
|
|||||||
import 'base/file_system.dart' show ensureDirectoryExists;
|
import 'base/file_system.dart' show ensureDirectoryExists;
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'toolchain.dart';
|
import 'toolchain.dart';
|
||||||
|
import 'zip.dart';
|
||||||
|
|
||||||
const String defaultMainPath = 'lib/main.dart';
|
const String defaultMainPath = 'lib/main.dart';
|
||||||
const String defaultAssetBasePath = '.';
|
const String defaultAssetBasePath = '.';
|
||||||
@ -140,20 +139,17 @@ dynamic _loadManifest(String manifestPath) {
|
|||||||
return loadYaml(manifestDescriptor);
|
return loadYaml(manifestDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _addAssetFile(Archive archive, _Asset asset) {
|
ZipEntry _createAssetEntry(_Asset asset) {
|
||||||
String source = asset.source ?? asset.key;
|
String source = asset.source ?? asset.key;
|
||||||
File file = new File('${asset.base}/$source');
|
File file = new File('${asset.base}/$source');
|
||||||
if (!file.existsSync()) {
|
if (!file.existsSync()) {
|
||||||
printError('Cannot find asset "$source" in directory "${path.absolute(asset.base)}".');
|
printError('Cannot find asset "$source" in directory "${path.absolute(asset.base)}".');
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
List<int> content = file.readAsBytesSync();
|
return new ZipEntry.fromFile(asset.key, file);
|
||||||
archive.addFile(new ArchiveFile.noCompress(asset.key, content.length, content));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveFile _createAssetManifest(Map<_Asset, List<_Asset>> assets) {
|
ZipEntry _createAssetManifest(Map<_Asset, List<_Asset>> assets) {
|
||||||
String key = 'AssetManifest.json';
|
|
||||||
Map<String, List<String>> json = <String, List<String>>{};
|
Map<String, List<String>> json = <String, List<String>>{};
|
||||||
for (_Asset main in assets.keys) {
|
for (_Asset main in assets.keys) {
|
||||||
List<String> variants = <String>[];
|
List<String> variants = <String>[];
|
||||||
@ -161,34 +157,25 @@ ArchiveFile _createAssetManifest(Map<_Asset, List<_Asset>> assets) {
|
|||||||
variants.add(variant.key);
|
variants.add(variant.key);
|
||||||
json[main.key] = variants;
|
json[main.key] = variants;
|
||||||
}
|
}
|
||||||
List<int> content = UTF8.encode(JSON.encode(json));
|
return new ZipEntry.fromString('AssetManifest.json', JSON.encode(json));
|
||||||
return new ArchiveFile.noCompress(key, content.length, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveFile _createFontManifest(Map manifestDescriptor) {
|
ZipEntry _createFontManifest(Map manifestDescriptor) {
|
||||||
if (manifestDescriptor != null && manifestDescriptor.containsKey('fonts')) {
|
if (manifestDescriptor != null && manifestDescriptor.containsKey('fonts')) {
|
||||||
List<int> content = UTF8.encode(JSON.encode(manifestDescriptor['fonts']));
|
return new ZipEntry.fromString('FontManifest.json', JSON.encode(manifestDescriptor['fonts']));
|
||||||
return new ArchiveFile.noCompress('FontManifest.json', content.length, content);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveFile _createSnapshotFile(String snapshotPath) {
|
/// Build the flx in the build/ directory and return `localBundlePath` on success.
|
||||||
File file = new File(snapshotPath);
|
Future<String> buildFlx(
|
||||||
List<int> content = file.readAsBytesSync();
|
|
||||||
return new ArchiveFile(_kSnapshotKey, content.length, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the flx in a temp dir and return `localBundlePath` on success.
|
|
||||||
Future<DirectoryResult> buildInTempDir(
|
|
||||||
Toolchain toolchain, {
|
Toolchain toolchain, {
|
||||||
String mainPath: defaultMainPath
|
String mainPath: defaultMainPath
|
||||||
}) async {
|
}) async {
|
||||||
int result;
|
int result;
|
||||||
Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools');
|
String localBundlePath = path.join('build', 'app.flx');
|
||||||
String localBundlePath = path.join(tempDir.path, 'app.flx');
|
String localSnapshotPath = path.join('build', 'snapshot_blob.bin');
|
||||||
String localSnapshotPath = path.join(tempDir.path, 'snapshot_blob.bin');
|
|
||||||
result = await build(
|
result = await build(
|
||||||
toolchain,
|
toolchain,
|
||||||
snapshotPath: localSnapshotPath,
|
snapshotPath: localSnapshotPath,
|
||||||
@ -196,7 +183,7 @@ Future<DirectoryResult> buildInTempDir(
|
|||||||
mainPath: mainPath
|
mainPath: mainPath
|
||||||
);
|
);
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
return new DirectoryResult(tempDir, localBundlePath);
|
return localBundlePath;
|
||||||
else
|
else
|
||||||
throw result;
|
throw result;
|
||||||
}
|
}
|
||||||
@ -227,7 +214,8 @@ Future<int> build(
|
|||||||
Map manifestDescriptor = _loadManifest(manifestPath);
|
Map manifestDescriptor = _loadManifest(manifestPath);
|
||||||
String assetBasePath = path.dirname(path.absolute(manifestPath));
|
String assetBasePath = path.dirname(path.absolute(manifestPath));
|
||||||
|
|
||||||
ArchiveFile snapshotFile = null;
|
File snapshotFile;
|
||||||
|
|
||||||
if (!precompiledSnapshot) {
|
if (!precompiledSnapshot) {
|
||||||
ensureDirectoryExists(snapshotPath);
|
ensureDirectoryExists(snapshotPath);
|
||||||
|
|
||||||
@ -239,7 +227,7 @@ Future<int> build(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotFile = _createSnapshotFile(snapshotPath);
|
snapshotFile = new File(snapshotPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return assemble(
|
return assemble(
|
||||||
@ -254,7 +242,7 @@ Future<int> build(
|
|||||||
|
|
||||||
Future<int> assemble({
|
Future<int> assemble({
|
||||||
Map manifestDescriptor: const {},
|
Map manifestDescriptor: const {},
|
||||||
ArchiveFile snapshotFile,
|
File snapshotFile,
|
||||||
String assetBasePath: defaultAssetBasePath,
|
String assetBasePath: defaultAssetBasePath,
|
||||||
String materialAssetBasePath: defaultMaterialAssetBasePath,
|
String materialAssetBasePath: defaultMaterialAssetBasePath,
|
||||||
String outputPath: defaultFlxOutputPath,
|
String outputPath: defaultFlxOutputPath,
|
||||||
@ -265,25 +253,32 @@ Future<int> assemble({
|
|||||||
Map<_Asset, List<_Asset>> assets = _parseAssets(manifestDescriptor, assetBasePath);
|
Map<_Asset, List<_Asset>> assets = _parseAssets(manifestDescriptor, assetBasePath);
|
||||||
assets.addAll(_parseMaterialAssets(manifestDescriptor, materialAssetBasePath));
|
assets.addAll(_parseMaterialAssets(manifestDescriptor, materialAssetBasePath));
|
||||||
|
|
||||||
Archive archive = new Archive();
|
ZipBuilder zipBuilder = new ZipBuilder();
|
||||||
|
|
||||||
if (snapshotFile != null)
|
if (snapshotFile != null)
|
||||||
archive.addFile(snapshotFile);
|
zipBuilder.addEntry(new ZipEntry.fromFile(_kSnapshotKey, snapshotFile));
|
||||||
|
|
||||||
for (_Asset asset in assets.keys) {
|
for (_Asset asset in assets.keys) {
|
||||||
if (!_addAssetFile(archive, asset))
|
ZipEntry assetEntry = _createAssetEntry(asset);
|
||||||
|
if (assetEntry == null)
|
||||||
return 1;
|
return 1;
|
||||||
|
else
|
||||||
|
zipBuilder.addEntry(assetEntry);
|
||||||
|
|
||||||
for (_Asset variant in assets[asset]) {
|
for (_Asset variant in assets[asset]) {
|
||||||
if (!_addAssetFile(archive, variant))
|
ZipEntry variantEntry = _createAssetEntry(variant);
|
||||||
|
if (variantEntry == null)
|
||||||
return 1;
|
return 1;
|
||||||
|
else
|
||||||
|
zipBuilder.addEntry(variantEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archive.addFile(_createAssetManifest(assets));
|
zipBuilder.addEntry(_createAssetManifest(assets));
|
||||||
|
|
||||||
ArchiveFile fontManifest = _createFontManifest(manifestDescriptor);
|
ZipEntry fontManifest = _createFontManifest(manifestDescriptor);
|
||||||
if (fontManifest != null)
|
if (fontManifest != null)
|
||||||
archive.addFile(fontManifest);
|
zipBuilder.addEntry(fontManifest);
|
||||||
|
|
||||||
AsymmetricKeyPair keyPair = keyPairFromPrivateKeyFileSync(privateKeyPath);
|
AsymmetricKeyPair keyPair = keyPairFromPrivateKeyFileSync(privateKeyPath);
|
||||||
printTrace('KeyPair from $privateKeyPath: $keyPair.');
|
printTrace('KeyPair from $privateKeyPath: $keyPair.');
|
||||||
@ -293,8 +288,10 @@ Future<int> assemble({
|
|||||||
CipherParameters.get().seedRandom();
|
CipherParameters.get().seedRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
printTrace('Encoding zip file.');
|
File zipFile = new File(outputPath.substring(0, outputPath.length - 4) + '.zip');
|
||||||
Uint8List zipBytes = new Uint8List.fromList(new ZipEncoder().encode(archive));
|
printTrace('Encoding zip file to ${zipFile.path}');
|
||||||
|
zipBuilder.createZip(zipFile, new Directory('build/flx'));
|
||||||
|
List<int> zipBytes = zipFile.readAsBytesSync();
|
||||||
|
|
||||||
ensureDirectoryExists(outputPath);
|
ensureDirectoryExists(outputPath);
|
||||||
|
|
||||||
@ -307,7 +304,7 @@ Future<int> assemble({
|
|||||||
);
|
);
|
||||||
bundle.writeSync();
|
bundle.writeSync();
|
||||||
|
|
||||||
printTrace('Built and signed flx at $outputPath.');
|
printTrace('Built $outputPath.');
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
117
packages/flutter_tools/lib/src/zip.dart
Normal file
117
packages/flutter_tools/lib/src/zip.dart
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// 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 'dart:convert' show UTF8;
|
||||||
|
|
||||||
|
import 'package:archive/archive.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'base/process.dart';
|
||||||
|
|
||||||
|
abstract class ZipBuilder {
|
||||||
|
factory ZipBuilder() {
|
||||||
|
if (exitsHappy(<String>['which', 'zip'])) {
|
||||||
|
return new _ZipToolBuilder();
|
||||||
|
} else {
|
||||||
|
return new _ArchiveZipBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipBuilder._();
|
||||||
|
|
||||||
|
List<ZipEntry> entries = <ZipEntry>[];
|
||||||
|
|
||||||
|
void addEntry(ZipEntry entry) => entries.add(entry);
|
||||||
|
|
||||||
|
void createZip(File outFile, Directory zipBuildDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZipEntry {
|
||||||
|
ZipEntry.fromFile(this.archivePath, File file) {
|
||||||
|
this._file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipEntry.fromString(this.archivePath, String contents) {
|
||||||
|
this._contents = contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String archivePath;
|
||||||
|
|
||||||
|
File _file;
|
||||||
|
String _contents;
|
||||||
|
|
||||||
|
bool get isStringEntry => _contents != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ArchiveZipBuilder extends ZipBuilder {
|
||||||
|
_ArchiveZipBuilder() : super._();
|
||||||
|
|
||||||
|
void createZip(File outFile, Directory zipBuildDir) {
|
||||||
|
Archive archive = new Archive();
|
||||||
|
|
||||||
|
for (ZipEntry entry in entries) {
|
||||||
|
if (entry.isStringEntry) {
|
||||||
|
List<int> data = UTF8.encode(entry._contents);
|
||||||
|
archive.addFile(new ArchiveFile.noCompress(entry.archivePath, data.length, data));
|
||||||
|
} else {
|
||||||
|
List<int> data = entry._file.readAsBytesSync();
|
||||||
|
archive.addFile(new ArchiveFile(entry.archivePath, data.length, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> zipData = new ZipEncoder().encode(archive);
|
||||||
|
outFile.writeAsBytesSync(zipData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ZipToolBuilder extends ZipBuilder {
|
||||||
|
_ZipToolBuilder() : super._();
|
||||||
|
|
||||||
|
void createZip(File outFile, Directory zipBuildDir) {
|
||||||
|
if (outFile.existsSync())
|
||||||
|
outFile.deleteSync();
|
||||||
|
|
||||||
|
if (zipBuildDir.existsSync())
|
||||||
|
zipBuildDir.deleteSync(recursive: true);
|
||||||
|
zipBuildDir.createSync(recursive: true);
|
||||||
|
|
||||||
|
for (ZipEntry entry in entries) {
|
||||||
|
if (entry.isStringEntry) {
|
||||||
|
List<int> data = UTF8.encode(entry._contents);
|
||||||
|
File file = new File(path.join(zipBuildDir.path, entry.archivePath));
|
||||||
|
file.parent.createSync(recursive: true);
|
||||||
|
file.writeAsBytesSync(data);
|
||||||
|
} else {
|
||||||
|
List<int> data = entry._file.readAsBytesSync();
|
||||||
|
File file = new File(path.join(zipBuildDir.path, entry.archivePath));
|
||||||
|
file.parent.createSync(recursive: true);
|
||||||
|
file.writeAsBytesSync(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runCheckedSync(
|
||||||
|
<String>['zip', '-q', outFile.absolute.path]..addAll(_getCompressedNames()),
|
||||||
|
workingDirectory: zipBuildDir.path,
|
||||||
|
truncateCommand: true
|
||||||
|
);
|
||||||
|
runCheckedSync(
|
||||||
|
<String>['zip', '-q', '-0', outFile.absolute.path]..addAll(_getStoredNames()),
|
||||||
|
workingDirectory: zipBuildDir.path,
|
||||||
|
truncateCommand: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<String> _getCompressedNames() {
|
||||||
|
return entries
|
||||||
|
.where((ZipEntry entry) => !entry.isStringEntry)
|
||||||
|
.map((ZipEntry entry) => entry.archivePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<String> _getStoredNames() {
|
||||||
|
return entries
|
||||||
|
.where((ZipEntry entry) => entry.isStringEntry)
|
||||||
|
.map((ZipEntry entry) => entry.archivePath);
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,7 @@ class Bundle {
|
|||||||
Bundle.fromContent({
|
Bundle.fromContent({
|
||||||
this.path,
|
this.path,
|
||||||
this.manifest,
|
this.manifest,
|
||||||
contentBytes,
|
List<int> contentBytes,
|
||||||
AsymmetricKeyPair keyPair
|
AsymmetricKeyPair keyPair
|
||||||
}) : _contentBytes = contentBytes {
|
}) : _contentBytes = contentBytes {
|
||||||
assert(path != null);
|
assert(path != null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user