Commit 12f75817 authored by Adam Barth's avatar Adam Barth

Refactor the build command so that it can be used internally

Instead of calling through `pub` to invoke build, this patch refactors the
build command so that it can be called directly.
parent d9af9399
......@@ -6,10 +6,10 @@ import 'dart:async';
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:args/command_runner.dart';
import 'package:yaml/yaml.dart';
import '../artifacts.dart';
import '../toolchain.dart';
import 'flutter_command.dart';
const String _kSnapshotKey = 'snapshot_blob.bin';
const List<String> _kDensities = const ['drawable-xxhdpi'];
......@@ -96,74 +96,80 @@ Future<ArchiveFile> _createFile(String key, String assetBase) async {
return new ArchiveFile.noCompress(key, content.length, content);
}
Future _compileSnapshot({
String compilerPath,
String mainPath,
String packageRoot,
String snapshotPath
}) async {
if (compilerPath == null) {
compilerPath = await ArtifactStore.getPath(Artifact.flutterCompiler);
}
ProcessResult result = await Process.run(compilerPath, [
mainPath,
'--package-root=$packageRoot',
'--snapshot=$snapshotPath'
]);
if (result.exitCode != 0) {
print(result.stdout);
print(result.stderr);
exit(result.exitCode);
}
}
Future<ArchiveFile> _createSnapshotFile(String snapshotPath) async {
File file = new File(snapshotPath);
List<int> content = await file.readAsBytes();
return new ArchiveFile(_kSnapshotKey, content.length, content);
}
class BuildCommand extends Command {
final name = 'build';
final description = 'Create a Flutter app.';
const String _kDefaultAssetBase = 'packages/material_design_icons/icons';
const String _kDefaultMainPath = 'lib/main.dart';
const String _kDefaultOutputPath = 'app.flx';
const String _kDefaultSnapshotPath = 'snapshot_blob.bin';
class BuildCommand extends FlutterCommand {
final String name = 'build';
final String description = 'Create a Flutter app.';
BuildCommand() {
argParser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons');
argParser.addOption('asset-base', defaultsTo: _kDefaultAssetBase);
argParser.addOption('compiler');
argParser.addOption('main', defaultsTo: 'lib/main.dart');
argParser.addOption('main', defaultsTo: _kDefaultMainPath);
argParser.addOption('manifest');
argParser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx');
argParser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin');
argParser.addOption('output-file', abbr: 'o', defaultsTo: _kDefaultOutputPath);
argParser.addOption('snapshot', defaultsTo: _kDefaultSnapshotPath);
}
@override
Future<int> run() async {
String manifestPath = argResults['manifest'];
String compilerPath = argResults['compiler'];
if (compilerPath == null)
await downloadToolchain();
else
toolchain = new Toolchain(compiler: new Compiler(compilerPath));
return await build(
assetBase: argResults['asset-base'],
mainPath: argResults['main'],
manifestPath: argResults['manifest'],
outputPath: argResults['output-file'],
snapshotPath: argResults['snapshot']
);
}
Future<int> build({
String assetBase: _kDefaultAssetBase,
String mainPath: _kDefaultMainPath,
String manifestPath,
String outputPath: _kDefaultOutputPath,
String snapshotPath: _kDefaultSnapshotPath
}) async {
Map manifestDescriptor = await _loadManifest(manifestPath);
Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath);
Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor);
Archive archive = new Archive();
String snapshotPath = argResults['snapshot'];
await _compileSnapshot(
compilerPath: argResults['compiler'],
mainPath: argResults['main'],
packageRoot: globalResults['package-root'],
snapshotPath: snapshotPath);
int result = await toolchain.compiler.compile(mainPath: mainPath, snapshotPath: snapshotPath);
if (result != 0)
return result;
archive.addFile(await _createSnapshotFile(snapshotPath));
for (_Asset asset in assets)
archive.addFile(await _createFile(asset.key, asset.base));
for (_MaterialAsset asset in materialAssets) {
ArchiveFile file = await _createFile(asset.key, argResults['asset-base']);
ArchiveFile file = await _createFile(asset.key, assetBase);
if (file != null)
archive.addFile(file);
}
File outputFile = new File(argResults['output-file']);
File outputFile = new File(outputPath);
await outputFile.writeAsString('#!mojo mojo:sky_viewer\n');
await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND);
await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND, flush: true);
return 0;
}
}
......@@ -8,6 +8,7 @@ import 'package:args/command_runner.dart';
import '../application_package.dart';
import '../device.dart';
import '../toolchain.dart';
import 'flutter_command_runner.dart';
abstract class FlutterCommand extends Command {
......@@ -18,6 +19,11 @@ abstract class FlutterCommand extends Command {
applicationPackages = await ApplicationPackageStore.forConfigs(runner.buildConfigurations);
}
Future downloadToolchain() async {
if (toolchain == null)
toolchain = await Toolchain.forConfigs(runner.buildConfigurations);
}
void connectToDevices() {
if (devices == null)
devices = new DeviceStore.forConfigs(runner.buildConfigurations);
......@@ -30,9 +36,11 @@ abstract class FlutterCommand extends Command {
void inheritFromParent(FlutterCommand other) {
applicationPackages = other.applicationPackages;
toolchain = other.toolchain;
devices = other.devices;
}
ApplicationPackageStore applicationPackages;
Toolchain toolchain;
DeviceStore devices;
}
......@@ -7,10 +7,11 @@ import 'dart:io';
import 'package:logging/logging.dart';
import 'flutter_command.dart';
import '../application_package.dart';
import '../device.dart';
import '../process.dart';
import 'build.dart';
import 'flutter_command.dart';
final Logger _logging = new Logger('sky_tools.listen');
......@@ -33,9 +34,13 @@ class ListenCommand extends FlutterCommand {
help: 'Target app path or filename to start.');
}
static const String _localFlutterBundle = 'app.flx';
static const String _remoteFlutterBundle = 'Documents/app.flx';
@override
Future<int> run() async {
await downloadApplicationPackagesAndConnectToDevices();
await downloadToolchain();
if (argResults.rest.length > 0) {
watchCommand = _initWatchCommand(argResults.rest);
......@@ -46,27 +51,9 @@ class ListenCommand extends FlutterCommand {
while (true) {
_logging.info('Updating running Flutter apps...');
// TODO(iansf): refactor build command so that this doesn't have
// to call out like this.
List<String> command = ['pub', 'run', 'sky_tools', 'build',];
try {
// In testing, sky-src-path isn't added to the options, and
// the ArgParser module throws an exception, so we have to
// catch and ignore the error in order to test.
if (globalResults.wasParsed('sky-src-path')) {
command.addAll([
// TODO(iansf): Don't rely on sky-src-path for the snapshotter.
'--compiler',
'${globalResults['sky-src-path']}'
'/out/ios_sim_Debug/clang_x64/sky_snapshot'
]);
}
} catch (e) {}
runSync(command);
String localFlutterBundle = 'app.flx';
String remoteFlutterBundle = 'Documents/app.flx';
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
builder.build(outputPath: _localFlutterBundle);
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
......@@ -76,11 +63,11 @@ class ListenCommand extends FlutterCommand {
await devices.android.startServer(
argResults['target'], true, argResults['checked'], package);
} else if (device is IOSDevice) {
device.pushFile(package, localFlutterBundle, remoteFlutterBundle);
device.pushFile(package, _localFlutterBundle, _remoteFlutterBundle);
} else if (device is IOSSimulator) {
// TODO(abarth): Move pushFile up to Device once Android supports
// pushing new bundles.
device.pushFile(package, localFlutterBundle, remoteFlutterBundle);
device.pushFile(package, _localFlutterBundle, _remoteFlutterBundle);
} else {
assert(false);
}
......
// Copyright 2015 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:path/path.dart' as path;
import 'artifacts.dart';
import 'build_configuration.dart';
import 'process.dart';
class Compiler {
Compiler(this._compilerPath);
String _compilerPath;
Future<int> compile({
String mainPath,
String snapshotPath
}) {
return runCommandAndStreamOutput([
_compilerPath,
mainPath,
'--package-root=${ArtifactStore.packageRoot}',
'--snapshot=$snapshotPath'
]);
}
}
class Toolchain {
Toolchain({ this.compiler });
final Compiler compiler;
static Future<Toolchain> forConfigs(List<BuildConfiguration> configs) async {
// TODO(abarth): Add a notion of "host platform" to the build configs.
BuildConfiguration config = configs.first;
String compilerPath = config.type == BuildType.prebuilt ?
await ArtifactStore.getPath(Artifact.flutterCompiler) :
path.join(config.buildDir, 'clang_x64', 'sky_snapshot');
return new Toolchain(compiler: new Compiler(compilerPath));
}
}
......@@ -7,6 +7,7 @@ import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/build_configuration.dart';
import 'package:sky_tools/src/commands/flutter_command.dart';
import 'package:sky_tools/src/device.dart';
import 'package:sky_tools/src/toolchain.dart';
class MockApplicationPackageStore extends ApplicationPackageStore {
MockApplicationPackageStore() : super(
......@@ -15,6 +16,15 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
iOSSimulator: new IOSApp(localPath: '/mock/path/to/iOSSimulator/SkyShell.app'));
}
class MockCompiler extends Mock implements Compiler {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class MockToolchain extends Toolchain {
MockToolchain() : super(compiler: new MockCompiler());
}
class MockAndroidDevice extends Mock implements AndroidDevice {
BuildPlatform get platform => BuildPlatform.android;
......@@ -46,5 +56,6 @@ class MockDeviceStore extends DeviceStore {
void applyMocksToCommand(FlutterCommand command) {
command
..applicationPackages = new MockApplicationPackageStore()
..toolchain = new MockToolchain()
..devices = new MockDeviceStore();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment