From 3167629990eb1bc41a4ab93e70c01904f14cc43c Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 20 Jul 2015 17:20:31 -0700 Subject: [PATCH 001/188] Add README.md and LICENSE --- packages/flutter_tools/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 packages/flutter_tools/README.md diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md new file mode 100644 index 0000000000..2e3689ac73 --- /dev/null +++ b/packages/flutter_tools/README.md @@ -0,0 +1,2 @@ +Sky Tools +========= From 688fb26ae396df171217c1f9ceafcb5f8cc88fd2 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 20 Jul 2015 17:22:22 -0700 Subject: [PATCH 002/188] Add a first draft of build_sky_apk.dart --- packages/flutter_tools/bin/build_sky_apk.dart | 68 +++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 7 ++ 2 files changed, 75 insertions(+) create mode 100644 packages/flutter_tools/bin/build_sky_apk.dart create mode 100644 packages/flutter_tools/pubspec.yaml diff --git a/packages/flutter_tools/bin/build_sky_apk.dart b/packages/flutter_tools/bin/build_sky_apk.dart new file mode 100644 index 0000000000..e98e3f85e5 --- /dev/null +++ b/packages/flutter_tools/bin/build_sky_apk.dart @@ -0,0 +1,68 @@ +// 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:io'; + +import 'package:args/args.dart'; + +const String kBuildToolsVersion = '22.0.1'; +const String kAndroidPlatformVersion = '22'; + +const String kKeystoreKeyName = "chromiumdebugkey"; +const String kKeystorePassword = "chromium"; + +const String kICUDataFile = 'icudtl.dat'; + +void run(String command, List args) { + ProcessResult result = Process.runSync(command, args); + stdout.write(result.stdout); + stderr.write(result.stderr); +} + +main(List argv) async { + ArgParser parser = new ArgParser(); + parser.addFlag('help', abbr: 'h', negatable: false); + parser.addOption('android-sdk'); + parser.addOption('skyx'); + + ArgResults args = parser.parse(argv); + + File unalignedApk = new File('out/Example.apk.unaligned'); + File finalApk = new File('out/Example.apk'); + File androidManifest = new File('artifacts/AndroidManifest.xml'); + File classesDex = new File('artifacts/classes.dex'); + File icuDataFile = new File('artifacts/$kICUDataFile'); + File keystore = new File('artifacts/chromium-debug.keystore'); + + String androidSDK = args['android-sdk']; + String buildTools = '$androidSDK/build-tools/$kBuildToolsVersion'; + String aapt = '$buildTools/aapt'; + String zipalign = '$buildTools/zipalign'; + File androidJar = new File('$androidSDK/platforms/android-$kAndroidPlatformVersion/android.jar'); + String jarsigner = 'jarsigner'; + + Directory assets = new Directory('out/assets'); + await assets.create(recursive: true); + + icuDataFile.copy('${assets.path}/$kICUDataFile'); + + run(aapt, [ + 'package', + '-M', androidManifest.path, + '-A', assets.path, + '-I', androidJar.path, + '-F', unalignedApk.path, + ]); + + run(aapt, [ 'add', '-f', unalignedApk.path, classesDex.path ]); + + run(jarsigner, [ + '-keystore', keystore.path, + '-storepass', kKeystorePassword, + unalignedApk.path, + kKeystoreKeyName, + ]); + + run(zipalign, ['4', unalignedApk.path, finalApk.path]); +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml new file mode 100644 index 0000000000..602d92108d --- /dev/null +++ b/packages/flutter_tools/pubspec.yaml @@ -0,0 +1,7 @@ +author: Chromium Authors +dependencies: + args: '>=0.13.0 <1.0.0' +description: Tools for building Sky applications +homepage: https://github.com/domokit/sky_tools +name: sky_tools +version: 0.0.1 From 646ff43f9bb405ce30de662d090ca7b95f699c1f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 21 Jul 2015 10:10:36 -0700 Subject: [PATCH 003/188] Iterate on build_sky_apk.dart --- packages/flutter_tools/bin/build_sky_apk.dart | 125 ++++++++++++------ 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/packages/flutter_tools/bin/build_sky_apk.dart b/packages/flutter_tools/bin/build_sky_apk.dart index e98e3f85e5..4d943e253f 100644 --- a/packages/flutter_tools/bin/build_sky_apk.dart +++ b/packages/flutter_tools/bin/build_sky_apk.dart @@ -12,12 +12,77 @@ const String kAndroidPlatformVersion = '22'; const String kKeystoreKeyName = "chromiumdebugkey"; const String kKeystorePassword = "chromium"; -const String kICUDataFile = 'icudtl.dat'; +class AssetBuilder { + final Directory outDir; -void run(String command, List args) { - ProcessResult result = Process.runSync(command, args); - stdout.write(result.stdout); - stderr.write(result.stderr); + Directory _assetDir; + + AssetBuilder(this.outDir) { + _assetDir = new Directory('${outDir.path}/assets'); + _assetDir.createSync(recursive: true); + } + + void add(File asset, String assetName) { + asset.copySync('${_assetDir.path}/$assetName'); + } + + Directory get directory => _assetDir; +} + +class ApkBuilder { + final String androidSDK; + + File _androidJar; + File _aapt; + File _zipalign; + String _jarsigner; + + ApkBuilder(this.androidSDK) { + _androidJar = new File('$androidSDK/platforms/android-$kAndroidPlatformVersion/android.jar'); + + String buildTools = '$androidSDK/build-tools/$kBuildToolsVersion'; + _aapt = new File('$buildTools/aapt'); + _zipalign = new File('$buildTools/zipalign'); + _jarsigner = 'jarsigner'; + } + + void package(File androidManifest, Directory assets, File outputApk) { + _run(_aapt.path, [ + 'package', + '-M', androidManifest.path, + '-A', assets.path, + '-I', _androidJar.path, + '-F', outputApk.path, + ]); + } + + void add(Directory base, String resource, File outputApk) { + _run(_aapt.path, [ + 'add', '-f', outputApk.absolute.path, resource, + ], workingDirectory: base.path); + } + + void sign(File keystore, String keystorePassword, String keyName, File outputApk) { + _run(_jarsigner, [ + '-keystore', keystore.path, + '-storepass', keystorePassword, + outputApk.path, + keyName, + ]); + } + + void align(File unalignedApk, File outputApk) { + _run(_zipalign.path, ['4', unalignedApk.path, outputApk.path]); + } + + void _run(String command, List args, { String workingDirectory }) { + ProcessResult result = Process.runSync( + command, args, workingDirectory: workingDirectory); + if (result.exitCode == 0) + return; + stdout.write(result.stdout); + stderr.write(result.stderr); + } } main(List argv) async { @@ -28,41 +93,27 @@ main(List argv) async { ArgResults args = parser.parse(argv); - File unalignedApk = new File('out/Example.apk.unaligned'); - File finalApk = new File('out/Example.apk'); - File androidManifest = new File('artifacts/AndroidManifest.xml'); - File classesDex = new File('artifacts/classes.dex'); - File icuDataFile = new File('artifacts/$kICUDataFile'); - File keystore = new File('artifacts/chromium-debug.keystore'); + Directory artifacts = new Directory('artifacts'); + File keystore = new File('${artifacts.path}/chromium-debug.keystore'); + File androidManifest = new File('${artifacts.path}/AndroidManifest.xml'); + File icuData = new File('${artifacts.path}/assets/icudtl.dat'); + File appSkyx = new File(args['skyx']); - String androidSDK = args['android-sdk']; - String buildTools = '$androidSDK/build-tools/$kBuildToolsVersion'; - String aapt = '$buildTools/aapt'; - String zipalign = '$buildTools/zipalign'; - File androidJar = new File('$androidSDK/platforms/android-$kAndroidPlatformVersion/android.jar'); - String jarsigner = 'jarsigner'; + Directory outDir = new Directory('out'); + outDir.createSync(recursive: true); - Directory assets = new Directory('out/assets'); - await assets.create(recursive: true); + AssetBuilder assetBuilder = new AssetBuilder(outDir); + assetBuilder.add(icuData, 'icudtl.dat'); + assetBuilder.add(appSkyx, 'app.skyx'); - icuDataFile.copy('${assets.path}/$kICUDataFile'); + ApkBuilder builder = new ApkBuilder(args['android-sdk']); - run(aapt, [ - 'package', - '-M', androidManifest.path, - '-A', assets.path, - '-I', androidJar.path, - '-F', unalignedApk.path, - ]); + File unalignedApk = new File('${outDir.path}/Example.apk.unaligned'); + File finalApk = new File('${outDir.path}/Example.apk'); - run(aapt, [ 'add', '-f', unalignedApk.path, classesDex.path ]); - - run(jarsigner, [ - '-keystore', keystore.path, - '-storepass', kKeystorePassword, - unalignedApk.path, - kKeystoreKeyName, - ]); - - run(zipalign, ['4', unalignedApk.path, finalApk.path]); + builder.package(androidManifest, assetBuilder.directory, unalignedApk); + builder.add(artifacts, 'classes.dex', unalignedApk); + builder.add(artifacts, 'lib/armeabi-v7a/libsky_shell.so', unalignedApk); + builder.sign(keystore, kKeystorePassword, kKeystoreKeyName, unalignedApk); + builder.align(unalignedApk, finalApk); } From 041275e80c1090b8770e3f2c1595293e7302441d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 22 Jul 2015 23:42:22 -0700 Subject: [PATCH 004/188] Add a basic HTTP server for Sky --- packages/flutter_tools/bin/sky_server.dart | 48 ++++++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 3 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_tools/bin/sky_server.dart diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart new file mode 100644 index 0000000000..17c80e913a --- /dev/null +++ b/packages/flutter_tools/bin/sky_server.dart @@ -0,0 +1,48 @@ +// 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 'dart:io'; + +import 'package:http_server/http_server.dart'; + +const String usage = 'Usage: sky_server PORT'; + +main(List argv) async { + if (argv.length != 1) { + print(usage); + return; + } + + int port; + try { + port = int.parse(argv[0]); + } catch(e) { + print(usage); + return; + } + + VirtualDirectory currentDirectory = new VirtualDirectory('.') + ..allowDirectoryListing = true; + + HttpServer server; + try { + server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port); + } catch(e) { + print(e); + return; + } + + server.defaultResponseHeaders + ..removeAll('x-content-type-options') + ..removeAll('x-frame-options') + ..removeAll('x-xss-protection') + ..add('cache-control', 'no-cache'); + + server + ..autoCompress = true + ..listen(currentDirectory.serveRequest); + + print('Sky server running on port $port'); +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 602d92108d..177253f4bf 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,6 +1,7 @@ author: Chromium Authors dependencies: - args: '>=0.13.0 <1.0.0' + args: ^0.13.0 + http_server: ^0.9.5 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools name: sky_tools From 386f6c545b66d114a1668e6671907ba745eaf754 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 22 Jul 2015 23:45:05 -0700 Subject: [PATCH 005/188] Add an environment dependency --- packages/flutter_tools/pubspec.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 177253f4bf..5905ab4c4e 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -3,6 +3,8 @@ dependencies: args: ^0.13.0 http_server: ^0.9.5 description: Tools for building Sky applications +environment: + sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools version: 0.0.1 From 470186cea5fea957b4bfb5fb023addfbc48d9bf4 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 22 Jul 2015 23:46:27 -0700 Subject: [PATCH 006/188] A little bit more README text --- packages/flutter_tools/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 2e3689ac73..5170b4aae9 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,2 +1,4 @@ Sky Tools ========= + +Tools for building Sky applications. From 0365fefaca6c0589c15fc906a78f4a60f1060f97 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 23 Jul 2015 00:13:51 -0700 Subject: [PATCH 007/188] Let sky_server follow links outside of the current directory --- packages/flutter_tools/bin/sky_server.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 17c80e913a..7308257fca 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -24,6 +24,8 @@ main(List argv) async { } VirtualDirectory currentDirectory = new VirtualDirectory('.') + ..followLinks = true + ..jailRoot = false ..allowDirectoryListing = true; HttpServer server; @@ -43,6 +45,4 @@ main(List argv) async { server ..autoCompress = true ..listen(currentDirectory.serveRequest); - - print('Sky server running on port $port'); } From 6d8255ff042dbd9762f76ee12cbc9c8a5bd390cb Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 23 Jul 2015 00:14:16 -0700 Subject: [PATCH 008/188] Roll version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 5905ab4c4e..a8a5c00589 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -7,4 +7,4 @@ environment: sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools -version: 0.0.1 +version: 0.0.2 From bb2f54bb91f858d11a0d632187c777dab6e25f99 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 23 Jul 2015 19:51:03 -0700 Subject: [PATCH 009/188] Use shelf instead of http_server This patch switches sky_server.dart to use shelf rather than http_server. --- packages/flutter_tools/bin/sky_server.dart | 18 +++++++----------- packages/flutter_tools/pubspec.yaml | 3 ++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 7308257fca..e84a949d2a 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:io'; -import 'package:http_server/http_server.dart'; +import 'package:shelf_static/shelf_static.dart'; +import 'package:shelf/shelf_io.dart' as io; +import 'package:shelf/shelf.dart'; const String usage = 'Usage: sky_server PORT'; @@ -23,26 +24,21 @@ main(List argv) async { return; } - VirtualDirectory currentDirectory = new VirtualDirectory('.') - ..followLinks = true - ..jailRoot = false - ..allowDirectoryListing = true; + Handler handler = createStaticHandler(Directory.current.path, + serveFilesOutsidePath: true); HttpServer server; try { - server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port); + server = await io.serve(handler, InternetAddress.LOOPBACK_IP_V4, port); } catch(e) { print(e); return; } + server.autoCompress = true; server.defaultResponseHeaders ..removeAll('x-content-type-options') ..removeAll('x-frame-options') ..removeAll('x-xss-protection') ..add('cache-control', 'no-cache'); - - server - ..autoCompress = true - ..listen(currentDirectory.serveRequest); } diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index a8a5c00589..ee1042ba1d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,7 +1,8 @@ author: Chromium Authors dependencies: args: ^0.13.0 - http_server: ^0.9.5 + shelf: ^0.6.2 + shelf_static: ^0.2.2 description: Tools for building Sky applications environment: sdk: ">=1.8.0 <2.0.0" From 08a64a92f81a6c663c858e6b73dfb74bacc3c61f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 23 Jul 2015 21:46:26 -0700 Subject: [PATCH 010/188] Roll version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index ee1042ba1d..d2c981709a 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -8,4 +8,4 @@ environment: sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools -version: 0.0.2 +version: 0.0.3 From b45a6442c5c7b2357f56f3751396f37e10bea432 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 27 Jul 2015 10:03:22 -0700 Subject: [PATCH 011/188] Turn off gzip This feature appears to be causing problems with OkHTTP's cache. --- packages/flutter_tools/bin/sky_server.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index e84a949d2a..49a321cb0b 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -35,7 +35,6 @@ main(List argv) async { return; } - server.autoCompress = true; server.defaultResponseHeaders ..removeAll('x-content-type-options') ..removeAll('x-frame-options') From d1f0a8d1e9bddb9c81fed6560cf0f6d5765c7f01 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 27 Jul 2015 10:04:07 -0700 Subject: [PATCH 012/188] Rev package version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index d2c981709a..32695b9068 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -8,4 +8,4 @@ environment: sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools -version: 0.0.3 +version: 0.0.4 From a982f0213b30a4baff3c825c42e24a2d89ef8e35 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 29 Jul 2015 16:27:41 -0700 Subject: [PATCH 013/188] Add the option to log to sky_server --- packages/flutter_tools/bin/sky_server.dart | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 49a321cb0b..191489ae50 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -4,29 +4,44 @@ import 'dart:io'; +import 'package:args/args.dart'; import 'package:shelf_static/shelf_static.dart'; import 'package:shelf/shelf_io.dart' as io; import 'package:shelf/shelf.dart'; -const String usage = 'Usage: sky_server PORT'; +void printUsage(parser) { + print('Usage: sky_server [-v] PORT'); + print(parser.usage); +} main(List argv) async { - if (argv.length != 1) { - print(usage); + ArgParser parser = new ArgParser(); + parser.addFlag('help', abbr: 'h', negatable: false, + help: 'Display this help message.'); + parser.addFlag('verbose', abbr: 'v', negatable: false, + help: 'Log requests to stdout.'); + + ArgResults args = parser.parse(argv); + + if (args['help'] || args.rest.length != 1) { + printUsage(parser); return; } int port; try { - port = int.parse(argv[0]); + port = int.parse(args.rest[0]); } catch(e) { - print(usage); + printUsage(parser); return; } Handler handler = createStaticHandler(Directory.current.path, serveFilesOutsidePath: true); + if (args['verbose']) + handler = const Pipeline().addMiddleware(logRequests()).addHandler(handler); + HttpServer server; try { server = await io.serve(handler, InternetAddress.LOOPBACK_IP_V4, port); From 0d57d606506af82bf767edb859d0cfd4004bcc95 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 29 Jul 2015 16:28:05 -0700 Subject: [PATCH 014/188] Rev version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 32695b9068..f27ec3fa16 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -8,4 +8,4 @@ environment: sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools -version: 0.0.4 +version: 0.0.5 From a9e4336a7bd80dbdd182f31e4fd3d7bc230b6d0c Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 31 Jul 2015 20:27:50 -0700 Subject: [PATCH 015/188] Update shelf_static and enable directory listing --- packages/flutter_tools/bin/sky_server.dart | 2 +- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 191489ae50..7f899d5045 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -37,7 +37,7 @@ main(List argv) async { } Handler handler = createStaticHandler(Directory.current.path, - serveFilesOutsidePath: true); + serveFilesOutsidePath: true, listDirectories: true); if (args['verbose']) handler = const Pipeline().addMiddleware(logRequests()).addHandler(handler); diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index f27ec3fa16..895cc04461 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -2,7 +2,7 @@ author: Chromium Authors dependencies: args: ^0.13.0 shelf: ^0.6.2 - shelf_static: ^0.2.2 + shelf_static: ^0.2.3 description: Tools for building Sky applications environment: sdk: ">=1.8.0 <2.0.0" From 777f0c35d48ae4067d7deefde76c9117a0aee37d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 31 Jul 2015 20:32:27 -0700 Subject: [PATCH 016/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 895cc04461..835e2b0094 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -8,4 +8,4 @@ environment: sdk: ">=1.8.0 <2.0.0" homepage: https://github.com/domokit/sky_tools name: sky_tools -version: 0.0.5 +version: 0.0.6 From 0257cbd12ba2f8a2a028899e45c889707e3b391c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 6 Aug 2015 14:25:55 -0700 Subject: [PATCH 017/188] build sky_tools using travis --- packages/flutter_tools/pubspec.yaml | 14 ++++++++------ packages/flutter_tools/tool/travis.sh | 13 +++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 packages/flutter_tools/tool/travis.sh diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 835e2b0094..2d84548475 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,11 +1,13 @@ +name: sky_tools +version: 0.0.6 +description: Tools for building Sky applications +homepage: https://github.com/domokit/sky_tools author: Chromium Authors + +environment: + sdk: ">=1.8.0 <2.0.0" + dependencies: args: ^0.13.0 shelf: ^0.6.2 shelf_static: ^0.2.3 -description: Tools for building Sky applications -environment: - sdk: ">=1.8.0 <2.0.0" -homepage: https://github.com/domokit/sky_tools -name: sky_tools -version: 0.0.6 diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh new file mode 100644 index 0000000000..63cd53e54a --- /dev/null +++ b/packages/flutter_tools/tool/travis.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# 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. + +# Fast fail the script on failures. +set -e + +# Verify that the libraries are error free. +dartanalyzer --fatal-warnings \ + bin/build_sky_apk.dart \ + lib/sky_server.dart From 990b3628497f362965af938785d9300c9ade382a Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 6 Aug 2015 18:50:52 -0700 Subject: [PATCH 018/188] Update README.md Add a travis badge. --- packages/flutter_tools/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 5170b4aae9..e253de5754 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,4 +1,5 @@ -Sky Tools -========= +# sky_tools + +[![Build Status](https://travis-ci.org/domokit/sky_tools.svg)](https://travis-ci.org/domokit/sky_tools) Tools for building Sky applications. From 4aa0fae54da549d5719c7a5019c7444e96fff8c1 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 6 Aug 2015 18:53:14 -0700 Subject: [PATCH 019/188] make tool/travis.sh executable --- packages/flutter_tools/tool/travis.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 packages/flutter_tools/tool/travis.sh diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh old mode 100644 new mode 100755 From 66657a81eee78a242d8e8e4d3058db31bf78222e Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 6 Aug 2015 18:58:33 -0700 Subject: [PATCH 020/188] fix typo in tool/travis.sh --- packages/flutter_tools/tool/travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index 63cd53e54a..f7b4013815 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -10,4 +10,4 @@ set -e # Verify that the libraries are error free. dartanalyzer --fatal-warnings \ bin/build_sky_apk.dart \ - lib/sky_server.dart + bin/sky_server.dart From 471d7b48020f16c620399d9f790d3c4d73a3fea6 Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Fri, 7 Aug 2015 09:26:41 -0700 Subject: [PATCH 021/188] Refactor per abarth feedback --- packages/flutter_tools/bin/sky_server.dart | 29 ++++++++++++++++++++-- packages/flutter_tools/pubspec.yaml | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 7f899d5045..795680ef37 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -8,18 +8,33 @@ import 'package:args/args.dart'; import 'package:shelf_static/shelf_static.dart'; import 'package:shelf/shelf_io.dart' as io; import 'package:shelf/shelf.dart'; +import 'package:shelf_route/shelf_route.dart' as shelf_route; void printUsage(parser) { print('Usage: sky_server [-v] PORT'); print(parser.usage); } +void addRoute(var router, String route, String path) { + router.add( + route, + ['GET'], + createStaticHandler( + path, + serveFilesOutsidePath: true, + listDirectories: true + ), exactMatch: false + ); +} + main(List argv) async { ArgParser parser = new ArgParser(); parser.addFlag('help', abbr: 'h', negatable: false, help: 'Display this help message.'); parser.addFlag('verbose', abbr: 'v', negatable: false, help: 'Log requests to stdout.'); + parser.addOption('route', allowMultiple: true, splitCommas: false, + help: 'Adds a virtual directory to the root.'); ArgResults args = parser.parse(argv); @@ -36,8 +51,18 @@ main(List argv) async { return; } - Handler handler = createStaticHandler(Directory.current.path, - serveFilesOutsidePath: true, listDirectories: true); + var router = shelf_route.router(); + + if (args['route'] != null) { + for (String arg in args['route']) { + List parsedArgs = arg.split(','); + addRoute(router, parsedArgs[0], parsedArgs[1]); + } + } + + addRoute(router, '/', Directory.current.path); + + var handler = router.handler; if (args['verbose']) handler = const Pipeline().addMiddleware(logRequests()).addHandler(handler); diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 2d84548475..3f97cb7daf 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -11,3 +11,4 @@ dependencies: args: ^0.13.0 shelf: ^0.6.2 shelf_static: ^0.2.3 + shelf_route: ^0.13.4 From b2679dbc7144b42fc3b760399ae46cad3fd5f8dc Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Fri, 7 Aug 2015 12:57:12 -0700 Subject: [PATCH 022/188] Update version TBR=abarth --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 3f97cb7daf..c8d15b90bd 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.6 +version: 0.0.7 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 2efd13159c5246da13e58b111c2eb58e0ed91a6c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 7 Aug 2015 14:20:14 -0700 Subject: [PATCH 023/188] add a sky_tools init command to create a new project --- packages/flutter_tools/bin/sky_tools.dart | 47 ++++++ packages/flutter_tools/lib/src/common.dart | 27 ++++ packages/flutter_tools/lib/src/init.dart | 162 +++++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 7 + packages/flutter_tools/test/init_test.dart | 53 +++++++ packages/flutter_tools/tool/travis.sh | 3 +- 6 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_tools/bin/sky_tools.dart create mode 100644 packages/flutter_tools/lib/src/common.dart create mode 100644 packages/flutter_tools/lib/src/init.dart create mode 100644 packages/flutter_tools/test/init_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart new file mode 100644 index 0000000000..c0771249ed --- /dev/null +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -0,0 +1,47 @@ +// 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:io'; + +import 'package:args/args.dart'; +import 'package:sky_tools/src/common.dart'; +import 'package:sky_tools/src/init.dart'; + +void main(List args) { + Map handlers = {}; + + ArgParser parser = new ArgParser(); + parser.addSeparator('options:'); + parser.addFlag('help', + abbr: 'h', negatable: false, help: 'Display this help message.'); + parser.addSeparator('commands:'); + + CommandHandler handler = new InitCommandHandler(); + parser.addCommand(handler.name, handler.parser); + handlers[handler.name] = handler; + + ArgResults results = parser.parse(args); + + if (results['help']) { + _printUsage(parser, handlers); + } else if (results.command != null) { + handlers[results.command.name].processArgResults(results.command); + } else { + _printUsage(parser, handlers, 'No command specified.'); + exit(1); + } +} + +void _printUsage(ArgParser parser, Map handlers, + [String message]) { + if (message != null) { + print('${message}\n'); + } + print('usage: sky_tools [arguments]'); + print(''); + print(parser.usage); + handlers.forEach((String command, CommandHandler handler) { + print(' ${command.padRight(10)} ${handler.description}'); + }); +} diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart new file mode 100644 index 0000000000..ef5d5fc078 --- /dev/null +++ b/packages/flutter_tools/lib/src/common.dart @@ -0,0 +1,27 @@ +// 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 'package:args/args.dart'; + +abstract class CommandHandler { + final String name; + final String description; + + CommandHandler(this.name, this.description); + + ArgParser get parser; + + void processArgResults(ArgResults results); + + void printUsage([String message]) { + if (message != null) { + print('${message}\n'); + } + print('usage: sky_tools ${name} [arguments]'); + print(''); + print(parser.usage); + } + + String toString() => name; +} diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart new file mode 100644 index 0000000000..f331bce192 --- /dev/null +++ b/packages/flutter_tools/lib/src/init.dart @@ -0,0 +1,162 @@ +// 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. + +library sky_tools.init; + +import 'dart:async'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:path/path.dart' as p; + +import 'common.dart'; + +class InitCommandHandler extends CommandHandler { + InitCommandHandler() : super('init', 'Create a new sky project.'); + + ArgParser get parser { + ArgParser parser = new ArgParser(); + parser.addFlag('help', + abbr: 'h', negatable: false, help: 'Display this help message.'); + // parser.addOption('template', + // abbr: 't', + // help: "The template to generate; either sky-simple or sky-full."); + parser.addOption('out', abbr: 'o', help: 'The output directory.'); + parser.addFlag('pub', defaultsTo: true, + help: 'Whether to run pub after the project has been created.'); + return parser; + } + + Future processArgResults(ArgResults results) async { + if (results['help']) { + printUsage(); + return new Future.value(); + } + + if (!results.wasParsed('out')) { + printUsage('No option specified for the output directory.'); + return new Future.value(); + } + + Directory out = new Directory(results['out']); + + // TODO: confirm overwrite with user + //if (out.existsSync()) + + new SkySimpleTemplate().generateInto(out); + + print(''); + + String message = 'All done! To run your application:\n' + 'cd ${out.path}\n' + './packages/sky/sky_tool start'; + + if (results['pub']) { + print("Running pub get..."); + Process process = await Process.start('pub', ['get'], workingDirectory: out.path); + stdout.addStream(process.stdout); + stderr.addStream(process.stderr); + int code = await process.exitCode; + if (code == 0) { + print('\n${message}'); + } + } else { + print(message); + } + } +} + +abstract class Template { + final String name; + final String description; + + Map files = {}; + + Template(this.name, this.description); + + void generateInto(Directory dir) { + String projectName = _normalizeProjectName(p.basename(dir.path)); + print('Creating ${p.basename(projectName)}...'); + dir.createSync(recursive: true); + + files.forEach((String path, String contents) { + contents = contents + .replaceAll('{{projectName}}', projectName) + .replaceAll('{{description}}', description); + File file = new File(p.join(dir.path, path)); + file.parent.createSync(); + file.writeAsStringSync(contents); + print(file.path); + }); + } + + String toString() => name; +} + +class SkySimpleTemplate extends Template { + SkySimpleTemplate() : super('sky-simple', 'A minimal Sky project.') { + files['.gitignore']= _gitignore; + files['pubspec.yaml']= _pubspec; + files['README.md']= _readme; + files['lib/main.dart']= _libMain; + } +} + +String _normalizeProjectName(String name) { + name = name.replaceAll('-', '_').replaceAll(' ', '_'); + // Strip any extension (like .dart). + if (name.contains('.')) { + name = name.substring(0, name.indexOf('.')); + } + return name; +} + +const _gitignore = r''' +.DS_Store +.idea +.packages +.pub/ +build/ +packages +pubspec.lock +'''; + +const _readme = r''' +# {{projectName}} + +{{description}} + +## Getting Started + +For help getting started, view our online +[readme](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md). +'''; + +const _pubspec = r''' +name: {{projectName}} +description: {{description}} +dependencies: + sky: any + sky_tools: any +'''; + +const _libMain = r''' +import 'package:sky/widgets.dart'; + +class HelloWorldApp extends App { + Widget build() { + return new Scaffold( + toolbar: new ToolBar(center: new Text("Demo")), + body: new Material(child: new Center(child: new Text("Hello world!"))), + floatingActionButton: new FloatingActionButton( + child: new Icon(type: 'content/add', size: 24) + ) + ); + } +} + +void main() { + runApp(new HelloWorldApp()); +} +'''; diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 2d84548475..8915f9eeb6 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -9,5 +9,12 @@ environment: dependencies: args: ^0.13.0 + path: ^1.3.0 shelf: ^0.6.2 shelf_static: ^0.2.3 + +dev_dependencies: + test: ^0.12.0 + +executable: + sky_tools: diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart new file mode 100644 index 0000000000..66340eb390 --- /dev/null +++ b/packages/flutter_tools/test/init_test.dart @@ -0,0 +1,53 @@ +// 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:io'; + +import 'package:args/args.dart'; +import 'package:path/path.dart' as p; +import 'package:sky_tools/src/init.dart'; +import 'package:test/test.dart'; + +main() => defineTests(); + +defineTests() { + group('', () { + Directory temp; + + setUp(() { + temp = Directory.systemTemp.createTempSync('sky_tools'); + }); + + tearDown(() { + temp.deleteSync(recursive: true); + }); + + // Verify that we create a project that os well-formed. + test('init sky-simple', () async { + InitCommandHandler handler = new InitCommandHandler(); + _MockArgResults results = new _MockArgResults(); + results.values['help'] = false; + results.values['pub'] = true; + results.values['out'] = temp.path; + await handler.processArgResults(results); + String path = p.join(temp.path, 'lib/main.dart'); + print(path); + expect(new File(path).existsSync(), true); + ProcessResult exec = Process.runSync('dartanalyzer', [path], + workingDirectory: temp.path); + expect(exec.exitCode, 0); + }); + }); +} + +class _MockArgResults implements ArgResults { + Map values = {}; + operator [](String name) => values[name]; + List get arguments => null; + ArgResults get command => null; + String get name => null; + Iterable get options => values.keys; + List get rest => null; + bool wasParsed(String name) => values.containsKey(name); +} diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index f7b4013815..c435e02632 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -10,4 +10,5 @@ set -e # Verify that the libraries are error free. dartanalyzer --fatal-warnings \ bin/build_sky_apk.dart \ - bin/sky_server.dart + bin/sky_server.dart \ + bin/sky_tools.dart From b114623b1a90afb74337dde32ec4c715ffa4ecf1 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 7 Aug 2015 14:26:50 -0700 Subject: [PATCH 024/188] fix typo --- packages/flutter_tools/test/init_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 66340eb390..08d8fb22f1 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -23,7 +23,7 @@ defineTests() { temp.deleteSync(recursive: true); }); - // Verify that we create a project that os well-formed. + // Verify that we create a project that is well-formed. test('init sky-simple', () async { InitCommandHandler handler = new InitCommandHandler(); _MockArgResults results = new _MockArgResults(); From 987ce972d65ddaabea695f1989f51dfc8ef179d4 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 7 Aug 2015 16:42:10 -0700 Subject: [PATCH 025/188] use mustache4dart --- packages/flutter_tools/lib/src/init.dart | 27 +++++++++++------------- packages/flutter_tools/pubspec.yaml | 1 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index f331bce192..383961387b 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:io'; import 'package:args/args.dart'; +import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; import 'common.dart'; @@ -16,12 +17,10 @@ class InitCommandHandler extends CommandHandler { InitCommandHandler() : super('init', 'Create a new sky project.'); ArgParser get parser { + // TODO: Add a --template option for template selection when we have more than one. ArgParser parser = new ArgParser(); parser.addFlag('help', abbr: 'h', negatable: false, help: 'Display this help message.'); - // parser.addOption('template', - // abbr: 't', - // help: "The template to generate; either sky-simple or sky-full."); parser.addOption('out', abbr: 'o', help: 'The output directory.'); parser.addFlag('pub', defaultsTo: true, help: 'Whether to run pub after the project has been created.'); @@ -39,11 +38,9 @@ class InitCommandHandler extends CommandHandler { return new Future.value(); } + // TODO: Confirm overwrite of an existing directory with the user. Directory out = new Directory(results['out']); - // TODO: confirm overwrite with user - //if (out.existsSync()) - new SkySimpleTemplate().generateInto(out); print(''); @@ -81,9 +78,11 @@ abstract class Template { dir.createSync(recursive: true); files.forEach((String path, String contents) { - contents = contents - .replaceAll('{{projectName}}', projectName) - .replaceAll('{{description}}', description); + Map m = { + 'projectName': projectName, + 'description': description + }; + contents = mustache.render(contents, m); File file = new File(p.join(dir.path, path)); file.parent.createSync(); file.writeAsStringSync(contents); @@ -147,12 +146,10 @@ import 'package:sky/widgets.dart'; class HelloWorldApp extends App { Widget build() { return new Scaffold( - toolbar: new ToolBar(center: new Text("Demo")), - body: new Material(child: new Center(child: new Text("Hello world!"))), - floatingActionButton: new FloatingActionButton( - child: new Icon(type: 'content/add', size: 24) - ) - ); + toolbar: new ToolBar(center: new Text("Demo")), + body: new Material(child: new Center(child: new Text("Hello world!"))), + floatingActionButton: new FloatingActionButton( + child: new Icon(type: 'content/add', size: 24))); } } diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 6614022aaf..6fe0a875b8 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: args: ^0.13.0 + mustache4dart: ^1.0.0 path: ^1.3.0 shelf: ^0.6.2 shelf_route: ^0.13.4 From fd21db9436f21cbaf6e8e1cfa5e4d42929d1eee6 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 8 Aug 2015 04:05:32 -0700 Subject: [PATCH 026/188] add a changelog --- packages/flutter_tools/README.md | 45 +++++++++++++++++++ packages/flutter_tools/bin/build_sky_apk.dart | 7 +++ packages/flutter_tools/bin/sky_server.dart | 4 +- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index e253de5754..b91c7dc7c9 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -3,3 +3,48 @@ [![Build Status](https://travis-ci.org/domokit/sky_tools.svg)](https://travis-ci.org/domokit/sky_tools) Tools for building Sky applications. + +## Installing + +To install, run: + + pub global activate sky_tools + +or, depend on this package in your pubspec: + +```yaml +dependencies: + sky_tools: any +``` + +## Running sky_tools + +Run `sky_tools` (or `pub global run sky_tools`) to see a list of available +commands. + +- *init*: create a new project + +Then run a sky_tools command, like `init`: + + sky_tools init --out my_sky_project + +## Running sky_tools:sky_server + +To serve the current directory using `sky_server`: + + pub run sky_tools:sky_server [-v] PORT + +## Running sky_tools:build_sky_apk + +``` +usage: pub run sky_tools:build_sky_apk + +-h, --help + --android-sdk + --skyx +``` + +## Filing Issues + +Please file reports on the +[GitHub Issue Tracker](https://github.com/domokit/sky_tools/issues). diff --git a/packages/flutter_tools/bin/build_sky_apk.dart b/packages/flutter_tools/bin/build_sky_apk.dart index 4d943e253f..b0f5086703 100644 --- a/packages/flutter_tools/bin/build_sky_apk.dart +++ b/packages/flutter_tools/bin/build_sky_apk.dart @@ -93,6 +93,13 @@ main(List argv) async { ArgResults args = parser.parse(argv); + if (args['help']) { + print('usage: pub run sky_tools:build_sky_apk '); + print(''); + print(parser.usage); + return; + } + Directory artifacts = new Directory('artifacts'); File keystore = new File('${artifacts.path}/chromium-debug.keystore'); File androidManifest = new File('${artifacts.path}/AndroidManifest.xml'); diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 795680ef37..5a2ea1ad48 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -70,9 +70,11 @@ main(List argv) async { HttpServer server; try { server = await io.serve(handler, InternetAddress.LOOPBACK_IP_V4, port); + print('Serving ${Directory.current.absolute.path} from ' + 'http://${server.address.address}:${server.port}.'); } catch(e) { print(e); - return; + exit(1); } server.defaultResponseHeaders From 52f78d4fe4a860f41f7f48c7c4f994843ccbb3de Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 8 Aug 2015 04:09:10 -0700 Subject: [PATCH 027/188] run tests on the bot --- packages/flutter_tools/tool/travis.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index c435e02632..945a36b9d5 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -12,3 +12,6 @@ dartanalyzer --fatal-warnings \ bin/build_sky_apk.dart \ bin/sky_server.dart \ bin/sky_tools.dart + +# And run our tests. +pub run test From a03deafac8b20710023a37bc5f0d94fc5627477a Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sun, 9 Aug 2015 23:50:16 -0700 Subject: [PATCH 028/188] update the sky_tools template and readme --- packages/flutter_tools/README.md | 6 ++--- packages/flutter_tools/bin/sky_tools.dart | 9 +++++++- packages/flutter_tools/lib/src/init.dart | 26 +++++++++++++--------- packages/flutter_tools/test/init_test.dart | 4 ++-- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index b91c7dc7c9..51ff9fb1d3 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -20,11 +20,11 @@ dependencies: ## Running sky_tools Run `sky_tools` (or `pub global run sky_tools`) to see a list of available -commands. +commands: -- *init*: create a new project +- `init` to create a new project -Then run a sky_tools command, like `init`: +Then, run a `sky_tools` command: sky_tools init --out my_sky_project diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index c0771249ed..e5b165af1d 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -21,7 +21,14 @@ void main(List args) { parser.addCommand(handler.name, handler.parser); handlers[handler.name] = handler; - ArgResults results = parser.parse(args); + ArgResults results; + + try { + results = parser.parse(args); + } catch (e) { + _printUsage(parser, handlers, e is FormatException ? e.message : '${e}'); + exit(1); + } if (results['help']) { _printUsage(parser, handlers); diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 383961387b..dd8ce41de3 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -45,9 +45,16 @@ class InitCommandHandler extends CommandHandler { print(''); - String message = 'All done! To run your application:\n' - 'cd ${out.path}\n' - './packages/sky/sky_tool start'; + String message = '''All done! To run your application: + + cd ${out.path} + ./packages/sky/sky_tool start + +Or if the Sky APK is not already on your device, run: + + ./packages/sky/sky_tool start --install + + '''; if (results['pub']) { print("Running pub get..."); @@ -128,8 +135,8 @@ const _readme = r''' ## Getting Started -For help getting started, view our online -[readme](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md). +For help getting started with Sky, view our online +[documentation](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md). '''; const _pubspec = r''' @@ -137,23 +144,20 @@ name: {{projectName}} description: {{description}} dependencies: sky: any - sky_tools: any '''; const _libMain = r''' import 'package:sky/widgets.dart'; +void main() => runApp(new HelloWorldApp()); + class HelloWorldApp extends App { Widget build() { return new Scaffold( - toolbar: new ToolBar(center: new Text("Demo")), + toolbar: new ToolBar(center: new Text("Sky Demo")), body: new Material(child: new Center(child: new Text("Hello world!"))), floatingActionButton: new FloatingActionButton( child: new Icon(type: 'content/add', size: 24))); } } - -void main() { - runApp(new HelloWorldApp()); -} '''; diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 08d8fb22f1..688e41ae83 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -32,9 +32,9 @@ defineTests() { results.values['out'] = temp.path; await handler.processArgResults(results); String path = p.join(temp.path, 'lib/main.dart'); - print(path); expect(new File(path).existsSync(), true); - ProcessResult exec = Process.runSync('dartanalyzer', [path], + ProcessResult exec = Process.runSync( + 'dartanalyzer', ['--fatal-warnings', path], workingDirectory: temp.path); expect(exec.exitCode, 0); }); From 1a80827dc89799071c3eb587c07b997b21ee70ce Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sun, 9 Aug 2015 23:54:15 -0700 Subject: [PATCH 029/188] bump the pubspec version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 6fe0a875b8..309b7bbe56 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.8 +version: 0.0.8+1 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From ef163af495a4ca6677f4ba04bfb7a52c0a23c98b Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 10 Aug 2015 17:22:05 -0700 Subject: [PATCH 030/188] fix a typo in the pubspec --- packages/flutter_tools/pubspec.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 309b7bbe56..1ca58b3c39 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.8+1 +version: 0.0.8+2 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors @@ -18,5 +18,6 @@ dependencies: dev_dependencies: test: ^0.12.0 -executable: +# Add the bin/sky_tools.dart script to the scripts pub installs. +executables: sky_tools: From 9b5bd5805ba485c3e71e28b6a50c6651111b70ef Mon Sep 17 00:00:00 2001 From: Eric Seidel Date: Mon, 10 Aug 2015 17:29:38 -0700 Subject: [PATCH 031/188] Add a dependency on sky_tools This is needed to make pub run sky_tools:sky_server work. Partial fix for https://github.com/domokit/sky_engine/issues/539 --- packages/flutter_tools/lib/src/init.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index dd8ce41de3..99de87e2e3 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -144,6 +144,7 @@ name: {{projectName}} description: {{description}} dependencies: sky: any + sky_tools: any '''; const _libMain = r''' From 83bff59a0ad8eaf9b02cbdc2ed2f9c33fdc298e5 Mon Sep 17 00:00:00 2001 From: Eric Seidel Date: Mon, 10 Aug 2015 17:41:40 -0700 Subject: [PATCH 032/188] Rev the pubspec version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 1ca58b3c39..2cea7271be 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.8+2 +version: 0.0.8+3 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From d0cec84e527c87537147d043bc642fe2fb392eaa Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 12 Aug 2015 09:06:55 -0700 Subject: [PATCH 033/188] Be more agressive about not caching with sky_server --- packages/flutter_tools/bin/sky_server.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 5a2ea1ad48..fd2c1ec708 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -81,5 +81,5 @@ main(List argv) async { ..removeAll('x-content-type-options') ..removeAll('x-frame-options') ..removeAll('x-xss-protection') - ..add('cache-control', 'no-cache'); + ..add('cache-control', 'no-store'); } From e98f8cc03d8718fba23b76cb725e4a02f54ad2e5 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 12 Aug 2015 09:09:00 -0700 Subject: [PATCH 034/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 2cea7271be..aa57a6bdd4 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.8+3 +version: 0.0.9 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From f7fa689da4c716d4d48edecf44e276eec6244351 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 13 Aug 2015 11:20:19 -0700 Subject: [PATCH 035/188] Add support for HEAD requests --- packages/flutter_tools/bin/sky_server.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index fd2c1ec708..486591a64f 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -18,7 +18,7 @@ void printUsage(parser) { void addRoute(var router, String route, String path) { router.add( route, - ['GET'], + ['GET', 'HEAD'], createStaticHandler( path, serveFilesOutsidePath: true, From f77983baa8eb3e0042491590c9dd5bcd49cd53ea Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 19 Aug 2015 23:12:00 -0700 Subject: [PATCH 036/188] Adds experimental `pub run sky_tools:sky_test` command This command uses package:test to run Dart tests with sky_shell. For this to work, we need https://github.com/dart-lang/test/tree/hacky-loader-hook to land. We're also not smart enough to find sky_shell ourselves yet. Instead, we take the path as input using an environment variable. Eventually, we'll be able to get the sky_shell executable from package:sky_engine, but we don't yet ship that executable. --- packages/flutter_tools/bin/sky_test.dart | 11 ++ .../lib/src/test/json_socket.dart | 19 +++ .../flutter_tools/lib/src/test/loader.dart | 131 +++++++++++++++ .../lib/src/test/remote_listener.dart | 153 ++++++++++++++++++ .../lib/src/test/remote_test.dart | 67 ++++++++ packages/flutter_tools/pubspec.yaml | 2 - 6 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_tools/bin/sky_test.dart create mode 100644 packages/flutter_tools/lib/src/test/json_socket.dart create mode 100644 packages/flutter_tools/lib/src/test/loader.dart create mode 100644 packages/flutter_tools/lib/src/test/remote_listener.dart create mode 100644 packages/flutter_tools/lib/src/test/remote_test.dart diff --git a/packages/flutter_tools/bin/sky_test.dart b/packages/flutter_tools/bin/sky_test.dart new file mode 100644 index 0000000000..30082368d6 --- /dev/null +++ b/packages/flutter_tools/bin/sky_test.dart @@ -0,0 +1,11 @@ +// 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 'package:test/src/executable.dart' as executable; +import 'package:sky_tools/src/test/loader.dart' as loader; + +main(List args) { + loader.installHook(); + return executable.main(args); +} diff --git a/packages/flutter_tools/lib/src/test/json_socket.dart b/packages/flutter_tools/lib/src/test/json_socket.dart new file mode 100644 index 0000000000..56ee44f61f --- /dev/null +++ b/packages/flutter_tools/lib/src/test/json_socket.dart @@ -0,0 +1,19 @@ +// 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 'dart:convert'; +import 'dart:io'; + +class JSONSocket { + JSONSocket(WebSocket socket) + : _socket = socket, stream = socket.map(JSON.decode).asBroadcastStream(); + + final WebSocket _socket; + final Stream stream; + + void send(dynamic data) { + _socket.add(JSON.encode(data)); + } +} diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart new file mode 100644 index 0000000000..793e9ef439 --- /dev/null +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -0,0 +1,131 @@ +// 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 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart' as p; +import 'package:sky_tools/src/test/json_socket.dart'; +import 'package:sky_tools/src/test/remote_test.dart'; +import 'package:stack_trace/stack_trace.dart'; +import 'package:test/src/backend/metadata.dart'; +import 'package:test/src/backend/test_platform.dart'; +import 'package:test/src/runner/configuration.dart'; +import 'package:test/src/runner/load_exception.dart'; +import 'package:test/src/runner/loader.dart'; +import 'package:test/src/runner/runner_suite.dart'; +import 'package:test/src/runner/vm/environment.dart'; +import 'package:test/src/util/io.dart'; +import 'package:test/src/util/remote_exception.dart'; + +void installHook() { + Loader.loadVMFileHook = _loadVMFile; +} + +final String _kSkyShell = Platform.environment['SKY_SHELL']; +const String _kHost = '127.0.0.1'; +const String _kPath = '/runner'; + +class _ServerInfo { + final String url; + final Future socket; + final HttpServer server; + + _ServerInfo(this.server, this.url, this.socket); +} + +Future<_ServerInfo> _createServer() async { + HttpServer server = await HttpServer.bind(_kHost, 0); + Completer socket = new Completer(); + server.listen((HttpRequest request) { + if (request.uri.path == _kPath) + socket.complete(WebSocketTransformer.upgrade(request)); + }); + return new _ServerInfo(server, 'ws://$_kHost:${server.port}$_kPath', socket.future); +} + +Future _startProcess(String path, { String packageRoot }) { + assert(_kSkyShell != null); // Please provide the path to the shell in the SKY_SHELL environment variable. + return Process.start(_kSkyShell, [ + '--enable-checked-mode', + '--non-interactive', + '--package-root=$packageRoot', + path, + ]); +} + +Future _loadVMFile(String path, + Metadata metadata, + Configuration config) async { + String encodedMetadata = Uri.encodeComponent(JSON.encode( + metadata.serialize())); + _ServerInfo info = await _createServer(); + Directory tempDir = await Directory.systemTemp.createTemp( + 'dart_test_listener'); + File listenerFile = new File('${tempDir.path}/listener.dart'); + await listenerFile.create(); + await listenerFile.writeAsString(''' +import 'dart:convert'; + +import 'package:test/src/backend/metadata.dart'; +import 'package:sky_tools/src/test/remote_listener.dart'; + +import '${p.toUri(p.absolute(path))}' as test; + +void main() { + String server = Uri.decodeComponent('${Uri.encodeComponent(info.url)}'); + Metadata metadata = new Metadata.deserialize( + JSON.decode(Uri.decodeComponent('$encodedMetadata'))); + RemoteListener.start(server, metadata, () => test.main); +} +'''); + + Process process = await _startProcess(listenerFile.path, + packageRoot: p.absolute(config.packageRoot)); + + JSONSocket socket = new JSONSocket(await info.socket); + + await tempDir.delete(recursive: true); + + void shutdown() { + process.kill(); + info.server.close(force: true); + } + + var completer = new Completer(); + + StreamSubscription subscription; + subscription = socket.stream.listen((response) { + if (response["type"] == "print") { + print(response["line"]); + } else if (response["type"] == "loadException") { + shutdown(); + completer.completeError( + new LoadException(path, response["message"]), + new Trace.current()); + } else if (response["type"] == "error") { + shutdown(); + var asyncError = RemoteException.deserialize(response["error"]); + completer.completeError( + new LoadException(path, asyncError.error), + asyncError.stackTrace); + } else { + assert(response["type"] == "success"); + subscription.cancel(); + completer.complete(response["tests"]); + } + }); + + return new RunnerSuite(const VMEnvironment(), + (await completer.future).map((test) { + var testMetadata = new Metadata.deserialize(test['metadata']); + return new RemoteTest(test['name'], testMetadata, socket, test['index']); + }), + metadata: metadata, + path: path, + platform: TestPlatform.vm, + os: currentOS, + onClose: shutdown); +} diff --git a/packages/flutter_tools/lib/src/test/remote_listener.dart b/packages/flutter_tools/lib/src/test/remote_listener.dart new file mode 100644 index 0000000000..838e21d5eb --- /dev/null +++ b/packages/flutter_tools/lib/src/test/remote_listener.dart @@ -0,0 +1,153 @@ +// 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 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:stack_trace/stack_trace.dart'; +import 'package:test/src/backend/declarer.dart'; +import 'package:test/src/backend/live_test.dart'; +import 'package:test/src/backend/metadata.dart'; +import 'package:test/src/backend/operating_system.dart'; +import 'package:test/src/backend/suite.dart'; +import 'package:test/src/backend/test_platform.dart'; +import 'package:test/src/backend/test.dart'; +import 'package:test/src/util/remote_exception.dart'; + +final OperatingSystem currentOS = (() { + var name = Platform.operatingSystem; + var os = OperatingSystem.findByIoName(name); + if (os != null) return os; + + throw new UnsupportedError('Unsupported operating system "$name".'); +})(); + +typedef AsyncFunction(); + +class RemoteListener { + final Suite _suite; + final WebSocket _socket; + LiveTest _liveTest; + + static Future start(String server, Metadata metadata, Function getMain()) async { + WebSocket socket = await WebSocket.connect(server); + // Capture any top-level errors (mostly lazy syntax errors, since other are + // caught below) and report them to the parent isolate. We set errors + // non-fatal because otherwise they'll be double-printed. + var errorPort = new ReceivePort(); + Isolate.current.setErrorsFatal(false); + Isolate.current.addErrorListener(errorPort.sendPort); + errorPort.listen((message) { + // Masquerade as an IsoalteSpawnException because that's what this would + // be if the error had been detected statically. + var error = new IsolateSpawnException(message[0]); + var stackTrace = + message[1] == null ? new Trace([]) : new Trace.parse(message[1]); + socket.add(JSON.encode({ + "type": "error", + "error": RemoteException.serialize(error, stackTrace) + })); + }); + + var main; + try { + main = getMain(); + } on NoSuchMethodError catch (_) { + _sendLoadException(socket, "No top-level main() function defined."); + return; + } + + if (main is! Function) { + _sendLoadException(socket, "Top-level main getter is not a function."); + return; + } else if (main is! AsyncFunction) { + _sendLoadException( + socket, "Top-level main() function takes arguments."); + return; + } + + var declarer = new Declarer(); + try { + await runZoned(() => new Future.sync(main), zoneValues: { + #test.declarer: declarer + }, zoneSpecification: new ZoneSpecification(print: (_, __, ___, line) { + socket.add(JSON.encode({"type": "print", "line": line})); + })); + } catch (error, stackTrace) { + socket.add(JSON.encode({ + "type": "error", + "error": RemoteException.serialize(error, stackTrace) + })); + return; + } + + Suite suite = new Suite(declarer.tests, + platform: TestPlatform.vm, os: currentOS, metadata: metadata); + new RemoteListener._(suite, socket)._listen(); + } + + static void _sendLoadException(WebSocket socket, String message) { + socket.add(JSON.encode({"type": "loadException", "message": message})); + } + + RemoteListener._(this._suite, this._socket); + + void _send(data) { + _socket.add(JSON.encode(data)); + } + + void _listen() { + List tests = []; + for (var i = 0; i < _suite.tests.length; i++) { + Test test = _suite.tests[i]; + tests.add({ + "name": test.name, + "metadata": test.metadata.serialize(), + "index": i, + }); + } + + _send({"type": "success", "tests": tests}); + _socket.listen(_handleCommand); + } + + void _handleCommand(String data) { + var message = JSON.decode(data); + if (message['command'] == 'run') { + assert(_liveTest == null); + Test test = _suite.tests[message['index']]; + _liveTest = test.load(_suite); + + _liveTest.onStateChange.listen((state) { + _send({ + "type": "state-change", + "status": state.status.name, + "result": state.result.name + }); + }); + + _liveTest.onError.listen((asyncError) { + _send({ + "type": "error", + "error": RemoteException.serialize( + asyncError.error, asyncError.stackTrace) + }); + }); + + _liveTest.onPrint.listen((line) { + _send({"type": "print", "line": line}); + }); + + _liveTest.run().then((_) { + _send({"type": "complete"}); + _liveTest = null; + }); + } else if (message['command'] == 'close') { + _liveTest.close(); + _liveTest = null; + } + } +} diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart new file mode 100644 index 0000000000..152c14375c --- /dev/null +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -0,0 +1,67 @@ +// 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 'package:test/src/backend/live_test.dart'; +import 'package:test/src/backend/live_test_controller.dart'; +import 'package:test/src/backend/metadata.dart'; +import 'package:test/src/backend/state.dart'; +import 'package:test/src/backend/suite.dart'; +import 'package:test/src/backend/test.dart'; +import 'package:test/src/util/remote_exception.dart'; + +import 'package:sky_tools/src/test/json_socket.dart'; + +class RemoteTest implements Test { + final String name; + final Metadata metadata; + + final JSONSocket _socket; + final int _index; + + RemoteTest(this.name, this.metadata, this._socket, this._index); + + LiveTest load(Suite suite) { + var controller; + var subscription; + + controller = new LiveTestController(suite, this, () { + controller.setState(const State(Status.running, Result.success)); + + _socket.send({'command': 'run', 'index': _index}); + + subscription = _socket.stream.listen((message) { + if (message['type'] == 'error') { + var asyncError = RemoteException.deserialize(message['error']); + controller.addError(asyncError.error, asyncError.stackTrace); + } else if (message['type'] == 'state-change') { + controller.setState( + new State( + new Status.parse(message['status']), + new Result.parse(message['result']))); + } else if (message['type'] == 'print') { + controller.print(message['line']); + } else { + assert(message['type'] == 'complete'); + subscription.cancel(); + subscription = null; + controller.completer.complete(); + } + }); + }, () { + _socket.send({'command': 'close'}); + if (subscription != null) { + subscription.cancel(); + subscription = null; + } + }); + return controller.liveTest; + } + + Test change({String name, Metadata metadata}) { + if (name == name && metadata == this.metadata) return this; + if (name == null) name = this.name; + if (metadata == null) metadata = this.metadata; + return new RemoteTest(name, metadata, _socket, _index); + } +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index aa57a6bdd4..75c3fed593 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -14,8 +14,6 @@ dependencies: shelf: ^0.6.2 shelf_route: ^0.13.4 shelf_static: ^0.2.3 - -dev_dependencies: test: ^0.12.0 # Add the bin/sky_tools.dart script to the scripts pub installs. From 01afe5a6126c0544a12af8b0b6a2e02206efc5ba Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 20 Aug 2015 19:46:48 -0700 Subject: [PATCH 037/188] Update loadVMFileHook to the real location --- packages/flutter_tools/lib/src/test/loader.dart | 4 ++-- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index 793e9ef439..6558256f65 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -14,14 +14,14 @@ import 'package:test/src/backend/metadata.dart'; import 'package:test/src/backend/test_platform.dart'; import 'package:test/src/runner/configuration.dart'; import 'package:test/src/runner/load_exception.dart'; -import 'package:test/src/runner/loader.dart'; import 'package:test/src/runner/runner_suite.dart'; +import 'package:test/src/runner/hack_load_vm_file_hook.dart' as hack; import 'package:test/src/runner/vm/environment.dart'; import 'package:test/src/util/io.dart'; import 'package:test/src/util/remote_exception.dart'; void installHook() { - Loader.loadVMFileHook = _loadVMFile; + hack.loadVMFileHook = _loadVMFile; } final String _kSkyShell = Platform.environment['SKY_SHELL']; diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 75c3fed593..78283bd2a0 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: shelf: ^0.6.2 shelf_route: ^0.13.4 shelf_static: ^0.2.3 - test: ^0.12.0 + test: ">=0.12.4+5 <0.12.5" # Add the bin/sky_tools.dart script to the scripts pub installs. executables: From f4ed42e55b63451242e793e61e6d3666831664f5 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 20 Aug 2015 20:58:18 -0700 Subject: [PATCH 038/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 78283bd2a0..995f1b9183 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.9 +version: 0.0.10 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 384ded5b9113c90b9f642793e94855eb984dd514 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Thu, 10 Sep 2015 14:40:25 -0700 Subject: [PATCH 039/188] Begin refactoring API around CommandHandlers and using it consistently in subclasses. Also applies autoreformatting to init.dart. --- packages/flutter_tools/lib/src/common.dart | 4 +++- packages/flutter_tools/lib/src/init.dart | 28 ++++++++++++---------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart index ef5d5fc078..9fc6d19f17 100644 --- a/packages/flutter_tools/lib/src/common.dart +++ b/packages/flutter_tools/lib/src/common.dart @@ -2,6 +2,8 @@ // 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:args/args.dart'; abstract class CommandHandler { @@ -12,7 +14,7 @@ abstract class CommandHandler { ArgParser get parser; - void processArgResults(ArgResults results); + Future processArgResults(ArgResults results); void printUsage([String message]) { if (message != null) { diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 99de87e2e3..884e0c9e9b 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -22,20 +22,22 @@ class InitCommandHandler extends CommandHandler { parser.addFlag('help', abbr: 'h', negatable: false, help: 'Display this help message.'); parser.addOption('out', abbr: 'o', help: 'The output directory.'); - parser.addFlag('pub', defaultsTo: true, + parser.addFlag('pub', + defaultsTo: true, help: 'Whether to run pub after the project has been created.'); return parser; } - Future processArgResults(ArgResults results) async { + @override + Future processArgResults(ArgResults results) async { if (results['help']) { printUsage(); - return new Future.value(); + return 0; } if (!results.wasParsed('out')) { printUsage('No option specified for the output directory.'); - return new Future.value(); + return 2; } // TODO: Confirm overwrite of an existing directory with the user. @@ -58,7 +60,8 @@ Or if the Sky APK is not already on your device, run: if (results['pub']) { print("Running pub get..."); - Process process = await Process.start('pub', ['get'], workingDirectory: out.path); + Process process = + await Process.start('pub', ['get'], workingDirectory: out.path); stdout.addStream(process.stdout); stderr.addStream(process.stderr); int code = await process.exitCode; @@ -67,7 +70,9 @@ Or if the Sky APK is not already on your device, run: } } else { print(message); + return 2; } + return 0; } } @@ -85,10 +90,7 @@ abstract class Template { dir.createSync(recursive: true); files.forEach((String path, String contents) { - Map m = { - 'projectName': projectName, - 'description': description - }; + Map m = {'projectName': projectName, 'description': description}; contents = mustache.render(contents, m); File file = new File(p.join(dir.path, path)); file.parent.createSync(); @@ -102,10 +104,10 @@ abstract class Template { class SkySimpleTemplate extends Template { SkySimpleTemplate() : super('sky-simple', 'A minimal Sky project.') { - files['.gitignore']= _gitignore; - files['pubspec.yaml']= _pubspec; - files['README.md']= _readme; - files['lib/main.dart']= _libMain; + files['.gitignore'] = _gitignore; + files['pubspec.yaml'] = _pubspec; + files['README.md'] = _readme; + files['lib/main.dart'] = _libMain; } } From 81746e980ca80422d46d84194108fbad99f107f6 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 11 Sep 2015 12:20:20 -0700 Subject: [PATCH 040/188] Initial sketch of install command and what devices look like. --- packages/flutter_tools/bin/sky_tools.dart | 20 ++++-- packages/flutter_tools/lib/src/common.dart | 1 + packages/flutter_tools/lib/src/device.dart | 67 +++++++++++++++++++++ packages/flutter_tools/lib/src/install.dart | 46 ++++++++++++++ 4 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 packages/flutter_tools/lib/src/device.dart create mode 100644 packages/flutter_tools/lib/src/install.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index e5b165af1d..db5217550d 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; +import 'package:sky_tools/src/install.dart'; void main(List args) { Map handlers = {}; @@ -17,9 +18,13 @@ void main(List args) { abbr: 'h', negatable: false, help: 'Display this help message.'); parser.addSeparator('commands:'); - CommandHandler handler = new InitCommandHandler(); - parser.addCommand(handler.name, handler.parser); - handlers[handler.name] = handler; + for (CommandHandler handler in [ + new InitCommandHandler(), + new InstallCommandHandler() + ]) { + parser.addCommand(handler.name, handler.parser); + handlers[handler.name] = handler; + } ArgResults results; @@ -33,7 +38,14 @@ void main(List args) { if (results['help']) { _printUsage(parser, handlers); } else if (results.command != null) { - handlers[results.command.name].processArgResults(results.command); + handlers[results.command.name] + .processArgResults(results.command) + .then((int code) => exit(code)) + .catchError((e, stack) { + print('Error running ' + results.command.name + ': $e'); + print(stack); + exit(2); + }); } else { _printUsage(parser, handlers, 'No command specified.'); exit(1); diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart index 9fc6d19f17..c3e1ba18c6 100644 --- a/packages/flutter_tools/lib/src/common.dart +++ b/packages/flutter_tools/lib/src/common.dart @@ -14,6 +14,7 @@ abstract class CommandHandler { ArgParser get parser; + /// @return 0 for no errors or warnings executing command, 1 for warnings, 2 for errors. Future processArgResults(ArgResults results); void printUsage([String message]) { diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart new file mode 100644 index 0000000000..3167abb456 --- /dev/null +++ b/packages/flutter_tools/lib/src/device.dart @@ -0,0 +1,67 @@ +// 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. + +library sky_tools.device; + +abstract class _Device { + final String id; + static Map _deviceCache = {}; + + factory _Device(String className, [String id = null]) { + if (id == null) { + if (className == AndroidDevice.className) { + id = AndroidDevice.defaultDeviceID; + } else { + throw 'Attempted to create a Device of unknown type $className'; + } + } + + return _deviceCache.putIfAbsent(id, () { + if (className == AndroidDevice.className) { + final device = new AndroidDevice._(id); + _deviceCache[id] = device; + return device; + } else { + throw 'Attempted to create a Device of unknown type $className'; + } + }); + } + + _Device._(this.id); + + /// Install an app package on the current device + bool installApp(String path); + + /// Check if the current device needs an installation + bool needsInstall(); + + /// Check if the device is currently connected + bool isConnected(); +} + +class AndroidDevice extends _Device { + static const String className = 'AndroidDevice'; + static final String defaultDeviceID = 'default'; + + factory AndroidDevice([String id = null]) { + return new _Device(className, id); + } + + AndroidDevice._(id) : super._(id); + + @override + bool installApp(String path) { + return false; + } + + @override + bool needsInstall() { + return true; + } + + @override + bool isConnected() { + return true; + } +} diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart new file mode 100644 index 0000000000..1a89deead2 --- /dev/null +++ b/packages/flutter_tools/lib/src/install.dart @@ -0,0 +1,46 @@ +// 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. + +library sky_tools.install; + +import 'dart:async'; + +import 'package:args/args.dart'; + +import 'common.dart'; +import 'device.dart'; + +class InstallCommandHandler extends CommandHandler { + InstallCommandHandler() + : super('install', 'Install your Sky app on attached devices.'); + + @override + ArgParser get parser { + ArgParser parser = new ArgParser(); + parser.addFlag('help', + abbr: 'h', negatable: false, help: 'Display this help message.'); + return parser; + } + + @override + Future processArgResults(ArgResults results) async { + if (results['help']) { + printUsage(); + return 0; + } + + bool installedSomewhere = false; + + AndroidDevice android = new AndroidDevice(); + if (android.isConnected()) { + installedSomewhere = installedSomewhere || android.installApp(''); + } + + if (installedSomewhere) { + return 0; + } else { + return 2; + } + } +} From ab441685e2c20f7e86401bc951c31bad3e655262 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 11 Sep 2015 15:00:02 -0700 Subject: [PATCH 041/188] Test install command --- packages/flutter_tools/lib/src/device.dart | 2 +- packages/flutter_tools/pubspec.yaml | 3 ++ packages/flutter_tools/test/install_test.dart | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_tools/test/install_test.dart diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 3167abb456..510e51ca4b 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -52,7 +52,7 @@ class AndroidDevice extends _Device { @override bool installApp(String path) { - return false; + return true; } @override diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 995f1b9183..c86a20a31f 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -16,6 +16,9 @@ dependencies: shelf_static: ^0.2.3 test: ">=0.12.4+5 <0.12.5" +dev_dependencies: + mockito: "^0.10.1" + # Add the bin/sky_tools.dart script to the scripts pub installs. executables: sky_tools: diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart new file mode 100644 index 0000000000..5e51c20946 --- /dev/null +++ b/packages/flutter_tools/test/install_test.dart @@ -0,0 +1,31 @@ +// 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. + +library install_test; + +import 'package:args/args.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/install.dart'; +import 'package:test/test.dart'; + +main() => defineTests(); + +defineTests() { + group('install', () { + test('install returns 0', () { + MockArgResults results = new MockArgResults(); + when(results['help']).thenReturn(false); + InstallCommandHandler handler = new InstallCommandHandler(); + handler + .processArgResults(results) + .then((int code) => expect(code, equals(0))); + }); + }); +} + +@proxy +class MockArgResults extends Mock implements ArgResults { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} From e6b45c5023fbd8fc7b8a8566b74a0604552903e8 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 11 Sep 2015 16:12:27 -0700 Subject: [PATCH 042/188] use mokito in the init tests --- packages/flutter_tools/lib/src/init.dart | 10 ++++---- packages/flutter_tools/test/init_test.dart | 24 +++++++------------ packages/flutter_tools/test/install_test.dart | 9 ++----- packages/flutter_tools/test/src/common.dart | 12 ++++++++++ 4 files changed, 26 insertions(+), 29 deletions(-) create mode 100644 packages/flutter_tools/test/src/common.dart diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 884e0c9e9b..21def3b427 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -65,13 +65,10 @@ Or if the Sky APK is not already on your device, run: stdout.addStream(process.stdout); stderr.addStream(process.stderr); int code = await process.exitCode; - if (code == 0) { - print('\n${message}'); - } - } else { - print(message); - return 2; + if (code != 0) return code; } + + print(message); return 0; } } @@ -146,6 +143,7 @@ name: {{projectName}} description: {{description}} dependencies: sky: any +dev_dependencies: sky_tools: any '''; diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 688e41ae83..85adba7b72 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -4,11 +4,13 @@ import 'dart:io'; -import 'package:args/args.dart'; +import 'package:mockito/mockito.dart'; import 'package:path/path.dart' as p; import 'package:sky_tools/src/init.dart'; import 'package:test/test.dart'; +import 'src/common.dart'; + main() => defineTests(); defineTests() { @@ -26,10 +28,11 @@ defineTests() { // Verify that we create a project that is well-formed. test('init sky-simple', () async { InitCommandHandler handler = new InitCommandHandler(); - _MockArgResults results = new _MockArgResults(); - results.values['help'] = false; - results.values['pub'] = true; - results.values['out'] = temp.path; + MockArgResults results = new MockArgResults(); + when(results['help']).thenReturn(false); + when(results['pub']).thenReturn(true); + when(results.wasParsed('out')).thenReturn(true); + when(results['out']).thenReturn(temp.path); await handler.processArgResults(results); String path = p.join(temp.path, 'lib/main.dart'); expect(new File(path).existsSync(), true); @@ -40,14 +43,3 @@ defineTests() { }); }); } - -class _MockArgResults implements ArgResults { - Map values = {}; - operator [](String name) => values[name]; - List get arguments => null; - ArgResults get command => null; - String get name => null; - Iterable get options => values.keys; - List get rest => null; - bool wasParsed(String name) => values.containsKey(name); -} diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index 5e51c20946..5343c8f94b 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -4,11 +4,12 @@ library install_test; -import 'package:args/args.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/install.dart'; import 'package:test/test.dart'; +import 'src/common.dart'; + main() => defineTests(); defineTests() { @@ -23,9 +24,3 @@ defineTests() { }); }); } - -@proxy -class MockArgResults extends Mock implements ArgResults { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart new file mode 100644 index 0000000000..1570ec0df2 --- /dev/null +++ b/packages/flutter_tools/test/src/common.dart @@ -0,0 +1,12 @@ +// 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 'package:args/args.dart'; +import 'package:mockito/mockito.dart'; + +@proxy +class MockArgResults extends Mock implements ArgResults { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} From 5678c12433dcbcd7e474a7aed4f827ff7b88bb58 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 15 Sep 2015 16:08:59 -0700 Subject: [PATCH 043/188] Add verbose flag to sky_tools and basic logging capabilities. --- packages/flutter_tools/bin/sky_tools.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index db5217550d..d79e402430 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -5,17 +5,27 @@ import 'dart:io'; import 'package:args/args.dart'; +import 'package:logging/logging.dart'; import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; void main(List args) { + Logger.root.level = Level.WARNING; + Logger.root.onRecord.listen((LogRecord rec) { + print('${rec.level.name}: ${rec.message}'); + }); + Map handlers = {}; ArgParser parser = new ArgParser(); parser.addSeparator('options:'); parser.addFlag('help', abbr: 'h', negatable: false, help: 'Display this help message.'); + parser.addFlag('verbose', + abbr: 'v', + negatable: false, + help: 'Noisy logging, including all shell commands executed.'); parser.addSeparator('commands:'); for (CommandHandler handler in [ @@ -35,6 +45,10 @@ void main(List args) { exit(1); } + if (results['verbose']) { + Logger.root.level = Level.INFO; + } + if (results['help']) { _printUsage(parser, handlers); } else if (results.command != null) { From b72d67a8fea84e1b508ee8319033147df155b1a5 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 15 Sep 2015 16:17:47 -0700 Subject: [PATCH 044/188] Private setup methods for AndroidDevice. --- packages/flutter_tools/bin/sky_tools.dart | 6 + packages/flutter_tools/lib/src/device.dart | 122 +++++++++++++++++- .../lib/src/process_wrapper.dart | 22 ++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_tools/lib/src/process_wrapper.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index d79e402430..efc0dcbbd9 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -14,6 +14,12 @@ void main(List args) { Logger.root.level = Level.WARNING; Logger.root.onRecord.listen((LogRecord rec) { print('${rec.level.name}: ${rec.message}'); + if (rec.error != null) { + print(rec.error); + } + if (rec.stackTrace != null) { + print(rec.stackTrace); + } }); Map handlers = {}; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 510e51ca4b..ae439341be 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -4,6 +4,15 @@ library sky_tools.device; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'process_wrapper.dart'; + +final Logger _logging = new Logger('sky_tools.device'); + abstract class _Device { final String id; static Map _deviceCache = {}; @@ -41,14 +50,28 @@ abstract class _Device { } class AndroidDevice extends _Device { + static const String _ADB_PATH = 'adb'; + static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default'; + String _adbPath; + String get adbPath => _adbPath; + factory AndroidDevice([String id = null]) { return new _Device(className, id); } - AndroidDevice._(id) : super._(id); + AndroidDevice._(id) : super._(id) { + _updatePaths(); + + // Checking for lollipop only needs to be done if we are starting an + // app, but it has an important side effect, which is to discard any + // progress messages if the adb server is restarted. + if (!_checkForAdb() || !_checkForLollipopOrLater()) { + _logging.severe('Unable to run on Android.'); + } + } @override bool installApp(String path) { @@ -64,4 +87,101 @@ class AndroidDevice extends _Device { bool isConnected() { return true; } + + void _updatePaths() { + if (Platform.environment.containsKey('ANDROID_HOME')) { + String androidHomeDir = Platform.environment['ANDROID_HOME']; + String adbPath1 = + path.join(androidHomeDir, 'sdk', 'platform-tools', 'adb'); + String adbPath2 = path.join(androidHomeDir, 'platform-tools', 'adb'); + if (FileSystemEntity.isFileSync(adbPath1)) { + _adbPath = adbPath1; + } else if (FileSystemEntity.isFileSync(adbPath2)) { + _adbPath = adbPath2; + } else { + _logging.info('"adb" not found at\n "$adbPath1" or\n "$adbPath2"\n' + + 'using default path "$_ADB_PATH"'); + _adbPath = _ADB_PATH; + } + } else { + _adbPath = _ADB_PATH; + } + } + + bool _isValidAdbVersion(String adbVersion) { + // Sample output: 'Android Debug Bridge version 1.0.31' + Match versionFields = + new RegExp(r'(\d+)\.(\d+)\.(\d+)').firstMatch(adbVersion); + if (versionFields != null) { + int majorVersion = int.parse(versionFields[1]); + int minorVersion = int.parse(versionFields[2]); + int patchVersion = int.parse(versionFields[3]); + if (majorVersion > 1) { + return true; + } + if (majorVersion == 1 && minorVersion > 0) { + return true; + } + if (majorVersion == 1 && minorVersion == 0 && patchVersion >= 32) { + return true; + } + return false; + } + _logging.warning( + 'Unrecognized adb version string $adbVersion. Skipping version check.'); + return true; + } + + bool _checkForAdb() { + try { + String adbVersion = runCheckedSync([adbPath, 'version']); + if (_isValidAdbVersion(adbVersion)) { + return true; + } + + String locatedAdbPath = runCheckedSync(['which', 'adb']); + _logging.severe('"$locatedAdbPath" is too old. ' + 'Please install version 1.0.32 or later.\n' + 'Try setting ANDROID_HOME to the path to your Android SDK install. ' + 'Android builds are unavailable.'); + } catch (e, stack) { + _logging.severe('"adb" not found in \$PATH. ' + 'Please install the Android SDK or set ANDROID_HOME ' + 'to the path of your Android SDK install.'); + _logging.info(e); + _logging.info(stack); + } + return false; + } + + bool _checkForLollipopOrLater() { + try { + // If the server is automatically restarted, then we get irrelevant + // output lines like this, which we want to ignore: + // adb server is out of date. killing.. + // * daemon started successfully * + runCheckedSync([adbPath, 'start-server']); + + // Sample output: '22' + String sdkVersion = + runCheckedSync([adbPath, 'shell', 'getprop', 'ro.build.version.sdk']) + .trimRight(); + + int sdkVersionParsed = + int.parse(sdkVersion, onError: (String source) => null); + if (sdkVersionParsed == null) { + _logging.severe('Unexpected response from getprop: "$sdkVersion"'); + return false; + } + if (sdkVersionParsed < 22) { + _logging.severe('Version "$sdkVersion" of the Android SDK is too old. ' + 'Please install Lollipop (version 22) or later.'); + return false; + } + return true; + } catch (e, stack) { + _logging.severe('Unexpected failure from adb: ', e, stack); + } + return false; + } } diff --git a/packages/flutter_tools/lib/src/process_wrapper.dart b/packages/flutter_tools/lib/src/process_wrapper.dart new file mode 100644 index 0000000000..4a17f3419d --- /dev/null +++ b/packages/flutter_tools/lib/src/process_wrapper.dart @@ -0,0 +1,22 @@ +// 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. + +library sky_tools.process_wrapper; + +import 'dart:io'; +import 'package:logging/logging.dart'; + +final Logger _logging = new Logger('sky_tools.process_wrapper'); +String runCheckedSync(List cmd) { + _logging.info(cmd.join(' ')); + ProcessResult results = + Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList()); + if (results.exitCode != 0) { + throw 'Error code ' + + results.exitCode.toString() + + ' returned when attempting to run command: ' + + cmd.join(' '); + } + return results.stdout; +} From d8d87f18336d46737b16b10aad02472546b1e658 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 16 Sep 2015 10:57:15 -0700 Subject: [PATCH 045/188] Add very-verbose flag and automatically log some more process-related things in verbose and very-verbose modes. --- packages/flutter_tools/bin/sky_tools.dart | 8 ++++++++ packages/flutter_tools/lib/src/process_wrapper.dart | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index efc0dcbbd9..2cd3e6f0f3 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -32,6 +32,10 @@ void main(List args) { abbr: 'v', negatable: false, help: 'Noisy logging, including all shell commands executed.'); + parser.addFlag('very-verbose', + negatable: false, + help: 'Very noisy logging, including the output of all ' + 'shell commands executed.'); parser.addSeparator('commands:'); for (CommandHandler handler in [ @@ -55,6 +59,10 @@ void main(List args) { Logger.root.level = Level.INFO; } + if (results['very-verbose']) { + Logger.root.level = Level.FINE; + } + if (results['help']) { _printUsage(parser, handlers); } else if (results.command != null) { diff --git a/packages/flutter_tools/lib/src/process_wrapper.dart b/packages/flutter_tools/lib/src/process_wrapper.dart index 4a17f3419d..3aaec4da75 100644 --- a/packages/flutter_tools/lib/src/process_wrapper.dart +++ b/packages/flutter_tools/lib/src/process_wrapper.dart @@ -13,10 +13,14 @@ String runCheckedSync(List cmd) { ProcessResult results = Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList()); if (results.exitCode != 0) { + if (results.stderr.length > 0) { + _logging.info('Errors logged: ' + results.stderr); + } throw 'Error code ' + results.exitCode.toString() + ' returned when attempting to run command: ' + cmd.join(' '); } + _logging.fine(results.stdout.trim()); return results.stdout; } From c5ea40980ade9f959dae29d29b482a8481884af8 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 16 Sep 2015 12:33:11 -0700 Subject: [PATCH 046/188] Most of the infrastructure needed to install an APK on Android. --- packages/flutter_tools/lib/src/device.dart | 112 ++++++++++++++---- packages/flutter_tools/lib/src/install.dart | 9 +- packages/flutter_tools/test/install_test.dart | 8 +- packages/flutter_tools/test/src/common.dart | 7 +- 4 files changed, 104 insertions(+), 32 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index ae439341be..80f5d6b07b 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -40,13 +40,13 @@ abstract class _Device { _Device._(this.id); /// Install an app package on the current device - bool installApp(String path); - - /// Check if the current device needs an installation - bool needsInstall(); + bool installApp(String appPath, String appPackageID, String appFileName); /// Check if the device is currently connected bool isConnected(); + + /// Check if the current version of the given app is already installed + bool isAppInstalled(String appPath, String appPackageID, String appFileName); } class AndroidDevice extends _Device { @@ -57,54 +57,44 @@ class AndroidDevice extends _Device { String _adbPath; String get adbPath => _adbPath; + bool _hasAdb = false; + bool _hasValidAndroid = false; factory AndroidDevice([String id = null]) { return new _Device(className, id); } AndroidDevice._(id) : super._(id) { - _updatePaths(); + _adbPath = _getAdbPath(); + _hasAdb = _checkForAdb(); // Checking for lollipop only needs to be done if we are starting an // app, but it has an important side effect, which is to discard any // progress messages if the adb server is restarted. - if (!_checkForAdb() || !_checkForLollipopOrLater()) { + _hasValidAndroid = _checkForLollipopOrLater(); + + if (!_hasAdb || !_hasValidAndroid) { _logging.severe('Unable to run on Android.'); } } - @override - bool installApp(String path) { - return true; - } - - @override - bool needsInstall() { - return true; - } - - @override - bool isConnected() { - return true; - } - - void _updatePaths() { + String _getAdbPath() { if (Platform.environment.containsKey('ANDROID_HOME')) { String androidHomeDir = Platform.environment['ANDROID_HOME']; String adbPath1 = path.join(androidHomeDir, 'sdk', 'platform-tools', 'adb'); String adbPath2 = path.join(androidHomeDir, 'platform-tools', 'adb'); if (FileSystemEntity.isFileSync(adbPath1)) { - _adbPath = adbPath1; + return adbPath1; } else if (FileSystemEntity.isFileSync(adbPath2)) { - _adbPath = adbPath2; + return adbPath2; } else { _logging.info('"adb" not found at\n "$adbPath1" or\n "$adbPath2"\n' + 'using default path "$_ADB_PATH"'); - _adbPath = _ADB_PATH; + return _ADB_PATH; } } else { - _adbPath = _ADB_PATH; + return _ADB_PATH; } } @@ -184,4 +174,74 @@ class AndroidDevice extends _Device { } return false; } + + String _getDeviceSha1Path(String appPackageID, String appFileName) { + return '/sdcard/$appPackageID/$appFileName.sha1'; + } + + String _getDeviceApkSha1(String appPackageID, String appFileName) { + return runCheckedSync([ + adbPath, + 'shell', + 'cat', + _getDeviceSha1Path(appPackageID, appFileName) + ]); + } + + String _getSourceSha1(String apkPath) { + String sha1 = + runCheckedSync(['shasum', '-a', '1', '-p', apkPath]).split(' ')[0]; + return sha1; + } + + @override + bool isAppInstalled(String appPath, String appPackageID, String appFileName) { + if (!isConnected()) { + return false; + } + if (runCheckedSync([adbPath, 'shell', 'pm', 'path', appPackageID]) == '') { + _logging.info( + 'TODO(iansf): move this log to the caller. $appFileName is not on the device. Installing now...'); + return false; + } + if (_getDeviceApkSha1(appPackageID, appFileName) != + _getSourceSha1(appPath)) { + _logging.info( + 'TODO(iansf): move this log to the caller. $appFileName is out of date. Installing now...'); + return false; + } + return true; + } + + @override + bool installApp(String appPath, String appPackageID, String appFileName) { + if (!isConnected()) { + _logging.info('Android device not connected. Not installing.'); + return false; + } + if (!FileSystemEntity.isFileSync(appPath)) { + _logging.severe('"$appPath" does not exist.'); + return false; + } + + runCheckedSync([adbPath, 'install', '-r', appPath]); + + Directory tempDir = Directory.systemTemp; + String sha1Path = path.join( + tempDir.path, appPath.replaceAll(path.separator, '_'), '.sha1'); + File sha1TempFile = new File(sha1Path); + sha1TempFile.writeAsStringSync(_getSourceSha1(appPath), flush: true); + runCheckedSync([ + adbPath, + 'push', + sha1Path, + _getDeviceSha1Path(appPackageID, appFileName) + ]); + sha1TempFile.deleteSync(); + return true; + } + + @override + bool isConnected() => _hasValidAndroid; + } } diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index 1a89deead2..d0b30d6ed7 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -12,7 +12,8 @@ import 'common.dart'; import 'device.dart'; class InstallCommandHandler extends CommandHandler { - InstallCommandHandler() + AndroidDevice android = null; + InstallCommandHandler([this.android]) : super('install', 'Install your Sky app on attached devices.'); @override @@ -32,9 +33,11 @@ class InstallCommandHandler extends CommandHandler { bool installedSomewhere = false; - AndroidDevice android = new AndroidDevice(); + if (android == null) { + android = new AndroidDevice(); + } if (android.isConnected()) { - installedSomewhere = installedSomewhere || android.installApp(''); + installedSomewhere = installedSomewhere || android.installApp('', '', ''); } if (installedSomewhere) { diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index 5343c8f94b..44151913c0 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -14,10 +14,14 @@ main() => defineTests(); defineTests() { group('install', () { - test('install returns 0', () { + test('returns 0 when Android is connected and ready for an install', () { + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(true); + when(android.installApp(any, any, any)).thenReturn(true); + InstallCommandHandler handler = new InstallCommandHandler(android); + MockArgResults results = new MockArgResults(); when(results['help']).thenReturn(false); - InstallCommandHandler handler = new InstallCommandHandler(); handler .processArgResults(results) .then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 1570ec0df2..575dd97391 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -4,9 +4,14 @@ import 'package:args/args.dart'; import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/device.dart'; -@proxy class MockArgResults extends Mock implements ArgResults { @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } + +class MockAndroidDevice extends Mock implements AndroidDevice { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} From 76a51409c5e962df84f9a74c47efbe99427d88b0 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 16 Sep 2015 14:35:09 -0700 Subject: [PATCH 047/188] Remove stray } This stray line was causing an analyzer error. --- packages/flutter_tools/lib/src/device.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 80f5d6b07b..55475555bb 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -243,5 +243,4 @@ class AndroidDevice extends _Device { @override bool isConnected() => _hasValidAndroid; - } } From f379a01946ad0d4637834886c6de13e368ec006e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 16 Sep 2015 16:43:28 -0700 Subject: [PATCH 048/188] Handle subprocess crashes during testing --- .../flutter_tools/lib/src/test/loader.dart | 73 ++++++++++++------- .../lib/src/test/remote_test.dart | 22 +++--- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index 6558256f65..fe755a6c29 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -82,42 +82,59 @@ void main() { } '''); + Completer completer = new Completer(); + Process process = await _startProcess(listenerFile.path, packageRoot: p.absolute(config.packageRoot)); - JSONSocket socket = new JSONSocket(await info.socket); - - await tempDir.delete(recursive: true); - - void shutdown() { - process.kill(); - info.server.close(force: true); + Future cleanupTempDirectory() async { + if (tempDir == null) + return; + Directory dirToDelete = tempDir; + tempDir = null; + await dirToDelete.delete(recursive: true); } - var completer = new Completer(); - - StreamSubscription subscription; - subscription = socket.stream.listen((response) { - if (response["type"] == "print") { - print(response["line"]); - } else if (response["type"] == "loadException") { - shutdown(); + process.exitCode.then((int exitCode) async { + info.server.close(force: true); + await cleanupTempDirectory(); + if (!completer.isCompleted) { + String error = await process.stderr.transform(UTF8.decoder).first; completer.completeError( - new LoadException(path, response["message"]), - new Trace.current()); - } else if (response["type"] == "error") { - shutdown(); - var asyncError = RemoteException.deserialize(response["error"]); - completer.completeError( - new LoadException(path, asyncError.error), - asyncError.stackTrace); - } else { - assert(response["type"] == "success"); - subscription.cancel(); - completer.complete(response["tests"]); + new LoadException(path, error), new Trace.current()); } }); + Future socket = (() async { + return new JSONSocket(await info.socket); + })(); + + socket.then((JSONSocket socket) async { + await cleanupTempDirectory(); + + StreamSubscription subscription; + subscription = socket.stream.listen((response) { + if (response["type"] == "print") { + print(response["line"]); + } else if (response["type"] == "loadException") { + process.kill(); + completer.completeError( + new LoadException(path, response["message"]), + new Trace.current()); + } else if (response["type"] == "error") { + process.kill(); + AsyncError asyncError = RemoteException.deserialize(response["error"]); + completer.completeError( + new LoadException(path, asyncError.error), + asyncError.stackTrace); + } else { + assert(response["type"] == "success"); + subscription.cancel(); + completer.complete(response["tests"]); + } + }); + }); + return new RunnerSuite(const VMEnvironment(), (await completer.future).map((test) { var testMetadata = new Metadata.deserialize(test['metadata']); @@ -127,5 +144,5 @@ void main() { path: path, platform: TestPlatform.vm, os: currentOS, - onClose: shutdown); + onClose: process.kill); } diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart index 152c14375c..a89471b74c 100644 --- a/packages/flutter_tools/lib/src/test/remote_test.dart +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -2,6 +2,8 @@ // 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:test/src/backend/live_test.dart'; import 'package:test/src/backend/live_test_controller.dart'; import 'package:test/src/backend/metadata.dart'; @@ -16,23 +18,24 @@ class RemoteTest implements Test { final String name; final Metadata metadata; - final JSONSocket _socket; + final Future _socket; final int _index; RemoteTest(this.name, this.metadata, this._socket, this._index); LiveTest load(Suite suite) { - var controller; - var subscription; + LiveTestController controller; + StreamSubscription subscription; - controller = new LiveTestController(suite, this, () { + controller = new LiveTestController(suite, this, () async { controller.setState(const State(Status.running, Result.success)); - _socket.send({'command': 'run', 'index': _index}); + JSONSocket socket = await _socket; + socket.send({'command': 'run', 'index': _index}); - subscription = _socket.stream.listen((message) { + subscription = socket.stream.listen((message) { if (message['type'] == 'error') { - var asyncError = RemoteException.deserialize(message['error']); + AsyncError asyncError = RemoteException.deserialize(message['error']); controller.addError(asyncError.error, asyncError.stackTrace); } else if (message['type'] == 'state-change') { controller.setState( @@ -48,8 +51,9 @@ class RemoteTest implements Test { controller.completer.complete(); } }); - }, () { - _socket.send({'command': 'close'}); + }, () async { + JSONSocket socket = await _socket; + socket.send({'command': 'close'}); if (subscription != null) { subscription.cancel(); subscription = null; From e8ef4704ea902f84ef587f49524cc239bbd1013c Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 16 Sep 2015 16:48:49 -0700 Subject: [PATCH 049/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index c86a20a31f..0cc90a5ece 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.10 +version: 0.0.11 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From cf41a63396c5ce34ed9f64c163e91699909e4522 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 17 Sep 2015 13:37:53 -0700 Subject: [PATCH 050/188] Add a build command to sky_tools This command will produce an flx package. Currently, this command doesn't work because we don't have the Flutter compiler downloaded from Google storage yet. A future patch will make that happen. --- packages/flutter_tools/bin/sky_tools.dart | 4 +- packages/flutter_tools/lib/src/artifacts.dart | 21 +++ packages/flutter_tools/lib/src/build.dart | 168 ++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 4 +- 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_tools/lib/src/artifacts.dart create mode 100644 packages/flutter_tools/lib/src/build.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 2cd3e6f0f3..ef4f5b18e2 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:logging/logging.dart'; +import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; @@ -39,8 +40,9 @@ void main(List args) { parser.addSeparator('commands:'); for (CommandHandler handler in [ + new BuildCommandHandler(), new InitCommandHandler(), - new InstallCommandHandler() + new InstallCommandHandler(), ]) { parser.addCommand(handler.name, handler.parser); handlers[handler.name] = handler; diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart new file mode 100644 index 0000000000..85e9e29655 --- /dev/null +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -0,0 +1,21 @@ +// 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 'dart:io'; + +enum Artifact { + FlutterCompiler +} + +class _ArtifactStore { + _ArtifactStore._(); + + Future getPath(Artifact artifact) async { + // TODO(abarth): Download artifacts from cloud storage. + return new File(''); + } +} + +final _ArtifactStore artifactStore = new _ArtifactStore._(); diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart new file mode 100644 index 0000000000..7e7eac00aa --- /dev/null +++ b/packages/flutter_tools/lib/src/build.dart @@ -0,0 +1,168 @@ +// 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:io'; +import 'dart:async'; + +import 'package:archive/archive.dart'; +import 'package:args/args.dart'; +import 'package:yaml/yaml.dart'; + +import 'common.dart'; +import 'artifacts.dart'; + +const String kSnapshotKey = 'snapshot_blob.bin'; +const List kDensities = const ['drawable-xxhdpi']; +const List kThemes = const ['white', 'black']; +const List kSizes = const [24]; + +class Asset { + final String base; + final String key; + + Asset({ this.base, this.key }); +} + +Iterable parseAssets(Map manifestDescriptor, String manifestPath) sync* { + if (manifestDescriptor == null || !manifestDescriptor.containsKey('assets')) + return; + String basePath = new File(manifestPath).parent.path; + for (String asset in manifestDescriptor['assets']) + yield new Asset(base: basePath, key: asset); +} + +class MaterialAsset { + final String name; + final String density; + final String theme; + final int size; + + MaterialAsset(Map descriptor) + : name = descriptor['name'], + density = descriptor['density'], + theme = descriptor['theme'], + size = descriptor['size']; + + String get key { + List parts = name.split('/'); + String category = parts[0]; + String subtype = parts[1]; + return '$category/$density/ic_${subtype}_${theme}_${size}dp.png'; + } +} + +List generateValues(Map assetDescriptor, String key, List defaults) { + if (assetDescriptor.containsKey(key)) + return [assetDescriptor[key]]; + return defaults; +} + +Iterable generateMaterialAssets(Map assetDescriptor) sync* { + Map currentAssetDescriptor = new Map.from(assetDescriptor); + for (String density in generateValues(assetDescriptor, 'density', kDensities)) { + currentAssetDescriptor['density'] = density; + for (String theme in generateValues(assetDescriptor, 'theme', kThemes)) { + currentAssetDescriptor['theme'] = theme; + for (int size in generateValues(assetDescriptor, 'size', kSizes)) { + currentAssetDescriptor['size'] = size; + yield new MaterialAsset(currentAssetDescriptor); + } + } + } +} + +Iterable parseMaterialAssets(Map manifestDescriptor) sync* { + if (manifestDescriptor == null || !manifestDescriptor.containsKey('material-design-icons')) + return; + for (Map assetDescriptor in manifestDescriptor['material-design-icons']) { + for (MaterialAsset asset in generateMaterialAssets(assetDescriptor)) { + yield asset; + } + } +} + +Future loadManifest(String manifestPath) async { + if (manifestPath == null) + return null; + String manifestDescriptor = await new File(manifestPath).readAsString(); + return loadYaml(manifestDescriptor); +} + +Future createFile(String key, String assetBase) async { + File file = new File('${assetBase}/${key}'); + if (!await file.exists()) + return null; + List content = await file.readAsBytes(); + return new ArchiveFile.noCompress(key, content.length, content); +} + +Future compileSnapshot({ + String mainPath, + String packageRoot, + String snapshotPath +}) async { + File compiler = await artifactStore.getPath(Artifact.FlutterCompiler); + await Process.run(compiler.path, [ + mainPath, + '--package-root=$packageRoot', + '--snapshot=$snapshotPath' + ]); +} + +Future createSnapshotFile(String snapshotPath) async { + File file = new File(snapshotPath); + List content = await file.readAsBytes(); + return new ArchiveFile(kSnapshotKey, content.length, content); +} + +class BuildCommandHandler extends CommandHandler { + BuildCommandHandler() : super('build', 'Create an Flutter package.'); + + ArgParser get parser { + ArgParser parser = new ArgParser(); + parser.addFlag('help', abbr: 'h', negatable: false); + parser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons'); + parser.addOption('main', defaultsTo: 'lib/main.dart'); + parser.addOption('manifest', defaultsTo: 'flutter.yaml'); + parser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); + parser.addOption('package-root', defaultsTo: 'packages'); + parser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin'); + return parser; + } + + @override + Future processArgResults(ArgResults results) async { + if (results['help']) { + print(parser.usage); + return 0; + } + + String manifestPath = results['manifest']; + Map manifestDescriptor = await loadManifest(manifestPath); + Iterable assets = parseAssets(manifestDescriptor, manifestPath); + Iterable materialAssets = parseMaterialAssets(manifestDescriptor); + + Archive archive = new Archive(); + + String snapshotPath = results['snapshot']; + await compileSnapshot( + mainPath: results['main'], + packageRoot: results['package-root'], + snapshotPath: snapshotPath); + 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, results['asset-base']); + if (file != null) + archive.addFile(file); + } + + File outputFile = new File(results['output-file']); + await outputFile.writeAsBytes(new ZipEncoder().encode(archive)); + return 0; + } +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 0cc90a5ece..8fc42a5adb 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -8,13 +8,15 @@ environment: sdk: ">=1.8.0 <2.0.0" dependencies: + archive: ^1.0.20 args: ^0.13.0 mustache4dart: ^1.0.0 path: ^1.3.0 - shelf: ^0.6.2 shelf_route: ^0.13.4 shelf_static: ^0.2.3 + shelf: ^0.6.2 test: ">=0.12.4+5 <0.12.5" + yaml: ^2.1.3 dev_dependencies: mockito: "^0.10.1" From a75e79f0ceb31bc91ffb12c9036f31f997d8653b Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 17 Sep 2015 14:02:02 -0700 Subject: [PATCH 051/188] Address review comments from @iansf --- packages/flutter_tools/lib/src/artifacts.dart | 2 + packages/flutter_tools/lib/src/build.dart | 72 ++++++++++--------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 85e9e29655..379724b175 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +library sky_tools.artifacts; + import 'dart:async'; import 'dart:io'; diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 7e7eac00aa..84c9a86752 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -2,43 +2,45 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; +library sky_tools.build; + import 'dart:async'; +import 'dart:io'; import 'package:archive/archive.dart'; import 'package:args/args.dart'; import 'package:yaml/yaml.dart'; -import 'common.dart'; import 'artifacts.dart'; +import 'common.dart'; -const String kSnapshotKey = 'snapshot_blob.bin'; -const List kDensities = const ['drawable-xxhdpi']; -const List kThemes = const ['white', 'black']; -const List kSizes = const [24]; +const String _kSnapshotKey = 'snapshot_blob.bin'; +const List _kDensities = const ['drawable-xxhdpi']; +const List _kThemes = const ['white', 'black']; +const List _kSizes = const [24]; -class Asset { +class _Asset { final String base; final String key; - Asset({ this.base, this.key }); + _Asset({ this.base, this.key }); } -Iterable parseAssets(Map manifestDescriptor, String manifestPath) sync* { +Iterable<_Asset> _parseAssets(Map manifestDescriptor, String manifestPath) sync* { if (manifestDescriptor == null || !manifestDescriptor.containsKey('assets')) return; String basePath = new File(manifestPath).parent.path; for (String asset in manifestDescriptor['assets']) - yield new Asset(base: basePath, key: asset); + yield new _Asset(base: basePath, key: asset); } -class MaterialAsset { +class _MaterialAsset { final String name; final String density; final String theme; final int size; - MaterialAsset(Map descriptor) + _MaterialAsset(Map descriptor) : name = descriptor['name'], density = descriptor['density'], theme = descriptor['theme'], @@ -52,44 +54,44 @@ class MaterialAsset { } } -List generateValues(Map assetDescriptor, String key, List defaults) { +List _generateValues(Map assetDescriptor, String key, List defaults) { if (assetDescriptor.containsKey(key)) return [assetDescriptor[key]]; return defaults; } -Iterable generateMaterialAssets(Map assetDescriptor) sync* { +Iterable<_MaterialAsset> _generateMaterialAssets(Map assetDescriptor) sync* { Map currentAssetDescriptor = new Map.from(assetDescriptor); - for (String density in generateValues(assetDescriptor, 'density', kDensities)) { + for (String density in _generateValues(assetDescriptor, 'density', _kDensities)) { currentAssetDescriptor['density'] = density; - for (String theme in generateValues(assetDescriptor, 'theme', kThemes)) { + for (String theme in _generateValues(assetDescriptor, 'theme', _kThemes)) { currentAssetDescriptor['theme'] = theme; - for (int size in generateValues(assetDescriptor, 'size', kSizes)) { + for (int size in _generateValues(assetDescriptor, 'size', _kSizes)) { currentAssetDescriptor['size'] = size; - yield new MaterialAsset(currentAssetDescriptor); + yield new _MaterialAsset(currentAssetDescriptor); } } } } -Iterable parseMaterialAssets(Map manifestDescriptor) sync* { +Iterable<_MaterialAsset> _parseMaterialAssets(Map manifestDescriptor) sync* { if (manifestDescriptor == null || !manifestDescriptor.containsKey('material-design-icons')) return; for (Map assetDescriptor in manifestDescriptor['material-design-icons']) { - for (MaterialAsset asset in generateMaterialAssets(assetDescriptor)) { + for (_MaterialAsset asset in _generateMaterialAssets(assetDescriptor)) { yield asset; } } } -Future loadManifest(String manifestPath) async { +Future _loadManifest(String manifestPath) async { if (manifestPath == null) return null; String manifestDescriptor = await new File(manifestPath).readAsString(); return loadYaml(manifestDescriptor); } -Future createFile(String key, String assetBase) async { +Future _createFile(String key, String assetBase) async { File file = new File('${assetBase}/${key}'); if (!await file.exists()) return null; @@ -97,7 +99,7 @@ Future createFile(String key, String assetBase) async { return new ArchiveFile.noCompress(key, content.length, content); } -Future compileSnapshot({ +Future _compileSnapshot({ String mainPath, String packageRoot, String snapshotPath @@ -110,14 +112,14 @@ Future compileSnapshot({ ]); } -Future createSnapshotFile(String snapshotPath) async { +Future _createSnapshotFile(String snapshotPath) async { File file = new File(snapshotPath); List content = await file.readAsBytes(); - return new ArchiveFile(kSnapshotKey, content.length, content); + return new ArchiveFile(_kSnapshotKey, content.length, content); } class BuildCommandHandler extends CommandHandler { - BuildCommandHandler() : super('build', 'Create an Flutter package.'); + BuildCommandHandler() : super('build', 'Create a Flutter package.'); ArgParser get parser { ArgParser parser = new ArgParser(); @@ -139,24 +141,24 @@ class BuildCommandHandler extends CommandHandler { } String manifestPath = results['manifest']; - Map manifestDescriptor = await loadManifest(manifestPath); - Iterable assets = parseAssets(manifestDescriptor, manifestPath); - Iterable materialAssets = parseMaterialAssets(manifestDescriptor); + Map manifestDescriptor = await _loadManifest(manifestPath); + Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath); + Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor); Archive archive = new Archive(); String snapshotPath = results['snapshot']; - await compileSnapshot( + await _compileSnapshot( mainPath: results['main'], packageRoot: results['package-root'], snapshotPath: snapshotPath); - archive.addFile(await createSnapshotFile(snapshotPath)); + archive.addFile(await _createSnapshotFile(snapshotPath)); - for (Asset asset in assets) - archive.addFile(await createFile(asset.key, asset.base)); + for (_Asset asset in assets) + archive.addFile(await _createFile(asset.key, asset.base)); - for (MaterialAsset asset in materialAssets) { - ArchiveFile file = await createFile(asset.key, results['asset-base']); + for (_MaterialAsset asset in materialAssets) { + ArchiveFile file = await _createFile(asset.key, results['asset-base']); if (file != null) archive.addFile(file); } From 3e9ceec0b1b5e2f64eacd0a5126526851201f25e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 17 Sep 2015 15:12:15 -0700 Subject: [PATCH 052/188] Improve sky_tools build so that it works from sky_engine With this patch, we can switch over to using this command from the build system in the sky_engine repo. --- packages/flutter_tools/lib/src/build.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 84c9a86752..f9368e5585 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -100,16 +100,24 @@ Future _createFile(String key, String assetBase) async { } Future _compileSnapshot({ + String compilerPath, String mainPath, String packageRoot, String snapshotPath }) async { - File compiler = await artifactStore.getPath(Artifact.FlutterCompiler); - await Process.run(compiler.path, [ + File compiler = compilerPath == null ? + await artifactStore.getPath(Artifact.FlutterCompiler) : + new File(compilerPath); + ProcessResult result = await Process.run(compiler.path, [ mainPath, '--package-root=$packageRoot', '--snapshot=$snapshotPath' ]); + if (result.exitCode != 0) { + print(result.stdout); + print(result.stderr); + exit(result.exitCode); + } } Future _createSnapshotFile(String snapshotPath) async { @@ -125,8 +133,9 @@ class BuildCommandHandler extends CommandHandler { ArgParser parser = new ArgParser(); parser.addFlag('help', abbr: 'h', negatable: false); parser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons'); + parser.addOption('compiler'); parser.addOption('main', defaultsTo: 'lib/main.dart'); - parser.addOption('manifest', defaultsTo: 'flutter.yaml'); + parser.addOption('manifest'); parser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); parser.addOption('package-root', defaultsTo: 'packages'); parser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin'); @@ -149,6 +158,7 @@ class BuildCommandHandler extends CommandHandler { String snapshotPath = results['snapshot']; await _compileSnapshot( + compilerPath: results['compiler'], mainPath: results['main'], packageRoot: results['package-root'], snapshotPath: snapshotPath); From 6626817c8626a4fef7f4cbafebbf2d46738ee325 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 17 Sep 2015 15:26:31 -0700 Subject: [PATCH 053/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 8fc42a5adb..61e8e62f80 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.11 +version: 0.0.12 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 0b2a946b237de1ee66ca87c0ee3f446696676d03 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 17 Sep 2015 15:27:30 -0700 Subject: [PATCH 054/188] Prepend mojo shebang to skyx files skyx files are zips, so they can have anything at the start. Having a shebang line at the start makes it easier to run skyx files in a mojo environment. --- packages/flutter_tools/lib/src/build.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index f9368e5585..55052ab60e 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -174,7 +174,8 @@ class BuildCommandHandler extends CommandHandler { } File outputFile = new File(results['output-file']); - await outputFile.writeAsBytes(new ZipEncoder().encode(archive)); + await outputFile.writeAsString('#!mojo mojo:sky_viewer\n') + await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND); return 0; } } From 670f14e0f83017a7f6f18f6844b1c2964bd3c224 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 18 Sep 2015 16:29:47 -0700 Subject: [PATCH 055/188] Add missing semicolon --- packages/flutter_tools/lib/src/build.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 55052ab60e..94bc04e329 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -174,7 +174,7 @@ class BuildCommandHandler extends CommandHandler { } File outputFile = new File(results['output-file']); - await outputFile.writeAsString('#!mojo mojo:sky_viewer\n') + await outputFile.writeAsString('#!mojo mojo:sky_viewer\n'); await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND); return 0; } From cbc35dfacbcb5cea8ab782bdd26006cc304de5c9 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 18 Sep 2015 14:29:25 -0700 Subject: [PATCH 056/188] Download sky_snapshot from the cloud This adds logic to download and use the sky_snapshot binary from Google cloud storage when running the 'sky_tools build' command. The downloaded binary is put into lib/cache/... The binary is chosen to match the REVISION in the sky_engine package in the packages directory of whichever package the user wishes to build a flx from. Known issues: *) Assumes linux-x64 host *) Assumes download will always produce valid executable *) No clearing of stale cache entries --- packages/flutter_tools/lib/src/artifacts.dart | 45 +++++++++++++++++-- packages/flutter_tools/lib/src/build.dart | 7 ++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 379724b175..6b91f47c18 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -8,15 +8,52 @@ import 'dart:async'; import 'dart:io'; enum Artifact { - FlutterCompiler + FlutterCompiler, } class _ArtifactStore { _ArtifactStore._(); - Future getPath(Artifact artifact) async { - // TODO(abarth): Download artifacts from cloud storage. - return new File(''); + Future _downloadFile(String url, File file) async { + HttpClient httpClient = new HttpClient(); + HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); + HttpClientResponse response = await request.close(); + if (response.statusCode != 200) throw new Exception(response.reasonPhrase); + IOSink sink = file.openWrite(); + await sink.addStream(response); + await sink.close(); + } + + Future _getEngineRevision(String packageRoot) { + return new File(packageRoot + '/sky_engine/REVISION').readAsString(); + } + + Future _cacheDir(String engineRevision, String packageRoot) async { + String cacheDirPath = '${packageRoot}/sky_tools/cache/sky_engine/${engineRevision}/'; + Directory cacheDir = new Directory(cacheDirPath); + if (!await cacheDir.exists()) { + await cacheDir.create(recursive: true); + } + return cacheDir; + } + + Future getPath(Artifact artifact, String packageRoot) async { + String engineRevision = await _getEngineRevision(packageRoot); + Directory cacheDir = await _cacheDir(engineRevision, packageRoot); + + if (artifact == Artifact.FlutterCompiler) { + File skySnapshotFile = new File(cacheDir.path + 'sky_snapshot'); + if (!await skySnapshotFile.exists()) { + print('Downloading sky_snapshot from the cloud, one moment please...'); + String googleStorageUrl = 'https://storage.googleapis.com/mojo/sky/shell/linux-x64/${engineRevision}/sky_snapshot'; + await _downloadFile(googleStorageUrl, skySnapshotFile); + ProcessResult result = await Process.run('chmod', ['u+x', skySnapshotFile.path]); + if (result.exitCode != 0) throw new Exception(result.stderr); + } + return skySnapshotFile.path; + } + + return ''; } } diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index f9368e5585..df39fcf0c0 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -105,10 +105,9 @@ Future _compileSnapshot({ String packageRoot, String snapshotPath }) async { - File compiler = compilerPath == null ? - await artifactStore.getPath(Artifact.FlutterCompiler) : - new File(compilerPath); - ProcessResult result = await Process.run(compiler.path, [ + if (compilerPath == null) + compilerPath = await artifactStore.getPath(Artifact.FlutterCompiler, packageRoot); + ProcessResult result = await Process.run(compilerPath, [ mainPath, '--package-root=$packageRoot', '--snapshot=$snapshotPath' From a320e712deffe29bea478509f783f25bbb31facc Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 18 Sep 2015 17:03:14 -0700 Subject: [PATCH 057/188] Address review feedback from pull request #30 --- packages/flutter_tools/lib/src/artifacts.dart | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 6b91f47c18..f67bf57e5f 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -7,6 +7,10 @@ library sky_tools.artifacts; import 'dart:async'; import 'dart:io'; +import 'package:logging/logging.dart'; + +final Logger _logging = new Logger('sky_tools.device'); + enum Artifact { FlutterCompiler, } @@ -15,13 +19,16 @@ class _ArtifactStore { _ArtifactStore._(); Future _downloadFile(String url, File file) async { - HttpClient httpClient = new HttpClient(); - HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); - HttpClientResponse response = await request.close(); - if (response.statusCode != 200) throw new Exception(response.reasonPhrase); - IOSink sink = file.openWrite(); - await sink.addStream(response); - await sink.close(); + _logging.fine('Downloading $url to ${file.path}'); + HttpClient httpClient = new HttpClient(); + HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); + HttpClientResponse response = await request.close(); + _logging.fine('Received response'); + if (response.statusCode != 200) throw new Exception(response.reasonPhrase); + IOSink sink = file.openWrite(); + await sink.addStream(response); + await sink.close(); + _logging.fine('Wrote file'); } Future _getEngineRevision(String packageRoot) { @@ -44,7 +51,7 @@ class _ArtifactStore { if (artifact == Artifact.FlutterCompiler) { File skySnapshotFile = new File(cacheDir.path + 'sky_snapshot'); if (!await skySnapshotFile.exists()) { - print('Downloading sky_snapshot from the cloud, one moment please...'); + _logging.info('Downloading sky_snapshot from the cloud, one moment please...'); String googleStorageUrl = 'https://storage.googleapis.com/mojo/sky/shell/linux-x64/${engineRevision}/sky_snapshot'; await _downloadFile(googleStorageUrl, skySnapshotFile); ProcessResult result = await Process.run('chmod', ['u+x', skySnapshotFile.path]); From a4ff100408a6962b5134c8bd075859e5f5378f63 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 18 Sep 2015 20:59:32 -0700 Subject: [PATCH 058/188] check all the dart code in the project, not just selected libraries --- packages/flutter_tools/lib/src/artifacts.dart | 2 +- packages/flutter_tools/tool/travis.sh | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index f67bf57e5f..c7a51b8bd7 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -45,7 +45,7 @@ class _ArtifactStore { } Future getPath(Artifact artifact, String packageRoot) async { - String engineRevision = await _getEngineRevision(packageRoot); + String engineRevision = await _getEngineRevision(packageRoot); Directory cacheDir = await _cacheDir(engineRevision, packageRoot); if (artifact == Artifact.FlutterCompiler) { diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index 945a36b9d5..a926214e6f 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -8,10 +8,8 @@ set -e # Verify that the libraries are error free. -dartanalyzer --fatal-warnings \ - bin/build_sky_apk.dart \ - bin/sky_server.dart \ - bin/sky_tools.dart +pub global activate tuneup +pub global run tuneup check # And run our tests. pub run test From 577f8e4a1e264540ad60343892bce4589b432a69 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sat, 19 Sep 2015 08:45:18 -0700 Subject: [PATCH 059/188] Update README.md s/domokit/flutter/ --- packages/flutter_tools/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 51ff9fb1d3..faf02236dc 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,6 +1,6 @@ # sky_tools -[![Build Status](https://travis-ci.org/domokit/sky_tools.svg)](https://travis-ci.org/domokit/sky_tools) +[![Build Status](https://travis-ci.org/flutter/sky_tools.svg)](https://travis-ci.org/flutter/sky_tools) Tools for building Sky applications. @@ -47,4 +47,4 @@ usage: pub run sky_tools:build_sky_apk ## Filing Issues Please file reports on the -[GitHub Issue Tracker](https://github.com/domokit/sky_tools/issues). +[GitHub Issue Tracker](https://github.com/flutter/sky_tools/issues). From fe5d511340e89127c265f9fc5deb8fcf9b24cdc1 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sat, 19 Sep 2015 08:50:26 -0700 Subject: [PATCH 060/188] Update README.md s/sky_tools/tools/ --- packages/flutter_tools/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index faf02236dc..e4bf33bc82 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,6 +1,6 @@ -# sky_tools +# tools -[![Build Status](https://travis-ci.org/flutter/sky_tools.svg)](https://travis-ci.org/flutter/sky_tools) +[![Build Status](https://travis-ci.org/flutter/tools.svg)](https://travis-ci.org/flutter/tools) Tools for building Sky applications. @@ -47,4 +47,4 @@ usage: pub run sky_tools:build_sky_apk ## Filing Issues Please file reports on the -[GitHub Issue Tracker](https://github.com/flutter/sky_tools/issues). +[GitHub Issue Tracker](https://github.com/flutter/tools/issues). From 88a5cf95d2a1b8c8c6fcd53d321ddebc58e96a1b Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 21 Sep 2015 11:04:40 -0700 Subject: [PATCH 061/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 61e8e62f80..9d6de13f57 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.12 +version: 0.0.13 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 542da8ccd5f3ec201e76c14b5c5a442e798ff0af Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 18 Sep 2015 11:10:48 -0700 Subject: [PATCH 062/188] Adds a run_mojo command that downloads sky_viewer.mojo and runs an flx in it This initial version assumes the developer has mojo_shell and all other services sitting on disk somewhere and that they're on linux and only want to run on linux. This can be generalized down the line to support more use cases. This downloads the sky_viewer.mojo corresponding to the packages/sky_engine/REVISION in the developer's directory, so they can specify whatever revision they want. sky_tools run_mojo downloads sky_viewer.mojo into its cache directory if it is not present and constructs a command line to pass to mojo_shell that maps the shebang stamped into the flx to the downloaded sky_viewer.mojo. Since sky_viewer.mojo lives in the cloud and mojo_shell can load from the cloud this could also map to an https URL. This should likely be an option. --- packages/flutter_tools/bin/sky_tools.dart | 2 + packages/flutter_tools/lib/src/artifacts.dart | 45 ++++++++---- packages/flutter_tools/lib/src/run_mojo.dart | 70 +++++++++++++++++++ 3 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 packages/flutter_tools/lib/src/run_mojo.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index ef4f5b18e2..c4275ef8c1 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -10,6 +10,7 @@ import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/run_mojo.dart'; void main(List args) { Logger.root.level = Level.WARNING; @@ -43,6 +44,7 @@ void main(List args) { new BuildCommandHandler(), new InitCommandHandler(), new InstallCommandHandler(), + new RunMojoCommandHandler(), ]) { parser.addCommand(handler.name, handler.parser); handlers[handler.name] = handler; diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index c7a51b8bd7..29420f679e 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -11,13 +11,16 @@ import 'package:logging/logging.dart'; final Logger _logging = new Logger('sky_tools.device'); -enum Artifact { - FlutterCompiler, -} +enum Artifact { FlutterCompiler, SkyViewerMojo, } class _ArtifactStore { _ArtifactStore._(); + // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 + String _googleStorageUrl(String category, String name, String engineRevision) { + return 'https://storage.googleapis.com/mojo/sky/${category}/linux-x64/${engineRevision}/${name}'; + } + Future _downloadFile(String url, File file) async { _logging.fine('Downloading $url to ${file.path}'); HttpClient httpClient = new HttpClient(); @@ -44,23 +47,39 @@ class _ArtifactStore { return cacheDir; } + // Whether the artifact needs to be marked as executable on disk. + bool _needsToBeExecutable(Artifact artifact) { + return artifact == Artifact.FlutterCompiler; + } + Future getPath(Artifact artifact, String packageRoot) async { String engineRevision = await _getEngineRevision(packageRoot); Directory cacheDir = await _cacheDir(engineRevision, packageRoot); + String category, name; + if (artifact == Artifact.FlutterCompiler) { - File skySnapshotFile = new File(cacheDir.path + 'sky_snapshot'); - if (!await skySnapshotFile.exists()) { - _logging.info('Downloading sky_snapshot from the cloud, one moment please...'); - String googleStorageUrl = 'https://storage.googleapis.com/mojo/sky/shell/linux-x64/${engineRevision}/sky_snapshot'; - await _downloadFile(googleStorageUrl, skySnapshotFile); - ProcessResult result = await Process.run('chmod', ['u+x', skySnapshotFile.path]); - if (result.exitCode != 0) throw new Exception(result.stderr); - } - return skySnapshotFile.path; + category = 'shell'; + name = 'sky_snapshot'; + } else if (artifact == Artifact.SkyViewerMojo) { + category = 'viewer'; + name = 'sky_viewer.mojo'; + } else { + // Unknown artifact. + return ''; } - return ''; + File cachedFile = new File(cacheDir.path + name); + if (!await cachedFile.exists()) { + _logging.info('Downloading ${name} from the cloud, one moment please...'); + String googleStorageUrl = _googleStorageUrl(category, name, engineRevision); + await _downloadFile(googleStorageUrl, cachedFile); + if (_needsToBeExecutable(artifact)) { + ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]); + if (result.exitCode != 0) throw new Exception(result.stderr); + } + } + return cachedFile.path; } } diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart new file mode 100644 index 0000000000..d396c71476 --- /dev/null +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -0,0 +1,70 @@ +// 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. + +library sky_tools.run_mojo; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts.dart'; +import 'common.dart'; + +final Logger _logging = new Logger('sky_tools.run_mojo'); + +class RunMojoCommandHandler extends CommandHandler { + RunMojoCommandHandler() : super('run_mojo', 'Run a Flutter app in mojo.'); + + ArgParser get parser { + ArgParser parser = new ArgParser(); + parser.addFlag('help', abbr: 'h', negatable: false); + parser.addOption('package-root', defaultsTo: 'packages'); + parser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); + parser.addOption('app', defaultsTo: 'app.flx'); + return parser; + } + + Future _makePathAbsolute(String relativePath) async { + File file = new File(relativePath); + if (!await file.exists()) { + throw new Exception("Path \"${relativePath}\" does not exist"); + } + return file.absolute.path; + } + + @override + Future processArgResults(ArgResults results) async { + if (results['help']) { + print(parser.usage); + return 0; + } + if (results['mojo-path'] == null) { + _logging.severe('Must specify --mojo-path to mojo_run'); + return 1; + } + String packageRoot = results['package-root']; + String appPath = await _makePathAbsolute(results['app']); + String viewerPath = await _makePathAbsolute(await artifactStore.getPath(Artifact.SkyViewerMojo, packageRoot)); + String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo_shell')); + List mojoRunArgs = [ + 'mojo:window_manager file://${appPath}', + '--url-mappings=mojo:window_manager=mojo:kiosk_wm,mojo:sky_viewer=file://${viewerPath}' + ]; + _logging.fine("Starting ${mojoShellPath} with args: ${mojoRunArgs}"); + Process proc = await Process.start(mojoShellPath, mojoRunArgs); + proc.stdout.transform(UTF8.decoder).listen((data) { + stdout.write(data); + }); + proc.stderr.transform(UTF8.decoder).listen((data) { + stderr.write(data); + }); + int exitCode = await proc.exitCode; + if (exitCode != 0) throw new Exception(exitCode); + return 0; + } +} From 20d9d6a9e290410e04bf830aa30785a83279eaf7 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 22 Sep 2015 10:38:54 -0700 Subject: [PATCH 063/188] Make sky_tools help strings consistently refer to Flutter app, not package --- packages/flutter_tools/lib/src/build.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 85574a5b82..5b03493f82 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -126,7 +126,7 @@ Future _createSnapshotFile(String snapshotPath) async { } class BuildCommandHandler extends CommandHandler { - BuildCommandHandler() : super('build', 'Create a Flutter package.'); + BuildCommandHandler() : super('build', 'Create a Flutter app.'); ArgParser get parser { ArgParser parser = new ArgParser(); From 1532005ee619b5fd0ed33a6444408a921b4c6b0e Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 22 Sep 2015 10:53:55 -0700 Subject: [PATCH 064/188] fix a path issue when creating new flutter apps --- packages/flutter_tools/lib/src/common.dart | 3 ++- packages/flutter_tools/lib/src/init.dart | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart index c3e1ba18c6..ee65ebd56b 100644 --- a/packages/flutter_tools/lib/src/common.dart +++ b/packages/flutter_tools/lib/src/common.dart @@ -14,7 +14,8 @@ abstract class CommandHandler { ArgParser get parser; - /// @return 0 for no errors or warnings executing command, 1 for warnings, 2 for errors. + /// Returns 0 for no errors or warnings executing command, 1 for warnings, 2 + /// for errors. Future processArgResults(ArgResults results); void printUsage([String message]) { diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 21def3b427..105908ac05 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -89,6 +89,7 @@ abstract class Template { files.forEach((String path, String contents) { Map m = {'projectName': projectName, 'description': description}; contents = mustache.render(contents, m); + path = path.replaceAll('/', Platform.pathSeparator); File file = new File(p.join(dir.path, path)); file.parent.createSync(); file.writeAsStringSync(contents); From 0cc758d24e8be14ed6127e9b468635861a3495b2 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 18 Sep 2015 17:11:30 -0700 Subject: [PATCH 065/188] Set up plumbing for getting relevant paths to the right places without too much global state. `dart bin/sky_tools.dart --debug --sky-src-path=/path/to/sky/src/ install` now works. --- packages/flutter_tools/bin/sky_tools.dart | 88 +++++++++++++- .../lib/src/application_package.dart | 113 ++++++++++++++++++ packages/flutter_tools/lib/src/device.dart | 53 ++++---- packages/flutter_tools/lib/src/install.dart | 8 +- packages/flutter_tools/test/install_test.dart | 7 +- 5 files changed, 234 insertions(+), 35 deletions(-) create mode 100644 packages/flutter_tools/lib/src/application_package.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index c4275ef8c1..6e0576c0ae 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -11,6 +11,7 @@ import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/run_mojo.dart'; +import 'package:sky_tools/src/application_package.dart'; void main(List args) { Logger.root.level = Level.WARNING; @@ -27,7 +28,7 @@ void main(List args) { Map handlers = {}; ArgParser parser = new ArgParser(); - parser.addSeparator('options:'); + parser.addSeparator('basic options:'); parser.addFlag('help', abbr: 'h', negatable: false, help: 'Display this help message.'); parser.addFlag('verbose', @@ -38,6 +39,55 @@ void main(List args) { negatable: false, help: 'Very noisy logging, including the output of all ' 'shell commands executed.'); + + parser.addSeparator('build selection options:'); + parser.addFlag('debug', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the debug build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Not normally required.'); + parser.addFlag('release', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the release build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Note that release is not compatible with the listen command ' + 'on iOS devices and simulators. Not normally required.'); + parser.addOption('sky-src-path', + help: 'Path to your Sky src directory, if you are building Sky locally. ' + 'Ignored if neither debug nor release is set. Not normally required.'); + parser.addOption('android-debug-build-path', + help: + 'Path to your Android Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Debug/'); + parser.addOption('android-release-build-path', + help: + 'Path to your Android Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Release/'); + parser.addOption('ios-debug-build-path', + help: + 'Path to your iOS Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Debug/'); + parser.addOption('ios-release-build-path', + help: + 'Path to your iOS Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Release/'); + parser.addOption('ios-sim-debug-build-path', + help: + 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Debug/'); + parser.addOption('ios-sim-release-build-path', + help: + 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Release/'); + parser.addSeparator('commands:'); for (CommandHandler handler in [ @@ -67,6 +117,8 @@ void main(List args) { Logger.root.level = Level.FINE; } + _setupPaths(results); + if (results['help']) { _printUsage(parser, handlers); } else if (results.command != null) { @@ -84,6 +136,40 @@ void main(List args) { } } +void _setupPaths(ArgResults results) { + if (results['debug'] || results['release']) { + if (results['sky-src-path'] == null) { + // TODO(iansf): Figure out how to get the default src path + assert(false); + } + ApplicationPackageFactory.srcPath = results['sky-src-path']; + } else { + assert(false); + // TODO(iansf): set paths up for commands using PREBUILT binaries + // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, + // BuildPlatform.android, results['android-debug-build-path']); + } + + if (results['debug']) { + ApplicationPackageFactory.defaultBuildType = BuildType.debug; + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.android, results['android-debug-build-path']); + ApplicationPackageFactory.setBuildPath( + BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); + } + if (results['release']) { + ApplicationPackageFactory.defaultBuildType = BuildType.release; + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.android, results['android-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS, + results['ios-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); + } +} + void _printUsage(ArgParser parser, Map handlers, [String message]) { if (message != null) { diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart new file mode 100644 index 0000000000..37b7e5ed3f --- /dev/null +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -0,0 +1,113 @@ +// 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. + +library sky_tools.application_package; + +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +final Logger _logging = new Logger('sky_tools.application_package'); + +abstract class ApplicationPackage { + /// Path to the directory the apk or bundle lives in. + String appDir; + + /// Path to the actual apk or bundle. + String get appPath => path.join(appDir, appFileName); + + /// Package ID from the Android Manifest or equivalent. + String appPackageID; + + /// File name of the apk or bundle. + String appFileName; + + ApplicationPackage(this.appDir, this.appPackageID, this.appFileName); +} + +class AndroidApk extends ApplicationPackage { + static const String _apkName = 'SkyShell.apk'; + static const String _androidPackage = 'org.domokit.sky.shell'; + + AndroidApk(String appDir, + [String appPackageID = _androidPackage, String appFileName = _apkName]) + : super(path.join(appDir, 'apks'), appPackageID, appFileName); +} + +enum BuildType { prebuilt, release, debug, } + +enum BuildPlatform { android, iOS, iOSSimulator, mac, linux, } + +class ApplicationPackageFactory { + static final Map> _buildPaths = + _initBuildPaths(); + + /// Path to your Sky src directory, if you are building Sky locally. + /// Required if you are requesting release or debug BuildTypes. + static String _srcPath = null; + static String get srcPath => _srcPath; + static void set srcPath(String newPath) { + _srcPath = path.normalize(newPath); + } + + /// Default BuildType chosen if no BuildType is specified. + static BuildType defaultBuildType = BuildType.prebuilt; + + /// Default BuildPlatforms chosen if no BuildPlatforms are specified. + static List defaultBuildPlatforms = [BuildPlatform.android]; + + static Map getAvailableApplicationPackages( + {BuildType requestedType, List requestedPlatforms}) { + if (requestedType == null) { + requestedType = defaultBuildType; + } + if (requestedPlatforms == null) { + requestedPlatforms = defaultBuildPlatforms; + } + + Map packages = {}; + for (BuildPlatform platform in requestedPlatforms) { + String buildPath = _getBuildPath(requestedType, platform); + switch (platform) { + case BuildPlatform.android: + packages[platform] = new AndroidApk(buildPath); + break; + default: + // TODO(iansf): Add other platforms + assert(false); + } + } + return packages; + } + + static Map> _initBuildPaths() { + Map> buildPaths = {}; + for (BuildPlatform platform in BuildPlatform.values) { + buildPaths[platform] = {}; + } + return buildPaths; + } + + static String _getBuildPath(BuildType type, BuildPlatform platform) { + String path = _buildPaths[platform][type]; + // You must set paths before getting them + assert(path != null); + return path; + } + + static void setBuildPath( + BuildType type, BuildPlatform platform, String buildPath) { + // You must set srcPath before attempting to set a BuildPath for + // non prebuilt ApplicationPackages. + assert(type != BuildType.prebuilt || srcPath != null); + if (type != BuildType.prebuilt) { + buildPath = path.join(srcPath, buildPath); + } + if (!FileSystemEntity.isDirectorySync(buildPath)) { + _logging.warning('$buildPath is not a valid directory'); + } + _buildPaths[platform][type] = path.normalize(buildPath); + } +} diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 55475555bb..ffe3c56269 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -10,6 +10,7 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'process_wrapper.dart'; +import 'application_package.dart'; final Logger _logging = new Logger('sky_tools.device'); @@ -40,13 +41,13 @@ abstract class _Device { _Device._(this.id); /// Install an app package on the current device - bool installApp(String appPath, String appPackageID, String appFileName); + bool installApp(ApplicationPackage app); /// Check if the device is currently connected bool isConnected(); /// Check if the current version of the given app is already installed - bool isAppInstalled(String appPath, String appPackageID, String appFileName); + bool isAppInstalled(ApplicationPackage app); } class AndroidDevice extends _Device { @@ -175,68 +176,58 @@ class AndroidDevice extends _Device { return false; } - String _getDeviceSha1Path(String appPackageID, String appFileName) { - return '/sdcard/$appPackageID/$appFileName.sha1'; + String _getDeviceSha1Path(ApplicationPackage app) { + return '/sdcard/${app.appPackageID}/${app.appFileName}.sha1'; } - String _getDeviceApkSha1(String appPackageID, String appFileName) { - return runCheckedSync([ - adbPath, - 'shell', - 'cat', - _getDeviceSha1Path(appPackageID, appFileName) - ]); + String _getDeviceApkSha1(ApplicationPackage app) { + return runCheckedSync([adbPath, 'shell', 'cat', _getDeviceSha1Path(app)]); } - String _getSourceSha1(String apkPath) { + String _getSourceSha1(ApplicationPackage app) { String sha1 = - runCheckedSync(['shasum', '-a', '1', '-p', apkPath]).split(' ')[0]; + runCheckedSync(['shasum', '-a', '1', '-p', app.appPath]).split(' ')[0]; return sha1; } @override - bool isAppInstalled(String appPath, String appPackageID, String appFileName) { + bool isAppInstalled(ApplicationPackage app) { if (!isConnected()) { return false; } - if (runCheckedSync([adbPath, 'shell', 'pm', 'path', appPackageID]) == '') { + if (runCheckedSync([adbPath, 'shell', 'pm', 'path', app.appPackageID]) == + '') { _logging.info( - 'TODO(iansf): move this log to the caller. $appFileName is not on the device. Installing now...'); + 'TODO(iansf): move this log to the caller. ${app.appFileName} is not on the device. Installing now...'); return false; } - if (_getDeviceApkSha1(appPackageID, appFileName) != - _getSourceSha1(appPath)) { + if (_getDeviceApkSha1(app) != _getSourceSha1(app)) { _logging.info( - 'TODO(iansf): move this log to the caller. $appFileName is out of date. Installing now...'); + 'TODO(iansf): move this log to the caller. ${app.appFileName} is out of date. Installing now...'); return false; } return true; } @override - bool installApp(String appPath, String appPackageID, String appFileName) { + bool installApp(ApplicationPackage app) { if (!isConnected()) { _logging.info('Android device not connected. Not installing.'); return false; } - if (!FileSystemEntity.isFileSync(appPath)) { - _logging.severe('"$appPath" does not exist.'); + if (!FileSystemEntity.isFileSync(app.appPath)) { + _logging.severe('"${app.appPath}" does not exist.'); return false; } - runCheckedSync([adbPath, 'install', '-r', appPath]); + runCheckedSync([adbPath, 'install', '-r', app.appPath]); Directory tempDir = Directory.systemTemp; String sha1Path = path.join( - tempDir.path, appPath.replaceAll(path.separator, '_'), '.sha1'); + tempDir.path, (app.appPath + '.sha1').replaceAll(path.separator, '_')); File sha1TempFile = new File(sha1Path); - sha1TempFile.writeAsStringSync(_getSourceSha1(appPath), flush: true); - runCheckedSync([ - adbPath, - 'push', - sha1Path, - _getDeviceSha1Path(appPackageID, appFileName) - ]); + sha1TempFile.writeAsStringSync(_getSourceSha1(app), flush: true); + runCheckedSync([adbPath, 'push', sha1Path, _getDeviceSha1Path(app)]); sha1TempFile.deleteSync(); return true; } diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index d0b30d6ed7..56583da77d 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'package:args/args.dart'; +import 'application_package.dart'; import 'common.dart'; import 'device.dart'; @@ -33,11 +34,14 @@ class InstallCommandHandler extends CommandHandler { bool installedSomewhere = false; + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); if (android == null) { android = new AndroidDevice(); } - if (android.isConnected()) { - installedSomewhere = installedSomewhere || android.installApp('', '', ''); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + if (androidApp != null && android.isConnected()) { + installedSomewhere = installedSomewhere || android.installApp(androidApp); } if (installedSomewhere) { diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index 44151913c0..00e91806b3 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -6,6 +6,7 @@ library install_test; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/application_package.dart'; import 'package:test/test.dart'; import 'src/common.dart'; @@ -15,9 +16,13 @@ main() => defineTests(); defineTests() { group('install', () { test('returns 0 when Android is connected and ready for an install', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); - when(android.installApp(any, any, any)).thenReturn(true); + when(android.installApp(any)).thenReturn(true); InstallCommandHandler handler = new InstallCommandHandler(android); MockArgResults results = new MockArgResults(); From 343d96a48e098a6511ab8142a8067c20b26d2960 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 22 Sep 2015 15:17:11 -0700 Subject: [PATCH 066/188] =?UTF-8?q?Some=20basic=20tests=20for=20AndroidDev?= =?UTF-8?q?ice=20that=20don=E2=80=99t=20require=20a=20device=20to=20be=20a?= =?UTF-8?q?ttached.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/android_device_test.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 packages/flutter_tools/test/android_device_test.dart diff --git a/packages/flutter_tools/test/android_device_test.dart b/packages/flutter_tools/test/android_device_test.dart new file mode 100644 index 0000000000..2265f1c5d3 --- /dev/null +++ b/packages/flutter_tools/test/android_device_test.dart @@ -0,0 +1,32 @@ +// 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. + +library android_device_test; + +import 'package:sky_tools/src/device.dart'; +import 'package:test/test.dart'; + +main() => defineTests(); + +defineTests() { + group('android_device', () { + test('uses the correct default ID', () { + AndroidDevice android = new AndroidDevice(); + expect(android.id, equals(AndroidDevice.defaultDeviceID)); + }); + + test('stores the requested id', () { + String deviceID = '1234'; + AndroidDevice android = new AndroidDevice(deviceID); + expect(android.id, equals(deviceID)); + }); + + test('correctly creates only one of each requested device id', () { + String deviceID = '1234'; + AndroidDevice a1 = new AndroidDevice(deviceID); + AndroidDevice a2 = new AndroidDevice(deviceID); + expect(a1, equals(a2)); + }); + }); +} From 20e53552390e4bd37bdee21d3d114a2732133e22 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 22 Sep 2015 14:41:47 -0700 Subject: [PATCH 067/188] Teach sky_tools mojo_run to run on Android (using mojo devtools) This teaches sky_tools mojo_run --android to invoke mojo's devtool's mojo_run script with the right flags for invoking sky_viewer on android. This tells the devtools script to load sky_viewer.mojo from https://storage.googleapis.com/... and to load app.flx (or whatever the developer specifies as --app) from the filesystem using the devtools http server. --- packages/flutter_tools/lib/src/artifacts.dart | 19 +++--- packages/flutter_tools/lib/src/process.dart | 26 ++++++++ packages/flutter_tools/lib/src/run_mojo.dart | 60 ++++++++++++------- 3 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 packages/flutter_tools/lib/src/process.dart diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 29420f679e..9d0b1895d8 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -17,8 +17,8 @@ class _ArtifactStore { _ArtifactStore._(); // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 - String _googleStorageUrl(String category, String name, String engineRevision) { - return 'https://storage.googleapis.com/mojo/sky/${category}/linux-x64/${engineRevision}/${name}'; + String googleStorageUrl(String category, String platform, String engineRevision) { + return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${engineRevision}/'; } Future _downloadFile(String url, File file) async { @@ -34,10 +34,6 @@ class _ArtifactStore { _logging.fine('Wrote file'); } - Future _getEngineRevision(String packageRoot) { - return new File(packageRoot + '/sky_engine/REVISION').readAsString(); - } - Future _cacheDir(String engineRevision, String packageRoot) async { String cacheDirPath = '${packageRoot}/sky_tools/cache/sky_engine/${engineRevision}/'; Directory cacheDir = new Directory(cacheDirPath); @@ -52,8 +48,13 @@ class _ArtifactStore { return artifact == Artifact.FlutterCompiler; } + + Future getEngineRevision(String packageRoot) { + return new File(packageRoot + '/sky_engine/REVISION').readAsString(); + } + Future getPath(Artifact artifact, String packageRoot) async { - String engineRevision = await _getEngineRevision(packageRoot); + String engineRevision = await getEngineRevision(packageRoot); Directory cacheDir = await _cacheDir(engineRevision, packageRoot); String category, name; @@ -72,8 +73,8 @@ class _ArtifactStore { File cachedFile = new File(cacheDir.path + name); if (!await cachedFile.exists()) { _logging.info('Downloading ${name} from the cloud, one moment please...'); - String googleStorageUrl = _googleStorageUrl(category, name, engineRevision); - await _downloadFile(googleStorageUrl, cachedFile); + String url = googleStorageUrl(category, 'linux-x64', engineRevision) + name; + await _downloadFile(url, cachedFile); if (_needsToBeExecutable(artifact)) { ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]); if (result.exitCode != 0) throw new Exception(result.stderr); diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart new file mode 100644 index 0000000000..898ab86197 --- /dev/null +++ b/packages/flutter_tools/lib/src/process.dart @@ -0,0 +1,26 @@ +// 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. + +library sky_tools.process; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; + +final Logger _logging = new Logger('sky_tools.process'); + +// This runs the command and streams stdout/stderr from the child process to this process' stdout/stderr. +Future runCommandAndStreamOutput(String command, List args) async { + _logging.fine("Starting ${command} with args: ${args}"); + Process proc = await Process.start(command, args); + proc.stdout.transform(UTF8.decoder).listen((data) { + stdout.write(data); + }); + proc.stderr.transform(UTF8.decoder).listen((data) { + stderr.write(data); + }); + return proc.exitCode; +} diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index d396c71476..5330ea701d 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -5,7 +5,6 @@ library sky_tools.run_mojo; import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; @@ -14,6 +13,7 @@ import 'package:path/path.dart' as path; import 'artifacts.dart'; import 'common.dart'; +import 'process.dart'; final Logger _logging = new Logger('sky_tools.run_mojo'); @@ -22,10 +22,11 @@ class RunMojoCommandHandler extends CommandHandler { ArgParser get parser { ArgParser parser = new ArgParser(); + parser.addFlag('android', negatable: false, help: 'Run on an Android device'); parser.addFlag('help', abbr: 'h', negatable: false); - parser.addOption('package-root', defaultsTo: 'packages'); - parser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); parser.addOption('app', defaultsTo: 'app.flx'); + parser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); + parser.addOption('package-root', defaultsTo: 'packages'); return parser; } @@ -37,6 +38,36 @@ class RunMojoCommandHandler extends CommandHandler { return file.absolute.path; } + Future _runAndroid(ArgResults results, String appPath, String engineRevision) async { + String skyViewerUrl = artifactStore.googleStorageUrl('viewer', 'android-arm', engineRevision); + String command = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo', 'devtools', 'common', 'mojo_run')); + String appName = path.basename(appPath); + String appDir = path.dirname(appPath); + List args = [ + '--android', '--release', '--embed', 'http://app/$appName', + '--map-origin=http://app/=$appDir', + '--map-origin=http://sky_viewer/=$skyViewerUrl', + '--url-mappings=mojo:sky_viewer=http://sky_viewer/sky_viewer.mojo', + ]; + if (_logging.level <= Level.INFO) { + args.add('--verbose'); + if (_logging.level <= Level.FINE) { + args.add('--verbose'); + } + } + return runCommandAndStreamOutput(command, args); + } + + Future _runLinux(ArgResults results, String appPath, String packageRoot, String engineRevision) async { + String viewerPath = await _makePathAbsolute(await artifactStore.getPath(Artifact.SkyViewerMojo, packageRoot)); + String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', 'Release', 'mojo_shell')); + List mojoRunArgs = [ + 'mojo:window_manager file://${appPath}', + '--url-mappings=mojo:window_manager=mojo:kiosk_wm,mojo:sky_viewer=file://${viewerPath}' + ]; + return runCommandAndStreamOutput(mojoShellPath, mojoRunArgs); + } + @override Future processArgResults(ArgResults results) async { if (results['help']) { @@ -48,23 +79,12 @@ class RunMojoCommandHandler extends CommandHandler { return 1; } String packageRoot = results['package-root']; + String engineRevision = await artifactStore.getEngineRevision(packageRoot); String appPath = await _makePathAbsolute(results['app']); - String viewerPath = await _makePathAbsolute(await artifactStore.getPath(Artifact.SkyViewerMojo, packageRoot)); - String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo_shell')); - List mojoRunArgs = [ - 'mojo:window_manager file://${appPath}', - '--url-mappings=mojo:window_manager=mojo:kiosk_wm,mojo:sky_viewer=file://${viewerPath}' - ]; - _logging.fine("Starting ${mojoShellPath} with args: ${mojoRunArgs}"); - Process proc = await Process.start(mojoShellPath, mojoRunArgs); - proc.stdout.transform(UTF8.decoder).listen((data) { - stdout.write(data); - }); - proc.stderr.transform(UTF8.decoder).listen((data) { - stderr.write(data); - }); - int exitCode = await proc.exitCode; - if (exitCode != 0) throw new Exception(exitCode); - return 0; + if (results['android']) { + return _runAndroid(results, appPath, engineRevision); + } else { + return _runLinux(results, appPath, packageRoot, engineRevision); + } } } From 0758c5922f41fd61948e5341495c570f26c9f889 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 22 Sep 2015 15:42:37 -0700 Subject: [PATCH 068/188] Instantiate ArtifactStore explicit with packageRoot This teaches commands that need binary artifacts to explicitly instantiate an instance of the ArtifactStore with the appropriate packageRoot string. The ArtifactStore can then remember the package root and compute the engine revision when created and remember those for subsequence calls. --- packages/flutter_tools/lib/src/artifacts.dart | 36 +++++++++---------- packages/flutter_tools/lib/src/build.dart | 6 ++-- packages/flutter_tools/lib/src/run_mojo.dart | 14 ++++---- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 9d0b1895d8..1ac743f825 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -8,16 +8,24 @@ import 'dart:async'; import 'dart:io'; import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; -final Logger _logging = new Logger('sky_tools.device'); +final Logger _logging = new Logger('sky_tools.artifacts'); enum Artifact { FlutterCompiler, SkyViewerMojo, } -class _ArtifactStore { - _ArtifactStore._(); +class ArtifactStore { + String _engineRevision; + final String packageRoot; + + ArtifactStore(this.packageRoot) { + _engineRevision = new File(path.join(packageRoot, 'sky_engine', 'REVISION')).readAsStringSync(); + } + + String get engineRevision => _engineRevision; // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 - String googleStorageUrl(String category, String platform, String engineRevision) { + String googleStorageUrl(String category, String platform) { return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${engineRevision}/'; } @@ -34,8 +42,8 @@ class _ArtifactStore { _logging.fine('Wrote file'); } - Future _cacheDir(String engineRevision, String packageRoot) async { - String cacheDirPath = '${packageRoot}/sky_tools/cache/sky_engine/${engineRevision}/'; + Future _cacheDir() async { + String cacheDirPath = path.join(packageRoot, 'sky_tools', 'cache', 'sky_engine', engineRevision); Directory cacheDir = new Directory(cacheDirPath); if (!await cacheDir.exists()) { await cacheDir.create(recursive: true); @@ -48,14 +56,8 @@ class _ArtifactStore { return artifact == Artifact.FlutterCompiler; } - - Future getEngineRevision(String packageRoot) { - return new File(packageRoot + '/sky_engine/REVISION').readAsString(); - } - - Future getPath(Artifact artifact, String packageRoot) async { - String engineRevision = await getEngineRevision(packageRoot); - Directory cacheDir = await _cacheDir(engineRevision, packageRoot); + Future getPath(Artifact artifact) async { + Directory cacheDir = await _cacheDir(); String category, name; @@ -70,10 +72,10 @@ class _ArtifactStore { return ''; } - File cachedFile = new File(cacheDir.path + name); + File cachedFile = new File(path.join(cacheDir.path, name)); if (!await cachedFile.exists()) { _logging.info('Downloading ${name} from the cloud, one moment please...'); - String url = googleStorageUrl(category, 'linux-x64', engineRevision) + name; + String url = googleStorageUrl(category, 'linux-x64') + name; await _downloadFile(url, cachedFile); if (_needsToBeExecutable(artifact)) { ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]); @@ -83,5 +85,3 @@ class _ArtifactStore { return cachedFile.path; } } - -final _ArtifactStore artifactStore = new _ArtifactStore._(); diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 5b03493f82..0ee38cee89 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -105,8 +105,10 @@ Future _compileSnapshot({ String packageRoot, String snapshotPath }) async { - if (compilerPath == null) - compilerPath = await artifactStore.getPath(Artifact.FlutterCompiler, packageRoot); + if (compilerPath == null) { + ArtifactStore artifacts = new ArtifactStore(packageRoot); + compilerPath = await artifacts.getPath(Artifact.FlutterCompiler); + } ProcessResult result = await Process.run(compilerPath, [ mainPath, '--package-root=$packageRoot', diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 5330ea701d..71b6d2539c 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -38,8 +38,8 @@ class RunMojoCommandHandler extends CommandHandler { return file.absolute.path; } - Future _runAndroid(ArgResults results, String appPath, String engineRevision) async { - String skyViewerUrl = artifactStore.googleStorageUrl('viewer', 'android-arm', engineRevision); + Future _runAndroid(ArgResults results, String appPath, ArtifactStore artifacts) async { + String skyViewerUrl = artifacts.googleStorageUrl('viewer', 'android-arm'); String command = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo', 'devtools', 'common', 'mojo_run')); String appName = path.basename(appPath); String appDir = path.dirname(appPath); @@ -58,8 +58,8 @@ class RunMojoCommandHandler extends CommandHandler { return runCommandAndStreamOutput(command, args); } - Future _runLinux(ArgResults results, String appPath, String packageRoot, String engineRevision) async { - String viewerPath = await _makePathAbsolute(await artifactStore.getPath(Artifact.SkyViewerMojo, packageRoot)); + Future _runLinux(ArgResults results, String appPath, ArtifactStore artifacts) async { + String viewerPath = await _makePathAbsolute(await artifacts.getPath(Artifact.SkyViewerMojo)); String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', 'Release', 'mojo_shell')); List mojoRunArgs = [ 'mojo:window_manager file://${appPath}', @@ -79,12 +79,12 @@ class RunMojoCommandHandler extends CommandHandler { return 1; } String packageRoot = results['package-root']; - String engineRevision = await artifactStore.getEngineRevision(packageRoot); + ArtifactStore artifacts = new ArtifactStore(packageRoot); String appPath = await _makePathAbsolute(results['app']); if (results['android']) { - return _runAndroid(results, appPath, engineRevision); + return _runAndroid(results, appPath, artifacts); } else { - return _runLinux(results, appPath, packageRoot, engineRevision); + return _runLinux(results, appPath, artifacts); } } } From f735604acab4fb6ac42de8f54193582628e6ba45 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 22 Sep 2015 15:19:25 -0700 Subject: [PATCH 069/188] Add cache command to sky_tools with subcommands for populating/clearing This adds the following commands to sky_tools: sky_tools cache clear: Nukes all local artifacts in the cache sky_tools cache populate: Populates the cache with all known artifacts This is useful both to fix busted caches and to make sure that the cache is fully populated so that subsequent operations can proceed without needing network access. --- packages/flutter_tools/bin/sky_tools.dart | 2 + packages/flutter_tools/lib/src/artifacts.dart | 46 +++++++++---- packages/flutter_tools/lib/src/cache.dart | 68 +++++++++++++++++++ 3 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 packages/flutter_tools/lib/src/cache.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 6e0576c0ae..71c0fda31f 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:logging/logging.dart'; import 'package:sky_tools/src/build.dart'; +import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; @@ -92,6 +93,7 @@ void main(List args) { for (CommandHandler handler in [ new BuildCommandHandler(), + new CacheCommandHandler(), new InitCommandHandler(), new InstallCommandHandler(), new RunMojoCommandHandler(), diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 1ac743f825..717b363fba 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -43,33 +43,42 @@ class ArtifactStore { } Future _cacheDir() async { - String cacheDirPath = path.join(packageRoot, 'sky_tools', 'cache', 'sky_engine', engineRevision); - Directory cacheDir = new Directory(cacheDirPath); + Directory cacheDir = new Directory(path.join(packageRoot, 'sky_tools', 'cache')); if (!await cacheDir.exists()) { await cacheDir.create(recursive: true); } return cacheDir; } + Future _engineSpecificCacheDir() async { + Directory cacheDir = await _cacheDir(); + Directory engineSpecificDir = new Directory(path.join(cacheDir.path, 'sky_engine', engineRevision)); + + if (!await engineSpecificDir.exists()) { + await engineSpecificDir.create(recursive: true); + } + return engineSpecificDir; + } + // Whether the artifact needs to be marked as executable on disk. bool _needsToBeExecutable(Artifact artifact) { return artifact == Artifact.FlutterCompiler; } Future getPath(Artifact artifact) async { - Directory cacheDir = await _cacheDir(); + Directory cacheDir = await _engineSpecificCacheDir(); String category, name; - if (artifact == Artifact.FlutterCompiler) { - category = 'shell'; - name = 'sky_snapshot'; - } else if (artifact == Artifact.SkyViewerMojo) { - category = 'viewer'; - name = 'sky_viewer.mojo'; - } else { - // Unknown artifact. - return ''; + switch (artifact) { + case Artifact.FlutterCompiler: + category = 'shell'; + name = 'sky_snapshot'; + break; + case Artifact.SkyViewerMojo: + category = 'viewer'; + name = 'sky_viewer.mojo'; + break; } File cachedFile = new File(path.join(cacheDir.path, name)); @@ -84,4 +93,17 @@ class ArtifactStore { } return cachedFile.path; } + + Future clear() async { + Directory cacheDir = await _cacheDir(); + _logging.fine('Clearing cache directory ${cacheDir.path}'); + await cacheDir.delete(recursive: true); + } + + Future populate() async { + for (Artifact artifact in Artifact.values) { + _logging.fine('Populating cache with $artifact'); + await getPath(artifact); + } + } } diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart new file mode 100644 index 0000000000..d8e90453d8 --- /dev/null +++ b/packages/flutter_tools/lib/src/cache.dart @@ -0,0 +1,68 @@ +// 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. + +library sky_tools.cache; + +import 'dart:async'; + +import 'package:args/args.dart'; +import 'package:logging/logging.dart'; + +import 'artifacts.dart'; +import 'common.dart'; + +final Logger _logging = new Logger('sky_tools.cache'); + +class CacheCommandHandler extends CommandHandler { + CacheCommandHandler() : super('cache', 'Manages sky_tools\' cache of binary artifacts.'); + + ArgParser get parser { + ArgParser parser = new ArgParser(); + parser.addFlag('help', abbr: 'h', negatable: false); + parser.addOption('package-root', defaultsTo: 'packages'); + ArgParser clearParser = parser.addCommand('clear'); + clearParser.addFlag('help', abbr: 'h', negatable: false); + ArgParser populateParser = parser.addCommand('populate'); + populateParser.addFlag('help', abbr: 'h', negatable: false); + return parser; + } + + Future _clear(String packageRoot, ArgResults results) async { + if (results['help']) { + print('Clears all artifacts from the cache.'); + print(parser.usage); + return 0; + } + ArtifactStore artifacts = new ArtifactStore(packageRoot); + await artifacts.clear(); + return 0; + } + + Future _populate(String packageRoot, ArgResults results) async { + if (results['help']) { + print('Populates the cache with all known artifacts.'); + print(parser.usage); + return 0; + } + ArtifactStore artifacts = new ArtifactStore(packageRoot); + await artifacts.populate(); + return 0; + } + + @override + Future processArgResults(ArgResults results) async { + if (results['help'] || results.command == null) { + print(parser.usage); + return 0; + } + if (results.command.name == 'clear') { + return _clear(results['package-root'], results.command); + } else if (results.command.name == 'populate') { + return _populate(results['package-root'], results.command); + } else { + _logging.severe('Unknown cache command \"${results.command.name}\"'); + return 2; + } + } +} From be4a9d941fc06379fef3a44c5129615c0f43bfd8 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 23 Sep 2015 09:48:20 -0700 Subject: [PATCH 070/188] Forward additional arguments to runner in run_mojo command --- packages/flutter_tools/lib/src/run_mojo.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 71b6d2539c..e7547c5f52 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -55,17 +55,19 @@ class RunMojoCommandHandler extends CommandHandler { args.add('--verbose'); } } + args.addAll(results.rest); return runCommandAndStreamOutput(command, args); } Future _runLinux(ArgResults results, String appPath, ArtifactStore artifacts) async { String viewerPath = await _makePathAbsolute(await artifacts.getPath(Artifact.SkyViewerMojo)); String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', 'Release', 'mojo_shell')); - List mojoRunArgs = [ + List args = [ 'mojo:window_manager file://${appPath}', '--url-mappings=mojo:window_manager=mojo:kiosk_wm,mojo:sky_viewer=file://${viewerPath}' ]; - return runCommandAndStreamOutput(mojoShellPath, mojoRunArgs); + args.addAll(results.rest); + return runCommandAndStreamOutput(mojoShellPath, args); } @override From a49120c66735a7a1d4f358d3fe75599793cd2299 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 23 Sep 2015 10:34:53 -0700 Subject: [PATCH 071/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 9d6de13f57..07780c2722 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.13 +version: 0.0.14 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 206104b8200c8191a1a3c71037a8bf072525493d Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 24 Sep 2015 12:23:14 -0700 Subject: [PATCH 072/188] Update mojo_run to no longer embed a window manager sky_viewer.mojo no longer needs a window manager embedding as of sky_engine 0.0.27, so this updates the sky_tools run_mojo command so it works again. This really should be expressed in the pubspec dependencies to avoid broken combinations but that needs to happen on the sky side. --- packages/flutter_tools/lib/src/run_mojo.dart | 8 +++++--- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index e7547c5f52..2ff9a7c50f 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -44,7 +44,9 @@ class RunMojoCommandHandler extends CommandHandler { String appName = path.basename(appPath); String appDir = path.dirname(appPath); List args = [ - '--android', '--release', '--embed', 'http://app/$appName', + '--android', + '--release', + 'http://app/$appName', '--map-origin=http://app/=$appDir', '--map-origin=http://sky_viewer/=$skyViewerUrl', '--url-mappings=mojo:sky_viewer=http://sky_viewer/sky_viewer.mojo', @@ -63,8 +65,8 @@ class RunMojoCommandHandler extends CommandHandler { String viewerPath = await _makePathAbsolute(await artifacts.getPath(Artifact.SkyViewerMojo)); String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', 'Release', 'mojo_shell')); List args = [ - 'mojo:window_manager file://${appPath}', - '--url-mappings=mojo:window_manager=mojo:kiosk_wm,mojo:sky_viewer=file://${viewerPath}' + 'file://${appPath}', + '--url-mappings=mojo:sky_viewer=file://${viewerPath}' ]; args.addAll(results.rest); return runCommandAndStreamOutput(mojoShellPath, args); diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 07780c2722..311a3424de 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.14 +version: 0.0.15 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From cae053c3531ff0fa74ae2fecf77c655a85edc7e5 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Thu, 24 Sep 2015 11:13:20 -0700 Subject: [PATCH 073/188] Refactor all the commands to be Commands from the Args package. Also use CommandRunner for the top-level command. --- packages/flutter_tools/bin/sky_tools.dart | 279 ++++++++---------- packages/flutter_tools/lib/src/build.dart | 49 ++- packages/flutter_tools/lib/src/cache.dart | 79 ++--- packages/flutter_tools/lib/src/common.dart | 31 -- packages/flutter_tools/lib/src/device.dart | 2 +- packages/flutter_tools/lib/src/init.dart | 37 +-- packages/flutter_tools/lib/src/install.dart | 25 +- packages/flutter_tools/lib/src/run_mojo.dart | 39 +-- packages/flutter_tools/test/init_test.dart | 19 +- packages/flutter_tools/test/install_test.dart | 12 +- packages/flutter_tools/test/src/common.dart | 6 - 11 files changed, 221 insertions(+), 357 deletions(-) delete mode 100644 packages/flutter_tools/lib/src/common.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 71c0fda31f..6ce483df37 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -2,17 +2,126 @@ // 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:async'; import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/cache.dart'; -import 'package:sky_tools/src/common.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/run_mojo.dart'; -import 'package:sky_tools/src/application_package.dart'; + +class FlutterCommandRunner extends CommandRunner { + FlutterCommandRunner() : super('flutter', 'Manage your flutter app development.') { + argParser.addFlag('verbose', + abbr: 'v', + negatable: false, + help: 'Noisy logging, including all shell commands executed.'); + argParser.addFlag('very-verbose', + negatable: false, + help: 'Very noisy logging, including the output of all ' + 'shell commands executed.'); + + argParser.addSeparator('Global build selection options:'); + argParser.addFlag('debug', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the debug build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Not normally required.'); + argParser.addFlag('release', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the release build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Note that release is not compatible with the listen command ' + 'on iOS devices and simulators. Not normally required.'); + argParser.addOption('sky-src-path', + help: 'Path to your Sky src directory, if you are building Sky locally. ' + 'Ignored if neither debug nor release is set. Not normally required.'); + argParser.addOption('android-debug-build-path', + help: + 'Path to your Android Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Debug/'); + argParser.addOption('android-release-build-path', + help: + 'Path to your Android Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Release/'); + argParser.addOption('ios-debug-build-path', + help: + 'Path to your iOS Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Debug/'); + argParser.addOption('ios-release-build-path', + help: + 'Path to your iOS Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Release/'); + argParser.addOption('ios-sim-debug-build-path', + help: + 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Debug/'); + argParser.addOption('ios-sim-release-build-path', + help: + 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Release/'); + } + + Future runCommand(ArgResults topLevelResults) async { + if (topLevelResults['verbose']) { + Logger.root.level = Level.INFO; + } + + if (topLevelResults['very-verbose']) { + Logger.root.level = Level.FINE; + } + + _setupPaths(topLevelResults); + + return super.runCommand(topLevelResults); + } + + void _setupPaths(ArgResults results) { + if (results['debug'] || results['release']) { + if (results['sky-src-path'] == null) { + // TODO(iansf): Figure out how to get the default src path + assert(false); + } + ApplicationPackageFactory.srcPath = results['sky-src-path']; + } else { + assert(false); + // TODO(iansf): set paths up for commands using PREBUILT binaries + // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, + // BuildPlatform.android, results['android-debug-build-path']); + } + + if (results['debug']) { + ApplicationPackageFactory.defaultBuildType = BuildType.debug; + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.android, results['android-debug-build-path']); + ApplicationPackageFactory.setBuildPath( + BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); + } + if (results['release']) { + ApplicationPackageFactory.defaultBuildType = BuildType.release; + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.android, results['android-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS, + results['ios-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); + } + } +} void main(List args) { Logger.root.level = Level.WARNING; @@ -26,161 +135,11 @@ void main(List args) { } }); - Map handlers = {}; - - ArgParser parser = new ArgParser(); - parser.addSeparator('basic options:'); - parser.addFlag('help', - abbr: 'h', negatable: false, help: 'Display this help message.'); - parser.addFlag('verbose', - abbr: 'v', - negatable: false, - help: 'Noisy logging, including all shell commands executed.'); - parser.addFlag('very-verbose', - negatable: false, - help: 'Very noisy logging, including the output of all ' - 'shell commands executed.'); - - parser.addSeparator('build selection options:'); - parser.addFlag('debug', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the debug build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Not normally required.'); - parser.addFlag('release', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the release build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Note that release is not compatible with the listen command ' - 'on iOS devices and simulators. Not normally required.'); - parser.addOption('sky-src-path', - help: 'Path to your Sky src directory, if you are building Sky locally. ' - 'Ignored if neither debug nor release is set. Not normally required.'); - parser.addOption('android-debug-build-path', - help: - 'Path to your Android Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Debug/'); - parser.addOption('android-release-build-path', - help: - 'Path to your Android Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Release/'); - parser.addOption('ios-debug-build-path', - help: - 'Path to your iOS Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Debug/'); - parser.addOption('ios-release-build-path', - help: - 'Path to your iOS Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Release/'); - parser.addOption('ios-sim-debug-build-path', - help: - 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Debug/'); - parser.addOption('ios-sim-release-build-path', - help: - 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Release/'); - - parser.addSeparator('commands:'); - - for (CommandHandler handler in [ - new BuildCommandHandler(), - new CacheCommandHandler(), - new InitCommandHandler(), - new InstallCommandHandler(), - new RunMojoCommandHandler(), - ]) { - parser.addCommand(handler.name, handler.parser); - handlers[handler.name] = handler; - } - - ArgResults results; - - try { - results = parser.parse(args); - } catch (e) { - _printUsage(parser, handlers, e is FormatException ? e.message : '${e}'); - exit(1); - } - - if (results['verbose']) { - Logger.root.level = Level.INFO; - } - - if (results['very-verbose']) { - Logger.root.level = Level.FINE; - } - - _setupPaths(results); - - if (results['help']) { - _printUsage(parser, handlers); - } else if (results.command != null) { - handlers[results.command.name] - .processArgResults(results.command) - .then((int code) => exit(code)) - .catchError((e, stack) { - print('Error running ' + results.command.name + ': $e'); - print(stack); - exit(2); - }); - } else { - _printUsage(parser, handlers, 'No command specified.'); - exit(1); - } -} - -void _setupPaths(ArgResults results) { - if (results['debug'] || results['release']) { - if (results['sky-src-path'] == null) { - // TODO(iansf): Figure out how to get the default src path - assert(false); - } - ApplicationPackageFactory.srcPath = results['sky-src-path']; - } else { - assert(false); - // TODO(iansf): set paths up for commands using PREBUILT binaries - // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, - // BuildPlatform.android, results['android-debug-build-path']); - } - - if (results['debug']) { - ApplicationPackageFactory.defaultBuildType = BuildType.debug; - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.android, results['android-debug-build-path']); - ApplicationPackageFactory.setBuildPath( - BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); - } - if (results['release']) { - ApplicationPackageFactory.defaultBuildType = BuildType.release; - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.android, results['android-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS, - results['ios-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); - } -} - -void _printUsage(ArgParser parser, Map handlers, - [String message]) { - if (message != null) { - print('${message}\n'); - } - print('usage: sky_tools [arguments]'); - print(''); - print(parser.usage); - handlers.forEach((String command, CommandHandler handler) { - print(' ${command.padRight(10)} ${handler.description}'); - }); + new FlutterCommandRunner() + ..addCommand(new BuildCommand()) + ..addCommand(new CacheCommand()) + ..addCommand(new InitCommand()) + ..addCommand(new InstallCommand()) + ..addCommand(new RunMojoCommand()) + ..run(args); } diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 0ee38cee89..80c195b8d1 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -8,11 +8,10 @@ import 'dart:async'; import 'dart:io'; import 'package:archive/archive.dart'; -import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:yaml/yaml.dart'; import 'artifacts.dart'; -import 'common.dart'; const String _kSnapshotKey = 'snapshot_blob.bin'; const List _kDensities = const ['drawable-xxhdpi']; @@ -127,41 +126,33 @@ Future _createSnapshotFile(String snapshotPath) async { return new ArchiveFile(_kSnapshotKey, content.length, content); } -class BuildCommandHandler extends CommandHandler { - BuildCommandHandler() : super('build', 'Create a Flutter app.'); - - ArgParser get parser { - ArgParser parser = new ArgParser(); - parser.addFlag('help', abbr: 'h', negatable: false); - parser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons'); - parser.addOption('compiler'); - parser.addOption('main', defaultsTo: 'lib/main.dart'); - parser.addOption('manifest'); - parser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); - parser.addOption('package-root', defaultsTo: 'packages'); - parser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin'); - return parser; +class BuildCommand extends Command { + final name = 'build'; + final description = 'Create a Flutter app.'; + BuildCommand() { + argParser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons'); + argParser.addOption('compiler'); + argParser.addOption('main', defaultsTo: 'lib/main.dart'); + argParser.addOption('manifest'); + argParser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); + argParser.addOption('package-root', defaultsTo: 'packages'); + argParser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin'); } @override - Future processArgResults(ArgResults results) async { - if (results['help']) { - print(parser.usage); - return 0; - } - - String manifestPath = results['manifest']; + Future run() async { + String manifestPath = argResults['manifest']; Map manifestDescriptor = await _loadManifest(manifestPath); Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath); Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor); Archive archive = new Archive(); - String snapshotPath = results['snapshot']; + String snapshotPath = argResults['snapshot']; await _compileSnapshot( - compilerPath: results['compiler'], - mainPath: results['main'], - packageRoot: results['package-root'], + compilerPath: argResults['compiler'], + mainPath: argResults['main'], + packageRoot: argResults['package-root'], snapshotPath: snapshotPath); archive.addFile(await _createSnapshotFile(snapshotPath)); @@ -169,12 +160,12 @@ class BuildCommandHandler extends CommandHandler { archive.addFile(await _createFile(asset.key, asset.base)); for (_MaterialAsset asset in materialAssets) { - ArchiveFile file = await _createFile(asset.key, results['asset-base']); + ArchiveFile file = await _createFile(asset.key, argResults['asset-base']); if (file != null) archive.addFile(file); } - File outputFile = new File(results['output-file']); + File outputFile = new File(argResults['output-file']); await outputFile.writeAsString('#!mojo mojo:sky_viewer\n'); await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND); return 0; diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index d8e90453d8..2faa7b65a4 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -6,63 +6,48 @@ library sky_tools.cache; import 'dart:async'; -import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'artifacts.dart'; -import 'common.dart'; final Logger _logging = new Logger('sky_tools.cache'); -class CacheCommandHandler extends CommandHandler { - CacheCommandHandler() : super('cache', 'Manages sky_tools\' cache of binary artifacts.'); - - ArgParser get parser { - ArgParser parser = new ArgParser(); - parser.addFlag('help', abbr: 'h', negatable: false); - parser.addOption('package-root', defaultsTo: 'packages'); - ArgParser clearParser = parser.addCommand('clear'); - clearParser.addFlag('help', abbr: 'h', negatable: false); - ArgParser populateParser = parser.addCommand('populate'); - populateParser.addFlag('help', abbr: 'h', negatable: false); - return parser; +class CacheCommand extends Command { + final name = 'cache'; + final description = 'Manages sky_tools\' cache of binary artifacts.'; + CacheCommand() { + addSubcommand(new _ClearCommand()); + addSubcommand(new _PopulateCommand()); } +} - Future _clear(String packageRoot, ArgResults results) async { - if (results['help']) { - print('Clears all artifacts from the cache.'); - print(parser.usage); - return 0; - } - ArtifactStore artifacts = new ArtifactStore(packageRoot); - await artifacts.clear(); - return 0; - } - - Future _populate(String packageRoot, ArgResults results) async { - if (results['help']) { - print('Populates the cache with all known artifacts.'); - print(parser.usage); - return 0; - } - ArtifactStore artifacts = new ArtifactStore(packageRoot); - await artifacts.populate(); - return 0; +class _ClearCommand extends Command { + final name = 'clear'; + final description = 'Clears all artifacts from the cache.'; + _ClearCommand() { + argParser.addOption('package-root', defaultsTo: 'packages'); } @override - Future processArgResults(ArgResults results) async { - if (results['help'] || results.command == null) { - print(parser.usage); - return 0; - } - if (results.command.name == 'clear') { - return _clear(results['package-root'], results.command); - } else if (results.command.name == 'populate') { - return _populate(results['package-root'], results.command); - } else { - _logging.severe('Unknown cache command \"${results.command.name}\"'); - return 2; - } + Future run() async { + ArtifactStore artifacts = new ArtifactStore(argResults['package-root']); + await artifacts.populate(); + return 0; + } +} + +class _PopulateCommand extends Command { + final name = 'populate'; + final description = 'Populates the cache with all known artifacts.'; + _PopulateCommand() { + argParser.addOption('package-root', defaultsTo: 'packages'); + } + + @override + Future run() async { + ArtifactStore artifacts = new ArtifactStore(argResults['package-root']); + await artifacts.populate(); + return 0; } } diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart deleted file mode 100644 index ee65ebd56b..0000000000 --- a/packages/flutter_tools/lib/src/common.dart +++ /dev/null @@ -1,31 +0,0 @@ -// 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:args/args.dart'; - -abstract class CommandHandler { - final String name; - final String description; - - CommandHandler(this.name, this.description); - - ArgParser get parser; - - /// Returns 0 for no errors or warnings executing command, 1 for warnings, 2 - /// for errors. - Future processArgResults(ArgResults results); - - void printUsage([String message]) { - if (message != null) { - print('${message}\n'); - } - print('usage: sky_tools ${name} [arguments]'); - print(''); - print(parser.usage); - } - - String toString() => name; -} diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index ffe3c56269..85e66dd224 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -9,8 +9,8 @@ import 'dart:io'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import 'process_wrapper.dart'; import 'application_package.dart'; +import 'process_wrapper.dart'; final Logger _logging = new Logger('sky_tools.device'); diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 105908ac05..78456cb900 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -7,41 +7,30 @@ library sky_tools.init; import 'dart:async'; import 'dart:io'; -import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; -import 'common.dart'; - -class InitCommandHandler extends CommandHandler { - InitCommandHandler() : super('init', 'Create a new sky project.'); - - ArgParser get parser { - // TODO: Add a --template option for template selection when we have more than one. - ArgParser parser = new ArgParser(); - parser.addFlag('help', - abbr: 'h', negatable: false, help: 'Display this help message.'); - parser.addOption('out', abbr: 'o', help: 'The output directory.'); - parser.addFlag('pub', +class InitCommand extends Command { + final name = 'init'; + final description = 'Create a new sky project.'; + InitCommand() { + argParser.addOption('out', abbr: 'o', help: 'The output directory.'); + argParser.addFlag('pub', defaultsTo: true, help: 'Whether to run pub after the project has been created.'); - return parser; } @override - Future processArgResults(ArgResults results) async { - if (results['help']) { - printUsage(); - return 0; - } - - if (!results.wasParsed('out')) { - printUsage('No option specified for the output directory.'); + Future run() async { + if (!argResults.wasParsed('out')) { + print('No option specified for the output directory.'); + print(argParser.getUsage()); return 2; } // TODO: Confirm overwrite of an existing directory with the user. - Directory out = new Directory(results['out']); + Directory out = new Directory(argResults['out']); new SkySimpleTemplate().generateInto(out); @@ -58,7 +47,7 @@ Or if the Sky APK is not already on your device, run: '''; - if (results['pub']) { + if (argResults['pub']) { print("Running pub get..."); Process process = await Process.start('pub', ['get'], workingDirectory: out.path); diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index 56583da77d..93943aeff7 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -6,32 +6,19 @@ library sky_tools.install; import 'dart:async'; -import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'application_package.dart'; -import 'common.dart'; import 'device.dart'; -class InstallCommandHandler extends CommandHandler { +class InstallCommand extends Command { + final name = 'install'; + final description = 'Install your Flutter app on attached devices.'; AndroidDevice android = null; - InstallCommandHandler([this.android]) - : super('install', 'Install your Sky app on attached devices.'); + InstallCommand([this.android]); @override - ArgParser get parser { - ArgParser parser = new ArgParser(); - parser.addFlag('help', - abbr: 'h', negatable: false, help: 'Display this help message.'); - return parser; - } - - @override - Future processArgResults(ArgResults results) async { - if (results['help']) { - printUsage(); - return 0; - } - + Future run() async { bool installedSomewhere = false; Map packages = diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index e7547c5f52..cc5034245c 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -8,26 +8,23 @@ import 'dart:async'; import 'dart:io'; import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'artifacts.dart'; -import 'common.dart'; import 'process.dart'; final Logger _logging = new Logger('sky_tools.run_mojo'); -class RunMojoCommandHandler extends CommandHandler { - RunMojoCommandHandler() : super('run_mojo', 'Run a Flutter app in mojo.'); - - ArgParser get parser { - ArgParser parser = new ArgParser(); - parser.addFlag('android', negatable: false, help: 'Run on an Android device'); - parser.addFlag('help', abbr: 'h', negatable: false); - parser.addOption('app', defaultsTo: 'app.flx'); - parser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); - parser.addOption('package-root', defaultsTo: 'packages'); - return parser; +class RunMojoCommand extends Command { + final name = 'run_mojo'; + final description = 'Run a Flutter app in mojo.'; + RunMojoCommand() { + argParser.addFlag('android', negatable: false, help: 'Run on an Android device'); + argParser.addOption('app', defaultsTo: 'app.flx'); + argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); + argParser.addOption('package-root', defaultsTo: 'packages'); } Future _makePathAbsolute(String relativePath) async { @@ -71,22 +68,18 @@ class RunMojoCommandHandler extends CommandHandler { } @override - Future processArgResults(ArgResults results) async { - if (results['help']) { - print(parser.usage); - return 0; - } - if (results['mojo-path'] == null) { + Future run() async { + if (argResults['mojo-path'] == null) { _logging.severe('Must specify --mojo-path to mojo_run'); return 1; } - String packageRoot = results['package-root']; + String packageRoot = argResults['package-root']; ArtifactStore artifacts = new ArtifactStore(packageRoot); - String appPath = await _makePathAbsolute(results['app']); - if (results['android']) { - return _runAndroid(results, appPath, artifacts); + String appPath = await _makePathAbsolute(argResults['app']); + if (argResults['android']) { + return _runAndroid(argResults, appPath, artifacts); } else { - return _runLinux(results, appPath, artifacts); + return _runLinux(argResults, appPath, artifacts); } } } diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 85adba7b72..0340dc5b74 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -4,13 +4,11 @@ import 'dart:io'; -import 'package:mockito/mockito.dart'; +import 'package:args/command_runner.dart'; import 'package:path/path.dart' as p; import 'package:sky_tools/src/init.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; - main() => defineTests(); defineTests() { @@ -27,14 +25,13 @@ defineTests() { // Verify that we create a project that is well-formed. test('init sky-simple', () async { - InitCommandHandler handler = new InitCommandHandler(); - MockArgResults results = new MockArgResults(); - when(results['help']).thenReturn(false); - when(results['pub']).thenReturn(true); - when(results.wasParsed('out')).thenReturn(true); - when(results['out']).thenReturn(temp.path); - await handler.processArgResults(results); - String path = p.join(temp.path, 'lib/main.dart'); + InitCommand command = new InitCommand(); + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + await runner.run(['init', '--out', temp.path]) + .then((int code) => expect(code, equals(0))); + + String path = p.join(temp.path, 'lib', 'main.dart'); expect(new File(path).existsSync(), true); ProcessResult exec = Process.runSync( 'dartanalyzer', ['--fatal-warnings', path], diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index 00e91806b3..f9c1c35c2e 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -4,9 +4,10 @@ library install_test; +import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/install.dart'; import 'package:test/test.dart'; import 'src/common.dart'; @@ -23,12 +24,11 @@ defineTests() { MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); when(android.installApp(any)).thenReturn(true); - InstallCommandHandler handler = new InstallCommandHandler(android); + InstallCommand command = new InstallCommand(android); - MockArgResults results = new MockArgResults(); - when(results['help']).thenReturn(false); - handler - .processArgResults(results) + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['install']) .then((int code) => expect(code, equals(0))); }); }); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 575dd97391..b49f2c3cba 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -2,15 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:args/args.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/device.dart'; -class MockArgResults extends Mock implements ArgResults { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - class MockAndroidDevice extends Mock implements AndroidDevice { @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); From 36c03cb40e8a56876b8d2ba9360aa866b232f563 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 11:13:15 -0700 Subject: [PATCH 074/188] Fix sky_tools cache clear command --- packages/flutter_tools/lib/src/cache.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 2faa7b65a4..21ff0cd0d9 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -32,7 +32,7 @@ class _ClearCommand extends Command { @override Future run() async { ArtifactStore artifacts = new ArtifactStore(argResults['package-root']); - await artifacts.populate(); + await artifacts.clear(); return 0; } } From b7fc56eb313fb9967cdd340187d748637ad58593 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 11:27:07 -0700 Subject: [PATCH 075/188] Make cached artifact location mirror local builds more closely --- packages/flutter_tools/lib/src/artifacts.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 717b363fba..ddc1808cb8 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -52,7 +52,11 @@ class ArtifactStore { Future _engineSpecificCacheDir() async { Directory cacheDir = await _cacheDir(); - Directory engineSpecificDir = new Directory(path.join(cacheDir.path, 'sky_engine', engineRevision)); + // For now, all downloaded artifacts are release mode host binaries so use + // a path that mirrors a local release build. + // TODO(jamesr): Add support for more configurations. + String config = 'Release'; + Directory engineSpecificDir = new Directory(path.join(cacheDir.path, 'sky_engine', engineRevision, config)); if (!await engineSpecificDir.exists()) { await engineSpecificDir.create(recursive: true); From c35c06e6f29033451f97eae1505146dc1f543aef Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 25 Sep 2015 13:28:21 -0700 Subject: [PATCH 076/188] rename sky ==> flutter in the starting app template --- packages/flutter_tools/lib/src/init.dart | 15 ++++++++------- packages/flutter_tools/test/init_test.dart | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 78456cb900..c058120288 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -13,7 +13,8 @@ import 'package:path/path.dart' as p; class InitCommand extends Command { final name = 'init'; - final description = 'Create a new sky project.'; + final description = 'Create a new Flutter project.'; + InitCommand() { argParser.addOption('out', abbr: 'o', help: 'The output directory.'); argParser.addFlag('pub', @@ -32,7 +33,7 @@ class InitCommand extends Command { // TODO: Confirm overwrite of an existing directory with the user. Directory out = new Directory(argResults['out']); - new SkySimpleTemplate().generateInto(out); + new FlutterSimpleTemplate().generateInto(out); print(''); @@ -89,8 +90,8 @@ abstract class Template { String toString() => name; } -class SkySimpleTemplate extends Template { - SkySimpleTemplate() : super('sky-simple', 'A minimal Sky project.') { +class FlutterSimpleTemplate extends Template { + FlutterSimpleTemplate() : super('flutter-simple', 'A minimal Flutter project.') { files['.gitignore'] = _gitignore; files['pubspec.yaml'] = _pubspec; files['README.md'] = _readme; @@ -124,8 +125,8 @@ const _readme = r''' ## Getting Started -For help getting started with Sky, view our online -[documentation](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md). +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). '''; const _pubspec = r''' @@ -145,7 +146,7 @@ void main() => runApp(new HelloWorldApp()); class HelloWorldApp extends App { Widget build() { return new Scaffold( - toolbar: new ToolBar(center: new Text("Sky Demo")), + toolbar: new ToolBar(center: new Text("Flutter Demo")), body: new Material(child: new Center(child: new Text("Hello world!"))), floatingActionButton: new FloatingActionButton( child: new Icon(type: 'content/add', size: 24))); diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 0340dc5b74..dc16b7cbac 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -24,7 +24,7 @@ defineTests() { }); // Verify that we create a project that is well-formed. - test('init sky-simple', () async { + test('init flutter-simple', () async { InitCommand command = new InitCommand(); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From f600beeb70e97a61ba50bf1d7d58a99159964462 Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 25 Sep 2015 14:27:08 -0700 Subject: [PATCH 077/188] run_mojo: Add --mojo-release and --mojo-debug flags. The flags control which build of mojo to run (Debug or Release). Default is Release. --- packages/flutter_tools/lib/src/run_mojo.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 5bfb8edb8f..cdc765b8c9 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -22,6 +22,9 @@ class RunMojoCommand extends Command { final description = 'Run a Flutter app in mojo.'; RunMojoCommand() { argParser.addFlag('android', negatable: false, help: 'Run on an Android device'); + argParser.addFlag('mojo-debug', negatable: false, help: 'Use Debug build of mojo'); + argParser.addFlag('mojo-release', negatable: false, help: 'Use Release build of mojo (default)'); + argParser.addOption('app', defaultsTo: 'app.flx'); argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); argParser.addOption('package-root', defaultsTo: 'packages'); @@ -40,9 +43,10 @@ class RunMojoCommand extends Command { String command = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo', 'devtools', 'common', 'mojo_run')); String appName = path.basename(appPath); String appDir = path.dirname(appPath); + String buildFlag = argResults['mojo-debug'] ? '--debug' : '--release'; List args = [ '--android', - '--release', + buildFlag, 'http://app/$appName', '--map-origin=http://app/=$appDir', '--map-origin=http://sky_viewer/=$skyViewerUrl', @@ -60,7 +64,8 @@ class RunMojoCommand extends Command { Future _runLinux(ArgResults results, String appPath, ArtifactStore artifacts) async { String viewerPath = await _makePathAbsolute(await artifacts.getPath(Artifact.SkyViewerMojo)); - String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', 'Release', 'mojo_shell')); + String mojoBuildType = argResults['mojo-debug'] ? 'Debug' : 'Release'; + String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', mojoBuildType, 'mojo_shell')); List args = [ 'file://${appPath}', '--url-mappings=mojo:sky_viewer=file://${viewerPath}' @@ -75,6 +80,10 @@ class RunMojoCommand extends Command { _logging.severe('Must specify --mojo-path to mojo_run'); return 1; } + if (argResults['mojo-debug'] && argResults['mojo-release']) { + _logging.severe('Cannot specify both --mojo-debug and --mojo-release'); + return 1; + } String packageRoot = argResults['package-root']; ArtifactStore artifacts = new ArtifactStore(packageRoot); String appPath = await _makePathAbsolute(argResults['app']); From 9996d4255e13648a8df1f2b4b59c670f585ee1af Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 15:43:03 -0700 Subject: [PATCH 078/188] Configure ArtifactStore for all commands and make 'package-root' universal This makes the 'package-root' option universal for sky_tools and configures the ArtifactStore with it statically at startup. The actual sky_engine revision is computed on demand. --- packages/flutter_tools/bin/sky_tools.dart | 5 ++++ packages/flutter_tools/lib/src/artifacts.dart | 28 +++++++++---------- packages/flutter_tools/lib/src/build.dart | 4 +-- packages/flutter_tools/lib/src/cache.dart | 12 ++------ packages/flutter_tools/lib/src/run_mojo.dart | 14 ++++------ 5 files changed, 28 insertions(+), 35 deletions(-) diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 6ce483df37..12052a8b0a 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -8,6 +8,7 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/artifacts.dart'; import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; @@ -72,6 +73,9 @@ class FlutterCommandRunner extends CommandRunner { 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' 'This path is relative to sky-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Release/'); + argParser.addOption('package-root', + help: 'Path to your packages directory.', + defaultsTo: 'packages'); } Future runCommand(ArgResults topLevelResults) async { @@ -89,6 +93,7 @@ class FlutterCommandRunner extends CommandRunner { } void _setupPaths(ArgResults results) { + ArtifactStore.packageRoot = results['package-root']; if (results['debug'] || results['release']) { if (results['sky-src-path'] == null) { // TODO(iansf): Figure out how to get the default src path diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index ddc1808cb8..1b95c80d10 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -15,21 +15,21 @@ final Logger _logging = new Logger('sky_tools.artifacts'); enum Artifact { FlutterCompiler, SkyViewerMojo, } class ArtifactStore { - String _engineRevision; - final String packageRoot; + static String packageRoot; + static String _engineRevision; - ArtifactStore(this.packageRoot) { - _engineRevision = new File(path.join(packageRoot, 'sky_engine', 'REVISION')).readAsStringSync(); + static String get engineRevision { + if (_engineRevision == null) + _engineRevision = new File(path.join(packageRoot, 'sky_engine', 'REVISION')).readAsStringSync(); + return _engineRevision; } - String get engineRevision => _engineRevision; - // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 - String googleStorageUrl(String category, String platform) { + static String googleStorageUrl(String category, String platform) { return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${engineRevision}/'; } - Future _downloadFile(String url, File file) async { + static Future _downloadFile(String url, File file) async { _logging.fine('Downloading $url to ${file.path}'); HttpClient httpClient = new HttpClient(); HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); @@ -42,7 +42,7 @@ class ArtifactStore { _logging.fine('Wrote file'); } - Future _cacheDir() async { + static Future _cacheDir() async { Directory cacheDir = new Directory(path.join(packageRoot, 'sky_tools', 'cache')); if (!await cacheDir.exists()) { await cacheDir.create(recursive: true); @@ -50,7 +50,7 @@ class ArtifactStore { return cacheDir; } - Future _engineSpecificCacheDir() async { + static Future _engineSpecificCacheDir() async { Directory cacheDir = await _cacheDir(); // For now, all downloaded artifacts are release mode host binaries so use // a path that mirrors a local release build. @@ -65,11 +65,11 @@ class ArtifactStore { } // Whether the artifact needs to be marked as executable on disk. - bool _needsToBeExecutable(Artifact artifact) { + static bool _needsToBeExecutable(Artifact artifact) { return artifact == Artifact.FlutterCompiler; } - Future getPath(Artifact artifact) async { + static Future getPath(Artifact artifact) async { Directory cacheDir = await _engineSpecificCacheDir(); String category, name; @@ -98,13 +98,13 @@ class ArtifactStore { return cachedFile.path; } - Future clear() async { + static Future clear() async { Directory cacheDir = await _cacheDir(); _logging.fine('Clearing cache directory ${cacheDir.path}'); await cacheDir.delete(recursive: true); } - Future populate() async { + static Future populate() async { for (Artifact artifact in Artifact.values) { _logging.fine('Populating cache with $artifact'); await getPath(artifact); diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index 80c195b8d1..f8fa12a13c 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -105,8 +105,7 @@ Future _compileSnapshot({ String snapshotPath }) async { if (compilerPath == null) { - ArtifactStore artifacts = new ArtifactStore(packageRoot); - compilerPath = await artifacts.getPath(Artifact.FlutterCompiler); + compilerPath = await ArtifactStore.getPath(Artifact.FlutterCompiler); } ProcessResult result = await Process.run(compilerPath, [ mainPath, @@ -135,7 +134,6 @@ class BuildCommand extends Command { argParser.addOption('main', defaultsTo: 'lib/main.dart'); argParser.addOption('manifest'); argParser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); - argParser.addOption('package-root', defaultsTo: 'packages'); argParser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin'); } diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 21ff0cd0d9..5571217e33 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -25,14 +25,10 @@ class CacheCommand extends Command { class _ClearCommand extends Command { final name = 'clear'; final description = 'Clears all artifacts from the cache.'; - _ClearCommand() { - argParser.addOption('package-root', defaultsTo: 'packages'); - } @override Future run() async { - ArtifactStore artifacts = new ArtifactStore(argResults['package-root']); - await artifacts.clear(); + await ArtifactStore.clear(); return 0; } } @@ -40,14 +36,10 @@ class _ClearCommand extends Command { class _PopulateCommand extends Command { final name = 'populate'; final description = 'Populates the cache with all known artifacts.'; - _PopulateCommand() { - argParser.addOption('package-root', defaultsTo: 'packages'); - } @override Future run() async { - ArtifactStore artifacts = new ArtifactStore(argResults['package-root']); - await artifacts.populate(); + await ArtifactStore.populate(); return 0; } } diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index cdc765b8c9..916de1cd80 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -38,8 +38,8 @@ class RunMojoCommand extends Command { return file.absolute.path; } - Future _runAndroid(ArgResults results, String appPath, ArtifactStore artifacts) async { - String skyViewerUrl = artifacts.googleStorageUrl('viewer', 'android-arm'); + Future _runAndroid(ArgResults results, String appPath) async { + String skyViewerUrl = ArtifactStore.googleStorageUrl('viewer', 'android-arm'); String command = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo', 'devtools', 'common', 'mojo_run')); String appName = path.basename(appPath); String appDir = path.dirname(appPath); @@ -62,8 +62,8 @@ class RunMojoCommand extends Command { return runCommandAndStreamOutput(command, args); } - Future _runLinux(ArgResults results, String appPath, ArtifactStore artifacts) async { - String viewerPath = await _makePathAbsolute(await artifacts.getPath(Artifact.SkyViewerMojo)); + Future _runLinux(ArgResults results, String appPath) async { + String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.SkyViewerMojo)); String mojoBuildType = argResults['mojo-debug'] ? 'Debug' : 'Release'; String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', mojoBuildType, 'mojo_shell')); List args = [ @@ -84,13 +84,11 @@ class RunMojoCommand extends Command { _logging.severe('Cannot specify both --mojo-debug and --mojo-release'); return 1; } - String packageRoot = argResults['package-root']; - ArtifactStore artifacts = new ArtifactStore(packageRoot); String appPath = await _makePathAbsolute(argResults['app']); if (argResults['android']) { - return _runAndroid(argResults, appPath, artifacts); + return _runAndroid(argResults, appPath); } else { - return _runLinux(argResults, appPath, artifacts); + return _runLinux(argResults, appPath); } } } From ed2877c09306b0a70077579df4a69153f13e8c88 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 15:59:36 -0700 Subject: [PATCH 079/188] Add --checked flag to mojo_run that runs sky_viewer in checked mode Also refactors the command line assembling to make a bit more sense. Fixes #53 --- packages/flutter_tools/lib/src/run_mojo.dart | 31 +++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 916de1cd80..9e26caab57 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -7,7 +7,6 @@ library sky_tools.run_mojo; import 'dart:async'; import 'dart:io'; -import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; @@ -17,11 +16,14 @@ import 'process.dart'; final Logger _logging = new Logger('sky_tools.run_mojo'); +enum _MojoConfig { Debug, Release } + class RunMojoCommand extends Command { final name = 'run_mojo'; final description = 'Run a Flutter app in mojo.'; RunMojoCommand() { argParser.addFlag('android', negatable: false, help: 'Run on an Android device'); + argParser.addFlag('checked', negatable: false, help: 'Run Flutter in checked mode'); argParser.addFlag('mojo-debug', negatable: false, help: 'Use Debug build of mojo'); argParser.addFlag('mojo-release', negatable: false, help: 'Use Release build of mojo (default)'); @@ -38,12 +40,12 @@ class RunMojoCommand extends Command { return file.absolute.path; } - Future _runAndroid(ArgResults results, String appPath) async { + Future _runAndroid(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { String skyViewerUrl = ArtifactStore.googleStorageUrl('viewer', 'android-arm'); - String command = await _makePathAbsolute(path.join(results['mojo-path'], 'mojo', 'devtools', 'common', 'mojo_run')); + String command = await _makePathAbsolute(path.join(mojoPath, 'mojo', 'devtools', 'common', 'mojo_run')); String appName = path.basename(appPath); String appDir = path.dirname(appPath); - String buildFlag = argResults['mojo-debug'] ? '--debug' : '--release'; + String buildFlag = mojoConfig == _MojoConfig.Debug ? '--debug' : '--release'; List args = [ '--android', buildFlag, @@ -58,19 +60,19 @@ class RunMojoCommand extends Command { args.add('--verbose'); } } - args.addAll(results.rest); + args.addAll(additionalArgs); return runCommandAndStreamOutput(command, args); } - Future _runLinux(ArgResults results, String appPath) async { + Future _runLinux(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.SkyViewerMojo)); - String mojoBuildType = argResults['mojo-debug'] ? 'Debug' : 'Release'; - String mojoShellPath = await _makePathAbsolute(path.join(results['mojo-path'], 'out', mojoBuildType, 'mojo_shell')); + String mojoBuildType = mojoConfig == _MojoConfig.Debug ? 'Debug' : 'Release'; + String mojoShellPath = await _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); List args = [ 'file://${appPath}', '--url-mappings=mojo:sky_viewer=file://${viewerPath}' ]; - args.addAll(results.rest); + args.addAll(additionalArgs); return runCommandAndStreamOutput(mojoShellPath, args); } @@ -84,11 +86,18 @@ class RunMojoCommand extends Command { _logging.severe('Cannot specify both --mojo-debug and --mojo-release'); return 1; } + List args = []; + if (argResults['checked']) { + args.add('--args-for=mojo:sky_viewer --enable-checked-mode'); + } + String mojoPath = argResults['mojo-path']; + _MojoConfig mojoConfig = argResults['mojo-debug'] ? _MojoConfig.Debug : _MojoConfig.Release; String appPath = await _makePathAbsolute(argResults['app']); + if (argResults['android']) { - return _runAndroid(argResults, appPath); + return _runAndroid(mojoPath, mojoConfig, appPath, args); } else { - return _runLinux(argResults, appPath); + return _runLinux(mojoPath, mojoConfig, appPath, args); } } } From fa59233746674fb15232e3571b9004f98b86c70b Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 25 Sep 2015 16:16:19 -0700 Subject: [PATCH 080/188] Add stop command and supporting Android support. --- packages/flutter_tools/bin/sky_tools.dart | 12 +++-- .../lib/src/application_package.dart | 10 +++- packages/flutter_tools/lib/src/device.dart | 26 ++++++++++ packages/flutter_tools/lib/src/stop.dart | 47 +++++++++++++++++++ packages/flutter_tools/test/stop_test.dart | 34 ++++++++++++++ 5 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 packages/flutter_tools/lib/src/stop.dart create mode 100644 packages/flutter_tools/test/stop_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 12052a8b0a..920ea5ecbb 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -14,9 +14,11 @@ import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/run_mojo.dart'; +import 'package:sky_tools/src/stop.dart'; class FlutterCommandRunner extends CommandRunner { - FlutterCommandRunner() : super('flutter', 'Manage your flutter app development.') { + FlutterCommandRunner() + : super('flutter', 'Manage your flutter app development.') { argParser.addFlag('verbose', abbr: 'v', negatable: false, @@ -41,7 +43,8 @@ class FlutterCommandRunner extends CommandRunner { 'not set. Note that release is not compatible with the listen command ' 'on iOS devices and simulators. Not normally required.'); argParser.addOption('sky-src-path', - help: 'Path to your Sky src directory, if you are building Sky locally. ' + help: + 'Path to your Sky src directory, if you are building Sky locally. ' 'Ignored if neither debug nor release is set. Not normally required.'); argParser.addOption('android-debug-build-path', help: @@ -120,8 +123,8 @@ class FlutterCommandRunner extends CommandRunner { ApplicationPackageFactory.defaultBuildType = BuildType.release; ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.android, results['android-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS, - results['ios-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.iOS, results['ios-release-build-path']); ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); } @@ -141,6 +144,7 @@ void main(List args) { }); new FlutterCommandRunner() + ..addCommand(new StopCommand()) ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 37b7e5ed3f..0739f77f0f 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -29,10 +29,16 @@ abstract class ApplicationPackage { class AndroidApk extends ApplicationPackage { static const String _apkName = 'SkyShell.apk'; - static const String _androidPackage = 'org.domokit.sky.shell'; + static const String _packageID = 'org.domokit.sky.shell'; + static const String _componentID = '$_packageID/$_packageID.SkyActivity'; + /// The path to the activity that should be launched. + /// Defaults to 'org.domokit.sky.shell/org.domokit.sky.shell.SkyActivity' + String component; AndroidApk(String appDir, - [String appPackageID = _androidPackage, String appFileName = _apkName]) + {String appPackageID: _packageID, + String appFileName: _apkName, + this.component: _componentID}) : super(path.join(appDir, 'apks'), appPackageID, appFileName); } diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 85e66dd224..22db7de79c 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -52,6 +52,7 @@ abstract class _Device { class AndroidDevice extends _Device { static const String _ADB_PATH = 'adb'; + static const String _serverPort = '9888'; static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default'; @@ -232,6 +233,31 @@ class AndroidDevice extends _Device { return true; } + + bool stop(AndroidApk apk) { + // Turn off reverse port forwarding + try { + runCheckedSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); + } catch (e) {} + // Stop the app + runCheckedSync([adbPath, 'shell', 'am', 'force-stop', apk.appPackageID]); + // Kill the server + try { + if (Platform.isMacOS) { + String pid = runCheckedSync(['lsof', '-i', ':$_serverPort', '-t']); + // Killing a pid with a shell command from within dart is hard, + // so use a library command, but it's still nice to give the + // equivalent command when doing verbose logging. + _logging.info('kill $pid'); + Process.killPid(int.parse(pid)); + } else { + runCheckedSync(['fuser', '-k', '$_serverPort/tcp']); + } + } catch (e) {} + + return true; + } + @override bool isConnected() => _hasValidAndroid; } diff --git a/packages/flutter_tools/lib/src/stop.dart b/packages/flutter_tools/lib/src/stop.dart new file mode 100644 index 0000000000..e2f288c27a --- /dev/null +++ b/packages/flutter_tools/lib/src/stop.dart @@ -0,0 +1,47 @@ +// 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. + +library sky_tools.stop; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/device.dart'; + +final Logger _logging = new Logger('sky_tools.stop'); + +class StopCommand extends Command { + final name = 'stop'; + final description = 'Stop your Flutter app on all attached devices.'; + AndroidDevice android = null; + + StopCommand([this.android]) { + if (android == null) { + android = new AndroidDevice(); + } + } + + @override + Future run() async { + if (stop()) { + return 0; + } else { + return 2; + } + } + + bool stop() { + bool stoppedSomething = false; + if (android.isConnected()) { + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + stoppedSomething = android.stop(androidApp) || stoppedSomething; + } + + return stoppedSomething; + } +} diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart new file mode 100644 index 0000000000..ff53eda398 --- /dev/null +++ b/packages/flutter_tools/test/stop_test.dart @@ -0,0 +1,34 @@ +// 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. + +library stop_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/stop.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('stop', () { + test('returns 0 when Android is connected and ready to be stopped', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(true); + when(android.stop(any)).thenReturn(true); + StopCommand command = new StopCommand(android); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['stop']).then((int code) => expect(code, equals(0))); + }); + }); +} From 9e2a83cfa3312293d45bf5e4548d5fd3571bc438 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 16:25:48 -0700 Subject: [PATCH 081/188] Fix sky_tools build command to look for package-root in global args --- packages/flutter_tools/lib/src/build.dart | 2 +- packages/flutter_tools/lib/src/run_mojo.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/build.dart index f8fa12a13c..3bef1f4079 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/build.dart @@ -150,7 +150,7 @@ class BuildCommand extends Command { await _compileSnapshot( compilerPath: argResults['compiler'], mainPath: argResults['main'], - packageRoot: argResults['package-root'], + packageRoot: globalResults['package-root'], snapshotPath: snapshotPath); archive.addFile(await _createSnapshotFile(snapshotPath)); diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 916de1cd80..f0d185d4d6 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -27,7 +27,6 @@ class RunMojoCommand extends Command { argParser.addOption('app', defaultsTo: 'app.flx'); argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); - argParser.addOption('package-root', defaultsTo: 'packages'); } Future _makePathAbsolute(String relativePath) async { From f7e20f4a4689c9fd203dc9dca9dcf91edb892bc3 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 25 Sep 2015 17:42:28 -0700 Subject: [PATCH 082/188] Check that the platform is linux before downloading linux-x64 binaries --- packages/flutter_tools/lib/src/artifacts.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 1b95c80d10..85d51cfd47 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -88,6 +88,8 @@ class ArtifactStore { File cachedFile = new File(path.join(cacheDir.path, name)); if (!await cachedFile.exists()) { _logging.info('Downloading ${name} from the cloud, one moment please...'); + if (!Platform.isLinux) + throw new Exception('Platform unsupported.'); String url = googleStorageUrl(category, 'linux-x64') + name; await _downloadFile(url, cachedFile); if (_needsToBeExecutable(artifact)) { From 8cac55a4e2d718dca83934f4f9bb6538cf0f6bd0 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 25 Sep 2015 16:29:47 -0700 Subject: [PATCH 083/188] Add sky_tools start command and associated android support. --- packages/flutter_tools/bin/sky_tools.dart | 9 +-- packages/flutter_tools/lib/src/device.dart | 63 ++++++++++++++++++ packages/flutter_tools/lib/src/install.dart | 16 +++-- packages/flutter_tools/lib/src/start.dart | 71 +++++++++++++++++++++ packages/flutter_tools/test/start_test.dart | 35 ++++++++++ 5 files changed, 184 insertions(+), 10 deletions(-) create mode 100644 packages/flutter_tools/lib/src/start.dart create mode 100644 packages/flutter_tools/test/start_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 920ea5ecbb..3ee3ebc888 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -14,11 +14,12 @@ import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/run_mojo.dart'; +import 'package:sky_tools/src/start.dart'; import 'package:sky_tools/src/stop.dart'; class FlutterCommandRunner extends CommandRunner { FlutterCommandRunner() - : super('flutter', 'Manage your flutter app development.') { + : super('flutter', 'Manage your Flutter app development.') { argParser.addFlag('verbose', abbr: 'v', negatable: false, @@ -77,8 +78,7 @@ class FlutterCommandRunner extends CommandRunner { 'This path is relative to sky-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Release/'); argParser.addOption('package-root', - help: 'Path to your packages directory.', - defaultsTo: 'packages'); + help: 'Path to your packages directory.', defaultsTo: 'packages'); } Future runCommand(ArgResults topLevelResults) async { @@ -144,11 +144,12 @@ void main(List args) { }); new FlutterCommandRunner() - ..addCommand(new StopCommand()) ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) ..addCommand(new RunMojoCommand()) + ..addCommand(new StartCommand()) + ..addCommand(new StopCommand()) ..run(args); } diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 22db7de79c..0cb5953865 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -4,7 +4,9 @@ library sky_tools.device; +import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; @@ -52,6 +54,7 @@ abstract class _Device { class AndroidDevice extends _Device { static const String _ADB_PATH = 'adb'; + static const String _observatoryPort = '8181'; static const String _serverPort = '9888'; static const String className = 'AndroidDevice'; @@ -233,6 +236,66 @@ class AndroidDevice extends _Device { return true; } + Future startServer( + String target, bool poke, bool checked, AndroidApk apk) async { + String serverRoot = ''; + String mainDart = ''; + String missingMessage = ''; + if (await FileSystemEntity.isDirectory(target)) { + serverRoot = target; + mainDart = path.join(serverRoot, 'lib', 'main.dart'); + missingMessage = 'Missing lib/main.dart in project: $serverRoot'; + } else { + serverRoot = Directory.current.path; + mainDart = target; + missingMessage = '$mainDart does not exist.'; + } + + if (!await FileSystemEntity.isFile(mainDart)) { + _logging.severe(missingMessage); + return false; + } + + // Set up port forwarding for observatory. + String observatoryPortString = 'tcp:$_observatoryPort'; + runCheckedSync( + [adbPath, 'forward', observatoryPortString, observatoryPortString]); + + // Actually start the server. + await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort], + workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); + + // Set up reverse port-forwarding so that the Android app can reach the + // server running on localhost. + String serverPortString = 'tcp:$_serverPort'; + runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]); + + String relativeDartMain = path.relative(mainDart, from: serverRoot); + String url = 'http://localhost:$_serverPort/$relativeDartMain'; + if (poke) { + url += '?rand=${new Random().nextDouble()}'; + } + + // Actually launch the app on Android. + List cmd = [ + adbPath, + 'shell', + 'am', + 'start', + '-a', + 'android.intent.action.VIEW', + '-d', + url, + ]; + if (checked) { + cmd.addAll(['--ez', 'enable-checked-mode', 'true']); + } + cmd.add(apk.component); + + runCheckedSync(cmd); + + return true; + } bool stop(AndroidApk apk) { // Turn off reverse port forwarding diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index 93943aeff7..c9db6a22e1 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -19,6 +19,14 @@ class InstallCommand extends Command { @override Future run() async { + if (install()) { + return 0; + } else { + return 2; + } + } + + bool install() { bool installedSomewhere = false; Map packages = @@ -28,13 +36,9 @@ class InstallCommand extends Command { } ApplicationPackage androidApp = packages[BuildPlatform.android]; if (androidApp != null && android.isConnected()) { - installedSomewhere = installedSomewhere || android.installApp(androidApp); + installedSomewhere = android.installApp(androidApp) || installedSomewhere; } - if (installedSomewhere) { - return 0; - } else { - return 2; - } + return installedSomewhere; } } diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart new file mode 100644 index 0000000000..acca2589ac --- /dev/null +++ b/packages/flutter_tools/lib/src/start.dart @@ -0,0 +1,71 @@ +// 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. + +library sky_tools.start; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/device.dart'; +import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/stop.dart'; + +final Logger _logging = new Logger('sky_tools.start'); + +class StartCommand extends Command { + final name = 'start'; + final description = 'Start your Flutter app on attached devices.'; + AndroidDevice android = null; + + StartCommand([this.android]) { + argParser.addFlag('poke', + negatable: false, + help: 'Restart the connection to the server (Android only).'); + argParser.addFlag('checked', + negatable: true, + defaultsTo: true, + help: 'Toggle Dart\'s checked mode.'); + argParser.addOption('target', + defaultsTo: '.', + abbr: 't', + help: 'Target app path or filename to start.'); + if (android == null) { + android = new AndroidDevice(); + } + } + + @override + Future run() async { + bool startedSomewhere = false; + bool poke = argResults['poke']; + if (!poke) { + StopCommand stopper = new StopCommand(android); + stopper.stop(); + + // Only install if the user did not specify a poke + InstallCommand installer = new InstallCommand(android); + startedSomewhere = installer.install(); + } + + bool startedOnAndroid = false; + if (android.isConnected()) { + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + + String target = path.absolute(argResults['target']); + startedOnAndroid = await android.startServer( + target, poke, argResults['checked'], androidApp); + } + + if (startedSomewhere || startedOnAndroid) { + return 0; + } else { + return 2; + } + } +} diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart new file mode 100644 index 0000000000..d3cbe5eb04 --- /dev/null +++ b/packages/flutter_tools/test/start_test.dart @@ -0,0 +1,35 @@ +// 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. + +library start_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/start.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('start', () { + test('returns 0 when Android is connected and ready to be started', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(true); + when(android.installApp(any)).thenReturn(true); + when(android.stop(any)).thenReturn(true); + StartCommand command = new StartCommand(android); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['start']).then((int code) => expect(code, equals(0))); + }); + }); +} From 61bfe5ce846e4b541d12f1382315225cc07cd50d Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 29 Sep 2015 10:16:10 -0700 Subject: [PATCH 084/188] Unify process_wrapper and process to have the same api and logging styles. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add unchecked runSync wrapper and use it in places where command failures don’t matter. --- packages/flutter_tools/lib/src/device.dart | 30 +++++++-------- packages/flutter_tools/lib/src/process.dart | 38 +++++++++++++++++-- .../lib/src/process_wrapper.dart | 26 ------------- packages/flutter_tools/lib/src/run_mojo.dart | 18 +++++---- 4 files changed, 57 insertions(+), 55 deletions(-) delete mode 100644 packages/flutter_tools/lib/src/process_wrapper.dart diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 0cb5953865..25579f93b1 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -12,7 +12,7 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'application_package.dart'; -import 'process_wrapper.dart'; +import 'process.dart'; final Logger _logging = new Logger('sky_tools.device'); @@ -299,24 +299,20 @@ class AndroidDevice extends _Device { bool stop(AndroidApk apk) { // Turn off reverse port forwarding - try { - runCheckedSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); - } catch (e) {} + runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); // Stop the app - runCheckedSync([adbPath, 'shell', 'am', 'force-stop', apk.appPackageID]); + runSync([adbPath, 'shell', 'am', 'force-stop', apk.appPackageID]); // Kill the server - try { - if (Platform.isMacOS) { - String pid = runCheckedSync(['lsof', '-i', ':$_serverPort', '-t']); - // Killing a pid with a shell command from within dart is hard, - // so use a library command, but it's still nice to give the - // equivalent command when doing verbose logging. - _logging.info('kill $pid'); - Process.killPid(int.parse(pid)); - } else { - runCheckedSync(['fuser', '-k', '$_serverPort/tcp']); - } - } catch (e) {} + if (Platform.isMacOS) { + String pid = runSync(['lsof', '-i', ':$_serverPort', '-t']); + // Killing a pid with a shell command from within dart is hard, + // so use a library command, but it's still nice to give the + // equivalent command when doing verbose logging. + _logging.info('kill $pid'); + Process.killPid(int.parse(pid)); + } else { + runSync(['fuser', '-k', '$_serverPort/tcp']); + } return true; } diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 898ab86197..3aad66029b 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -12,10 +12,12 @@ import 'package:logging/logging.dart'; final Logger _logging = new Logger('sky_tools.process'); -// This runs the command and streams stdout/stderr from the child process to this process' stdout/stderr. -Future runCommandAndStreamOutput(String command, List args) async { - _logging.fine("Starting ${command} with args: ${args}"); - Process proc = await Process.start(command, args); +/// This runs the command and streams stdout/stderr from the child process to +/// this process' stdout/stderr. +Future runCommandAndStreamOutput(List cmd) async { + _logging.info(cmd.join(' ')); + Process proc = + await Process.start(cmd[0], cmd.getRange(1, cmd.length).toList()); proc.stdout.transform(UTF8.decoder).listen((data) { stdout.write(data); }); @@ -24,3 +26,31 @@ Future runCommandAndStreamOutput(String command, List args) async { }); return proc.exitCode; } + +/// Run cmd and return stdout. +/// Throws an error if cmd exits with a non-zero value. +String runCheckedSync(List cmd) => + _runWithLoggingSync(cmd, checked: true); + +/// Run cmd and return stdout. +String runSync(List cmd) => _runWithLoggingSync(cmd); + +String _runWithLoggingSync(List cmd, {bool checked: false}) { + _logging.info(cmd.join(' ')); + ProcessResult results = + Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList()); + if (results.exitCode != 0) { + String errorDescription = 'Error code ${results.exitCode} ' + 'returned when attempting to run command: ${cmd.join(' ')}'; + _logging.fine(errorDescription); + if (results.stderr.length > 0) { + _logging.info('Errors logged: ${results.stderr.trim()}'); + } + + if (checked) { + throw errorDescription; + } + } + _logging.fine(results.stdout.trim()); + return results.stdout; +} diff --git a/packages/flutter_tools/lib/src/process_wrapper.dart b/packages/flutter_tools/lib/src/process_wrapper.dart deleted file mode 100644 index 3aaec4da75..0000000000 --- a/packages/flutter_tools/lib/src/process_wrapper.dart +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - -library sky_tools.process_wrapper; - -import 'dart:io'; -import 'package:logging/logging.dart'; - -final Logger _logging = new Logger('sky_tools.process_wrapper'); -String runCheckedSync(List cmd) { - _logging.info(cmd.join(' ')); - ProcessResult results = - Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList()); - if (results.exitCode != 0) { - if (results.stderr.length > 0) { - _logging.info('Errors logged: ' + results.stderr); - } - throw 'Error code ' + - results.exitCode.toString() + - ' returned when attempting to run command: ' + - cmd.join(' '); - } - _logging.fine(results.stdout.trim()); - return results.stdout; -} diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index 42b53839ce..e71bfd280d 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -45,7 +45,8 @@ class RunMojoCommand extends Command { String appName = path.basename(appPath); String appDir = path.dirname(appPath); String buildFlag = mojoConfig == _MojoConfig.Debug ? '--debug' : '--release'; - List args = [ + List cmd = [ + command, '--android', buildFlag, 'http://app/$appName', @@ -54,25 +55,26 @@ class RunMojoCommand extends Command { '--url-mappings=mojo:sky_viewer=http://sky_viewer/sky_viewer.mojo', ]; if (_logging.level <= Level.INFO) { - args.add('--verbose'); + cmd.add('--verbose'); if (_logging.level <= Level.FINE) { - args.add('--verbose'); + cmd.add('--verbose'); } } - args.addAll(additionalArgs); - return runCommandAndStreamOutput(command, args); + cmd.addAll(additionalArgs); + return runCommandAndStreamOutput(cmd); } Future _runLinux(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.SkyViewerMojo)); String mojoBuildType = mojoConfig == _MojoConfig.Debug ? 'Debug' : 'Release'; String mojoShellPath = await _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); - List args = [ + List cmd = [ + mojoShellPath, 'file://${appPath}', '--url-mappings=mojo:sky_viewer=file://${viewerPath}' ]; - args.addAll(additionalArgs); - return runCommandAndStreamOutput(mojoShellPath, args); + cmd.addAll(additionalArgs); + return runCommandAndStreamOutput(cmd); } @override From 5e11889160fff10d9995ea540bc97a756e5df933 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 29 Sep 2015 11:51:33 -0700 Subject: [PATCH 085/188] Add logs command and android implementation. --- packages/flutter_tools/bin/sky_tools.dart | 2 ++ packages/flutter_tools/lib/src/device.dart | 20 +++++++++++ packages/flutter_tools/lib/src/logs.dart | 42 ++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/flutter_tools/lib/src/logs.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 3ee3ebc888..7df07e1f93 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -13,6 +13,7 @@ import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/logs.dart'; import 'package:sky_tools/src/run_mojo.dart'; import 'package:sky_tools/src/start.dart'; import 'package:sky_tools/src/stop.dart'; @@ -148,6 +149,7 @@ void main(List args) { ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) + ..addCommand(new LogsCommand()) ..addCommand(new RunMojoCommand()) ..addCommand(new StartCommand()) ..addCommand(new StopCommand()) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 25579f93b1..6d9cc0f2a7 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -317,6 +317,26 @@ class AndroidDevice extends _Device { return true; } + void clearLogs() { + runSync([adbPath, 'logcat', '-c']); + } + + Future logs({bool clear: false}) { + if (clear) { + clearLogs(); + } + + return runCommandAndStreamOutput([ + adbPath, + 'logcat', + '-v', + 'tag', // Only log the tag and the message + '-s', + 'sky', + 'chromium', + ]); + } + @override bool isConnected() => _hasValidAndroid; } diff --git a/packages/flutter_tools/lib/src/logs.dart b/packages/flutter_tools/lib/src/logs.dart new file mode 100644 index 0000000000..841681f4ef --- /dev/null +++ b/packages/flutter_tools/lib/src/logs.dart @@ -0,0 +1,42 @@ +// 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. + +library sky_tools.logs; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:sky_tools/src/device.dart'; + +final Logger _logging = new Logger('sky_tools.logs'); + +class LogsCommand extends Command { + final name = 'logs'; + final description = 'Show logs for running Sky apps.'; + AndroidDevice android = null; + + LogsCommand([this.android]) { + argParser.addFlag('clear', + negatable: false, + help: 'Clear log history before reading from logs (Android only).'); + if (android == null) { + android = new AndroidDevice(); + } + } + + @override + Future run() async { + Future androidLogProcess = null; + if (android.isConnected()) { + androidLogProcess = android.logs(clear: argResults['clear']); + } + + if (androidLogProcess != null) { + await androidLogProcess; + } + + return 0; + } +} From a6635489f5b118e66deb7ec04f5988efe7de55b0 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 29 Sep 2015 14:15:29 -0700 Subject: [PATCH 086/188] Add optional prefix to runCommandAndStreamOutput for logs commands. --- packages/flutter_tools/lib/src/device.dart | 2 +- packages/flutter_tools/lib/src/process.dart | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 6d9cc0f2a7..66a02e97e2 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -334,7 +334,7 @@ class AndroidDevice extends _Device { '-s', 'sky', 'chromium', - ]); + ], prefix: 'ANDROID: '); } @override diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 3aad66029b..c703571bfe 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -14,15 +14,16 @@ final Logger _logging = new Logger('sky_tools.process'); /// This runs the command and streams stdout/stderr from the child process to /// this process' stdout/stderr. -Future runCommandAndStreamOutput(List cmd) async { +Future runCommandAndStreamOutput(List cmd, + {String prefix: ''}) async { _logging.info(cmd.join(' ')); Process proc = await Process.start(cmd[0], cmd.getRange(1, cmd.length).toList()); proc.stdout.transform(UTF8.decoder).listen((data) { - stdout.write(data); + stdout.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n'); }); proc.stderr.transform(UTF8.decoder).listen((data) { - stderr.write(data); + stderr.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n'); }); return proc.exitCode; } From 6a5fe1954022a7232342ee653ac8cf4f84bbf36b Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 29 Sep 2015 14:26:42 -0700 Subject: [PATCH 087/188] Basic sky_tools logs test. --- packages/flutter_tools/test/logs_test.dart | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/flutter_tools/test/logs_test.dart diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart new file mode 100644 index 0000000000..b26523ea17 --- /dev/null +++ b/packages/flutter_tools/test/logs_test.dart @@ -0,0 +1,33 @@ +// 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. + +library stop_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/logs.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('logs', () { + test('returns 0 when no device is connected', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + LogsCommand command = new LogsCommand(android); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['logs']).then((int code) => expect(code, equals(0))); + }); + }); +} From 00bed774ce0cf014c9ed664f8bd669c559224a0f Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 29 Sep 2015 17:10:47 -0700 Subject: [PATCH 088/188] =?UTF-8?q?Add=20listen=20command=20and=20basic=20?= =?UTF-8?q?test,=20and=20don=E2=80=99t=20do=20unnecessary=20repeated=20wor?= =?UTF-8?q?k=20when=20listening=20or=20poking=20the=20android=20server.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/flutter_tools/bin/sky_tools.dart | 2 + packages/flutter_tools/lib/src/device.dart | 24 ++-- packages/flutter_tools/lib/src/install.dart | 9 +- packages/flutter_tools/lib/src/listen.dart | 118 +++++++++++++++++++ packages/flutter_tools/test/listen_test.dart | 34 ++++++ 5 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 packages/flutter_tools/lib/src/listen.dart create mode 100644 packages/flutter_tools/test/listen_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 7df07e1f93..a0e2d71db2 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -13,6 +13,7 @@ import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/listen.dart'; import 'package:sky_tools/src/logs.dart'; import 'package:sky_tools/src/run_mojo.dart'; import 'package:sky_tools/src/start.dart'; @@ -149,6 +150,7 @@ void main(List args) { ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) + ..addCommand(new ListenCommand()) ..addCommand(new LogsCommand()) ..addCommand(new RunMojoCommand()) ..addCommand(new StartCommand()) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 66a02e97e2..f783b121bb 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -256,19 +256,21 @@ class AndroidDevice extends _Device { return false; } - // Set up port forwarding for observatory. - String observatoryPortString = 'tcp:$_observatoryPort'; - runCheckedSync( - [adbPath, 'forward', observatoryPortString, observatoryPortString]); + if (!poke) { + // Set up port forwarding for observatory. + String observatoryPortString = 'tcp:$_observatoryPort'; + runCheckedSync( + [adbPath, 'forward', observatoryPortString, observatoryPortString]); - // Actually start the server. - await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort], - workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); + // Actually start the server. + await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort], + workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); - // Set up reverse port-forwarding so that the Android app can reach the - // server running on localhost. - String serverPortString = 'tcp:$_serverPort'; - runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]); + // Set up reverse port-forwarding so that the Android app can reach the + // server running on localhost. + String serverPortString = 'tcp:$_serverPort'; + runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]); + } String relativeDartMain = path.relative(mainDart, from: serverRoot); String url = 'http://localhost:$_serverPort/$relativeDartMain'; diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index c9db6a22e1..f8f45239ac 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -15,7 +15,11 @@ class InstallCommand extends Command { final name = 'install'; final description = 'Install your Flutter app on attached devices.'; AndroidDevice android = null; - InstallCommand([this.android]); + InstallCommand([this.android]) { + if (android == null) { + android = new AndroidDevice(); + } + } @override Future run() async { @@ -31,9 +35,6 @@ class InstallCommand extends Command { Map packages = ApplicationPackageFactory.getAvailableApplicationPackages(); - if (android == null) { - android = new AndroidDevice(); - } ApplicationPackage androidApp = packages[BuildPlatform.android]; if (androidApp != null && android.isConnected()) { installedSomewhere = android.installApp(androidApp) || installedSomewhere; diff --git a/packages/flutter_tools/lib/src/listen.dart b/packages/flutter_tools/lib/src/listen.dart new file mode 100644 index 0000000000..47a3897928 --- /dev/null +++ b/packages/flutter_tools/lib/src/listen.dart @@ -0,0 +1,118 @@ +// 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. + +library sky_tools.listen; + +import 'dart:async'; +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/device.dart'; +import 'package:sky_tools/src/process.dart'; + +final Logger _logging = new Logger('sky_tools.listen'); + +class ListenCommand extends Command { + final name = 'listen'; + final description = 'Listen for changes to files and reload the running app ' + 'on all connected devices.'; + AndroidDevice android = null; + List watchCommand; + + /// Only run once. Used for testing. + bool singleRun; + + ListenCommand({this.android, this.singleRun: false}) { + argParser.addFlag('checked', + negatable: true, + defaultsTo: true, + help: 'Toggle Dart\'s checked mode.'); + argParser.addOption('target', + defaultsTo: '.', + abbr: 't', + help: 'Target app path or filename to start.'); + + if (android == null) { + android = new AndroidDevice(); + } + } + + @override + Future run() async { + if (argResults.rest.length > 0) { + watchCommand = _initWatchCommand(argResults.rest); + } else { + watchCommand = _initWatchCommand(['.']); + } + + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + + while (true) { + _logging.info('Updating running Sky apps...'); + + if (android.isConnected()) { + await android.startServer( + argResults['target'], true, argResults['checked'], androidApp); + } + + if (singleRun || !watchDirectory()) { + break; + } + } + + return 0; + } + + List _initWatchCommand(List directories) { + if (Platform.isMacOS) { + try { + runCheckedSync(['which', 'fswatch']); + } catch (e) { + _logging.severe('"listen" command is only useful if you have installed ' + 'fswatch on Mac. Run "brew install fswatch" to install it with ' + 'homebrew.'); + return null; + } + return ['fswatch', '-r', '-v', '-1']..addAll(directories); + } else if (Platform.isLinux) { + try { + runCheckedSync(['which', 'inotifywait']); + } catch (e) { + _logging.severe('"listen" command is only useful if you have installed ' + 'inotifywait on Linux. Run "apt-get install inotify-tools" or ' + 'equivalent to install it.'); + return null; + } + return [ + 'inotifywait', + '-r', + '-e', + // Only listen for events that matter, to avoid triggering constantly + // from the editor watching files + 'modify,close_write,move,create,delete', + ]..addAll(directories); + } else { + _logging.severe('"listen" command is only available on Mac and Linux.'); + } + return null; + } + + bool watchDirectory() { + if (watchCommand == null) { + return false; + } + + try { + runCheckedSync(watchCommand); + } catch (e) { + _logging.warning('Watching directories failed.', e); + return false; + } + return true; + } +} diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart new file mode 100644 index 0000000000..89510860a0 --- /dev/null +++ b/packages/flutter_tools/test/listen_test.dart @@ -0,0 +1,34 @@ +// 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. + +library stop_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/listen.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('listen', () { + test('returns 0 when no device is connected', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + ListenCommand command = + new ListenCommand(android: android, singleRun: true); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['listen']).then((int code) => expect(code, equals(0))); + }); + }); +} From b8085cd03365b1c63644b65fbda31d395cb36e96 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 30 Sep 2015 14:02:29 -0700 Subject: [PATCH 089/188] Add sky_tools trace command, Android implementation, and basic test. --- packages/flutter_tools/bin/sky_tools.dart | 2 + packages/flutter_tools/lib/src/device.dart | 56 +++++++++++++++++ packages/flutter_tools/lib/src/trace.dart | 69 +++++++++++++++++++++ packages/flutter_tools/test/trace_test.dart | 33 ++++++++++ 4 files changed, 160 insertions(+) create mode 100644 packages/flutter_tools/lib/src/trace.dart create mode 100644 packages/flutter_tools/test/trace_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index a0e2d71db2..a35d3e3f7c 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -18,6 +18,7 @@ import 'package:sky_tools/src/logs.dart'; import 'package:sky_tools/src/run_mojo.dart'; import 'package:sky_tools/src/start.dart'; import 'package:sky_tools/src/stop.dart'; +import 'package:sky_tools/src/trace.dart'; class FlutterCommandRunner extends CommandRunner { FlutterCommandRunner() @@ -155,5 +156,6 @@ void main(List args) { ..addCommand(new RunMojoCommand()) ..addCommand(new StartCommand()) ..addCommand(new StopCommand()) + ..addCommand(new TraceCommand()) ..run(args); } diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index f783b121bb..e75d17744f 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -339,6 +339,62 @@ class AndroidDevice extends _Device { ], prefix: 'ANDROID: '); } + void startTracing(AndroidApk apk) { + runCheckedSync([ + adbPath, + 'shell', + 'am', + 'broadcast', + '-a', + '${apk.appPackageID}.TRACING_START' + ]); + } + + String stopTracing(AndroidApk apk) { + clearLogs(); + runCheckedSync([ + adbPath, + 'shell', + 'am', + 'broadcast', + '-a', + '${apk.appPackageID}.TRACING_STOP' + ]); + + RegExp traceRegExp = new RegExp(r'Saving trace to (\S+)', multiLine: true); + RegExp completeRegExp = new RegExp(r'Trace complete', multiLine: true); + + String tracePath = null; + bool isComplete = false; + while (!isComplete) { + String logs = runSync([adbPath, 'logcat', '-d']); + Match fileMatch = traceRegExp.firstMatch(logs); + if (fileMatch[1] != null) { + tracePath = fileMatch[1]; + } + isComplete = completeRegExp.hasMatch(logs); + } + + if (tracePath != null) { + // adb root exits with 0 even if the command fails, + // so check the output string + String output = runSync([adbPath, 'root']); + if (new RegExp(r'.*cannot run as root.*').hasMatch(output)) { + _logging + .severe('Unable to download trace "${path.basename(tracePath)}"\n' + 'You need to be able to run adb as root ' + 'on your android device'); + return null; + } + runSync([adbPath, 'pull', tracePath]); + runSync([adbPath, 'shell', 'rm', tracePath]); + return path.basename(tracePath); + } + _logging.warning('No trace file detected. ' + 'Did you remember to start the trace before stopping it?'); + return null; + } + @override bool isConnected() => _hasValidAndroid; } diff --git a/packages/flutter_tools/lib/src/trace.dart b/packages/flutter_tools/lib/src/trace.dart new file mode 100644 index 0000000000..8d307f36cd --- /dev/null +++ b/packages/flutter_tools/lib/src/trace.dart @@ -0,0 +1,69 @@ +// 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. + +library sky_tools.trace; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/device.dart'; + +final Logger _logging = new Logger('sky_tools.trace'); + +class TraceCommand extends Command { + final name = 'trace'; + final description = 'Start and stop tracing a running Flutter app ' + '(Android only, requires root).\n' + 'To start a trace, wait, and then stop the trace, don\'t set any flags ' + 'except (optionally) duration.\n' + 'Otherwise, specify either start or stop to manually control the trace.'; + AndroidDevice android = null; + + TraceCommand([this.android]) { + argParser.addFlag('start', negatable: false, help: 'Start tracing.'); + argParser.addFlag('stop', negatable: false, help: 'Stop tracing.'); + argParser.addOption('duration', + defaultsTo: '10', abbr: 'd', help: 'Duration in seconds to trace.'); + if (android == null) { + android = new AndroidDevice(); + } + } + + @override + Future run() async { + if (!android.isConnected()) { + _logging.warning('No device connected, so no trace was completed.'); + return 1; + } + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + + if ((!argResults['start'] && !argResults['stop']) || + (argResults['start'] && argResults['stop'])) { + // Setting neither flags or both flags means do both commands and wait + // duration seconds in between. + android.startTracing(androidApp); + await new Future.delayed( + new Duration(seconds: int.parse(argResults['duration'])), + () => _stopTracing(androidApp)); + } else if (argResults['stop']) { + _stopTracing(androidApp); + } else { + android.startTracing(androidApp); + } + return 0; + } + + void _stopTracing(AndroidApk androidApp) { + String tracePath = android.stopTracing(androidApp); + if (tracePath == null) { + _logging.warning('No trace file saved.'); + } else { + _logging.warning('Trace file saved to $tracePath'); + } + } +} diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart new file mode 100644 index 0000000000..13059813a2 --- /dev/null +++ b/packages/flutter_tools/test/trace_test.dart @@ -0,0 +1,33 @@ +// 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. + +library trace_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; +import 'package:sky_tools/src/trace.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('trace', () { + test('returns 1 when no Android device is connected', () { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + TraceCommand command = new TraceCommand(android); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['trace']).then((int code) => expect(code, equals(1))); + }); + }); +} From d07ca9299591227bf50bc1976105c6f2cb96d10e Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 30 Sep 2015 15:24:39 -0700 Subject: [PATCH 090/188] Basic sky_tools list command, Android implementation, and test. --- packages/flutter_tools/bin/sky_tools.dart | 2 + packages/flutter_tools/lib/src/device.dart | 53 +++++++++++++++++-- packages/flutter_tools/lib/src/list.dart | 45 ++++++++++++++++ packages/flutter_tools/lib/src/trace.dart | 2 +- .../test/android_device_test.dart | 6 +-- packages/flutter_tools/test/list_test.dart | 30 +++++++++++ 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 packages/flutter_tools/lib/src/list.dart create mode 100644 packages/flutter_tools/test/list_test.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index a35d3e3f7c..efbd046cf9 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -13,6 +13,7 @@ import 'package:sky_tools/src/build.dart'; import 'package:sky_tools/src/cache.dart'; import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/list.dart'; import 'package:sky_tools/src/listen.dart'; import 'package:sky_tools/src/logs.dart'; import 'package:sky_tools/src/run_mojo.dart'; @@ -151,6 +152,7 @@ void main(List args) { ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) + ..addCommand(new ListCommand()) ..addCommand(new ListenCommand()) ..addCommand(new LogsCommand()) ..addCommand(new RunMojoCommand()) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index e75d17744f..6c15b3b9e8 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -60,13 +60,60 @@ class AndroidDevice extends _Device { static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default'; + String productID; + String modelID; + String deviceCodeName; + String _adbPath; String get adbPath => _adbPath; bool _hasAdb = false; bool _hasValidAndroid = false; - factory AndroidDevice([String id = null]) { - return new _Device(className, id); + factory AndroidDevice( + {String id: null, + String productID: null, + String modelID: null, + String deviceCodeName: null}) { + AndroidDevice device = new _Device(className, id); + device.productID = productID; + device.modelID = modelID; + device.deviceCodeName = deviceCodeName; + return device; + } + + /// mockAndroid argument is only to facilitate testing with mocks, so that + /// we don't have to rely on the test setup having adb available to it. + static List getAttachedDevices([AndroidDevice mockAndroid]) { + List devices = []; + String adbPath = _getAdbPath(); + if (mockAndroid != null) { + adbPath = mockAndroid.adbPath; + } + List output = + runSync([adbPath, 'devices', '-l']).trim().split('\n'); + RegExp deviceInfo = new RegExp( + r'^(\S+)\s+device\s+\S+\s+product:(\S+)\s+model:(\S+)\s+device:(\S+)$'); + // Skip first line, which is always 'List of devices attached'. + for (String line in output.skip(1)) { + Match match = deviceInfo.firstMatch(line); + if (match != null) { + String deviceID = match[1]; + String productID = match[2]; + String modelID = match[3]; + String deviceCodeName = match[4]; + + devices.add(new AndroidDevice( + id: deviceID, + productID: productID, + modelID: modelID, + deviceCodeName: deviceCodeName)); + } else { + _logging.warning('Unexpected failure parsing device information ' + 'from adb output:\n$line\n' + 'Please report a bug at http://flutter.io/'); + } + } + return devices; } AndroidDevice._(id) : super._(id) { @@ -83,7 +130,7 @@ class AndroidDevice extends _Device { } } - String _getAdbPath() { + static String _getAdbPath() { if (Platform.environment.containsKey('ANDROID_HOME')) { String androidHomeDir = Platform.environment['ANDROID_HOME']; String adbPath1 = diff --git a/packages/flutter_tools/lib/src/list.dart b/packages/flutter_tools/lib/src/list.dart new file mode 100644 index 0000000000..2270331172 --- /dev/null +++ b/packages/flutter_tools/lib/src/list.dart @@ -0,0 +1,45 @@ +// 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. + +library sky_tools.list; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; +import 'package:sky_tools/src/device.dart'; + +final Logger _logging = new Logger('sky_tools.list'); + +class ListCommand extends Command { + final name = 'list'; + final description = 'List all connected devices.'; + AndroidDevice android; + + ListCommand([this.android]) { + argParser.addFlag('details', + abbr: 'd', + negatable: false, + help: 'Log additional details about attached devices.'); + } + + @override + Future run() async { + bool details = argResults['details']; + if (details) { + print('Android Devices:'); + } + for (AndroidDevice device in AndroidDevice.getAttachedDevices(android)) { + if (details) { + print('${device.id}\t' + '${device.modelID}\t' + '${device.productID}\t' + '${device.deviceCodeName}'); + } else { + print(device.id); + } + } + return 0; + } +} diff --git a/packages/flutter_tools/lib/src/trace.dart b/packages/flutter_tools/lib/src/trace.dart index 8d307f36cd..4fbe1a2192 100644 --- a/packages/flutter_tools/lib/src/trace.dart +++ b/packages/flutter_tools/lib/src/trace.dart @@ -63,7 +63,7 @@ class TraceCommand extends Command { if (tracePath == null) { _logging.warning('No trace file saved.'); } else { - _logging.warning('Trace file saved to $tracePath'); + print('Trace file saved to $tracePath'); } } } diff --git a/packages/flutter_tools/test/android_device_test.dart b/packages/flutter_tools/test/android_device_test.dart index 2265f1c5d3..9841ebc348 100644 --- a/packages/flutter_tools/test/android_device_test.dart +++ b/packages/flutter_tools/test/android_device_test.dart @@ -18,14 +18,14 @@ defineTests() { test('stores the requested id', () { String deviceID = '1234'; - AndroidDevice android = new AndroidDevice(deviceID); + AndroidDevice android = new AndroidDevice(id: deviceID); expect(android.id, equals(deviceID)); }); test('correctly creates only one of each requested device id', () { String deviceID = '1234'; - AndroidDevice a1 = new AndroidDevice(deviceID); - AndroidDevice a2 = new AndroidDevice(deviceID); + AndroidDevice a1 = new AndroidDevice(id: deviceID); + AndroidDevice a2 = new AndroidDevice(id: deviceID); expect(a1, equals(a2)); }); }); diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart new file mode 100644 index 0000000000..19a9d26774 --- /dev/null +++ b/packages/flutter_tools/test/list_test.dart @@ -0,0 +1,30 @@ +// 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. + +library list_test; + +import 'package:args/command_runner.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/list.dart'; +import 'package:test/test.dart'; + +import 'src/common.dart'; + +main() => defineTests(); + +defineTests() { + group('list', () { + test('returns 0 when called', () { + MockAndroidDevice android = new MockAndroidDevice(); + // Avoid relying on adb being installed on the test system. + // Instead, cause the test to run the echo command. + when(android.adbPath).thenReturn('echo'); + + ListCommand command = new ListCommand(android); + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['list']).then((int code) => expect(code, equals(0))); + }); + }); +} From 6f640275bdbc58583a3d99e966f11b1f70716dc3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Oct 2015 14:58:29 -0700 Subject: [PATCH 091/188] Update dart project template --- packages/flutter_tools/lib/src/init.dart | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index c058120288..fb8a77d2b8 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -141,15 +141,6 @@ dev_dependencies: const _libMain = r''' import 'package:sky/widgets.dart'; -void main() => runApp(new HelloWorldApp()); +void main() => runApp(new Center(child: new Text('Hello, world!'))); -class HelloWorldApp extends App { - Widget build() { - return new Scaffold( - toolbar: new ToolBar(center: new Text("Flutter Demo")), - body: new Material(child: new Center(child: new Text("Hello world!"))), - floatingActionButton: new FloatingActionButton( - child: new Icon(type: 'content/add', size: 24))); - } -} '''; From 31498881ce63f4b88ca18e6d1bb4e9c317aef7c0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Oct 2015 15:40:36 -0700 Subject: [PATCH 092/188] Convert default project template to fn3 --- packages/flutter_tools/lib/src/init.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index fb8a77d2b8..87da609d94 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -141,6 +141,17 @@ dev_dependencies: const _libMain = r''' import 'package:sky/widgets.dart'; -void main() => runApp(new Center(child: new Text('Hello, world!'))); +void main() => runApp(new HelloWorldApp()); + +class HelloWorldApp extends StatelessComponent { + Widget build(BuildContext context) { + return new Scaffold( + toolbar: new ToolBar(center: new Text("Flutter Demo")), + body: new Material(child: new Center(child: new Text("Hello world!"))), + floatingActionButton: new FloatingActionButton( + child: new Icon(type: 'content/add', size: 24)) + ); + } +} '''; From 5c922df835b0fa8b2865488833fcadf74db3d2cb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Oct 2015 16:14:54 -0700 Subject: [PATCH 093/188] Use the App widget in the Flutter app template --- packages/flutter_tools/lib/src/init.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 87da609d94..a63505ecb4 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -141,9 +141,18 @@ dev_dependencies: const _libMain = r''' import 'package:sky/widgets.dart'; -void main() => runApp(new HelloWorldApp()); +void main() { + runApp( + new App( + title: "Flutter Demo", + routes: { + '/': (RouteArguments args) => new FlutterDemoHome() + } + ) + ); +} -class HelloWorldApp extends StatelessComponent { +class FlutterDemoHome extends StatelessComponent { Widget build(BuildContext context) { return new Scaffold( toolbar: new ToolBar(center: new Text("Flutter Demo")), From a15e8576588cdefdc81e8ebea5176c2439fe2163 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Oct 2015 16:52:16 -0700 Subject: [PATCH 094/188] Address PR concerns --- packages/flutter_tools/lib/src/init.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index a63505ecb4..b0cac56454 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -146,20 +146,21 @@ void main() { new App( title: "Flutter Demo", routes: { - '/': (RouteArguments args) => new FlutterDemoHome() + '/': (RouteArguments args) => new HelloWorldComponent() } ) ); } -class FlutterDemoHome extends StatelessComponent { +class HelloWorldComponent extends StatelessComponent { Widget build(BuildContext context) { return new Scaffold( toolbar: new ToolBar(center: new Text("Flutter Demo")), body: new Material(child: new Center(child: new Text("Hello world!"))), floatingActionButton: new FloatingActionButton( - child: new Icon(type: 'content/add', size: 24)) - ); + child: new Icon(type: 'content/add', size: 24) + ) + ); } } From ad155b23a53b9fe078d2b045539a919349a45b53 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 6 Oct 2015 10:53:29 -0700 Subject: [PATCH 095/188] Update pubspec revision to 0.0.16 --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 311a3424de..aec2802e54 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.15 +version: 0.0.16 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 0364590b83cc2416731fba06af8a923610e75f8d Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 11:36:48 -0700 Subject: [PATCH 096/188] delay looking for android devices --- packages/flutter_tools/lib/src/install.dart | 12 +++++++----- packages/flutter_tools/lib/src/listen.dart | 8 ++++---- packages/flutter_tools/lib/src/logs.dart | 7 ++++--- packages/flutter_tools/lib/src/start.dart | 7 ++++--- packages/flutter_tools/lib/src/stop.dart | 10 +++++----- packages/flutter_tools/lib/src/trace.dart | 7 ++++--- 6 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index f8f45239ac..f7ee8fc904 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -14,15 +14,17 @@ import 'device.dart'; class InstallCommand extends Command { final name = 'install'; final description = 'Install your Flutter app on attached devices.'; + AndroidDevice android = null; - InstallCommand([this.android]) { - if (android == null) { - android = new AndroidDevice(); - } - } + + InstallCommand([this.android]); @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + if (install()) { return 0; } else { diff --git a/packages/flutter_tools/lib/src/listen.dart b/packages/flutter_tools/lib/src/listen.dart index 47a3897928..31b00cf898 100644 --- a/packages/flutter_tools/lib/src/listen.dart +++ b/packages/flutter_tools/lib/src/listen.dart @@ -34,14 +34,14 @@ class ListenCommand extends Command { defaultsTo: '.', abbr: 't', help: 'Target app path or filename to start.'); - - if (android == null) { - android = new AndroidDevice(); - } } @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + if (argResults.rest.length > 0) { watchCommand = _initWatchCommand(argResults.rest); } else { diff --git a/packages/flutter_tools/lib/src/logs.dart b/packages/flutter_tools/lib/src/logs.dart index 841681f4ef..d3f90813d4 100644 --- a/packages/flutter_tools/lib/src/logs.dart +++ b/packages/flutter_tools/lib/src/logs.dart @@ -21,13 +21,14 @@ class LogsCommand extends Command { argParser.addFlag('clear', negatable: false, help: 'Clear log history before reading from logs (Android only).'); - if (android == null) { - android = new AndroidDevice(); - } } @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + Future androidLogProcess = null; if (android.isConnected()) { androidLogProcess = android.logs(clear: argResults['clear']); diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart index acca2589ac..dcc381e02c 100644 --- a/packages/flutter_tools/lib/src/start.dart +++ b/packages/flutter_tools/lib/src/start.dart @@ -33,13 +33,14 @@ class StartCommand extends Command { defaultsTo: '.', abbr: 't', help: 'Target app path or filename to start.'); - if (android == null) { - android = new AndroidDevice(); - } } @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + bool startedSomewhere = false; bool poke = argResults['poke']; if (!poke) { diff --git a/packages/flutter_tools/lib/src/stop.dart b/packages/flutter_tools/lib/src/stop.dart index e2f288c27a..c3edaf6d29 100644 --- a/packages/flutter_tools/lib/src/stop.dart +++ b/packages/flutter_tools/lib/src/stop.dart @@ -18,14 +18,14 @@ class StopCommand extends Command { final description = 'Stop your Flutter app on all attached devices.'; AndroidDevice android = null; - StopCommand([this.android]) { - if (android == null) { - android = new AndroidDevice(); - } - } + StopCommand([this.android]); @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + if (stop()) { return 0; } else { diff --git a/packages/flutter_tools/lib/src/trace.dart b/packages/flutter_tools/lib/src/trace.dart index 4fbe1a2192..830485100e 100644 --- a/packages/flutter_tools/lib/src/trace.dart +++ b/packages/flutter_tools/lib/src/trace.dart @@ -27,13 +27,14 @@ class TraceCommand extends Command { argParser.addFlag('stop', negatable: false, help: 'Stop tracing.'); argParser.addOption('duration', defaultsTo: '10', abbr: 'd', help: 'Duration in seconds to trace.'); - if (android == null) { - android = new AndroidDevice(); - } } @override Future run() async { + if (android == null) { + android = new AndroidDevice(); + } + if (!android.isConnected()) { _logging.warning('No device connected, so no trace was completed.'); return 1; From 12192d00c104b5269058ce55a46a2a4cdb289fbe Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 2 Oct 2015 08:34:09 -0700 Subject: [PATCH 097/188] Beginning implementation of IOSDevice. Implements list and install. Also update tests to be compatible with the presence of iOS and add tests for list and install. --- .../lib/src/application_package.dart | 17 ++- packages/flutter_tools/lib/src/device.dart | 121 +++++++++++++++++- packages/flutter_tools/lib/src/install.dart | 20 ++- packages/flutter_tools/lib/src/list.dart | 15 ++- packages/flutter_tools/lib/src/start.dart | 5 +- packages/flutter_tools/test/init_test.dart | 4 +- packages/flutter_tools/test/install_test.dart | 35 +++-- packages/flutter_tools/test/list_test.dart | 11 +- packages/flutter_tools/test/listen_test.dart | 5 +- packages/flutter_tools/test/logs_test.dart | 5 +- packages/flutter_tools/test/src/common.dart | 14 ++ packages/flutter_tools/test/start_test.dart | 12 +- packages/flutter_tools/test/stop_test.dart | 5 +- packages/flutter_tools/test/trace_test.dart | 5 +- 14 files changed, 229 insertions(+), 45 deletions(-) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 0739f77f0f..34afb4bcea 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -42,6 +42,15 @@ class AndroidApk extends ApplicationPackage { : super(path.join(appDir, 'apks'), appPackageID, appFileName); } +class IOSApp extends ApplicationPackage { + static const String _appName = 'SkyShell.app'; + static const String _packageID = 'com.google.SkyShell'; + + IOSApp(String appDir, + {String appPackageID: _packageID, String appFileName: _appName}) + : super(appDir, appPackageID, appFileName); +} + enum BuildType { prebuilt, release, debug, } enum BuildPlatform { android, iOS, iOSSimulator, mac, linux, } @@ -62,7 +71,10 @@ class ApplicationPackageFactory { static BuildType defaultBuildType = BuildType.prebuilt; /// Default BuildPlatforms chosen if no BuildPlatforms are specified. - static List defaultBuildPlatforms = [BuildPlatform.android]; + static List defaultBuildPlatforms = [ + BuildPlatform.android, + BuildPlatform.iOS, + ]; static Map getAvailableApplicationPackages( {BuildType requestedType, List requestedPlatforms}) { @@ -80,6 +92,9 @@ class ApplicationPackageFactory { case BuildPlatform.android: packages[platform] = new AndroidApk(buildPath); break; + case BuildPlatform.iOS: + packages[platform] = new IOSApp(buildPath); + break; default: // TODO(iansf): Add other platforms assert(false); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 6c15b3b9e8..b692a071ce 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -24,6 +24,8 @@ abstract class _Device { if (id == null) { if (className == AndroidDevice.className) { id = AndroidDevice.defaultDeviceID; + } else if (className == IOSDevice.className) { + id = IOSDevice.defaultDeviceID; } else { throw 'Attempted to create a Device of unknown type $className'; } @@ -34,6 +36,10 @@ abstract class _Device { final device = new AndroidDevice._(id); _deviceCache[id] = device; return device; + } else if (className == IOSDevice.className) { + final device = new IOSDevice._(id); + _deviceCache[id] = device; + return device; } else { throw 'Attempted to create a Device of unknown type $className'; } @@ -52,13 +58,120 @@ abstract class _Device { bool isAppInstalled(ApplicationPackage app); } +class IOSDevice extends _Device { + static const String className = 'IOSDevice'; + static final String defaultDeviceID = 'default_ios_id'; + + static const String _macInstructions = + 'To work with iOS devices, please install ideviceinstaller. ' + 'If you use homebrew, you can install it with ' + '"\$ brew install ideviceinstaller".'; + static const String _linuxInstructions = + 'To work with iOS devices, please install ideviceinstaller. ' + 'On Ubuntu or Debian, you can install it with ' + '"\$ apt-get install ideviceinstaller".'; + + String _installerPath; + String get installerPath => _installerPath; + + String _listerPath; + String get listerPath => _listerPath; + + String _informerPath; + String get informerPath => _informerPath; + + String _name; + String get name => _name; + + factory IOSDevice({String id, String name}) { + IOSDevice device = new _Device(className, id); + device._name = name; + return device; + } + + IOSDevice._(String id) : super._(id) { + _installerPath = _checkForCommand('ideviceinstaller'); + _listerPath = _checkForCommand('idevice_id'); + _informerPath = _checkForCommand('ideviceinfo'); + } + + static List getAttachedDevices([IOSDevice mockIOS]) { + List devices = []; + for (String id in _getAttachedDeviceIDs(mockIOS)) { + String name = _getDeviceName(id, mockIOS); + devices.add(new IOSDevice(id: id, name: name)); + } + return devices; + } + + static List _getAttachedDeviceIDs([IOSDevice mockIOS]) { + String listerPath = + (mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id'); + return runSync([listerPath, '-l']).trim().split('\n'); + } + + static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) { + String informerPath = (mockIOS != null) + ? mockIOS.informerPath + : _checkForCommand('ideviceinfo'); + return runSync([informerPath, '-k', 'DeviceName', '-u', deviceID]); + } + + static final Map _commandMap = {}; + static String _checkForCommand(String command, + [String macInstructions = _macInstructions, + String linuxInstructions = _linuxInstructions]) { + return _commandMap.putIfAbsent(command, () { + try { + command = runCheckedSync(['which', command]).trim(); + } catch (e) { + if (Platform.isMacOS) { + _logging.severe(macInstructions); + } else if (Platform.isLinux) { + _logging.severe(linuxInstructions); + } else { + _logging.severe('$command is not available on your platform.'); + } + exit(2); + } + return command; + }); + } + + @override + bool installApp(ApplicationPackage app) { + if (id == defaultDeviceID) { + runCheckedSync([installerPath, '-i', app.appPath]); + } else { + runCheckedSync([installerPath, '-u', id, '-i', app.appPath]); + } + return false; + } + + @override + bool isConnected() { + List ids = _getAttachedDeviceIDs(); + for (String id in ids) { + if (id == this.id || this.id == defaultDeviceID) { + return true; + } + } + return false; + } + + @override + bool isAppInstalled(ApplicationPackage app) { + return false; + } +} + class AndroidDevice extends _Device { static const String _ADB_PATH = 'adb'; static const String _observatoryPort = '8181'; static const String _serverPort = '9888'; static const String className = 'AndroidDevice'; - static final String defaultDeviceID = 'default'; + static final String defaultDeviceID = 'default_android_device'; String productID; String modelID; @@ -85,10 +198,8 @@ class AndroidDevice extends _Device { /// we don't have to rely on the test setup having adb available to it. static List getAttachedDevices([AndroidDevice mockAndroid]) { List devices = []; - String adbPath = _getAdbPath(); - if (mockAndroid != null) { - adbPath = mockAndroid.adbPath; - } + String adbPath = + (mockAndroid != null) ? mockAndroid.adbPath : _getAdbPath(); List output = runSync([adbPath, 'devices', '-l']).trim().split('\n'); RegExp deviceInfo = new RegExp( diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart index f7ee8fc904..d29ded1df3 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/install.dart @@ -16,15 +16,12 @@ class InstallCommand extends Command { final description = 'Install your Flutter app on attached devices.'; AndroidDevice android = null; + IOSDevice ios; - InstallCommand([this.android]); + InstallCommand({this.android, this.ios}); @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } - if (install()) { return 0; } else { @@ -33,15 +30,28 @@ class InstallCommand extends Command { } bool install() { + if (android == null) { + android = new AndroidDevice(); + } + if (ios == null) { + ios = new IOSDevice(); + } + bool installedSomewhere = false; Map packages = ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; + ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + if (androidApp != null && android.isConnected()) { installedSomewhere = android.installApp(androidApp) || installedSomewhere; } + if (iosApp != null && ios.isConnected()) { + installedSomewhere = ios.installApp(iosApp) || installedSomewhere; + } + return installedSomewhere; } } diff --git a/packages/flutter_tools/lib/src/list.dart b/packages/flutter_tools/lib/src/list.dart index 2270331172..752fe35659 100644 --- a/packages/flutter_tools/lib/src/list.dart +++ b/packages/flutter_tools/lib/src/list.dart @@ -16,8 +16,9 @@ class ListCommand extends Command { final name = 'list'; final description = 'List all connected devices.'; AndroidDevice android; + IOSDevice ios; - ListCommand([this.android]) { + ListCommand({this.android, this.ios}) { argParser.addFlag('details', abbr: 'd', negatable: false, @@ -40,6 +41,18 @@ class ListCommand extends Command { print(device.id); } } + + if (details) { + print('iOS Devices:'); + } + for (IOSDevice device in IOSDevice.getAttachedDevices(ios)) { + if (details) { + print('${device.id}\t${device.name}'); + } else { + print(device.id); + } + } + return 0; } } diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart index dcc381e02c..08af1a186d 100644 --- a/packages/flutter_tools/lib/src/start.dart +++ b/packages/flutter_tools/lib/src/start.dart @@ -20,8 +20,9 @@ class StartCommand extends Command { final name = 'start'; final description = 'Start your Flutter app on attached devices.'; AndroidDevice android = null; + IOSDevice ios = null; - StartCommand([this.android]) { + StartCommand({this.android, this.ios}) { argParser.addFlag('poke', negatable: false, help: 'Restart the connection to the server (Android only).'); @@ -48,7 +49,7 @@ class StartCommand extends Command { stopper.stop(); // Only install if the user did not specify a poke - InstallCommand installer = new InstallCommand(android); + InstallCommand installer = new InstallCommand(android: android, ios: ios); startedSomewhere = installer.install(); } diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index dc16b7cbac..12846be1e6 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This test can take a while due to network requests +@Timeout(const Duration(seconds: 60)) import 'dart:io'; import 'package:args/command_runner.dart'; @@ -27,7 +29,7 @@ defineTests() { test('init flutter-simple', () async { InitCommand command = new InitCommand(); CommandRunner runner = new CommandRunner('test_flutter', '') - ..addCommand(command); + ..addCommand(command); await runner.run(['init', '--out', temp.path]) .then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index f9c1c35c2e..ff9a91fcc8 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -6,7 +6,6 @@ library install_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/install.dart'; import 'package:test/test.dart'; @@ -17,19 +16,39 @@ main() => defineTests(); defineTests() { group('install', () { test('returns 0 when Android is connected and ready for an install', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); when(android.installApp(any)).thenReturn(true); - InstallCommand command = new InstallCommand(android); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(false); + when(ios.installApp(any)).thenReturn(false); + + InstallCommand command = new InstallCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') - ..addCommand(command); - runner.run(['install']) - .then((int code) => expect(code, equals(0))); + ..addCommand(command); + runner.run(['install']).then((int code) => expect(code, equals(0))); + }); + + test('returns 0 when iOS is connected and ready for an install', () { + applicationPackageSetup(); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + when(android.installApp(any)).thenReturn(false); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(true); + when(ios.installApp(any)).thenReturn(true); + + InstallCommand command = new InstallCommand(android: android, ios: ios); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['install']).then((int code) => expect(code, equals(0))); }); }); } diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index 19a9d26774..2ff1cff888 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -16,12 +16,21 @@ main() => defineTests(); defineTests() { group('list', () { test('returns 0 when called', () { + applicationPackageSetup(); + MockAndroidDevice android = new MockAndroidDevice(); // Avoid relying on adb being installed on the test system. // Instead, cause the test to run the echo command. when(android.adbPath).thenReturn('echo'); - ListCommand command = new ListCommand(android); + MockIOSDevice ios = new MockIOSDevice(); + // Avoid relying on idevice* being installed on the test system. + // Instead, cause the test to run the echo command. + when(ios.informerPath).thenReturn('echo'); + when(ios.installerPath).thenReturn('echo'); + when(ios.listerPath).thenReturn('echo'); + + ListCommand command = new ListCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); runner.run(['list']).then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index 89510860a0..75e142d56d 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -6,7 +6,6 @@ library stop_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/listen.dart'; import 'package:test/test.dart'; @@ -17,9 +16,7 @@ main() => defineTests(); defineTests() { group('listen', () { test('returns 0 when no device is connected', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(false); diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index b26523ea17..534c4faa75 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -6,7 +6,6 @@ library stop_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/logs.dart'; import 'package:test/test.dart'; @@ -17,9 +16,7 @@ main() => defineTests(); defineTests() { group('logs', () { test('returns 0 when no device is connected', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(false); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index b49f2c3cba..8903b16392 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -3,9 +3,23 @@ // found in the LICENSE file. import 'package:mockito/mockito.dart'; +import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/device.dart'; class MockAndroidDevice extends Mock implements AndroidDevice { @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } + +class MockIOSDevice extends Mock implements IOSDevice { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +void applicationPackageSetup() { + ApplicationPackageFactory.srcPath = './'; + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.android, './'); + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.iOS, './'); +} diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index d3cbe5eb04..732f18b29d 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -6,7 +6,6 @@ library start_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/start.dart'; import 'package:test/test.dart'; @@ -17,15 +16,18 @@ main() => defineTests(); defineTests() { group('start', () { test('returns 0 when Android is connected and ready to be started', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); when(android.installApp(any)).thenReturn(true); when(android.stop(any)).thenReturn(true); - StartCommand command = new StartCommand(android); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(false); + when(ios.installApp(any)).thenReturn(false); + + StartCommand command = new StartCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index ff53eda398..af6ec19159 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -6,7 +6,6 @@ library stop_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/stop.dart'; import 'package:test/test.dart'; @@ -17,9 +16,7 @@ main() => defineTests(); defineTests() { group('stop', () { test('returns 0 when Android is connected and ready to be stopped', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart index 13059813a2..726c78fc1d 100644 --- a/packages/flutter_tools/test/trace_test.dart +++ b/packages/flutter_tools/test/trace_test.dart @@ -6,7 +6,6 @@ library trace_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/trace.dart'; import 'package:test/test.dart'; @@ -17,9 +16,7 @@ main() => defineTests(); defineTests() { group('trace', () { test('returns 1 when no Android device is connected', () { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); + applicationPackageSetup(); MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(false); From 524d8379f475bd1b289b4ad49ceb77c65c847662 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 12:08:08 -0700 Subject: [PATCH 098/188] more verbose test failures --- packages/flutter_tools/test/init_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index dc16b7cbac..f4d113b7fc 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -36,6 +36,10 @@ defineTests() { ProcessResult exec = Process.runSync( 'dartanalyzer', ['--fatal-warnings', path], workingDirectory: temp.path); + if (exec.exitCode != 0) { + print(exec.stdout); + print(exec.stderr); + } expect(exec.exitCode, 0); }); }); From 60cf54d6c2bb4f37920aa3cd21ef82b6d1605be4 Mon Sep 17 00:00:00 2001 From: Alex Fandrianto Date: Tue, 6 Oct 2015 13:14:22 -0700 Subject: [PATCH 099/188] Forward additionalArgs in run_mojo.dart In 0.0.15, additional cmdline arguments were not dropped, but now in 0.0.16, the arguments are no longer forwarded. This patchset would restore this forwarding functionality. For example, the following command is intended to pass `enable-multiprocess` to the mojo_shell. `enable-multiprocess` in 0.0.16 does not get passed along, but this patchset would allow it to once again. pub run sky_tools -v --very-verbose run_mojo \ --mojo-path $MOJO_DIR/src \ --app app.flx --android \ -- \ --enable-multiprocess --- packages/flutter_tools/lib/src/run_mojo.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/run_mojo.dart index e71bfd280d..1b32b08a88 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/run_mojo.dart @@ -95,6 +95,7 @@ class RunMojoCommand extends Command { _MojoConfig mojoConfig = argResults['mojo-debug'] ? _MojoConfig.Debug : _MojoConfig.Release; String appPath = await _makePathAbsolute(argResults['app']); + args.addAll(argResults.rest); if (argResults['android']) { return _runAndroid(mojoPath, mojoConfig, appPath, args); } else { From 15cba8f2ebcf179d9a43d0caf6e6837c130c3249 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 14:35:18 -0700 Subject: [PATCH 100/188] rev the pubspec version --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index aec2802e54..6084c43339 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.16 +version: 0.0.17 description: Tools for building Sky applications homepage: https://github.com/domokit/sky_tools author: Chromium Authors From 5c6c3d9d81694c92f5d8f41ca650b73ad4a2ad2b Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 14:52:45 -0700 Subject: [PATCH 101/188] ws --- packages/flutter_tools/lib/src/init.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index b0cac56454..a4e2fe93c0 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -163,5 +163,4 @@ class HelloWorldComponent extends StatelessComponent { ); } } - '''; From f98f134ccfb00cd25e0269993fcbb2d73eeee6f6 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 15:48:04 -0700 Subject: [PATCH 102/188] ws changes to prod the build --- packages/flutter_tools/test/listen_test.dart | 2 +- packages/flutter_tools/test/logs_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index 75e142d56d..6a96ed8c99 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library stop_test; +library listen_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index 534c4faa75..19a5872dd8 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library stop_test; +library logs_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; From 369451761781472cf03a2d28eac155fbd97504bd Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 6 Oct 2015 16:00:24 -0700 Subject: [PATCH 103/188] run tests sequentially --- packages/flutter_tools/test/init_test.dart | 2 ++ packages/flutter_tools/tool/travis.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 8c3d8a30a3..8a68804922 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -4,6 +4,8 @@ // This test can take a while due to network requests @Timeout(const Duration(seconds: 60)) +library init_test; + import 'dart:io'; import 'package:args/command_runner.dart'; diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index a926214e6f..13a64ae8bb 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -12,4 +12,4 @@ pub global activate tuneup pub global run tuneup check # And run our tests. -pub run test +pub run test -j1 From 7990e874dfb11199c3714a2c72a694dc4f013bf1 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 6 Oct 2015 16:19:06 -0700 Subject: [PATCH 104/188] =?UTF-8?q?Just=20log,=20don=E2=80=99t=20exit=20wh?= =?UTF-8?q?en=20the=20user=20is=20missing=20access=20to=20a=20command.=20P?= =?UTF-8?q?revents=20inappropriately-failing=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/flutter_tools/lib/src/device.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index b692a071ce..e0f058e074 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -132,7 +132,6 @@ class IOSDevice extends _Device { } else { _logging.severe('$command is not available on your platform.'); } - exit(2); } return command; }); From d92f3fedde03c0071ad172d70be86186a0758c21 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 7 Oct 2015 11:26:55 -0700 Subject: [PATCH 105/188] Fix Flutter project template --- packages/flutter_tools/lib/src/init.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index a4e2fe93c0..4b61d6d3d6 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -155,7 +155,7 @@ void main() { class HelloWorldComponent extends StatelessComponent { Widget build(BuildContext context) { return new Scaffold( - toolbar: new ToolBar(center: new Text("Flutter Demo")), + toolBar: new ToolBar(center: new Text("Flutter Demo")), body: new Material(child: new Center(child: new Text("Hello world!"))), floatingActionButton: new FloatingActionButton( child: new Icon(type: 'content/add', size: 24) From 72cc4d6fde1dd9356c8dbe9ca9857692632838d1 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 7 Oct 2015 15:29:13 -0700 Subject: [PATCH 106/188] =?UTF-8?q?Don=E2=80=99t=20give=20a=20scary=20erro?= =?UTF-8?q?r=20about=20not=20being=20able=20to=20run=20on=20Android=20if?= =?UTF-8?q?=20there=E2=80=99s=20no=20Android=20device=20connected.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/flutter_tools/lib/src/device.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index e0f058e074..ed1d9ae536 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -230,13 +230,15 @@ class AndroidDevice extends _Device { _adbPath = _getAdbPath(); _hasAdb = _checkForAdb(); - // Checking for lollipop only needs to be done if we are starting an - // app, but it has an important side effect, which is to discard any - // progress messages if the adb server is restarted. - _hasValidAndroid = _checkForLollipopOrLater(); + if (isConnected()) { + // Checking for lollipop only needs to be done if we are starting an + // app, but it has an important side effect, which is to discard any + // progress messages if the adb server is restarted. + _hasValidAndroid = _checkForLollipopOrLater(); - if (!_hasAdb || !_hasValidAndroid) { - _logging.severe('Unable to run on Android.'); + if (!_hasAdb || !_hasValidAndroid) { + _logging.severe('Unable to run on Android.'); + } } } From 844678ddce73a17143cfa8f367bda7c900601a98 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Wed, 7 Oct 2015 15:13:24 -0700 Subject: [PATCH 107/188] Add implementation of start and stop commands for iOS. --- packages/flutter_tools/lib/src/device.dart | 50 ++++++++++++++++++++- packages/flutter_tools/lib/src/process.dart | 12 +++++ packages/flutter_tools/lib/src/start.dart | 18 ++++++-- packages/flutter_tools/lib/src/stop.dart | 30 ++++++++----- packages/flutter_tools/test/start_test.dart | 27 ++++++++++- packages/flutter_tools/test/stop_test.dart | 27 ++++++++++- 6 files changed, 146 insertions(+), 18 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index e0f058e074..fc07ceeb3b 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -56,6 +56,12 @@ abstract class _Device { /// Check if the current version of the given app is already installed bool isAppInstalled(ApplicationPackage app); + + /// Start an app package on the current device + Future startApp(ApplicationPackage app); + + /// Stop an app package on the current device + Future stopApp(ApplicationPackage app); } class IOSDevice extends _Device { @@ -80,6 +86,9 @@ class IOSDevice extends _Device { String _informerPath; String get informerPath => _informerPath; + String _debuggerPath; + String get debuggerPath => _debuggerPath; + String _name; String get name => _name; @@ -93,6 +102,7 @@ class IOSDevice extends _Device { _installerPath = _checkForCommand('ideviceinstaller'); _listerPath = _checkForCommand('idevice_id'); _informerPath = _checkForCommand('ideviceinfo'); + _debuggerPath = _checkForCommand('idevicedebug'); } static List getAttachedDevices([IOSDevice mockIOS]) { @@ -160,6 +170,37 @@ class IOSDevice extends _Device { @override bool isAppInstalled(ApplicationPackage app) { + try { + String apps = runCheckedSync([installerPath, '-l']); + if (new RegExp(app.appPackageID, multiLine: true).hasMatch(apps)) { + return true; + } + } catch (e) { + return false; + } + return false; + } + + @override + Future startApp(ApplicationPackage app) async { + if (!isAppInstalled(app)) { + return false; + } + // idevicedebug hangs forever after launching the app, so kill it after + // giving it plenty of time to send the launch command. + return runAndKill( + [debuggerPath, 'run', app.appPackageID], new Duration(seconds: 3)).then( + (_) { + return true; + }, onError: (e) { + _logging.info('Failure running $debuggerPath: ', e); + return false; + }); + } + + @override + Future stopApp(ApplicationPackage app) async { + // Currently we don't have a way to stop an app running on iOS. return false; } } @@ -456,7 +497,14 @@ class AndroidDevice extends _Device { return true; } - bool stop(AndroidApk apk) { + @override + Future startApp(AndroidApk apk) async { + // Android currently has to be started with startServer(...). + assert(false); + return false; + } + + Future stopApp(AndroidApk apk) async { // Turn off reverse port forwarding runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); // Stop the app diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index c703571bfe..b0402505bb 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -28,6 +28,18 @@ Future runCommandAndStreamOutput(List cmd, return proc.exitCode; } +Future runAndKill(List cmd, Duration timeout) async { + _logging.info(cmd.join(' ')); + Future proc = Process.start( + cmd[0], cmd.getRange(1, cmd.length).toList(), + mode: ProcessStartMode.DETACHED); + + return new Future.delayed(timeout, () async { + _logging.info('Intentionally killing ${cmd[0]}'); + Process.killPid((await proc).pid); + }); +} + /// Run cmd and return stdout. /// Throws an error if cmd exits with a non-zero value. String runCheckedSync(List cmd) => diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart index 08af1a186d..8d2dbc0dc2 100644 --- a/packages/flutter_tools/lib/src/start.dart +++ b/packages/flutter_tools/lib/src/start.dart @@ -41,22 +41,26 @@ class StartCommand extends Command { if (android == null) { android = new AndroidDevice(); } + if (ios == null) { + ios = new IOSDevice(); + } bool startedSomewhere = false; bool poke = argResults['poke']; if (!poke) { - StopCommand stopper = new StopCommand(android); + StopCommand stopper = new StopCommand(android: android, ios: ios); stopper.stop(); // Only install if the user did not specify a poke InstallCommand installer = new InstallCommand(android: android, ios: ios); - startedSomewhere = installer.install(); + installer.install(); } + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + bool startedOnAndroid = false; if (android.isConnected()) { - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; String target = path.absolute(argResults['target']); @@ -64,6 +68,12 @@ class StartCommand extends Command { target, poke, argResults['checked'], androidApp); } + if (ios.isConnected()) { + ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + + startedSomewhere = await ios.startApp(iosApp) || startedSomewhere; + } + if (startedSomewhere || startedOnAndroid) { return 0; } else { diff --git a/packages/flutter_tools/lib/src/stop.dart b/packages/flutter_tools/lib/src/stop.dart index c3edaf6d29..d1f9844f0e 100644 --- a/packages/flutter_tools/lib/src/stop.dart +++ b/packages/flutter_tools/lib/src/stop.dart @@ -17,29 +17,39 @@ class StopCommand extends Command { final name = 'stop'; final description = 'Stop your Flutter app on all attached devices.'; AndroidDevice android = null; + IOSDevice ios = null; - StopCommand([this.android]); + StopCommand({this.android, this.ios}); @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } - - if (stop()) { + if (await stop()) { return 0; } else { return 2; } } - bool stop() { + Future stop() async { + if (android == null) { + android = new AndroidDevice(); + } + if (ios == null) { + ios = new IOSDevice(); + } + bool stoppedSomething = false; + Map packages = + ApplicationPackageFactory.getAvailableApplicationPackages(); + if (android.isConnected()) { - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; - stoppedSomething = android.stop(androidApp) || stoppedSomething; + stoppedSomething = await android.stopApp(androidApp) || stoppedSomething; + } + + if (ios.isConnected()) { + ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + stoppedSomething = await ios.stopApp(iosApp) || stoppedSomething; } return stoppedSomething; diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index 732f18b29d..9ddfb0e999 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -21,11 +21,36 @@ defineTests() { MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); when(android.installApp(any)).thenReturn(true); - when(android.stop(any)).thenReturn(true); + when(android.startServer(any, any, any, any)).thenReturn(true); + when(android.stopApp(any)).thenReturn(true); MockIOSDevice ios = new MockIOSDevice(); when(ios.isConnected()).thenReturn(false); when(ios.installApp(any)).thenReturn(false); + when(ios.startApp(any)).thenReturn(false); + when(ios.startApp(any)).thenReturn(false); + + StartCommand command = new StartCommand(android: android, ios: ios); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['start']).then((int code) => expect(code, equals(0))); + }); + + test('returns 0 when iOS is connected and ready to be started', () { + applicationPackageSetup(); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + when(android.installApp(any)).thenReturn(false); + when(android.startServer(any, any, any, any)).thenReturn(false); + when(android.stopApp(any)).thenReturn(false); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(true); + when(ios.installApp(any)).thenReturn(true); + when(ios.startApp(any)).thenReturn(true); + when(ios.stopApp(any)).thenReturn(false); StartCommand command = new StartCommand(android: android, ios: ios); diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index af6ec19159..8d67d662bd 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -20,8 +20,31 @@ defineTests() { MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(true); - when(android.stop(any)).thenReturn(true); - StopCommand command = new StopCommand(android); + when(android.stopApp(any)).thenReturn(true); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(false); + when(ios.stopApp(any)).thenReturn(false); + + StopCommand command = new StopCommand(android: android, ios: ios); + + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + runner.run(['stop']).then((int code) => expect(code, equals(0))); + }); + + test('returns 0 when iOS is connected and ready to be stopped', () { + applicationPackageSetup(); + + MockAndroidDevice android = new MockAndroidDevice(); + when(android.isConnected()).thenReturn(false); + when(android.stopApp(any)).thenReturn(false); + + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(true); + when(ios.stopApp(any)).thenReturn(true); + + StopCommand command = new StopCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From f4ec459c233f7ddec274670918640836d621bd1b Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Thu, 8 Oct 2015 10:51:43 -0700 Subject: [PATCH 108/188] Add support for log commands on iOS. --- packages/flutter_tools/lib/src/device.dart | 9 +++++++++ packages/flutter_tools/lib/src/logs.dart | 17 ++++++++++++++-- packages/flutter_tools/lib/src/process.dart | 22 ++++++++++++++++----- packages/flutter_tools/test/logs_test.dart | 5 ++++- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 418ba521df..e51d27d076 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -89,6 +89,9 @@ class IOSDevice extends _Device { String _debuggerPath; String get debuggerPath => _debuggerPath; + String _loggerPath; + String get loggerPath => _loggerPath; + String _name; String get name => _name; @@ -103,6 +106,7 @@ class IOSDevice extends _Device { _listerPath = _checkForCommand('idevice_id'); _informerPath = _checkForCommand('ideviceinfo'); _debuggerPath = _checkForCommand('idevicedebug'); + _loggerPath = _checkForCommand('idevicesyslog'); } static List getAttachedDevices([IOSDevice mockIOS]) { @@ -203,6 +207,11 @@ class IOSDevice extends _Device { // Currently we don't have a way to stop an app running on iOS. return false; } + + Future logs({bool clear: false}) { + return runCommandAndStreamOutput([loggerPath], + prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*')); + } } class AndroidDevice extends _Device { diff --git a/packages/flutter_tools/lib/src/logs.dart b/packages/flutter_tools/lib/src/logs.dart index d3f90813d4..518b75e985 100644 --- a/packages/flutter_tools/lib/src/logs.dart +++ b/packages/flutter_tools/lib/src/logs.dart @@ -15,9 +15,10 @@ final Logger _logging = new Logger('sky_tools.logs'); class LogsCommand extends Command { final name = 'logs'; final description = 'Show logs for running Sky apps.'; - AndroidDevice android = null; + AndroidDevice android; + IOSDevice ios; - LogsCommand([this.android]) { + LogsCommand({this.android, this.ios}) { argParser.addFlag('clear', negatable: false, help: 'Clear log history before reading from logs (Android only).'); @@ -28,16 +29,28 @@ class LogsCommand extends Command { if (android == null) { android = new AndroidDevice(); } + if (ios == null) { + ios = new IOSDevice(); + } Future androidLogProcess = null; if (android.isConnected()) { androidLogProcess = android.logs(clear: argResults['clear']); } + Future iosLogProcess = null; + if (ios.isConnected()) { + iosLogProcess = ios.logs(clear: argResults['clear']); + } + if (androidLogProcess != null) { await androidLogProcess; } + if (iosLogProcess != null) { + await iosLogProcess; + } + return 0; } } diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index b0402505bb..7be82b2f8f 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -15,15 +15,27 @@ final Logger _logging = new Logger('sky_tools.process'); /// This runs the command and streams stdout/stderr from the child process to /// this process' stdout/stderr. Future runCommandAndStreamOutput(List cmd, - {String prefix: ''}) async { + {String prefix: '', RegExp filter}) async { _logging.info(cmd.join(' ')); Process proc = await Process.start(cmd[0], cmd.getRange(1, cmd.length).toList()); - proc.stdout.transform(UTF8.decoder).listen((data) { - stdout.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n'); + proc.stdout.transform(UTF8.decoder).listen((String data) { + List dataLines = data.trimRight().split('\n'); + if (filter != null) { + dataLines = dataLines.where((String s) => filter.hasMatch(s)); + } + if (dataLines.length > 0) { + stdout.write('$prefix${dataLines.join('\n$prefix')}\n'); + } }); - proc.stderr.transform(UTF8.decoder).listen((data) { - stderr.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n'); + proc.stderr.transform(UTF8.decoder).listen((String data) { + List dataLines = data.trimRight().split('\n'); + if (filter != null) { + dataLines = dataLines.where((String s) => filter.hasMatch(s)); + } + if (dataLines.length > 0) { + stderr.write('$prefix${dataLines.join('\n$prefix')}\n'); + } }); return proc.exitCode; } diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index 19a5872dd8..e0f2c38dba 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -20,7 +20,10 @@ defineTests() { MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(false); - LogsCommand command = new LogsCommand(android); + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(false); + + LogsCommand command = new LogsCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From e1c46f42cc33d5a1c6c936b68f047422c0a4057b Mon Sep 17 00:00:00 2001 From: Robert Martone Date: Fri, 9 Oct 2015 13:19:02 -0700 Subject: [PATCH 109/188] Changing Desc to match Github Desc. --- packages/flutter_tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index e4bf33bc82..618586a699 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/flutter/tools.svg)](https://travis-ci.org/flutter/tools) -Tools for building Sky applications. +Tools for building Flutter applications. ## Installing From ae324109956d5a9acf41812054eba57305bf67c5 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 9 Oct 2015 16:10:36 -0700 Subject: [PATCH 110/188] Get iOS devices working on Mac if you specify sky-src-path. --- packages/flutter_tools/lib/src/device.dart | 37 ++++++++++++++++ packages/flutter_tools/lib/src/listen.dart | 45 +++++++++++++++----- packages/flutter_tools/test/listen_test.dart | 4 +- 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index e51d27d076..36c9d0d9b5 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -92,6 +92,9 @@ class IOSDevice extends _Device { String _loggerPath; String get loggerPath => _loggerPath; + String _pusherPath; + String get pusherPath => _pusherPath; + String _name; String get name => _name; @@ -107,6 +110,13 @@ class IOSDevice extends _Device { _informerPath = _checkForCommand('ideviceinfo'); _debuggerPath = _checkForCommand('idevicedebug'); _loggerPath = _checkForCommand('idevicesyslog'); + _pusherPath = _checkForCommand( + 'ios-deploy', + 'To copy files to iOS devices, please install ios-deploy. ' + 'You can do this using homebrew as follows:\n' + '\$ brew tap flutter/flutter\n' + '\$ brew install ios-deploy', + 'Copying files to iOS devices is not currently supported on Linux.'); } static List getAttachedDevices([IOSDevice mockIOS]) { @@ -208,6 +218,33 @@ class IOSDevice extends _Device { return false; } + Future pushFile( + ApplicationPackage app, String localFile, String targetFile) async { + if (Platform.isMacOS) { + runSync([ + pusherPath, + '-t', + '1', + '--bundle_id', + app.appPackageID, + '--upload', + localFile, + '--to', + targetFile + ]); + return true; + } else { + // TODO(iansf): It may be possible to make this work on Linux. Since this + // functionality appears to be the only that prevents us from + // supporting iOS on Linux, it may be worth putting some time + // into investigating this. + // See https://bbs.archlinux.org/viewtopic.php?id=192655 + return false; + } + return false; + } + + /// Note that clear is not supported on iOS at this time. Future logs({bool clear: false}) { return runCommandAndStreamOutput([loggerPath], prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*')); diff --git a/packages/flutter_tools/lib/src/listen.dart b/packages/flutter_tools/lib/src/listen.dart index 31b00cf898..7dc0cf39b2 100644 --- a/packages/flutter_tools/lib/src/listen.dart +++ b/packages/flutter_tools/lib/src/listen.dart @@ -19,22 +19,14 @@ class ListenCommand extends Command { final name = 'listen'; final description = 'Listen for changes to files and reload the running app ' 'on all connected devices.'; - AndroidDevice android = null; + AndroidDevice android; + IOSDevice ios; List watchCommand; /// Only run once. Used for testing. bool singleRun; - ListenCommand({this.android, this.singleRun: false}) { - argParser.addFlag('checked', - negatable: true, - defaultsTo: true, - help: 'Toggle Dart\'s checked mode.'); - argParser.addOption('target', - defaultsTo: '.', - abbr: 't', - help: 'Target app path or filename to start.'); - } + ListenCommand({this.android, this.ios, this.singleRun: false}) {} @override Future run() async { @@ -42,6 +34,10 @@ class ListenCommand extends Command { android = new AndroidDevice(); } + if (ios == null) { + ios = new IOSDevice(); + } + if (argResults.rest.length > 0) { watchCommand = _initWatchCommand(argResults.rest); } else { @@ -51,10 +47,37 @@ class ListenCommand extends Command { Map packages = ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; + ApplicationPackage iosApp = packages[BuildPlatform.iOS]; while (true) { _logging.info('Updating running Sky apps...'); + // TODO(iansf): refactor build command so that this doesn't have + // to call out like this. + List 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_Debug/clang_x64/sky_snapshot' + ]); + } + } catch (e) {} + runSync(command); + + String localFLXPath = 'app.flx'; + String remoteFLXPath = 'Documents/app.flx'; + + if (ios.isConnected()) { + await ios.pushFile(iosApp, localFLXPath, remoteFLXPath); + } + if (android.isConnected()) { await android.startServer( argResults['target'], true, argResults['checked'], androidApp); diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index 6a96ed8c99..b2d40a1f59 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -20,8 +20,10 @@ defineTests() { MockAndroidDevice android = new MockAndroidDevice(); when(android.isConnected()).thenReturn(false); + MockIOSDevice ios = new MockIOSDevice(); + when(ios.isConnected()).thenReturn(false); ListenCommand command = - new ListenCommand(android: android, singleRun: true); + new ListenCommand(android: android, ios: ios, singleRun: true); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From f7646679dc1ea2340a7df786e899c13c7a1a985e Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 9 Oct 2015 16:55:43 -0700 Subject: [PATCH 111/188] update the starter app template --- packages/flutter_tools/lib/src/init.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart index 4b61d6d3d6..a50616800a 100644 --- a/packages/flutter_tools/lib/src/init.dart +++ b/packages/flutter_tools/lib/src/init.dart @@ -139,11 +139,12 @@ dev_dependencies: '''; const _libMain = r''' +import 'package:sky/material.dart'; import 'package:sky/widgets.dart'; void main() { runApp( - new App( + new MaterialApp( title: "Flutter Demo", routes: { '/': (RouteArguments args) => new HelloWorldComponent() From 287817f224085f7ca8a9fbdd29d54b1626f5f59d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 9 Oct 2015 23:22:20 -0700 Subject: [PATCH 112/188] Move Command subclasses into commands directory I'm trying to get a feel for the code by writing some simple cleanup patches. --- packages/flutter_tools/bin/sky_tools.dart | 22 +++++++++---------- .../lib/src/{ => commands}/build.dart | 2 +- .../lib/src/{ => commands}/cache.dart | 2 +- .../lib/src/{ => commands}/init.dart | 0 .../lib/src/{ => commands}/install.dart | 4 ++-- .../lib/src/{ => commands}/list.dart | 3 ++- .../lib/src/{ => commands}/listen.dart | 7 +++--- .../lib/src/{ => commands}/logs.dart | 3 ++- .../lib/src/{ => commands}/run_mojo.dart | 4 ++-- .../lib/src/{ => commands}/start.dart | 9 ++++---- .../lib/src/{ => commands}/stop.dart | 5 +++-- .../lib/src/{ => commands}/trace.dart | 5 +++-- packages/flutter_tools/test/init_test.dart | 2 +- packages/flutter_tools/test/install_test.dart | 2 +- packages/flutter_tools/test/list_test.dart | 2 +- packages/flutter_tools/test/listen_test.dart | 2 +- packages/flutter_tools/test/logs_test.dart | 2 +- packages/flutter_tools/test/start_test.dart | 2 +- packages/flutter_tools/test/stop_test.dart | 2 +- packages/flutter_tools/test/trace_test.dart | 2 +- 20 files changed, 44 insertions(+), 38 deletions(-) rename packages/flutter_tools/lib/src/{ => commands}/build.dart (99%) rename packages/flutter_tools/lib/src/{ => commands}/cache.dart (97%) rename packages/flutter_tools/lib/src/{ => commands}/init.dart (100%) rename packages/flutter_tools/lib/src/{ => commands}/install.dart (95%) rename packages/flutter_tools/lib/src/{ => commands}/list.dart (96%) rename packages/flutter_tools/lib/src/{ => commands}/listen.dart (96%) rename packages/flutter_tools/lib/src/{ => commands}/logs.dart (96%) rename packages/flutter_tools/lib/src/{ => commands}/run_mojo.dart (98%) rename packages/flutter_tools/lib/src/{ => commands}/start.dart (92%) rename packages/flutter_tools/lib/src/{ => commands}/stop.dart (93%) rename packages/flutter_tools/lib/src/{ => commands}/trace.dart (95%) diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index efbd046cf9..137a9f207f 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -9,17 +9,17 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/artifacts.dart'; -import 'package:sky_tools/src/build.dart'; -import 'package:sky_tools/src/cache.dart'; -import 'package:sky_tools/src/init.dart'; -import 'package:sky_tools/src/install.dart'; -import 'package:sky_tools/src/list.dart'; -import 'package:sky_tools/src/listen.dart'; -import 'package:sky_tools/src/logs.dart'; -import 'package:sky_tools/src/run_mojo.dart'; -import 'package:sky_tools/src/start.dart'; -import 'package:sky_tools/src/stop.dart'; -import 'package:sky_tools/src/trace.dart'; +import 'package:sky_tools/src/commands/build.dart'; +import 'package:sky_tools/src/commands/cache.dart'; +import 'package:sky_tools/src/commands/init.dart'; +import 'package:sky_tools/src/commands/install.dart'; +import 'package:sky_tools/src/commands/list.dart'; +import 'package:sky_tools/src/commands/listen.dart'; +import 'package:sky_tools/src/commands/logs.dart'; +import 'package:sky_tools/src/commands/run_mojo.dart'; +import 'package:sky_tools/src/commands/start.dart'; +import 'package:sky_tools/src/commands/stop.dart'; +import 'package:sky_tools/src/commands/trace.dart'; class FlutterCommandRunner extends CommandRunner { FlutterCommandRunner() diff --git a/packages/flutter_tools/lib/src/build.dart b/packages/flutter_tools/lib/src/commands/build.dart similarity index 99% rename from packages/flutter_tools/lib/src/build.dart rename to packages/flutter_tools/lib/src/commands/build.dart index 3bef1f4079..7874e9d039 100644 --- a/packages/flutter_tools/lib/src/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -11,7 +11,7 @@ import 'package:archive/archive.dart'; import 'package:args/command_runner.dart'; import 'package:yaml/yaml.dart'; -import 'artifacts.dart'; +import '../artifacts.dart'; const String _kSnapshotKey = 'snapshot_blob.bin'; const List _kDensities = const ['drawable-xxhdpi']; diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/commands/cache.dart similarity index 97% rename from packages/flutter_tools/lib/src/cache.dart rename to packages/flutter_tools/lib/src/commands/cache.dart index 5571217e33..74c2ac62a4 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/commands/cache.dart @@ -9,7 +9,7 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'artifacts.dart'; +import '../artifacts.dart'; final Logger _logging = new Logger('sky_tools.cache'); diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/commands/init.dart similarity index 100% rename from packages/flutter_tools/lib/src/init.dart rename to packages/flutter_tools/lib/src/commands/init.dart diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/commands/install.dart similarity index 95% rename from packages/flutter_tools/lib/src/install.dart rename to packages/flutter_tools/lib/src/commands/install.dart index d29ded1df3..459c03c4ba 100644 --- a/packages/flutter_tools/lib/src/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -8,8 +8,8 @@ import 'dart:async'; import 'package:args/command_runner.dart'; -import 'application_package.dart'; -import 'device.dart'; +import '../application_package.dart'; +import '../device.dart'; class InstallCommand extends Command { final name = 'install'; diff --git a/packages/flutter_tools/lib/src/list.dart b/packages/flutter_tools/lib/src/commands/list.dart similarity index 96% rename from packages/flutter_tools/lib/src/list.dart rename to packages/flutter_tools/lib/src/commands/list.dart index 752fe35659..507edfd626 100644 --- a/packages/flutter_tools/lib/src/list.dart +++ b/packages/flutter_tools/lib/src/commands/list.dart @@ -8,7 +8,8 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'package:sky_tools/src/device.dart'; + +import '../device.dart'; final Logger _logging = new Logger('sky_tools.list'); diff --git a/packages/flutter_tools/lib/src/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart similarity index 96% rename from packages/flutter_tools/lib/src/listen.dart rename to packages/flutter_tools/lib/src/commands/listen.dart index 7dc0cf39b2..843bf408a9 100644 --- a/packages/flutter_tools/lib/src/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -9,9 +9,10 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/device.dart'; -import 'package:sky_tools/src/process.dart'; + +import '../application_package.dart'; +import '../device.dart'; +import '../process.dart'; final Logger _logging = new Logger('sky_tools.listen'); diff --git a/packages/flutter_tools/lib/src/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart similarity index 96% rename from packages/flutter_tools/lib/src/logs.dart rename to packages/flutter_tools/lib/src/commands/logs.dart index 518b75e985..8fb59a511e 100644 --- a/packages/flutter_tools/lib/src/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -8,7 +8,8 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'package:sky_tools/src/device.dart'; + +import '../device.dart'; final Logger _logging = new Logger('sky_tools.logs'); diff --git a/packages/flutter_tools/lib/src/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart similarity index 98% rename from packages/flutter_tools/lib/src/run_mojo.dart rename to packages/flutter_tools/lib/src/commands/run_mojo.dart index 1b32b08a88..077fccbc9f 100644 --- a/packages/flutter_tools/lib/src/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -11,8 +11,8 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import 'artifacts.dart'; -import 'process.dart'; +import '../artifacts.dart'; +import '../process.dart'; final Logger _logging = new Logger('sky_tools.run_mojo'); diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/commands/start.dart similarity index 92% rename from packages/flutter_tools/lib/src/start.dart rename to packages/flutter_tools/lib/src/commands/start.dart index 8d2dbc0dc2..ed11b2645d 100644 --- a/packages/flutter_tools/lib/src/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -9,10 +9,11 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/device.dart'; -import 'package:sky_tools/src/install.dart'; -import 'package:sky_tools/src/stop.dart'; + +import '../application_package.dart'; +import '../device.dart'; +import 'install.dart'; +import 'stop.dart'; final Logger _logging = new Logger('sky_tools.start'); diff --git a/packages/flutter_tools/lib/src/stop.dart b/packages/flutter_tools/lib/src/commands/stop.dart similarity index 93% rename from packages/flutter_tools/lib/src/stop.dart rename to packages/flutter_tools/lib/src/commands/stop.dart index d1f9844f0e..c6f3573e90 100644 --- a/packages/flutter_tools/lib/src/stop.dart +++ b/packages/flutter_tools/lib/src/commands/stop.dart @@ -8,8 +8,9 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/device.dart'; + +import '../application_package.dart'; +import '../device.dart'; final Logger _logging = new Logger('sky_tools.stop'); diff --git a/packages/flutter_tools/lib/src/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart similarity index 95% rename from packages/flutter_tools/lib/src/trace.dart rename to packages/flutter_tools/lib/src/commands/trace.dart index 830485100e..3f9e68f8a1 100644 --- a/packages/flutter_tools/lib/src/trace.dart +++ b/packages/flutter_tools/lib/src/commands/trace.dart @@ -8,8 +8,9 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/device.dart'; + +import '../application_package.dart'; +import '../device.dart'; final Logger _logging = new Logger('sky_tools.trace'); diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 8a68804922..b69eaa74fc 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -10,7 +10,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:path/path.dart' as p; -import 'package:sky_tools/src/init.dart'; +import 'package:sky_tools/src/commands/init.dart'; import 'package:test/test.dart'; main() => defineTests(); diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index ff9a91fcc8..35173cbe56 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -6,7 +6,7 @@ library install_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/install.dart'; +import 'package:sky_tools/src/commands/install.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index 2ff1cff888..53096cef66 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -6,7 +6,7 @@ library list_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/list.dart'; +import 'package:sky_tools/src/commands/list.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index b2d40a1f59..0f10bce369 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -6,7 +6,7 @@ library listen_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/listen.dart'; +import 'package:sky_tools/src/commands/listen.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index e0f2c38dba..595620f75d 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -6,7 +6,7 @@ library logs_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/logs.dart'; +import 'package:sky_tools/src/commands/logs.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index 9ddfb0e999..c6d72a9cf8 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -6,7 +6,7 @@ library start_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/start.dart'; +import 'package:sky_tools/src/commands/start.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index 8d67d662bd..2089c437ef 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -6,7 +6,7 @@ library stop_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/stop.dart'; +import 'package:sky_tools/src/commands/stop.dart'; import 'package:test/test.dart'; import 'src/common.dart'; diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart index 726c78fc1d..7de1fd84c1 100644 --- a/packages/flutter_tools/test/trace_test.dart +++ b/packages/flutter_tools/test/trace_test.dart @@ -6,7 +6,7 @@ library trace_test; import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/trace.dart'; +import 'package:sky_tools/src/commands/trace.dart'; import 'package:test/test.dart'; import 'src/common.dart'; From 7ba6342f8831d68517490fc628968be0447daaaa Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 10 Oct 2015 00:00:50 -0700 Subject: [PATCH 113/188] remove an unused import --- packages/flutter_tools/lib/src/commands/init.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index a50616800a..f8000acd31 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -140,7 +140,6 @@ dev_dependencies: const _libMain = r''' import 'package:sky/material.dart'; -import 'package:sky/widgets.dart'; void main() { runApp( From 9148e1779f95244988373f7bd987b7f18fb207ed Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 9 Oct 2015 23:41:40 -0700 Subject: [PATCH 114/188] Expose `main` via executable.dart Expose the main entry point for the tools via the library lets us run the tools from the Flutter package, which simplifies the setup for end developers because they don't need to declare a dependency on sky_tools directly. --- packages/flutter_tools/bin/sky_tools.dart | 160 +------------------- packages/flutter_tools/lib/executable.dart | 167 +++++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 8 +- 3 files changed, 173 insertions(+), 162 deletions(-) create mode 100644 packages/flutter_tools/lib/executable.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index 137a9f207f..99338de253 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -2,162 +2,6 @@ // 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:sky_tools/executable.dart' as executable; -import 'package:args/args.dart'; -import 'package:args/command_runner.dart'; -import 'package:logging/logging.dart'; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/artifacts.dart'; -import 'package:sky_tools/src/commands/build.dart'; -import 'package:sky_tools/src/commands/cache.dart'; -import 'package:sky_tools/src/commands/init.dart'; -import 'package:sky_tools/src/commands/install.dart'; -import 'package:sky_tools/src/commands/list.dart'; -import 'package:sky_tools/src/commands/listen.dart'; -import 'package:sky_tools/src/commands/logs.dart'; -import 'package:sky_tools/src/commands/run_mojo.dart'; -import 'package:sky_tools/src/commands/start.dart'; -import 'package:sky_tools/src/commands/stop.dart'; -import 'package:sky_tools/src/commands/trace.dart'; - -class FlutterCommandRunner extends CommandRunner { - FlutterCommandRunner() - : super('flutter', 'Manage your Flutter app development.') { - argParser.addFlag('verbose', - abbr: 'v', - negatable: false, - help: 'Noisy logging, including all shell commands executed.'); - argParser.addFlag('very-verbose', - negatable: false, - help: 'Very noisy logging, including the output of all ' - 'shell commands executed.'); - - argParser.addSeparator('Global build selection options:'); - argParser.addFlag('debug', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the debug build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Not normally required.'); - argParser.addFlag('release', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the release build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Note that release is not compatible with the listen command ' - 'on iOS devices and simulators. Not normally required.'); - argParser.addOption('sky-src-path', - help: - 'Path to your Sky src directory, if you are building Sky locally. ' - 'Ignored if neither debug nor release is set. Not normally required.'); - argParser.addOption('android-debug-build-path', - help: - 'Path to your Android Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Debug/'); - argParser.addOption('android-release-build-path', - help: - 'Path to your Android Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Release/'); - argParser.addOption('ios-debug-build-path', - help: - 'Path to your iOS Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Debug/'); - argParser.addOption('ios-release-build-path', - help: - 'Path to your iOS Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Release/'); - argParser.addOption('ios-sim-debug-build-path', - help: - 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Debug/'); - argParser.addOption('ios-sim-release-build-path', - help: - 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Release/'); - argParser.addOption('package-root', - help: 'Path to your packages directory.', defaultsTo: 'packages'); - } - - Future runCommand(ArgResults topLevelResults) async { - if (topLevelResults['verbose']) { - Logger.root.level = Level.INFO; - } - - if (topLevelResults['very-verbose']) { - Logger.root.level = Level.FINE; - } - - _setupPaths(topLevelResults); - - return super.runCommand(topLevelResults); - } - - void _setupPaths(ArgResults results) { - ArtifactStore.packageRoot = results['package-root']; - if (results['debug'] || results['release']) { - if (results['sky-src-path'] == null) { - // TODO(iansf): Figure out how to get the default src path - assert(false); - } - ApplicationPackageFactory.srcPath = results['sky-src-path']; - } else { - assert(false); - // TODO(iansf): set paths up for commands using PREBUILT binaries - // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, - // BuildPlatform.android, results['android-debug-build-path']); - } - - if (results['debug']) { - ApplicationPackageFactory.defaultBuildType = BuildType.debug; - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.android, results['android-debug-build-path']); - ApplicationPackageFactory.setBuildPath( - BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); - } - if (results['release']) { - ApplicationPackageFactory.defaultBuildType = BuildType.release; - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.android, results['android-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.iOS, results['ios-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); - } - } -} - -void main(List args) { - Logger.root.level = Level.WARNING; - Logger.root.onRecord.listen((LogRecord rec) { - print('${rec.level.name}: ${rec.message}'); - if (rec.error != null) { - print(rec.error); - } - if (rec.stackTrace != null) { - print(rec.stackTrace); - } - }); - - new FlutterCommandRunner() - ..addCommand(new BuildCommand()) - ..addCommand(new CacheCommand()) - ..addCommand(new InitCommand()) - ..addCommand(new InstallCommand()) - ..addCommand(new ListCommand()) - ..addCommand(new ListenCommand()) - ..addCommand(new LogsCommand()) - ..addCommand(new RunMojoCommand()) - ..addCommand(new StartCommand()) - ..addCommand(new StopCommand()) - ..addCommand(new TraceCommand()) - ..run(args); -} +main(List args) => executable.main(args); diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart new file mode 100644 index 0000000000..301e58964c --- /dev/null +++ b/packages/flutter_tools/lib/executable.dart @@ -0,0 +1,167 @@ +// 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:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; + +import 'src/application_package.dart'; +import 'src/artifacts.dart'; +import 'src/commands/build.dart'; +import 'src/commands/cache.dart'; +import 'src/commands/init.dart'; +import 'src/commands/install.dart'; +import 'src/commands/list.dart'; +import 'src/commands/listen.dart'; +import 'src/commands/logs.dart'; +import 'src/commands/run_mojo.dart'; +import 'src/commands/start.dart'; +import 'src/commands/stop.dart'; +import 'src/commands/trace.dart'; + +class _FlutterCommandRunner extends CommandRunner { + _FlutterCommandRunner() + : super('flutter', 'Manage your Flutter app development.') { + argParser.addFlag('verbose', + abbr: 'v', + negatable: false, + help: 'Noisy logging, including all shell commands executed.'); + argParser.addFlag('very-verbose', + negatable: false, + help: 'Very noisy logging, including the output of all ' + 'shell commands executed.'); + + argParser.addSeparator('Global build selection options:'); + argParser.addFlag('debug', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the debug build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Not normally required.'); + argParser.addFlag('release', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the release build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Note that release is not compatible with the listen command ' + 'on iOS devices and simulators. Not normally required.'); + argParser.addOption('sky-src-path', + help: + 'Path to your Sky src directory, if you are building Sky locally. ' + 'Ignored if neither debug nor release is set. Not normally required.'); + argParser.addOption('android-debug-build-path', + help: + 'Path to your Android Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Debug/'); + argParser.addOption('android-release-build-path', + help: + 'Path to your Android Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Release/'); + argParser.addOption('ios-debug-build-path', + help: + 'Path to your iOS Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Debug/'); + argParser.addOption('ios-release-build-path', + help: + 'Path to your iOS Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Release/'); + argParser.addOption('ios-sim-debug-build-path', + help: + 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Debug/'); + argParser.addOption('ios-sim-release-build-path', + help: + 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Release/'); + argParser.addOption('package-root', + help: 'Path to your packages directory.', defaultsTo: 'packages'); + } + + Future runCommand(ArgResults topLevelResults) async { + if (topLevelResults['verbose']) { + Logger.root.level = Level.INFO; + } + + if (topLevelResults['very-verbose']) { + Logger.root.level = Level.FINE; + } + + _setupPaths(topLevelResults); + + return super.runCommand(topLevelResults); + } + + void _setupPaths(ArgResults results) { + ArtifactStore.packageRoot = results['package-root']; + if (results['debug'] || results['release']) { + if (results['sky-src-path'] == null) { + // TODO(iansf): Figure out how to get the default src path + assert(false); + } + ApplicationPackageFactory.srcPath = results['sky-src-path']; + } else { + assert(false); + // TODO(iansf): set paths up for commands using PREBUILT binaries + // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, + // BuildPlatform.android, results['android-debug-build-path']); + } + + if (results['debug']) { + ApplicationPackageFactory.defaultBuildType = BuildType.debug; + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.android, results['android-debug-build-path']); + ApplicationPackageFactory.setBuildPath( + BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.debug, + BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); + } + if (results['release']) { + ApplicationPackageFactory.defaultBuildType = BuildType.release; + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.android, results['android-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.iOS, results['ios-release-build-path']); + ApplicationPackageFactory.setBuildPath(BuildType.release, + BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); + } + } +} + +/// Main entry point for commands. +/// +/// This function is intended to be used from the [flutter] command line tool. +void main(List args) { + Logger.root.level = Level.WARNING; + Logger.root.onRecord.listen((LogRecord rec) { + print('${rec.level.name}: ${rec.message}'); + if (rec.error != null) { + print(rec.error); + } + if (rec.stackTrace != null) { + print(rec.stackTrace); + } + }); + + new _FlutterCommandRunner() + ..addCommand(new BuildCommand()) + ..addCommand(new CacheCommand()) + ..addCommand(new InitCommand()) + ..addCommand(new InstallCommand()) + ..addCommand(new ListCommand()) + ..addCommand(new ListenCommand()) + ..addCommand(new LogsCommand()) + ..addCommand(new RunMojoCommand()) + ..addCommand(new StartCommand()) + ..addCommand(new StopCommand()) + ..addCommand(new TraceCommand()) + ..run(args); +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 6084c43339..149acaed57 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,8 +1,8 @@ name: sky_tools -version: 0.0.17 -description: Tools for building Sky applications -homepage: https://github.com/domokit/sky_tools -author: Chromium Authors +version: 0.0.18 +description: Tools for building Flutter applications +homepage: http://flutter.io +author: Flutter Authors environment: sdk: ">=1.8.0 <2.0.0" From 25eaaefd266c46a1f2171e2b0b4cae39c38b7296 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sun, 11 Oct 2015 11:57:22 -0700 Subject: [PATCH 115/188] Switch init command over to package:flutter --- .../flutter_tools/lib/src/commands/init.dart | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index f8000acd31..6fd0bcaa72 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -40,11 +40,11 @@ class InitCommand extends Command { String message = '''All done! To run your application: cd ${out.path} - ./packages/sky/sky_tool start + ./packages/flutter/sky_tool start -Or if the Sky APK is not already on your device, run: +Or if the Flutter APK is not already on your device, run: - ./packages/sky/sky_tool start --install + ./packages/flutter/sky_tool start --install '''; @@ -108,7 +108,7 @@ String _normalizeProjectName(String name) { return name; } -const _gitignore = r''' +const String _gitignore = r''' .DS_Store .idea .packages @@ -118,7 +118,7 @@ packages pubspec.lock '''; -const _readme = r''' +const String _readme = r''' # {{projectName}} {{description}} @@ -129,36 +129,45 @@ For help getting started with Flutter, view our online [documentation](http://flutter.io/). '''; -const _pubspec = r''' +const String _pubspec = r''' name: {{projectName}} description: {{description}} dependencies: - sky: any + flutter: ">=0.0.2 <0.1.0" dev_dependencies: sky_tools: any '''; -const _libMain = r''' -import 'package:sky/material.dart'; +const String _libMain = r''' +import 'package:flutter/material.dart'; void main() { runApp( new MaterialApp( title: "Flutter Demo", - routes: { - '/': (RouteArguments args) => new HelloWorldComponent() + routes: { + '/': (RouteArguments args) => new FlutterDemo() } ) ); } -class HelloWorldComponent extends StatelessComponent { +class FlutterDemo extends StatelessComponent { Widget build(BuildContext context) { return new Scaffold( - toolBar: new ToolBar(center: new Text("Flutter Demo")), - body: new Material(child: new Center(child: new Text("Hello world!"))), + toolBar: new ToolBar( + center: new Text("Flutter Demo") + ), + body: new Material( + child: new Center( + child: new Text("Hello world!") + ) + ), floatingActionButton: new FloatingActionButton( - child: new Icon(type: 'content/add', size: 24) + child: new Icon( + type: 'content/add', + size: 24 + ) ) ); } From 4191ed49219143c2ff13c2ca0d40b5ce9699ab95 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sun, 11 Oct 2015 12:33:59 -0700 Subject: [PATCH 116/188] Don't mention --install The --install command isn't needed anymore. We automatically detect when we need to install the APK. --- packages/flutter_tools/lib/src/commands/init.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 6fd0bcaa72..9a1de2f89e 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -41,12 +41,7 @@ class InitCommand extends Command { cd ${out.path} ./packages/flutter/sky_tool start - -Or if the Flutter APK is not already on your device, run: - - ./packages/flutter/sky_tool start --install - - '''; +'''; if (argResults['pub']) { print("Running pub get..."); From a6a3f212ee194da777b0f994d4c02adf0619bc2e Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Fri, 9 Oct 2015 18:37:21 -0700 Subject: [PATCH 117/188] IOSSimulator implementation. Also fixes some minor bugs with iOS and Android interactions. --- .../lib/src/application_package.dart | 4 + .../lib/src/commands/install.dart | 24 +- .../flutter_tools/lib/src/commands/list.dart | 14 +- .../lib/src/commands/listen.dart | 14 +- .../flutter_tools/lib/src/commands/logs.dart | 15 +- .../flutter_tools/lib/src/commands/start.dart | 32 ++- .../flutter_tools/lib/src/commands/stop.dart | 15 +- .../flutter_tools/lib/src/commands/trace.dart | 2 +- packages/flutter_tools/lib/src/device.dart | 258 +++++++++++++++++- packages/flutter_tools/lib/src/process.dart | 16 +- packages/flutter_tools/test/install_test.dart | 11 +- packages/flutter_tools/test/list_test.dart | 8 +- packages/flutter_tools/test/listen_test.dart | 6 +- packages/flutter_tools/test/logs_test.dart | 5 +- packages/flutter_tools/test/src/common.dart | 7 + packages/flutter_tools/test/start_test.dart | 20 +- packages/flutter_tools/test/stop_test.dart | 14 +- 17 files changed, 416 insertions(+), 49 deletions(-) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 34afb4bcea..f613db4c81 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -74,6 +74,7 @@ class ApplicationPackageFactory { static List defaultBuildPlatforms = [ BuildPlatform.android, BuildPlatform.iOS, + BuildPlatform.iOSSimulator, ]; static Map getAvailableApplicationPackages( @@ -95,6 +96,9 @@ class ApplicationPackageFactory { case BuildPlatform.iOS: packages[platform] = new IOSApp(buildPath); break; + case BuildPlatform.iOSSimulator: + packages[platform] = new IOSApp(buildPath); + break; default: // TODO(iansf): Add other platforms assert(false); diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart index 459c03c4ba..5f98fd5c11 100644 --- a/packages/flutter_tools/lib/src/commands/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -15,27 +15,38 @@ class InstallCommand extends Command { final name = 'install'; final description = 'Install your Flutter app on attached devices.'; - AndroidDevice android = null; + AndroidDevice android; IOSDevice ios; + IOSSimulator iosSim; - InstallCommand({this.android, this.ios}); + InstallCommand({this.android, this.ios, this.iosSim}) { + argParser.addFlag('boot', + help: 'Boot the iOS Simulator if it isn\'t already running.'); + } @override Future run() async { - if (install()) { + if (install(argResults['boot'])) { return 0; } else { return 2; } } - bool install() { + bool install([bool boot = false]) { if (android == null) { android = new AndroidDevice(); } if (ios == null) { ios = new IOSDevice(); } + if (iosSim == null) { + iosSim = new IOSSimulator(); + } + + if (boot) { + iosSim.boot(); + } bool installedSomewhere = false; @@ -43,6 +54,7 @@ class InstallCommand extends Command { ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; if (androidApp != null && android.isConnected()) { installedSomewhere = android.installApp(androidApp) || installedSomewhere; @@ -52,6 +64,10 @@ class InstallCommand extends Command { installedSomewhere = ios.installApp(iosApp) || installedSomewhere; } + if (iosSimApp != null && iosSim.isConnected()) { + installedSomewhere = iosSim.installApp(iosSimApp) || installedSomewhere; + } + return installedSomewhere; } } diff --git a/packages/flutter_tools/lib/src/commands/list.dart b/packages/flutter_tools/lib/src/commands/list.dart index 507edfd626..5af6c50d2c 100644 --- a/packages/flutter_tools/lib/src/commands/list.dart +++ b/packages/flutter_tools/lib/src/commands/list.dart @@ -18,8 +18,9 @@ class ListCommand extends Command { final description = 'List all connected devices.'; AndroidDevice android; IOSDevice ios; + IOSSimulator iosSim; - ListCommand({this.android, this.ios}) { + ListCommand({this.android, this.ios, this.iosSim}) { argParser.addFlag('details', abbr: 'd', negatable: false, @@ -54,6 +55,17 @@ class ListCommand extends Command { } } + if (details) { + print('iOS Simulators:'); + } + for (IOSSimulator device in IOSSimulator.getAttachedDevices(iosSim)) { + if (details) { + print('${device.id}\t${device.name}'); + } else { + print(device.id); + } + } + return 0; } } diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index 843bf408a9..b08a2a69ce 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -22,22 +22,25 @@ class ListenCommand extends Command { 'on all connected devices.'; AndroidDevice android; IOSDevice ios; + IOSSimulator iosSim; List watchCommand; /// Only run once. Used for testing. bool singleRun; - ListenCommand({this.android, this.ios, this.singleRun: false}) {} + ListenCommand({this.android, this.ios, this.iosSim, this.singleRun: false}) {} @override Future run() async { if (android == null) { android = new AndroidDevice(); } - if (ios == null) { ios = new IOSDevice(); } + if (iosSim == null) { + iosSim = new IOSSimulator(); + } if (argResults.rest.length > 0) { watchCommand = _initWatchCommand(argResults.rest); @@ -49,6 +52,7 @@ class ListenCommand extends Command { ApplicationPackageFactory.getAvailableApplicationPackages(); ApplicationPackage androidApp = packages[BuildPlatform.android]; ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; while (true) { _logging.info('Updating running Sky apps...'); @@ -66,7 +70,7 @@ class ListenCommand extends Command { // TODO(iansf): Don't rely on sky-src-path for the snapshotter. '--compiler', '${globalResults['sky-src-path']}' - '/out/ios_Debug/clang_x64/sky_snapshot' + '/out/ios_sim_Debug/clang_x64/sky_snapshot' ]); } } catch (e) {} @@ -79,6 +83,10 @@ class ListenCommand extends Command { await ios.pushFile(iosApp, localFLXPath, remoteFLXPath); } + if (iosSim.isConnected()) { + await iosSim.pushFile(iosSimApp, localFLXPath, remoteFLXPath); + } + if (android.isConnected()) { await android.startServer( argResults['target'], true, argResults['checked'], androidApp); diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart index 8fb59a511e..f488a5fbe8 100644 --- a/packages/flutter_tools/lib/src/commands/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -18,8 +18,9 @@ class LogsCommand extends Command { final description = 'Show logs for running Sky apps.'; AndroidDevice android; IOSDevice ios; + IOSSimulator iosSim; - LogsCommand({this.android, this.ios}) { + LogsCommand({this.android, this.ios, this.iosSim}) { argParser.addFlag('clear', negatable: false, help: 'Clear log history before reading from logs (Android only).'); @@ -33,6 +34,9 @@ class LogsCommand extends Command { if (ios == null) { ios = new IOSDevice(); } + if (iosSim == null) { + iosSim = new IOSSimulator(); + } Future androidLogProcess = null; if (android.isConnected()) { @@ -44,6 +48,11 @@ class LogsCommand extends Command { iosLogProcess = ios.logs(clear: argResults['clear']); } + Future iosSimLogProcess = null; + if (iosSim.isConnected()) { + iosSimLogProcess = iosSim.logs(clear: argResults['clear']); + } + if (androidLogProcess != null) { await androidLogProcess; } @@ -52,6 +61,10 @@ class LogsCommand extends Command { await iosLogProcess; } + if (iosSimLogProcess != null) { + await iosSimLogProcess; + } + return 0; } } diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index ed11b2645d..67f177333b 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -20,10 +20,11 @@ final Logger _logging = new Logger('sky_tools.start'); class StartCommand extends Command { final name = 'start'; final description = 'Start your Flutter app on attached devices.'; - AndroidDevice android = null; - IOSDevice ios = null; + AndroidDevice android; + IOSDevice ios; + IOSSimulator iosSim; - StartCommand({this.android, this.ios}) { + StartCommand({this.android, this.ios, this.iosSim}) { argParser.addFlag('poke', negatable: false, help: 'Restart the connection to the server (Android only).'); @@ -35,6 +36,8 @@ class StartCommand extends Command { defaultsTo: '.', abbr: 't', help: 'Target app path or filename to start.'); + argParser.addFlag('boot', + help: 'Boot the iOS Simulator if it isn\'t already running.'); } @override @@ -45,6 +48,9 @@ class StartCommand extends Command { if (ios == null) { ios = new IOSDevice(); } + if (iosSim == null) { + iosSim = new IOSSimulator(); + } bool startedSomewhere = false; bool poke = argResults['poke']; @@ -53,28 +59,32 @@ class StartCommand extends Command { stopper.stop(); // Only install if the user did not specify a poke - InstallCommand installer = new InstallCommand(android: android, ios: ios); - installer.install(); + InstallCommand installer = + new InstallCommand(android: android, ios: ios, iosSim: iosSim); + installer.install(argResults['boot']); } Map packages = ApplicationPackageFactory.getAvailableApplicationPackages(); + ApplicationPackage androidApp = packages[BuildPlatform.android]; + ApplicationPackage iosApp = packages[BuildPlatform.iOS]; + ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; bool startedOnAndroid = false; - if (android.isConnected()) { - ApplicationPackage androidApp = packages[BuildPlatform.android]; - + if (androidApp != null && android.isConnected()) { String target = path.absolute(argResults['target']); startedOnAndroid = await android.startServer( target, poke, argResults['checked'], androidApp); } - if (ios.isConnected()) { - ApplicationPackage iosApp = packages[BuildPlatform.iOS]; - + if (iosApp != null && ios.isConnected()) { startedSomewhere = await ios.startApp(iosApp) || startedSomewhere; } + if (iosSimApp != null && iosSim.isConnected()) { + startedSomewhere = await iosSim.startApp(iosSimApp) || startedSomewhere; + } + if (startedSomewhere || startedOnAndroid) { return 0; } else { diff --git a/packages/flutter_tools/lib/src/commands/stop.dart b/packages/flutter_tools/lib/src/commands/stop.dart index c6f3573e90..3d5128e7f9 100644 --- a/packages/flutter_tools/lib/src/commands/stop.dart +++ b/packages/flutter_tools/lib/src/commands/stop.dart @@ -17,10 +17,11 @@ final Logger _logging = new Logger('sky_tools.stop'); class StopCommand extends Command { final name = 'stop'; final description = 'Stop your Flutter app on all attached devices.'; - AndroidDevice android = null; - IOSDevice ios = null; + AndroidDevice android; + IOSDevice ios; + IOSSimulator iosSim; - StopCommand({this.android, this.ios}); + StopCommand({this.android, this.ios, this.iosSim}); @override Future run() async { @@ -38,6 +39,9 @@ class StopCommand extends Command { if (ios == null) { ios = new IOSDevice(); } + if (iosSim == null) { + iosSim = new IOSSimulator(); + } bool stoppedSomething = false; Map packages = @@ -53,6 +57,11 @@ class StopCommand extends Command { stoppedSomething = await ios.stopApp(iosApp) || stoppedSomething; } + if (iosSim.isConnected()) { + ApplicationPackage iosApp = packages[BuildPlatform.iOSSimulator]; + stoppedSomething = await iosSim.stopApp(iosApp) || stoppedSomething; + } + return stoppedSomething; } } diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart index 3f9e68f8a1..6b41635f30 100644 --- a/packages/flutter_tools/lib/src/commands/trace.dart +++ b/packages/flutter_tools/lib/src/commands/trace.dart @@ -21,7 +21,7 @@ class TraceCommand extends Command { 'To start a trace, wait, and then stop the trace, don\'t set any flags ' 'except (optionally) duration.\n' 'Otherwise, specify either start or stop to manually control the trace.'; - AndroidDevice android = null; + AndroidDevice android; TraceCommand([this.android]) { argParser.addFlag('start', negatable: false, help: 'Start tracing.'); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 36c9d0d9b5..69ae496aac 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -26,6 +26,8 @@ abstract class _Device { id = AndroidDevice.defaultDeviceID; } else if (className == IOSDevice.className) { id = IOSDevice.defaultDeviceID; + } else if (className == IOSSimulator.className) { + id = IOSSimulator.defaultDeviceID; } else { throw 'Attempted to create a Device of unknown type $className'; } @@ -40,6 +42,10 @@ abstract class _Device { final device = new IOSDevice._(id); _deviceCache[id] = device; return device; + } else if (className == IOSSimulator.className) { + final device = new IOSSimulator._(id); + _deviceCache[id] = device; + return device; } else { throw 'Attempted to create a Device of unknown type $className'; } @@ -128,10 +134,13 @@ class IOSDevice extends _Device { return devices; } - static List _getAttachedDeviceIDs([IOSDevice mockIOS]) { + static Iterable _getAttachedDeviceIDs([IOSDevice mockIOS]) { String listerPath = (mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id'); - return runSync([listerPath, '-l']).trim().split('\n'); + return runSync([listerPath, '-l']) + .trim() + .split('\n') + .where((String s) => s != null && s.length > 0); } static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) { @@ -163,17 +172,22 @@ class IOSDevice extends _Device { @override bool installApp(ApplicationPackage app) { - if (id == defaultDeviceID) { - runCheckedSync([installerPath, '-i', app.appPath]); - } else { - runCheckedSync([installerPath, '-u', id, '-i', app.appPath]); + try { + if (id == defaultDeviceID) { + runCheckedSync([installerPath, '-i', app.appPath]); + } else { + runCheckedSync([installerPath, '-u', id, '-i', app.appPath]); + } + return true; + } catch (e) { + return false; } return false; } @override bool isConnected() { - List ids = _getAttachedDeviceIDs(); + Iterable ids = _getAttachedDeviceIDs(); for (String id in ids) { if (id == this.id || this.id == defaultDeviceID) { return true; @@ -245,12 +259,234 @@ class IOSDevice extends _Device { } /// Note that clear is not supported on iOS at this time. - Future logs({bool clear: false}) { + Future logs({bool clear: false}) async { + if (!isConnected()) { + return 2; + } return runCommandAndStreamOutput([loggerPath], prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*')); } } +class IOSSimulator extends _Device { + static const String className = 'IOSSimulator'; + static final String defaultDeviceID = 'default_ios_sim_id'; + + static const String _macInstructions = + 'To work with iOS devices, please install ideviceinstaller. ' + 'If you use homebrew, you can install it with ' + '"\$ brew install ideviceinstaller".'; + + static String _xcrunPath = path.join('/usr', 'bin', 'xcrun'); + + String _iOSSimPath; + String get iOSSimPath => _iOSSimPath; + + String get xcrunPath => _xcrunPath; + + String _name; + String get name => _name; + + factory IOSSimulator({String id, String name, String iOSSimulatorPath}) { + IOSSimulator device = new _Device(className, id); + device._name = name; + if (iOSSimulatorPath == null) { + iOSSimulatorPath = path.join('/Applications', 'iOS Simulator.app', + 'Contents', 'MacOS', 'iOS Simulator'); + } + device._iOSSimPath = iOSSimulatorPath; + return device; + } + + IOSSimulator._(String id) : super._(id) {} + + static String _getRunningSimulatorID([IOSSimulator mockIOS]) { + String xcrunPath = mockIOS != null ? mockIOS.xcrunPath : _xcrunPath; + String output = runCheckedSync([xcrunPath, 'simctl', 'list', 'devices']); + + Match match; + Iterable matches = new RegExp(r'[^\(]+\(([^\)]+)\) \(Booted\)', + multiLine: true).allMatches(output); + if (matches.length > 1) { + // More than one simulator is listed as booted, which is not allowed but + // sometimes happens erroneously. Kill them all because we don't know + // which one is actually running. + _logging.warning('Multiple running simulators were detected, ' + 'which is not supposed to happen.'); + for (Match m in matches) { + if (m.groupCount > 0) { + _logging.warning('Killing simulator ${m.group(1)}'); + runSync([xcrunPath, 'simctl', 'shutdown', m.group(1)]); + } + } + } else if (matches.length == 1) { + match = matches.first; + } + + if (match != null && match.groupCount > 0) { + return match.group(1); + } else { + _logging.info('No running simulators found'); + return null; + } + } + + String _getSimulatorPath() { + String deviceID = id == defaultDeviceID ? _getRunningSimulatorID() : id; + String homeDirectory = path.absolute(Platform.environment['HOME']); + if (deviceID == null) { + return null; + } + return path.join(homeDirectory, 'Library', 'Developer', 'CoreSimulator', + 'Devices', deviceID); + } + + String _getSimulatorAppHomeDirectory(ApplicationPackage app) { + String simulatorPath = _getSimulatorPath(); + if (simulatorPath == null) { + return null; + } + return path.join(simulatorPath, 'data'); + } + + static List getAttachedDevices([IOSSimulator mockIOS]) { + List devices = []; + String id = _getRunningSimulatorID(mockIOS); + if (id != null) { + // TODO(iansf): get the simulator's name + // String name = _getDeviceName(id, mockIOS); + devices.add(new IOSSimulator(id: id)); + } + return devices; + } + + Future boot() async { + if (!Platform.isMacOS) { + return false; + } + if (isConnected()) { + return true; + } + if (id == defaultDeviceID) { + runDetached([iOSSimPath]); + + Future checkConnection([int attempts = 20]) async { + if (attempts == 0) { + _logging.info('Timed out waiting for iOS Simulator $id to boot.'); + return false; + } + if (!isConnected()) { + _logging.info('Waiting for iOS Simulator $id to boot...'); + return new Future.delayed(new Duration(milliseconds: 500), + () => checkConnection(attempts - 1)); + } + return true; + } + return checkConnection(); + } else { + try { + runCheckedSync([xcrunPath, 'simctl', 'boot', id]); + } catch (e) { + _logging.warning('Unable to boot iOS Simulator $id: ', e); + return false; + } + } + return false; + } + + @override + bool installApp(ApplicationPackage app) { + if (!isConnected()) { + return false; + } + try { + if (id == defaultDeviceID) { + runCheckedSync([xcrunPath, 'simctl', 'install', 'booted', app.appPath]); + } else { + runCheckedSync([xcrunPath, 'simctl', 'install', id, app.appPath]); + } + return true; + } catch (e) { + return false; + } + } + + @override + bool isConnected() { + if (!Platform.isMacOS) { + return false; + } + String simulatorID = _getRunningSimulatorID(); + if (simulatorID == null) { + return false; + } else if (id == defaultDeviceID) { + return true; + } else { + return _getRunningSimulatorID() == id; + } + } + + @override + bool isAppInstalled(ApplicationPackage app) { + try { + String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app); + return FileSystemEntity.isDirectorySync(simulatorHomeDirectory); + } catch (e) { + return false; + } + } + + @override + Future startApp(ApplicationPackage app) async { + if (!isAppInstalled(app)) { + return false; + } + try { + if (id == defaultDeviceID) { + runCheckedSync( + [xcrunPath, 'simctl', 'launch', 'booted', app.appPackageID]); + } else { + runCheckedSync([xcrunPath, 'simctl', 'launch', id, app.appPackageID]); + } + return true; + } catch (e) { + return false; + } + } + + @override + Future stopApp(ApplicationPackage app) async { + // Currently we don't have a way to stop an app running on iOS. + return false; + } + + Future pushFile( + ApplicationPackage app, String localFile, String targetFile) async { + if (Platform.isMacOS) { + String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app); + runCheckedSync( + ['cp', localFile, path.join(simulatorHomeDirectory, targetFile)]); + return true; + } + return false; + } + + Future logs({bool clear: false}) async { + if (!isConnected()) { + return 2; + } + String homeDirectory = path.absolute(Platform.environment['HOME']); + String simulatorDeviceID = _getRunningSimulatorID(); + String logFilePath = path.join(homeDirectory, 'Library', 'Logs', + 'CoreSimulator', simulatorDeviceID, 'system.log'); + if (clear) { + runSync(['rm', logFilePath]); + } + return runCommandAndStreamOutput(['tail', '-f', logFilePath], + prefix: 'IOS SIM: ', filter: new RegExp(r'.*SkyShell.*')); + } +} + class AndroidDevice extends _Device { static const String _ADB_PATH = 'adb'; static const String _observatoryPort = '8181'; @@ -576,7 +812,11 @@ class AndroidDevice extends _Device { runSync([adbPath, 'logcat', '-c']); } - Future logs({bool clear: false}) { + Future logs({bool clear: false}) async { + if (!isConnected()) { + return 2; + } + if (clear) { clearLogs(); } diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 7be82b2f8f..7e22f52598 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -22,7 +22,7 @@ Future runCommandAndStreamOutput(List cmd, proc.stdout.transform(UTF8.decoder).listen((String data) { List dataLines = data.trimRight().split('\n'); if (filter != null) { - dataLines = dataLines.where((String s) => filter.hasMatch(s)); + dataLines = dataLines.where((String s) => filter.hasMatch(s)).toList(); } if (dataLines.length > 0) { stdout.write('$prefix${dataLines.join('\n$prefix')}\n'); @@ -41,17 +41,21 @@ Future runCommandAndStreamOutput(List cmd, } Future runAndKill(List cmd, Duration timeout) async { - _logging.info(cmd.join(' ')); - Future proc = Process.start( - cmd[0], cmd.getRange(1, cmd.length).toList(), - mode: ProcessStartMode.DETACHED); - + Future proc = runDetached(cmd); return new Future.delayed(timeout, () async { _logging.info('Intentionally killing ${cmd[0]}'); Process.killPid((await proc).pid); }); } +Future runDetached(List cmd) async { + _logging.info(cmd.join(' ')); + Future proc = Process.start( + cmd[0], cmd.getRange(1, cmd.length).toList(), + mode: ProcessStartMode.DETACHED); + return proc; +} + /// Run cmd and return stdout. /// Throws an error if cmd exits with a non-zero value. String runCheckedSync(List cmd) => diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index 35173cbe56..b73db0a2c2 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -26,6 +26,10 @@ defineTests() { when(ios.isConnected()).thenReturn(false); when(ios.installApp(any)).thenReturn(false); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.installApp(any)).thenReturn(false); + InstallCommand command = new InstallCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') @@ -44,7 +48,12 @@ defineTests() { when(ios.isConnected()).thenReturn(true); when(ios.installApp(any)).thenReturn(true); - InstallCommand command = new InstallCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.installApp(any)).thenReturn(false); + + InstallCommand command = + new InstallCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index 53096cef66..aabf36484c 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -30,7 +30,13 @@ defineTests() { when(ios.installerPath).thenReturn('echo'); when(ios.listerPath).thenReturn('echo'); - ListCommand command = new ListCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + // Avoid relying on xcrun being installed on the test system. + // Instead, cause the test to run the echo command. + when(iosSim.xcrunPath).thenReturn('echo'); + + ListCommand command = + new ListCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); runner.run(['list']).then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index 0f10bce369..41a47c736a 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -22,8 +22,10 @@ defineTests() { when(android.isConnected()).thenReturn(false); MockIOSDevice ios = new MockIOSDevice(); when(ios.isConnected()).thenReturn(false); - ListenCommand command = - new ListenCommand(android: android, ios: ios, singleRun: true); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + ListenCommand command = new ListenCommand( + android: android, ios: ios, iosSim: iosSim, singleRun: true); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index 595620f75d..0fad92fb7a 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -22,8 +22,11 @@ defineTests() { when(android.isConnected()).thenReturn(false); MockIOSDevice ios = new MockIOSDevice(); when(ios.isConnected()).thenReturn(false); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); - LogsCommand command = new LogsCommand(android: android, ios: ios); + LogsCommand command = + new LogsCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 8903b16392..c935ce6705 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -16,10 +16,17 @@ class MockIOSDevice extends Mock implements IOSDevice { dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } +class MockIOSSimulator extends Mock implements IOSSimulator { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + void applicationPackageSetup() { ApplicationPackageFactory.srcPath = './'; ApplicationPackageFactory.setBuildPath( BuildType.prebuilt, BuildPlatform.android, './'); ApplicationPackageFactory.setBuildPath( BuildType.prebuilt, BuildPlatform.iOS, './'); + ApplicationPackageFactory.setBuildPath( + BuildType.prebuilt, BuildPlatform.iOSSimulator, './'); } diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index c6d72a9cf8..793d65247d 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -28,9 +28,16 @@ defineTests() { when(ios.isConnected()).thenReturn(false); when(ios.installApp(any)).thenReturn(false); when(ios.startApp(any)).thenReturn(false); - when(ios.startApp(any)).thenReturn(false); + when(ios.stopApp(any)).thenReturn(false); - StartCommand command = new StartCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.installApp(any)).thenReturn(false); + when(iosSim.startApp(any)).thenReturn(false); + when(iosSim.stopApp(any)).thenReturn(false); + + StartCommand command = + new StartCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); @@ -52,7 +59,14 @@ defineTests() { when(ios.startApp(any)).thenReturn(true); when(ios.stopApp(any)).thenReturn(false); - StartCommand command = new StartCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.installApp(any)).thenReturn(false); + when(iosSim.startApp(any)).thenReturn(false); + when(iosSim.stopApp(any)).thenReturn(false); + + StartCommand command = + new StartCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index 2089c437ef..2e64af13a7 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -26,7 +26,12 @@ defineTests() { when(ios.isConnected()).thenReturn(false); when(ios.stopApp(any)).thenReturn(false); - StopCommand command = new StopCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.stopApp(any)).thenReturn(false); + + StopCommand command = + new StopCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); @@ -44,7 +49,12 @@ defineTests() { when(ios.isConnected()).thenReturn(true); when(ios.stopApp(any)).thenReturn(true); - StopCommand command = new StopCommand(android: android, ios: ios); + MockIOSSimulator iosSim = new MockIOSSimulator(); + when(iosSim.isConnected()).thenReturn(false); + when(iosSim.stopApp(any)).thenReturn(false); + + StopCommand command = + new StopCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From bdd20661d7ce84292ef294bc5e11ac905979d63f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sun, 11 Oct 2015 19:42:50 -0700 Subject: [PATCH 118/188] Teach sky_tools about prebuilt artifacts This patch makes `flutter start` work without a clone of the engine git repository. Making this work pulled a relatively large refactor of how the commands interact with application packages and devices. Now commands that want to interact with application packages or devices inherit from a common base class that holds stores of those objects as members. In production, the commands download and connect to devices based on the build configuration stored on the FlutterCommandRunner. In testing, these fields are used to mock out the real application package and devices. --- packages/flutter_tools/lib/executable.dart | 137 +-------------- .../lib/src/application_package.dart | 166 ++++++++---------- packages/flutter_tools/lib/src/artifacts.dart | 30 +++- .../lib/src/build_configuration.dart | 37 ++++ .../flutter_tools/lib/src/commands/build.dart | 4 +- .../flutter_tools/lib/src/commands/cache.dart | 14 +- .../lib/src/commands/flutter_command.dart | 38 ++++ .../src/commands/flutter_command_runner.dart | 161 +++++++++++++++++ .../flutter_tools/lib/src/commands/init.dart | 6 +- .../lib/src/commands/install.dart | 63 ++----- .../flutter_tools/lib/src/commands/list.dart | 32 ++-- .../lib/src/commands/listen.dart | 72 +++----- .../flutter_tools/lib/src/commands/logs.dart | 50 +----- .../lib/src/commands/run_mojo.dart | 10 +- .../flutter_tools/lib/src/commands/start.dart | 72 +++----- .../flutter_tools/lib/src/commands/stop.dart | 53 ++---- .../flutter_tools/lib/src/commands/trace.dart | 34 ++-- packages/flutter_tools/lib/src/device.dart | 146 ++++++++++----- .../test/android_device_test.dart | 8 +- packages/flutter_tools/test/install_test.dart | 46 ++--- packages/flutter_tools/test/list_test.dart | 24 ++- packages/flutter_tools/test/listen_test.dart | 19 +- packages/flutter_tools/test/logs_test.dart | 20 +-- packages/flutter_tools/test/src/common.dart | 32 ---- packages/flutter_tools/test/src/mocks.dart | 50 ++++++ packages/flutter_tools/test/start_test.dart | 72 ++++---- packages/flutter_tools/test/stop_test.dart | 46 ++--- packages/flutter_tools/test/trace_test.dart | 10 +- 28 files changed, 730 insertions(+), 722 deletions(-) create mode 100644 packages/flutter_tools/lib/src/build_configuration.dart create mode 100644 packages/flutter_tools/lib/src/commands/flutter_command.dart create mode 100644 packages/flutter_tools/lib/src/commands/flutter_command_runner.dart delete mode 100644 packages/flutter_tools/test/src/common.dart create mode 100644 packages/flutter_tools/test/src/mocks.dart diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 301e58964c..1385672451 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -2,14 +2,9 @@ // 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:args/args.dart'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'src/application_package.dart'; -import 'src/artifacts.dart'; +import 'src/commands/flutter_command_runner.dart'; import 'src/commands/build.dart'; import 'src/commands/cache.dart'; import 'src/commands/init.dart'; @@ -22,136 +17,20 @@ import 'src/commands/start.dart'; import 'src/commands/stop.dart'; import 'src/commands/trace.dart'; -class _FlutterCommandRunner extends CommandRunner { - _FlutterCommandRunner() - : super('flutter', 'Manage your Flutter app development.') { - argParser.addFlag('verbose', - abbr: 'v', - negatable: false, - help: 'Noisy logging, including all shell commands executed.'); - argParser.addFlag('very-verbose', - negatable: false, - help: 'Very noisy logging, including the output of all ' - 'shell commands executed.'); - - argParser.addSeparator('Global build selection options:'); - argParser.addFlag('debug', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the debug build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Not normally required.'); - argParser.addFlag('release', - negatable: false, - help: - 'Set this if you are building Sky locally and want to use the release build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' - 'not set. Note that release is not compatible with the listen command ' - 'on iOS devices and simulators. Not normally required.'); - argParser.addOption('sky-src-path', - help: - 'Path to your Sky src directory, if you are building Sky locally. ' - 'Ignored if neither debug nor release is set. Not normally required.'); - argParser.addOption('android-debug-build-path', - help: - 'Path to your Android Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Debug/'); - argParser.addOption('android-release-build-path', - help: - 'Path to your Android Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/android_Release/'); - argParser.addOption('ios-debug-build-path', - help: - 'Path to your iOS Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Debug/'); - argParser.addOption('ios-release-build-path', - help: - 'Path to your iOS Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_Release/'); - argParser.addOption('ios-sim-debug-build-path', - help: - 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Debug/'); - argParser.addOption('ios-sim-release-build-path', - help: - 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', - defaultsTo: 'out/ios_sim_Release/'); - argParser.addOption('package-root', - help: 'Path to your packages directory.', defaultsTo: 'packages'); - } - - Future runCommand(ArgResults topLevelResults) async { - if (topLevelResults['verbose']) { - Logger.root.level = Level.INFO; - } - - if (topLevelResults['very-verbose']) { - Logger.root.level = Level.FINE; - } - - _setupPaths(topLevelResults); - - return super.runCommand(topLevelResults); - } - - void _setupPaths(ArgResults results) { - ArtifactStore.packageRoot = results['package-root']; - if (results['debug'] || results['release']) { - if (results['sky-src-path'] == null) { - // TODO(iansf): Figure out how to get the default src path - assert(false); - } - ApplicationPackageFactory.srcPath = results['sky-src-path']; - } else { - assert(false); - // TODO(iansf): set paths up for commands using PREBUILT binaries - // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT, - // BuildPlatform.android, results['android-debug-build-path']); - } - - if (results['debug']) { - ApplicationPackageFactory.defaultBuildType = BuildType.debug; - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.android, results['android-debug-build-path']); - ApplicationPackageFactory.setBuildPath( - BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.debug, - BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']); - } - if (results['release']) { - ApplicationPackageFactory.defaultBuildType = BuildType.release; - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.android, results['android-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.iOS, results['ios-release-build-path']); - ApplicationPackageFactory.setBuildPath(BuildType.release, - BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); - } - } -} - /// Main entry point for commands. /// /// This function is intended to be used from the [flutter] command line tool. void main(List args) { Logger.root.level = Level.WARNING; - Logger.root.onRecord.listen((LogRecord rec) { - print('${rec.level.name}: ${rec.message}'); - if (rec.error != null) { - print(rec.error); - } - if (rec.stackTrace != null) { - print(rec.stackTrace); - } + Logger.root.onRecord.listen((LogRecord record) { + print('${record.level.name}: ${record.message}'); + if (record.error != null) + print(record.error); + if (record.stackTrace != null) + print(record.stackTrace); }); - new _FlutterCommandRunner() + new FlutterCommandRunner() ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index f613db4c81..09a2061ba3 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -2,137 +2,119 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.application_package; - -import 'dart:io'; +import 'dart:async'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; +import 'artifacts.dart'; +import 'build_configuration.dart'; + final Logger _logging = new Logger('sky_tools.application_package'); abstract class ApplicationPackage { - /// Path to the directory the apk or bundle lives in. - String appDir; - /// Path to the actual apk or bundle. - String get appPath => path.join(appDir, appFileName); + final String localPath; /// Package ID from the Android Manifest or equivalent. - String appPackageID; + final String id; /// File name of the apk or bundle. - String appFileName; + final String name; - ApplicationPackage(this.appDir, this.appPackageID, this.appFileName); + ApplicationPackage({ + String localPath, + this.id + }) : localPath = localPath, name = path.basename(localPath) { + assert(localPath != null); + assert(id != null); + } } class AndroidApk extends ApplicationPackage { - static const String _apkName = 'SkyShell.apk'; - static const String _packageID = 'org.domokit.sky.shell'; - static const String _componentID = '$_packageID/$_packageID.SkyActivity'; + static const String _defaultName = 'SkyShell.apk'; + static const String _defaultId = 'org.domokit.sky.shell'; + static const String _defaultLaunchActivity = '$_defaultId/$_defaultId.SkyActivity'; /// The path to the activity that should be launched. /// Defaults to 'org.domokit.sky.shell/org.domokit.sky.shell.SkyActivity' - String component; - AndroidApk(String appDir, - {String appPackageID: _packageID, - String appFileName: _apkName, - this.component: _componentID}) - : super(path.join(appDir, 'apks'), appPackageID, appFileName); + final String launchActivity; + + AndroidApk({ + String localPath, + String id: _defaultId, + this.launchActivity: _defaultLaunchActivity + }) : super(localPath: localPath, id: id) { + assert(launchActivity != null); + } } class IOSApp extends ApplicationPackage { - static const String _appName = 'SkyShell.app'; - static const String _packageID = 'com.google.SkyShell'; + static const String _defaultName = 'SkyShell.app'; + static const String _defaultId = 'com.google.SkyShell'; - IOSApp(String appDir, - {String appPackageID: _packageID, String appFileName: _appName}) - : super(appDir, appPackageID, appFileName); + IOSApp({ + String localPath, + String id: _defaultId + }) : super(localPath: localPath, id: id); } -enum BuildType { prebuilt, release, debug, } +class ApplicationPackageStore { + final AndroidApk android; + final IOSApp iOS; + final IOSApp iOSSimulator; -enum BuildPlatform { android, iOS, iOSSimulator, mac, linux, } + ApplicationPackageStore({ this.android, this.iOS, this.iOSSimulator }); -class ApplicationPackageFactory { - static final Map> _buildPaths = - _initBuildPaths(); - - /// Path to your Sky src directory, if you are building Sky locally. - /// Required if you are requesting release or debug BuildTypes. - static String _srcPath = null; - static String get srcPath => _srcPath; - static void set srcPath(String newPath) { - _srcPath = path.normalize(newPath); + ApplicationPackage getPackageForPlatform(BuildPlatform platform) { + switch (platform) { + case BuildPlatform.android: + return android; + case BuildPlatform.iOS: + return iOS; + case BuildPlatform.iOSSimulator: + return iOSSimulator; + case BuildPlatform.mac: + case BuildPlatform.linux: + return null; + } } - /// Default BuildType chosen if no BuildType is specified. - static BuildType defaultBuildType = BuildType.prebuilt; + static Future forConfigs(List configs) async { + AndroidApk android; + IOSApp iOS; + IOSApp iOSSimulator; - /// Default BuildPlatforms chosen if no BuildPlatforms are specified. - static List defaultBuildPlatforms = [ - BuildPlatform.android, - BuildPlatform.iOS, - BuildPlatform.iOSSimulator, - ]; - - static Map getAvailableApplicationPackages( - {BuildType requestedType, List requestedPlatforms}) { - if (requestedType == null) { - requestedType = defaultBuildType; - } - if (requestedPlatforms == null) { - requestedPlatforms = defaultBuildPlatforms; - } - - Map packages = {}; - for (BuildPlatform platform in requestedPlatforms) { - String buildPath = _getBuildPath(requestedType, platform); - switch (platform) { + for (BuildConfiguration config in configs) { + switch (config.platform) { case BuildPlatform.android: - packages[platform] = new AndroidApk(buildPath); + assert(android == null); + String localPath = config.type == BuildType.prebuilt ? + await ArtifactStore.getPath(Artifact.flutterShell) : + path.join(config.buildDir, 'apks', AndroidApk._defaultName); + android = new AndroidApk(localPath: localPath); break; + case BuildPlatform.iOS: - packages[platform] = new IOSApp(buildPath); + assert(iOS == null); + assert(config.type != BuildType.prebuilt); + iOS = new IOSApp(localPath: path.join(config.buildDir, IOSApp._defaultName)); break; + case BuildPlatform.iOSSimulator: - packages[platform] = new IOSApp(buildPath); + assert(iOSSimulator == null); + assert(config.type != BuildType.prebuilt); + iOSSimulator = new IOSApp(localPath: path.join(config.buildDir, IOSApp._defaultName)); break; - default: - // TODO(iansf): Add other platforms + + case BuildPlatform.mac: + case BuildPlatform.linux: + // TODO(abarth): Support mac and linux targets. assert(false); + break; } } - return packages; - } - static Map> _initBuildPaths() { - Map> buildPaths = {}; - for (BuildPlatform platform in BuildPlatform.values) { - buildPaths[platform] = {}; - } - return buildPaths; - } - - static String _getBuildPath(BuildType type, BuildPlatform platform) { - String path = _buildPaths[platform][type]; - // You must set paths before getting them - assert(path != null); - return path; - } - - static void setBuildPath( - BuildType type, BuildPlatform platform, String buildPath) { - // You must set srcPath before attempting to set a BuildPath for - // non prebuilt ApplicationPackages. - assert(type != BuildType.prebuilt || srcPath != null); - if (type != BuildType.prebuilt) { - buildPath = path.join(srcPath, buildPath); - } - if (!FileSystemEntity.isDirectorySync(buildPath)) { - _logging.warning('$buildPath is not a valid directory'); - } - _buildPaths[platform][type] = path.normalize(buildPath); + return new ApplicationPackageStore(android: android, iOS: iOS, iOSSimulator: iOSSimulator); } } diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 85d51cfd47..751cbd81ac 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -12,7 +12,11 @@ import 'package:path/path.dart' as path; final Logger _logging = new Logger('sky_tools.artifacts'); -enum Artifact { FlutterCompiler, SkyViewerMojo, } +enum Artifact { + flutterCompiler, + flutterShell, + skyViewerMojo, +} class ArtifactStore { static String packageRoot; @@ -66,20 +70,27 @@ class ArtifactStore { // Whether the artifact needs to be marked as executable on disk. static bool _needsToBeExecutable(Artifact artifact) { - return artifact == Artifact.FlutterCompiler; + return artifact == Artifact.flutterCompiler; } static Future getPath(Artifact artifact) async { Directory cacheDir = await _engineSpecificCacheDir(); - String category, name; + String category; + String platform; + String name; switch (artifact) { - case Artifact.FlutterCompiler: + case Artifact.flutterCompiler: category = 'shell'; name = 'sky_snapshot'; break; - case Artifact.SkyViewerMojo: + case Artifact.flutterShell: + category = 'shell'; + platform = 'android-arm'; + name = 'SkyShell.apk'; + break; + case Artifact.skyViewerMojo: category = 'viewer'; name = 'sky_viewer.mojo'; break; @@ -88,9 +99,12 @@ class ArtifactStore { File cachedFile = new File(path.join(cacheDir.path, name)); if (!await cachedFile.exists()) { _logging.info('Downloading ${name} from the cloud, one moment please...'); - if (!Platform.isLinux) - throw new Exception('Platform unsupported.'); - String url = googleStorageUrl(category, 'linux-x64') + name; + if (platform == null) { + if (!Platform.isLinux) + throw new Exception('Platform unsupported.'); + platform = 'linux-x64'; + } + String url = googleStorageUrl(category, platform) + name; await _downloadFile(url, cachedFile); if (_needsToBeExecutable(artifact)) { ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]); diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart new file mode 100644 index 0000000000..0db0299e7c --- /dev/null +++ b/packages/flutter_tools/lib/src/build_configuration.dart @@ -0,0 +1,37 @@ +// 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 'package:path/path.dart' as path; + +enum BuildType { + prebuilt, + release, + debug, +} + +enum BuildPlatform { + android, + iOS, + iOSSimulator, + mac, + linux, +} + +class BuildConfiguration { + BuildConfiguration.prebuilt({ this.platform }) + : type = BuildType.prebuilt, buildDir = null; + + BuildConfiguration.local({ + this.type, + this.platform, + String enginePath, + String buildPath + }) : buildDir = path.normalize(path.join(enginePath, buildPath)) { + assert(type == BuildType.debug || type == BuildType.release); + } + + final BuildType type; + final BuildPlatform platform; + final String buildDir; +} diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 7874e9d039..b547240f1a 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.build; - import 'dart:async'; import 'dart:io'; @@ -105,7 +103,7 @@ Future _compileSnapshot({ String snapshotPath }) async { if (compilerPath == null) { - compilerPath = await ArtifactStore.getPath(Artifact.FlutterCompiler); + compilerPath = await ArtifactStore.getPath(Artifact.flutterCompiler); } ProcessResult result = await Process.run(compilerPath, [ mainPath, diff --git a/packages/flutter_tools/lib/src/commands/cache.dart b/packages/flutter_tools/lib/src/commands/cache.dart index 74c2ac62a4..ac8d185800 100644 --- a/packages/flutter_tools/lib/src/commands/cache.dart +++ b/packages/flutter_tools/lib/src/commands/cache.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.cache; - import 'dart:async'; import 'package:args/command_runner.dart'; @@ -14,8 +12,8 @@ import '../artifacts.dart'; final Logger _logging = new Logger('sky_tools.cache'); class CacheCommand extends Command { - final name = 'cache'; - final description = 'Manages sky_tools\' cache of binary artifacts.'; + final String name = 'cache'; + final String description = 'Manages sky_tools\' cache of binary artifacts.'; CacheCommand() { addSubcommand(new _ClearCommand()); addSubcommand(new _PopulateCommand()); @@ -23,8 +21,8 @@ class CacheCommand extends Command { } class _ClearCommand extends Command { - final name = 'clear'; - final description = 'Clears all artifacts from the cache.'; + final String name = 'clear'; + final String description = 'Clears all artifacts from the cache.'; @override Future run() async { @@ -34,8 +32,8 @@ class _ClearCommand extends Command { } class _PopulateCommand extends Command { - final name = 'populate'; - final description = 'Populates the cache with all known artifacts.'; + final String name = 'populate'; + final String description = 'Populates the cache with all known artifacts.'; @override Future run() async { diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart new file mode 100644 index 0000000000..55038b941f --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart @@ -0,0 +1,38 @@ +// 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:args/command_runner.dart'; + +import '../application_package.dart'; +import '../device.dart'; +import 'flutter_command_runner.dart'; + +abstract class FlutterCommand extends Command { + FlutterCommandRunner get runner => super.runner; + + Future downloadApplicationPackages() async { + if (applicationPackages == null) + applicationPackages = await ApplicationPackageStore.forConfigs(runner.buildConfigurations); + } + + void connectToDevices() { + if (devices == null) + devices = new DeviceStore.forConfigs(runner.buildConfigurations); + } + + Future downloadApplicationPackagesAndConnectToDevices() async { + await downloadApplicationPackages(); + connectToDevices(); + } + + void inheritFromParent(FlutterCommand other) { + applicationPackages = other.applicationPackages; + devices = other.devices; + } + + ApplicationPackageStore applicationPackages; + DeviceStore devices; +} diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart new file mode 100644 index 0000000000..b346ecfd76 --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -0,0 +1,161 @@ +// 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 'dart:io'; + +import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; + +import '../artifacts.dart'; +import '../build_configuration.dart'; + +final Logger _logging = new Logger('sky_tools.flutter_command_runner'); + +class FlutterCommandRunner extends CommandRunner { + FlutterCommandRunner() + : super('flutter', 'Manage your Flutter app development.') { + argParser.addFlag('verbose', + abbr: 'v', + negatable: false, + help: 'Noisy logging, including all shell commands executed.'); + argParser.addFlag('very-verbose', + negatable: false, + help: 'Very noisy logging, including the output of all ' + 'shell commands executed.'); + + argParser.addSeparator('Global build selection options:'); + argParser.addFlag('debug', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the debug build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Not normally required.'); + argParser.addFlag('release', + negatable: false, + help: + 'Set this if you are building Sky locally and want to use the release build products. ' + 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'not set. Note that release is not compatible with the listen command ' + 'on iOS devices and simulators. Not normally required.'); + argParser.addOption('sky-src-path', + help: + 'Path to your Sky src directory, if you are building Sky locally. ' + 'Ignored if neither debug nor release is set. Not normally required.'); + argParser.addOption('android-debug-build-path', + help: + 'Path to your Android Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Debug/'); + argParser.addOption('android-release-build-path', + help: + 'Path to your Android Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/android_Release/'); + argParser.addOption('ios-debug-build-path', + help: + 'Path to your iOS Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Debug/'); + argParser.addOption('ios-release-build-path', + help: + 'Path to your iOS Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_Release/'); + argParser.addOption('ios-sim-debug-build-path', + help: + 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Debug/'); + argParser.addOption('ios-sim-release-build-path', + help: + 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' + 'This path is relative to sky-src-path. Not normally required.', + defaultsTo: 'out/ios_sim_Release/'); + argParser.addOption('package-root', + help: 'Path to your packages directory.', defaultsTo: 'packages'); + } + + List buildConfigurations; + + Future runCommand(ArgResults globalResults) async { + if (globalResults['verbose']) + Logger.root.level = Level.INFO; + + if (globalResults['very-verbose']) + Logger.root.level = Level.FINE; + + ArtifactStore.packageRoot = globalResults['package-root']; + buildConfigurations = _createBuildConfigurations(globalResults); + + return super.runCommand(globalResults); + } + + List _createBuildConfigurations(ArgResults globalResults) { + // TODO(iansf): Figure out how to get the default src path + String enginePath = globalResults['sky-src-path']; + + List configs = []; + + if (enginePath == null) { + configs.add(new BuildConfiguration.prebuilt(platform: BuildPlatform.android)); + } else { + if (!FileSystemEntity.isDirectorySync(enginePath)) + _logging.warning('$enginePath is not a valid directory'); + + if (globalResults['debug']) { + configs.add(new BuildConfiguration.local( + type: BuildType.debug, + platform: BuildPlatform.android, + enginePath: enginePath, + buildPath: globalResults['android-debug-build-path'] + )); + + if (Platform.isMacOS) { + configs.add(new BuildConfiguration.local( + type: BuildType.debug, + platform: BuildPlatform.iOS, + enginePath: enginePath, + buildPath: globalResults['ios-debug-build-path'] + )); + + configs.add(new BuildConfiguration.local( + type: BuildType.debug, + platform: BuildPlatform.iOSSimulator, + enginePath: enginePath, + buildPath: globalResults['ios-sim-debug-build-path'] + )); + } + } + + if (globalResults['release']) { + configs.add(new BuildConfiguration.local( + type: BuildType.release, + platform: BuildPlatform.android, + enginePath: enginePath, + buildPath: globalResults['android-release-build-path'] + )); + + if (Platform.isMacOS) { + configs.add(new BuildConfiguration.local( + type: BuildType.release, + platform: BuildPlatform.iOS, + enginePath: enginePath, + buildPath: globalResults['ios-release-build-path'] + )); + + configs.add(new BuildConfiguration.local( + type: BuildType.release, + platform: BuildPlatform.iOSSimulator, + enginePath: enginePath, + buildPath: globalResults['ios-sim-release-build-path'] + )); + } + } + } + + return configs; + } +} diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 9a1de2f89e..c72dac2055 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.init; - import 'dart:async'; import 'dart:io'; @@ -12,8 +10,8 @@ import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; class InitCommand extends Command { - final name = 'init'; - final description = 'Create a new Flutter project.'; + final String name = 'init'; + final String description = 'Create a new Flutter project.'; InitCommand() { argParser.addOption('out', abbr: 'o', help: 'The output directory.'); diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart index 5f98fd5c11..9dc7daaf22 100644 --- a/packages/flutter_tools/lib/src/commands/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -2,70 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.install; - import 'dart:async'; -import 'package:args/command_runner.dart'; - import '../application_package.dart'; import '../device.dart'; +import 'flutter_command.dart'; -class InstallCommand extends Command { - final name = 'install'; - final description = 'Install your Flutter app on attached devices.'; +class InstallCommand extends FlutterCommand { + final String name = 'install'; + final String description = 'Install your Flutter app on attached devices.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; - - InstallCommand({this.android, this.ios, this.iosSim}) { + InstallCommand() { argParser.addFlag('boot', help: 'Boot the iOS Simulator if it isn\'t already running.'); } @override Future run() async { - if (install(argResults['boot'])) { - return 0; - } else { - return 2; - } + await downloadApplicationPackagesAndConnectToDevices(); + return install(boot: argResults['boot']) ? 0 : 2; } - bool install([bool boot = false]) { - if (android == null) { - android = new AndroidDevice(); - } - if (ios == null) { - ios = new IOSDevice(); - } - if (iosSim == null) { - iosSim = new IOSSimulator(); - } - - if (boot) { - iosSim.boot(); - } + bool install({ bool boot: false }) { + if (boot) + devices.iOSSimulator?.boot(); bool installedSomewhere = false; - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); - ApplicationPackage androidApp = packages[BuildPlatform.android]; - ApplicationPackage iosApp = packages[BuildPlatform.iOS]; - ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; - - if (androidApp != null && android.isConnected()) { - installedSomewhere = android.installApp(androidApp) || installedSomewhere; - } - - if (iosApp != null && ios.isConnected()) { - installedSomewhere = ios.installApp(iosApp) || installedSomewhere; - } - - if (iosSimApp != null && iosSim.isConnected()) { - installedSomewhere = iosSim.installApp(iosSimApp) || installedSomewhere; + for (Device device in devices.all) { + ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); + if (package == null || !device.isConnected()) + continue; + if (device.installApp(package)) + installedSomewhere = true; } return installedSomewhere; diff --git a/packages/flutter_tools/lib/src/commands/list.dart b/packages/flutter_tools/lib/src/commands/list.dart index 5af6c50d2c..e81ca0b86e 100644 --- a/packages/flutter_tools/lib/src/commands/list.dart +++ b/packages/flutter_tools/lib/src/commands/list.dart @@ -2,25 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.list; - import 'dart:async'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'flutter_command.dart'; import '../device.dart'; final Logger _logging = new Logger('sky_tools.list'); -class ListCommand extends Command { - final name = 'list'; - final description = 'List all connected devices.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; +class ListCommand extends FlutterCommand { + final String name = 'list'; + final String description = 'List all connected devices.'; - ListCommand({this.android, this.ios, this.iosSim}) { + ListCommand() { argParser.addFlag('details', abbr: 'd', negatable: false, @@ -29,11 +24,14 @@ class ListCommand extends Command { @override Future run() async { + connectToDevices(); + bool details = argResults['details']; - if (details) { + + if (details) print('Android Devices:'); - } - for (AndroidDevice device in AndroidDevice.getAttachedDevices(android)) { + + for (AndroidDevice device in AndroidDevice.getAttachedDevices(devices.android)) { if (details) { print('${device.id}\t' '${device.modelID}\t' @@ -44,10 +42,10 @@ class ListCommand extends Command { } } - if (details) { + if (details) print('iOS Devices:'); - } - for (IOSDevice device in IOSDevice.getAttachedDevices(ios)) { + + for (IOSDevice device in IOSDevice.getAttachedDevices(devices.iOS)) { if (details) { print('${device.id}\t${device.name}'); } else { @@ -58,7 +56,7 @@ class ListCommand extends Command { if (details) { print('iOS Simulators:'); } - for (IOSSimulator device in IOSSimulator.getAttachedDevices(iosSim)) { + for (IOSSimulator device in IOSSimulator.getAttachedDevices(devices.iOSSimulator)) { if (details) { print('${device.id}\t${device.name}'); } else { diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index b08a2a69ce..9e294c5961 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -2,45 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.listen; - import 'dart:async'; import 'dart:io'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'flutter_command.dart'; import '../application_package.dart'; import '../device.dart'; import '../process.dart'; final Logger _logging = new Logger('sky_tools.listen'); -class ListenCommand extends Command { - final name = 'listen'; - final description = 'Listen for changes to files and reload the running app ' - 'on all connected devices.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; +class ListenCommand extends FlutterCommand { + final String name = 'listen'; + final String description = 'Listen for changes to files and reload the running app on all connected devices.'; List watchCommand; /// Only run once. Used for testing. bool singleRun; - ListenCommand({this.android, this.ios, this.iosSim, this.singleRun: false}) {} + ListenCommand({ this.singleRun: false }); @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } - if (ios == null) { - ios = new IOSDevice(); - } - if (iosSim == null) { - iosSim = new IOSSimulator(); - } + await downloadApplicationPackagesAndConnectToDevices(); if (argResults.rest.length > 0) { watchCommand = _initWatchCommand(argResults.rest); @@ -48,14 +34,8 @@ class ListenCommand extends Command { watchCommand = _initWatchCommand(['.']); } - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); - ApplicationPackage androidApp = packages[BuildPlatform.android]; - ApplicationPackage iosApp = packages[BuildPlatform.iOS]; - ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; - while (true) { - _logging.info('Updating running Sky apps...'); + _logging.info('Updating running Flutter apps...'); // TODO(iansf): refactor build command so that this doesn't have // to call out like this. @@ -76,25 +56,29 @@ class ListenCommand extends Command { } catch (e) {} runSync(command); - String localFLXPath = 'app.flx'; - String remoteFLXPath = 'Documents/app.flx'; + String localFlutterBundle = 'app.flx'; + String remoteFlutterBundle = 'Documents/app.flx'; - if (ios.isConnected()) { - await ios.pushFile(iosApp, localFLXPath, remoteFLXPath); + for (Device device in devices.all) { + ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); + if (package == null || !device.isConnected()) + continue; + if (device is AndroidDevice) { + await devices.android.startServer( + argResults['target'], true, argResults['checked'], package); + } else if (device is IOSDevice) { + 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); + } else { + assert(false); + } } - if (iosSim.isConnected()) { - await iosSim.pushFile(iosSimApp, localFLXPath, remoteFLXPath); - } - - if (android.isConnected()) { - await android.startServer( - argResults['target'], true, argResults['checked'], androidApp); - } - - if (singleRun || !watchDirectory()) { + if (singleRun || !watchDirectory()) break; - } } return 0; @@ -135,9 +119,8 @@ class ListenCommand extends Command { } bool watchDirectory() { - if (watchCommand == null) { + if (watchCommand == null) return false; - } try { runCheckedSync(watchCommand); @@ -145,6 +128,7 @@ class ListenCommand extends Command { _logging.warning('Watching directories failed.', e); return false; } + return true; } } diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart index f488a5fbe8..68a49798fc 100644 --- a/packages/flutter_tools/lib/src/commands/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -2,25 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.logs; - import 'dart:async'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'flutter_command.dart'; import '../device.dart'; final Logger _logging = new Logger('sky_tools.logs'); -class LogsCommand extends Command { +class LogsCommand extends FlutterCommand { final name = 'logs'; final description = 'Show logs for running Sky apps.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; - LogsCommand({this.android, this.ios, this.iosSim}) { + LogsCommand() { argParser.addFlag('clear', negatable: false, help: 'Clear log history before reading from logs (Android only).'); @@ -28,42 +23,15 @@ class LogsCommand extends Command { @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } - if (ios == null) { - ios = new IOSDevice(); - } - if (iosSim == null) { - iosSim = new IOSSimulator(); - } + connectToDevices(); - Future androidLogProcess = null; - if (android.isConnected()) { - androidLogProcess = android.logs(clear: argResults['clear']); - } + bool clear = argResults['clear']; - Future iosLogProcess = null; - if (ios.isConnected()) { - iosLogProcess = ios.logs(clear: argResults['clear']); - } + Iterable> results = devices.all.map( + (Device device) => device.logs(clear: clear)); - Future iosSimLogProcess = null; - if (iosSim.isConnected()) { - iosSimLogProcess = iosSim.logs(clear: argResults['clear']); - } - - if (androidLogProcess != null) { - await androidLogProcess; - } - - if (iosLogProcess != null) { - await iosLogProcess; - } - - if (iosSimLogProcess != null) { - await iosSimLogProcess; - } + for (Future result in results) + await result; return 0; } diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index 077fccbc9f..48679f8ccd 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.run_mojo; - import 'dart:async'; import 'dart:io'; @@ -19,8 +17,9 @@ final Logger _logging = new Logger('sky_tools.run_mojo'); enum _MojoConfig { Debug, Release } class RunMojoCommand extends Command { - final name = 'run_mojo'; - final description = 'Run a Flutter app in mojo.'; + final String name = 'run_mojo'; + final String description = 'Run a Flutter app in mojo.'; + RunMojoCommand() { argParser.addFlag('android', negatable: false, help: 'Run on an Android device'); argParser.addFlag('checked', negatable: false, help: 'Run Flutter in checked mode'); @@ -31,6 +30,7 @@ class RunMojoCommand extends Command { argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); } + // TODO(abarth): Why not use path.absolute? Future _makePathAbsolute(String relativePath) async { File file = new File(relativePath); if (!await file.exists()) { @@ -65,7 +65,7 @@ class RunMojoCommand extends Command { } Future _runLinux(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { - String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.SkyViewerMojo)); + String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.skyViewerMojo)); String mojoBuildType = mojoConfig == _MojoConfig.Debug ? 'Debug' : 'Release'; String mojoShellPath = await _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); List cmd = [ diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index 67f177333b..77e3c69e60 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -2,29 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.start; - import 'dart:async'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import '../application_package.dart'; import '../device.dart'; +import 'flutter_command.dart'; import 'install.dart'; import 'stop.dart'; final Logger _logging = new Logger('sky_tools.start'); -class StartCommand extends Command { - final name = 'start'; - final description = 'Start your Flutter app on attached devices.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; +class StartCommand extends FlutterCommand { + final String name = 'start'; + final String description = 'Start your Flutter app on attached devices.'; - StartCommand({this.android, this.ios, this.iosSim}) { + StartCommand() { argParser.addFlag('poke', negatable: false, help: 'Restart the connection to the server (Android only).'); @@ -42,53 +37,36 @@ class StartCommand extends Command { @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } - if (ios == null) { - ios = new IOSDevice(); - } - if (iosSim == null) { - iosSim = new IOSSimulator(); - } + await downloadApplicationPackagesAndConnectToDevices(); - bool startedSomewhere = false; bool poke = argResults['poke']; if (!poke) { - StopCommand stopper = new StopCommand(android: android, ios: ios); + StopCommand stopper = new StopCommand(); + stopper.inheritFromParent(this); stopper.stop(); // Only install if the user did not specify a poke - InstallCommand installer = - new InstallCommand(android: android, ios: ios, iosSim: iosSim); - installer.install(argResults['boot']); + InstallCommand installer = new InstallCommand(); + installer.inheritFromParent(this); + installer.install(boot: argResults['boot']); } - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); - ApplicationPackage androidApp = packages[BuildPlatform.android]; - ApplicationPackage iosApp = packages[BuildPlatform.iOS]; - ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator]; + bool startedSomething = false; - bool startedOnAndroid = false; - if (androidApp != null && android.isConnected()) { - String target = path.absolute(argResults['target']); - startedOnAndroid = await android.startServer( - target, poke, argResults['checked'], androidApp); + for (Device device in devices.all) { + ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); + if (package == null || !device.isConnected()) + continue; + if (device is AndroidDevice) { + String target = path.absolute(argResults['target']); + if (await device.startServer(target, poke, argResults['checked'], package)) + startedSomething = true; + } else { + if (await device.startApp(package)) + startedSomething = true; + } } - if (iosApp != null && ios.isConnected()) { - startedSomewhere = await ios.startApp(iosApp) || startedSomewhere; - } - - if (iosSimApp != null && iosSim.isConnected()) { - startedSomewhere = await iosSim.startApp(iosSimApp) || startedSomewhere; - } - - if (startedSomewhere || startedOnAndroid) { - return 0; - } else { - return 2; - } + return startedSomething ? 0 : 2; } } diff --git a/packages/flutter_tools/lib/src/commands/stop.dart b/packages/flutter_tools/lib/src/commands/stop.dart index 3d5128e7f9..6118b9b639 100644 --- a/packages/flutter_tools/lib/src/commands/stop.dart +++ b/packages/flutter_tools/lib/src/commands/stop.dart @@ -2,64 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.stop; - import 'dart:async'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import '../application_package.dart'; import '../device.dart'; +import 'flutter_command.dart'; final Logger _logging = new Logger('sky_tools.stop'); -class StopCommand extends Command { - final name = 'stop'; - final description = 'Stop your Flutter app on all attached devices.'; - AndroidDevice android; - IOSDevice ios; - IOSSimulator iosSim; - - StopCommand({this.android, this.ios, this.iosSim}); +class StopCommand extends FlutterCommand { + final String name = 'stop'; + final String description = 'Stop your Flutter app on all attached devices.'; @override Future run() async { - if (await stop()) { - return 0; - } else { - return 2; - } + await downloadApplicationPackagesAndConnectToDevices(); + return await stop() ? 0 : 2; } Future stop() async { - if (android == null) { - android = new AndroidDevice(); - } - if (ios == null) { - ios = new IOSDevice(); - } - if (iosSim == null) { - iosSim = new IOSSimulator(); - } - bool stoppedSomething = false; - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); - if (android.isConnected()) { - ApplicationPackage androidApp = packages[BuildPlatform.android]; - stoppedSomething = await android.stopApp(androidApp) || stoppedSomething; - } - - if (ios.isConnected()) { - ApplicationPackage iosApp = packages[BuildPlatform.iOS]; - stoppedSomething = await ios.stopApp(iosApp) || stoppedSomething; - } - - if (iosSim.isConnected()) { - ApplicationPackage iosApp = packages[BuildPlatform.iOSSimulator]; - stoppedSomething = await iosSim.stopApp(iosApp) || stoppedSomething; + for (Device device in devices.all) { + ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); + if (package == null || !device.isConnected()) + continue; + if (await device.stopApp(package)) + stoppedSomething = true; } return stoppedSomething; diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart index 6b41635f30..7ad257ffe8 100644 --- a/packages/flutter_tools/lib/src/commands/trace.dart +++ b/packages/flutter_tools/lib/src/commands/trace.dart @@ -2,28 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.trace; - import 'dart:async'; -import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'flutter_command.dart'; import '../application_package.dart'; import '../device.dart'; final Logger _logging = new Logger('sky_tools.trace'); -class TraceCommand extends Command { - final name = 'trace'; - final description = 'Start and stop tracing a running Flutter app ' +class TraceCommand extends FlutterCommand { + final String name = 'trace'; + final String description = 'Start and stop tracing a running Flutter app ' '(Android only, requires root).\n' 'To start a trace, wait, and then stop the trace, don\'t set any flags ' 'except (optionally) duration.\n' 'Otherwise, specify either start or stop to manually control the trace.'; - AndroidDevice android; - TraceCommand([this.android]) { + TraceCommand() { argParser.addFlag('start', negatable: false, help: 'Start tracing.'); argParser.addFlag('stop', negatable: false, help: 'Stop tracing.'); argParser.addOption('duration', @@ -32,35 +29,32 @@ class TraceCommand extends Command { @override Future run() async { - if (android == null) { - android = new AndroidDevice(); - } + await downloadApplicationPackagesAndConnectToDevices(); - if (!android.isConnected()) { + if (!devices.android.isConnected()) { _logging.warning('No device connected, so no trace was completed.'); return 1; } - Map packages = - ApplicationPackageFactory.getAvailableApplicationPackages(); - ApplicationPackage androidApp = packages[BuildPlatform.android]; + + ApplicationPackage androidApp = applicationPackages.android; if ((!argResults['start'] && !argResults['stop']) || (argResults['start'] && argResults['stop'])) { // Setting neither flags or both flags means do both commands and wait // duration seconds in between. - android.startTracing(androidApp); + devices.android.startTracing(androidApp); await new Future.delayed( new Duration(seconds: int.parse(argResults['duration'])), - () => _stopTracing(androidApp)); + () => _stopTracing(devices.android, androidApp)); } else if (argResults['stop']) { - _stopTracing(androidApp); + _stopTracing(devices.android, androidApp); } else { - android.startTracing(androidApp); + devices.android.startTracing(androidApp); } return 0; } - void _stopTracing(AndroidApk androidApp) { + void _stopTracing(AndroidDevice android, AndroidApk androidApp) { String tracePath = android.stopTracing(androidApp); if (tracePath == null) { _logging.warning('No trace file saved.'); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 69ae496aac..41878583ed 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -12,15 +12,16 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'application_package.dart'; +import 'build_configuration.dart'; import 'process.dart'; final Logger _logging = new Logger('sky_tools.device'); -abstract class _Device { +abstract class Device { final String id; - static Map _deviceCache = {}; + static Map _deviceCache = {}; - factory _Device(String className, [String id = null]) { + factory Device._unique(String className, [String id = null]) { if (id == null) { if (className == AndroidDevice.className) { id = AndroidDevice.defaultDeviceID; @@ -52,7 +53,7 @@ abstract class _Device { }); } - _Device._(this.id); + Device._(this.id); /// Install an app package on the current device bool installApp(ApplicationPackage app); @@ -63,6 +64,10 @@ abstract class _Device { /// Check if the current version of the given app is already installed bool isAppInstalled(ApplicationPackage app); + BuildPlatform get platform; + + Future logs({bool clear: false}); + /// Start an app package on the current device Future startApp(ApplicationPackage app); @@ -70,7 +75,7 @@ abstract class _Device { Future stopApp(ApplicationPackage app); } -class IOSDevice extends _Device { +class IOSDevice extends Device { static const String className = 'IOSDevice'; static final String defaultDeviceID = 'default_ios_id'; @@ -105,7 +110,7 @@ class IOSDevice extends _Device { String get name => _name; factory IOSDevice({String id, String name}) { - IOSDevice device = new _Device(className, id); + IOSDevice device = new Device._unique(className, id); device._name = name; return device; } @@ -174,9 +179,9 @@ class IOSDevice extends _Device { bool installApp(ApplicationPackage app) { try { if (id == defaultDeviceID) { - runCheckedSync([installerPath, '-i', app.appPath]); + runCheckedSync([installerPath, '-i', app.localPath]); } else { - runCheckedSync([installerPath, '-u', id, '-i', app.appPath]); + runCheckedSync([installerPath, '-u', id, '-i', app.localPath]); } return true; } catch (e) { @@ -200,7 +205,7 @@ class IOSDevice extends _Device { bool isAppInstalled(ApplicationPackage app) { try { String apps = runCheckedSync([installerPath, '-l']); - if (new RegExp(app.appPackageID, multiLine: true).hasMatch(apps)) { + if (new RegExp(app.id, multiLine: true).hasMatch(apps)) { return true; } } catch (e) { @@ -217,7 +222,7 @@ class IOSDevice extends _Device { // idevicedebug hangs forever after launching the app, so kill it after // giving it plenty of time to send the launch command. return runAndKill( - [debuggerPath, 'run', app.appPackageID], new Duration(seconds: 3)).then( + [debuggerPath, 'run', app.id], new Duration(seconds: 3)).then( (_) { return true; }, onError: (e) { @@ -240,7 +245,7 @@ class IOSDevice extends _Device { '-t', '1', '--bundle_id', - app.appPackageID, + app.id, '--upload', localFile, '--to', @@ -258,6 +263,9 @@ class IOSDevice extends _Device { return false; } + @override + BuildPlatform get platform => BuildPlatform.iOS; + /// Note that clear is not supported on iOS at this time. Future logs({bool clear: false}) async { if (!isConnected()) { @@ -268,7 +276,7 @@ class IOSDevice extends _Device { } } -class IOSSimulator extends _Device { +class IOSSimulator extends Device { static const String className = 'IOSSimulator'; static final String defaultDeviceID = 'default_ios_sim_id'; @@ -288,7 +296,7 @@ class IOSSimulator extends _Device { String get name => _name; factory IOSSimulator({String id, String name, String iOSSimulatorPath}) { - IOSSimulator device = new _Device(className, id); + IOSSimulator device = new Device._unique(className, id); device._name = name; if (iOSSimulatorPath == null) { iOSSimulatorPath = path.join('/Applications', 'iOS Simulator.app', @@ -401,9 +409,9 @@ class IOSSimulator extends _Device { } try { if (id == defaultDeviceID) { - runCheckedSync([xcrunPath, 'simctl', 'install', 'booted', app.appPath]); + runCheckedSync([xcrunPath, 'simctl', 'install', 'booted', app.localPath]); } else { - runCheckedSync([xcrunPath, 'simctl', 'install', id, app.appPath]); + runCheckedSync([xcrunPath, 'simctl', 'install', id, app.localPath]); } return true; } catch (e) { @@ -444,9 +452,9 @@ class IOSSimulator extends _Device { try { if (id == defaultDeviceID) { runCheckedSync( - [xcrunPath, 'simctl', 'launch', 'booted', app.appPackageID]); + [xcrunPath, 'simctl', 'launch', 'booted', app.id]); } else { - runCheckedSync([xcrunPath, 'simctl', 'launch', id, app.appPackageID]); + runCheckedSync([xcrunPath, 'simctl', 'launch', id, app.id]); } return true; } catch (e) { @@ -471,6 +479,9 @@ class IOSSimulator extends _Device { return false; } + @override + BuildPlatform get platform => BuildPlatform.iOSSimulator; + Future logs({bool clear: false}) async { if (!isConnected()) { return 2; @@ -487,7 +498,7 @@ class IOSSimulator extends _Device { } } -class AndroidDevice extends _Device { +class AndroidDevice extends Device { static const String _ADB_PATH = 'adb'; static const String _observatoryPort = '8181'; static const String _serverPort = '9888'; @@ -509,7 +520,7 @@ class AndroidDevice extends _Device { String productID: null, String modelID: null, String deviceCodeName: null}) { - AndroidDevice device = new _Device(className, id); + AndroidDevice device = new Device._unique(className, id); device.productID = productID; device.modelID = modelID; device.deviceCodeName = deviceCodeName; @@ -553,15 +564,13 @@ class AndroidDevice extends _Device { _adbPath = _getAdbPath(); _hasAdb = _checkForAdb(); - if (isConnected()) { - // Checking for lollipop only needs to be done if we are starting an - // app, but it has an important side effect, which is to discard any - // progress messages if the adb server is restarted. - _hasValidAndroid = _checkForLollipopOrLater(); + // Checking for lollipop only needs to be done if we are starting an + // app, but it has an important side effect, which is to discard any + // progress messages if the adb server is restarted. + _hasValidAndroid = _checkForLollipopOrLater(); - if (!_hasAdb || !_hasValidAndroid) { - _logging.severe('Unable to run on Android.'); - } + if (!_hasAdb || !_hasValidAndroid) { + _logging.severe('Unable to run on Android.'); } } @@ -663,7 +672,7 @@ class AndroidDevice extends _Device { } String _getDeviceSha1Path(ApplicationPackage app) { - return '/sdcard/${app.appPackageID}/${app.appFileName}.sha1'; + return '/sdcard/${app.id}/${app.name}.sha1'; } String _getDeviceApkSha1(ApplicationPackage app) { @@ -672,7 +681,7 @@ class AndroidDevice extends _Device { String _getSourceSha1(ApplicationPackage app) { String sha1 = - runCheckedSync(['shasum', '-a', '1', '-p', app.appPath]).split(' ')[0]; + runCheckedSync(['shasum', '-a', '1', '-p', app.localPath]).split(' ')[0]; return sha1; } @@ -681,15 +690,15 @@ class AndroidDevice extends _Device { if (!isConnected()) { return false; } - if (runCheckedSync([adbPath, 'shell', 'pm', 'path', app.appPackageID]) == + if (runCheckedSync([adbPath, 'shell', 'pm', 'path', app.id]) == '') { _logging.info( - 'TODO(iansf): move this log to the caller. ${app.appFileName} is not on the device. Installing now...'); + 'TODO(iansf): move this log to the caller. ${app.name} is not on the device. Installing now...'); return false; } if (_getDeviceApkSha1(app) != _getSourceSha1(app)) { _logging.info( - 'TODO(iansf): move this log to the caller. ${app.appFileName} is out of date. Installing now...'); + 'TODO(iansf): move this log to the caller. ${app.name} is out of date. Installing now...'); return false; } return true; @@ -701,16 +710,16 @@ class AndroidDevice extends _Device { _logging.info('Android device not connected. Not installing.'); return false; } - if (!FileSystemEntity.isFileSync(app.appPath)) { - _logging.severe('"${app.appPath}" does not exist.'); + if (!FileSystemEntity.isFileSync(app.localPath)) { + _logging.severe('"${app.localPath}" does not exist.'); return false; } - runCheckedSync([adbPath, 'install', '-r', app.appPath]); + runCheckedSync([adbPath, 'install', '-r', app.localPath]); Directory tempDir = Directory.systemTemp; String sha1Path = path.join( - tempDir.path, (app.appPath + '.sha1').replaceAll(path.separator, '_')); + tempDir.path, (app.localPath + '.sha1').replaceAll(path.separator, '_')); File sha1TempFile = new File(sha1Path); sha1TempFile.writeAsStringSync(_getSourceSha1(app), flush: true); runCheckedSync([adbPath, 'push', sha1Path, _getDeviceSha1Path(app)]); @@ -774,7 +783,7 @@ class AndroidDevice extends _Device { if (checked) { cmd.addAll(['--ez', 'enable-checked-mode', 'true']); } - cmd.add(apk.component); + cmd.add(apk.launchActivity); runCheckedSync(cmd); @@ -792,7 +801,7 @@ class AndroidDevice extends _Device { // Turn off reverse port forwarding runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); // Stop the app - runSync([adbPath, 'shell', 'am', 'force-stop', apk.appPackageID]); + runSync([adbPath, 'shell', 'am', 'force-stop', apk.id]); // Kill the server if (Platform.isMacOS) { String pid = runSync(['lsof', '-i', ':$_serverPort', '-t']); @@ -808,6 +817,9 @@ class AndroidDevice extends _Device { return true; } + @override + BuildPlatform get platform => BuildPlatform.android; + void clearLogs() { runSync([adbPath, 'logcat', '-c']); } @@ -839,7 +851,7 @@ class AndroidDevice extends _Device { 'am', 'broadcast', '-a', - '${apk.appPackageID}.TRACING_START' + '${apk.id}.TRACING_START' ]); } @@ -851,7 +863,7 @@ class AndroidDevice extends _Device { 'am', 'broadcast', '-a', - '${apk.appPackageID}.TRACING_STOP' + '${apk.id}.TRACING_STOP' ]); RegExp traceRegExp = new RegExp(r'Saving trace to (\S+)', multiLine: true); @@ -891,3 +903,57 @@ class AndroidDevice extends _Device { @override bool isConnected() => _hasValidAndroid; } + +class DeviceStore { + final AndroidDevice android; + final IOSDevice iOS; + final IOSSimulator iOSSimulator; + + List get all { + List result = []; + if (android != null) + result.add(android); + if (iOS != null) + result.add(iOS); + if (iOSSimulator != null) + result.add(iOSSimulator); + return result; + } + + DeviceStore({ + this.android, + this.iOS, + this.iOSSimulator + }); + + factory DeviceStore.forConfigs(List configs) { + AndroidDevice android; + IOSDevice iOS; + IOSSimulator iOSSimulator; + + for (BuildConfiguration config in configs) { + switch (config.platform) { + case BuildPlatform.android: + assert(android == null); + android = new AndroidDevice(); + break; + case BuildPlatform.iOS: + assert(iOS == null); + iOS = new IOSDevice(); + break; + case BuildPlatform.iOSSimulator: + assert(iOSSimulator == null); + iOSSimulator = new IOSSimulator(); + break; + + case BuildPlatform.mac: + case BuildPlatform.linux: + // TODO(abarth): Support mac and linux targets. + assert(false); + break; + } + } + + return new DeviceStore(android: android, iOS: iOS, iOSSimulator: iOSSimulator); + } +} diff --git a/packages/flutter_tools/test/android_device_test.dart b/packages/flutter_tools/test/android_device_test.dart index 9841ebc348..9a153b6228 100644 --- a/packages/flutter_tools/test/android_device_test.dart +++ b/packages/flutter_tools/test/android_device_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library android_device_test; - import 'package:sky_tools/src/device.dart'; import 'package:test/test.dart'; @@ -17,9 +15,9 @@ defineTests() { }); test('stores the requested id', () { - String deviceID = '1234'; - AndroidDevice android = new AndroidDevice(id: deviceID); - expect(android.id, equals(deviceID)); + String deviceId = '1234'; + AndroidDevice android = new AndroidDevice(id: deviceId); + expect(android.id, equals(deviceId)); }); test('correctly creates only one of each requested device id', () { diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index b73db0a2c2..f20d640648 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -2,35 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library install_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/install.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('install', () { test('returns 0 when Android is connected and ready for an install', () { - applicationPackageSetup(); + InstallCommand command = new InstallCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(true); - when(android.installApp(any)).thenReturn(true); + when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.installApp(any)).thenReturn(true); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(false); - when(ios.installApp(any)).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.installApp(any)).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.installApp(any)).thenReturn(false); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); - InstallCommand command = new InstallCommand(android: android, ios: ios); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); @@ -38,22 +34,18 @@ defineTests() { }); test('returns 0 when iOS is connected and ready for an install', () { - applicationPackageSetup(); + InstallCommand command = new InstallCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - when(android.installApp(any)).thenReturn(false); + when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.android.installApp(any)).thenReturn(false); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(true); - when(ios.installApp(any)).thenReturn(true); + when(mockDevices.iOS.isConnected()).thenReturn(true); + when(mockDevices.iOS.installApp(any)).thenReturn(true); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.installApp(any)).thenReturn(false); - - InstallCommand command = - new InstallCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index aabf36484c..83bf713fd0 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -2,41 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library list_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/list.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('list', () { test('returns 0 when called', () { - applicationPackageSetup(); + ListCommand command = new ListCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); // Avoid relying on adb being installed on the test system. // Instead, cause the test to run the echo command. - when(android.adbPath).thenReturn('echo'); + when(mockDevices.android.adbPath).thenReturn('echo'); - MockIOSDevice ios = new MockIOSDevice(); // Avoid relying on idevice* being installed on the test system. // Instead, cause the test to run the echo command. - when(ios.informerPath).thenReturn('echo'); - when(ios.installerPath).thenReturn('echo'); - when(ios.listerPath).thenReturn('echo'); + when(mockDevices.iOS.informerPath).thenReturn('echo'); + when(mockDevices.iOS.installerPath).thenReturn('echo'); + when(mockDevices.iOS.listerPath).thenReturn('echo'); - MockIOSSimulator iosSim = new MockIOSSimulator(); // Avoid relying on xcrun being installed on the test system. // Instead, cause the test to run the echo command. - when(iosSim.xcrunPath).thenReturn('echo'); + when(mockDevices.iOSSimulator.xcrunPath).thenReturn('echo'); + - ListCommand command = - new ListCommand(android: android, ios: ios, iosSim: iosSim); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); runner.run(['list']).then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/test/listen_test.dart b/packages/flutter_tools/test/listen_test.dart index 41a47c736a..7899a9103d 100644 --- a/packages/flutter_tools/test/listen_test.dart +++ b/packages/flutter_tools/test/listen_test.dart @@ -2,30 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library listen_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/listen.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('listen', () { test('returns 0 when no device is connected', () { - applicationPackageSetup(); + ListenCommand command = new ListenCommand(singleRun: true); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - ListenCommand command = new ListenCommand( - android: android, ios: ios, iosSim: iosSim, singleRun: true); + when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart index 0fad92fb7a..e43bf5cda8 100644 --- a/packages/flutter_tools/test/logs_test.dart +++ b/packages/flutter_tools/test/logs_test.dart @@ -2,31 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library logs_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/logs.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('logs', () { test('returns 0 when no device is connected', () { - applicationPackageSetup(); + LogsCommand command = new LogsCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - - LogsCommand command = - new LogsCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart deleted file mode 100644 index c935ce6705..0000000000 --- a/packages/flutter_tools/test/src/common.dart +++ /dev/null @@ -1,32 +0,0 @@ -// 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 'package:mockito/mockito.dart'; -import 'package:sky_tools/src/application_package.dart'; -import 'package:sky_tools/src/device.dart'; - -class MockAndroidDevice extends Mock implements AndroidDevice { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class MockIOSDevice extends Mock implements IOSDevice { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class MockIOSSimulator extends Mock implements IOSSimulator { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -void applicationPackageSetup() { - ApplicationPackageFactory.srcPath = './'; - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.android, './'); - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.iOS, './'); - ApplicationPackageFactory.setBuildPath( - BuildType.prebuilt, BuildPlatform.iOSSimulator, './'); -} diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart new file mode 100644 index 0000000000..f37971fa50 --- /dev/null +++ b/packages/flutter_tools/test/src/mocks.dart @@ -0,0 +1,50 @@ +// 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 'package:mockito/mockito.dart'; +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'; + +class MockApplicationPackageStore extends ApplicationPackageStore { + MockApplicationPackageStore() : super( + android: new AndroidApk(localPath: '/mock/path/to/android/SkyShell.apk'), + iOS: new IOSApp(localPath: '/mock/path/to/iOS/SkyShell.app'), + iOSSimulator: new IOSApp(localPath: '/mock/path/to/iOSSimulator/SkyShell.app')); +} + +class MockAndroidDevice extends Mock implements AndroidDevice { + BuildPlatform get platform => BuildPlatform.android; + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockIOSDevice extends Mock implements IOSDevice { + BuildPlatform get platform => BuildPlatform.iOS; + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockIOSSimulator extends Mock implements IOSSimulator { + BuildPlatform get platform => BuildPlatform.iOSSimulator; + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockDeviceStore extends DeviceStore { + MockDeviceStore() : super( + android: new MockAndroidDevice(), + iOS: new MockIOSDevice(), + iOSSimulator: new MockIOSSimulator()); +} + +void applyMocksToCommand(FlutterCommand command) { + command + ..applicationPackages = new MockApplicationPackageStore() + ..devices = new MockDeviceStore(); +} diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index 793d65247d..8b2a679481 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -2,42 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library start_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/start.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('start', () { test('returns 0 when Android is connected and ready to be started', () { - applicationPackageSetup(); + StartCommand command = new StartCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(true); - when(android.installApp(any)).thenReturn(true); - when(android.startServer(any, any, any, any)).thenReturn(true); - when(android.stopApp(any)).thenReturn(true); + when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.installApp(any)).thenReturn(true); + when(mockDevices.android.startServer(any, any, any, any)).thenReturn(true); + when(mockDevices.android.stopApp(any)).thenReturn(true); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(false); - when(ios.installApp(any)).thenReturn(false); - when(ios.startApp(any)).thenReturn(false); - when(ios.stopApp(any)).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.installApp(any)).thenReturn(false); + when(mockDevices.iOS.startApp(any)).thenReturn(false); + when(mockDevices.iOS.stopApp(any)).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.installApp(any)).thenReturn(false); - when(iosSim.startApp(any)).thenReturn(false); - when(iosSim.stopApp(any)).thenReturn(false); - - StartCommand command = - new StartCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); + when(mockDevices.iOSSimulator.startApp(any)).thenReturn(false); + when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); @@ -45,28 +39,24 @@ defineTests() { }); test('returns 0 when iOS is connected and ready to be started', () { - applicationPackageSetup(); + StartCommand command = new StartCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - when(android.installApp(any)).thenReturn(false); - when(android.startServer(any, any, any, any)).thenReturn(false); - when(android.stopApp(any)).thenReturn(false); + when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.android.installApp(any)).thenReturn(false); + when(mockDevices.android.startServer(any, any, any, any)).thenReturn(false); + when(mockDevices.android.stopApp(any)).thenReturn(false); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(true); - when(ios.installApp(any)).thenReturn(true); - when(ios.startApp(any)).thenReturn(true); - when(ios.stopApp(any)).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(true); + when(mockDevices.iOS.installApp(any)).thenReturn(true); + when(mockDevices.iOS.startApp(any)).thenReturn(true); + when(mockDevices.iOS.stopApp(any)).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.installApp(any)).thenReturn(false); - when(iosSim.startApp(any)).thenReturn(false); - when(iosSim.stopApp(any)).thenReturn(false); - - StartCommand command = - new StartCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); + when(mockDevices.iOSSimulator.startApp(any)).thenReturn(false); + when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index 2e64af13a7..79ea1fb5f9 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -9,29 +9,25 @@ import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/stop.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('stop', () { test('returns 0 when Android is connected and ready to be stopped', () { - applicationPackageSetup(); + StopCommand command = new StopCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(true); - when(android.stopApp(any)).thenReturn(true); + when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.stopApp(any)).thenReturn(true); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(false); - when(ios.stopApp(any)).thenReturn(false); + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.stopApp(any)).thenReturn(false); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.stopApp(any)).thenReturn(false); - - StopCommand command = - new StopCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); @@ -39,22 +35,18 @@ defineTests() { }); test('returns 0 when iOS is connected and ready to be stopped', () { - applicationPackageSetup(); + StopCommand command = new StopCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - when(android.stopApp(any)).thenReturn(false); + when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.android.stopApp(any)).thenReturn(false); - MockIOSDevice ios = new MockIOSDevice(); - when(ios.isConnected()).thenReturn(true); - when(ios.stopApp(any)).thenReturn(true); + when(mockDevices.iOS.isConnected()).thenReturn(true); + when(mockDevices.iOS.stopApp(any)).thenReturn(true); - MockIOSSimulator iosSim = new MockIOSSimulator(); - when(iosSim.isConnected()).thenReturn(false); - when(iosSim.stopApp(any)).thenReturn(false); - - StopCommand command = - new StopCommand(android: android, ios: ios, iosSim: iosSim); + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart index 7de1fd84c1..dc7c48d600 100644 --- a/packages/flutter_tools/test/trace_test.dart +++ b/packages/flutter_tools/test/trace_test.dart @@ -9,18 +9,18 @@ import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/trace.dart'; import 'package:test/test.dart'; -import 'src/common.dart'; +import 'src/mocks.dart'; main() => defineTests(); defineTests() { group('trace', () { test('returns 1 when no Android device is connected', () { - applicationPackageSetup(); + TraceCommand command = new TraceCommand(); + applyMocksToCommand(command); + MockDeviceStore mockDevices = command.devices; - MockAndroidDevice android = new MockAndroidDevice(); - when(android.isConnected()).thenReturn(false); - TraceCommand command = new TraceCommand(android); + when(mockDevices.android.isConnected()).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); From 8df5e9f738da87efcbe3c23309af01139be46067 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 00:49:39 -0700 Subject: [PATCH 119/188] Polish up some error handling This patch improves the error handling for several arbitrary issues I encountered while experimenting with the tool this evening. --- .../flutter_tools/lib/src/commands/install.dart | 4 ++-- .../flutter_tools/lib/src/commands/listen.dart | 11 ++++++++++- packages/flutter_tools/lib/src/device.dart | 17 +++++++++++++---- packages/flutter_tools/test/install_test.dart | 6 ++++++ packages/flutter_tools/test/start_test.dart | 6 ++++++ 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart index 9dc7daaf22..a8cb776ac3 100644 --- a/packages/flutter_tools/lib/src/commands/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -10,7 +10,7 @@ import 'flutter_command.dart'; class InstallCommand extends FlutterCommand { final String name = 'install'; - final String description = 'Install your Flutter app on attached devices.'; + final String description = 'Install Flutter apps on attached devices.'; InstallCommand() { argParser.addFlag('boot', @@ -31,7 +31,7 @@ class InstallCommand extends FlutterCommand { for (Device device in devices.all) { ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); - if (package == null || !device.isConnected()) + if (package == null || !device.isConnected() || device.isAppInstalled(package)) continue; if (device.installApp(package)) installedSomewhere = true; diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index 9e294c5961..4b9e59ea2e 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -22,7 +22,16 @@ class ListenCommand extends FlutterCommand { /// Only run once. Used for testing. bool singleRun; - ListenCommand({ this.singleRun: false }); + ListenCommand({ this.singleRun: false }) { + argParser.addFlag('checked', + negatable: true, + defaultsTo: true, + help: 'Toggle Dart\'s checked mode.'); + argParser.addOption('target', + defaultsTo: '.', + abbr: 't', + help: 'Target app path or filename to start.'); + } @override Future run() async { diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 41878583ed..ea874b6d3e 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -142,10 +142,15 @@ class IOSDevice extends Device { static Iterable _getAttachedDeviceIDs([IOSDevice mockIOS]) { String listerPath = (mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id'); - return runSync([listerPath, '-l']) - .trim() - .split('\n') - .where((String s) => s != null && s.length > 0); + String output; + try { + output = runSync([listerPath, '-l']); + } catch (e) { + return []; + } + return output.trim() + .split('\n') + .where((String s) => s != null && s.length > 0); } static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) { @@ -805,6 +810,10 @@ class AndroidDevice extends Device { // Kill the server if (Platform.isMacOS) { String pid = runSync(['lsof', '-i', ':$_serverPort', '-t']); + if (pid.isEmpty) { + _logging.fine('No process to kill for port $_serverPort'); + return true; + } // Killing a pid with a shell command from within dart is hard, // so use a library command, but it's still nice to give the // equivalent command when doing verbose logging. diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart index f20d640648..d10c652746 100644 --- a/packages/flutter_tools/test/install_test.dart +++ b/packages/flutter_tools/test/install_test.dart @@ -19,12 +19,15 @@ defineTests() { MockDeviceStore mockDevices = command.devices; when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(true); when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOS.installApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); @@ -39,12 +42,15 @@ defineTests() { MockDeviceStore mockDevices = command.devices; when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(false); when(mockDevices.iOS.isConnected()).thenReturn(true); + when(mockDevices.iOS.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOS.installApp(any)).thenReturn(true); when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); CommandRunner runner = new CommandRunner('test_flutter', '') diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index 8b2a679481..f9719c5e82 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -19,16 +19,19 @@ defineTests() { MockDeviceStore mockDevices = command.devices; when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(true); when(mockDevices.android.startServer(any, any, any, any)).thenReturn(true); when(mockDevices.android.stopApp(any)).thenReturn(true); when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOS.installApp(any)).thenReturn(false); when(mockDevices.iOS.startApp(any)).thenReturn(false); when(mockDevices.iOS.stopApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.startApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); @@ -44,16 +47,19 @@ defineTests() { MockDeviceStore mockDevices = command.devices; when(mockDevices.android.isConnected()).thenReturn(false); + when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(false); when(mockDevices.android.startServer(any, any, any, any)).thenReturn(false); when(mockDevices.android.stopApp(any)).thenReturn(false); when(mockDevices.iOS.isConnected()).thenReturn(true); + when(mockDevices.iOS.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOS.installApp(any)).thenReturn(true); when(mockDevices.iOS.startApp(any)).thenReturn(true); when(mockDevices.iOS.stopApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.isAppInstalled(any)).thenReturn(false); when(mockDevices.iOSSimulator.installApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.startApp(any)).thenReturn(false); when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); From a157e0eac024ac1447c3856cccdf9dd767823016 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 01:06:20 -0700 Subject: [PATCH 120/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 149acaed57..f9dad38ae1 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.18 +version: 0.0.19 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 0e06feee0123d182ebf9ee0356902d8d0b39e777 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 01:33:03 -0700 Subject: [PATCH 121/188] Add some print statements to smooth first run This patch adds a couple print statements to explain why the first run of `flutter start` takes a while. (We need to download the APK and install it on the device.) --- packages/flutter_tools/lib/src/artifacts.dart | 2 +- packages/flutter_tools/lib/src/commands/init.dart | 4 ++-- packages/flutter_tools/lib/src/device.dart | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 751cbd81ac..23daf0c2dd 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -34,7 +34,7 @@ class ArtifactStore { } static Future _downloadFile(String url, File file) async { - _logging.fine('Downloading $url to ${file.path}'); + print('Downloading $url to ${file.path}.'); HttpClient httpClient = new HttpClient(); HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); HttpClientResponse response = await request.close(); diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index c72dac2055..f968246ac4 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -37,8 +37,8 @@ class InitCommand extends Command { String message = '''All done! To run your application: - cd ${out.path} - ./packages/flutter/sky_tool start + \$ cd ${out.path} + \$ flutter start --checked '''; if (argResults['pub']) { diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index ea874b6d3e..603e95c2b2 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -720,6 +720,7 @@ class AndroidDevice extends Device { return false; } + print('Installing ${app.name} on device.'); runCheckedSync([adbPath, 'install', '-r', app.localPath]); Directory tempDir = Directory.systemTemp; From ca909c9252a4a32f3ed82592767a7676e4acb9f4 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 01:38:09 -0700 Subject: [PATCH 122/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index f9dad38ae1..35463814e6 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.19 +version: 0.0.20 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 9c4835100bcf0717c1811bc864f9917eaf3e1212 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 11:05:09 -0700 Subject: [PATCH 123/188] Handle the case of a non-connected Android device better Previous we dumped a stack trace. Now we produce a more sensible error. --- packages/flutter_tools/lib/src/device.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 603e95c2b2..e05c037b9b 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -653,6 +653,12 @@ class AndroidDevice extends Device { // * daemon started successfully * runCheckedSync([adbPath, 'start-server']); + String ready = runSync([adbPath, 'shell', 'echo', 'ready']); + if (ready.trim() != 'ready') { + _logging.info('Android device not found.'); + return false; + } + // Sample output: '22' String sdkVersion = runCheckedSync([adbPath, 'shell', 'getprop', 'ro.build.version.sdk']) @@ -670,8 +676,8 @@ class AndroidDevice extends Device { return false; } return true; - } catch (e, stack) { - _logging.severe('Unexpected failure from adb: ', e, stack); + } catch (e) { + _logging.severe('Unexpected failure from adb: ', e); } return false; } From d9af939995fcd5063493cf8d079ef45485d26357 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 12 Oct 2015 14:01:05 -0700 Subject: [PATCH 124/188] Improve error message when missing the package-root --- packages/flutter_tools/lib/src/artifacts.dart | 7 +++++-- .../lib/src/commands/flutter_command_runner.dart | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 23daf0c2dd..9bd6a06351 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -23,8 +23,11 @@ class ArtifactStore { static String _engineRevision; static String get engineRevision { - if (_engineRevision == null) - _engineRevision = new File(path.join(packageRoot, 'sky_engine', 'REVISION')).readAsStringSync(); + if (_engineRevision == null) { + File revisionFile = new File(path.join(packageRoot, 'sky_engine', 'REVISION')); + if (revisionFile.existsSync()) + _engineRevision = revisionFile.readAsStringSync(); + } return _engineRevision; } diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index b346ecfd76..74c55d966c 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -88,6 +88,18 @@ class FlutterCommandRunner extends CommandRunner { Logger.root.level = Level.FINE; ArtifactStore.packageRoot = globalResults['package-root']; + if (!FileSystemEntity.isDirectorySync(ArtifactStore.packageRoot)) { + String message = '${ArtifactStore.packageRoot} is not a valid directory.'; + if (ArtifactStore.packageRoot == 'packages') { + if (FileSystemEntity.isFileSync('pubspec.yaml')) + message += '\nDid you run `pub get` in this directory?'; + else + message += '\nDid you run this command from the same directory as your pubspec.yaml file?'; + } + _logging.severe(message); + return 2; + } + buildConfigurations = _createBuildConfigurations(globalResults); return super.runCommand(globalResults); From 12f75817ce1b489774edfbda7073f479a0309f43 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 13 Oct 2015 10:00:06 -0700 Subject: [PATCH 125/188] 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. --- .../flutter_tools/lib/src/commands/build.dart | 86 ++++++++++--------- .../lib/src/commands/flutter_command.dart | 8 ++ .../lib/src/commands/listen.dart | 35 +++----- packages/flutter_tools/lib/src/toolchain.dart | 45 ++++++++++ packages/flutter_tools/test/src/mocks.dart | 11 +++ 5 files changed, 121 insertions(+), 64 deletions(-) create mode 100644 packages/flutter_tools/lib/src/toolchain.dart diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index b547240f1a..af0b64fe86 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -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 _kDensities = const ['drawable-xxhdpi']; @@ -96,74 +96,80 @@ Future _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 _createSnapshotFile(String snapshotPath) async { File file = new File(snapshotPath); List 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 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 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; } } diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart index 55038b941f..d56cdcc9f7 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart @@ -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; } diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index 4b9e59ea2e..64dc314677 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -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 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 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); } diff --git a/packages/flutter_tools/lib/src/toolchain.dart b/packages/flutter_tools/lib/src/toolchain.dart new file mode 100644 index 0000000000..655e6b6b98 --- /dev/null +++ b/packages/flutter_tools/lib/src/toolchain.dart @@ -0,0 +1,45 @@ +// 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 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 forConfigs(List 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)); + } +} diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index f37971fa50..b971bd1831 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -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(); } From f0a1632dd6d052de2c629a8a51da2e1f331c8576 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 13 Oct 2015 10:35:53 -0700 Subject: [PATCH 126/188] Add a --local-build flag to detect engine src paths A common use case for members of the Flutter team is to have a dependency override for the flutter package that points back into the engine src tree. We can use that override to automatically detect the engine src path, which makes the command line shorter. --- .../src/commands/flutter_command_runner.dart | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index 74c55d966c..65dc778a29 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; import '../artifacts.dart'; import '../build_configuration.dart'; @@ -30,49 +31,55 @@ class FlutterCommandRunner extends CommandRunner { argParser.addFlag('debug', negatable: false, help: - 'Set this if you are building Sky locally and want to use the debug build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'Set this if you are building Flutter locally and want to use the debug build products. ' + 'When set, attempts to automaticaly determine engine-src-path if engine-src-path is ' 'not set. Not normally required.'); argParser.addFlag('release', negatable: false, help: - 'Set this if you are building Sky locally and want to use the release build products. ' - 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is ' + 'Set this if you are building Flutter locally and want to use the release build products. ' + 'When set, attempts to automaticaly determine engine-src-path if engine-src-path is ' 'not set. Note that release is not compatible with the listen command ' 'on iOS devices and simulators. Not normally required.'); - argParser.addOption('sky-src-path', + argParser.addFlag('local-build', + negatable: false, help: - 'Path to your Sky src directory, if you are building Sky locally. ' + 'Automatically detect your engine src directory from an overridden Flutter package.' + 'Useful if you are building Flutter locally and are using a dependency_override for' + 'the Flutter package that points to your engine src directory.'); + argParser.addOption('engine-src-path', + help: + 'Path to your engine src directory, if you are building Flutter locally. ' 'Ignored if neither debug nor release is set. Not normally required.'); argParser.addOption('android-debug-build-path', help: - 'Path to your Android Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'Path to your Android Debug out directory, if you are building Flutter locally. ' + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/android_Debug/'); argParser.addOption('android-release-build-path', help: - 'Path to your Android Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'Path to your Android Release out directory, if you are building Flutter locally. ' + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/android_Release/'); argParser.addOption('ios-debug-build-path', help: - 'Path to your iOS Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'Path to your iOS Debug out directory, if you are building Flutter locally. ' + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_Debug/'); argParser.addOption('ios-release-build-path', help: - 'Path to your iOS Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'Path to your iOS Release out directory, if you are building Flutter locally. ' + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_Release/'); argParser.addOption('ios-sim-debug-build-path', help: 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Debug/'); argParser.addOption('ios-sim-release-build-path', help: 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' - 'This path is relative to sky-src-path. Not normally required.', + 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Release/'); argParser.addOption('package-root', help: 'Path to your packages directory.', defaultsTo: 'packages'); @@ -106,8 +113,18 @@ class FlutterCommandRunner extends CommandRunner { } List _createBuildConfigurations(ArgResults globalResults) { - // TODO(iansf): Figure out how to get the default src path - String enginePath = globalResults['sky-src-path']; + String enginePath = globalResults['engine-src-path']; + bool isDebug = globalResults['debug']; + bool isRelease = globalResults['release']; + + if (enginePath == null && globalResults['local-build']) { + Directory flutterDir = new Directory(path.join(globalResults['package-root'], 'flutter')); + String realFlutterPath = flutterDir.resolveSymbolicLinksSync(); + + enginePath = path.dirname(path.dirname(path.dirname(path.dirname(realFlutterPath)))); + if (enginePath == '/' || enginePath.isEmpty) + enginePath = null; + } List configs = []; @@ -117,7 +134,10 @@ class FlutterCommandRunner extends CommandRunner { if (!FileSystemEntity.isDirectorySync(enginePath)) _logging.warning('$enginePath is not a valid directory'); - if (globalResults['debug']) { + if (!isDebug && !isRelease) + isDebug = true; + + if (isDebug) { configs.add(new BuildConfiguration.local( type: BuildType.debug, platform: BuildPlatform.android, @@ -142,7 +162,7 @@ class FlutterCommandRunner extends CommandRunner { } } - if (globalResults['release']) { + if (isRelease) { configs.add(new BuildConfiguration.local( type: BuildType.release, platform: BuildPlatform.android, From 80f30d9176e9534c510c7f3dd33ef6d7d3ab3173 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 13 Oct 2015 13:36:39 -0700 Subject: [PATCH 127/188] Don't dump a stack trace for a usage exception Instead, just print the usage. --- packages/flutter_tools/lib/executable.dart | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 1385672451..db06747998 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; +import 'dart:io'; + +import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'src/commands/flutter_command_runner.dart'; @@ -20,7 +24,7 @@ import 'src/commands/trace.dart'; /// Main entry point for commands. /// /// This function is intended to be used from the [flutter] command line tool. -void main(List args) { +Future main(List args) async { Logger.root.level = Level.WARNING; Logger.root.onRecord.listen((LogRecord record) { print('${record.level.name}: ${record.message}'); @@ -30,7 +34,7 @@ void main(List args) { print(record.stackTrace); }); - new FlutterCommandRunner() + FlutterCommandRunner runner = new FlutterCommandRunner() ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) ..addCommand(new InitCommand()) @@ -41,6 +45,12 @@ void main(List args) { ..addCommand(new RunMojoCommand()) ..addCommand(new StartCommand()) ..addCommand(new StopCommand()) - ..addCommand(new TraceCommand()) - ..run(args); + ..addCommand(new TraceCommand()); + + try { + await runner.run(args); + } on UsageException catch (e) { + print(e); + exit(1); + } } From 4477f7255a7082f97da8addef69f49a8be8f440e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 13 Oct 2015 16:57:59 -0700 Subject: [PATCH 128/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 35463814e6..33f0d83fe0 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.20 +version: 0.0.21 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 047038b95afb8a28f6a1cd793acc586b5ffaaad9 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 14 Oct 2015 13:49:41 -0700 Subject: [PATCH 129/188] Support non-rooted Android devices Now we use a debuggable APK and chmod the installation directory to be world readable/writable. Fixes https://github.com/flutter/engine/issues/126 --- packages/flutter_tools/lib/src/device.dart | 27 +++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index e05c037b9b..84d52fcf40 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -682,8 +682,12 @@ class AndroidDevice extends Device { return false; } + String _getDeviceDataPath(ApplicationPackage app) { + return '/data/data/${app.id}'; + } + String _getDeviceSha1Path(ApplicationPackage app) { - return '/sdcard/${app.id}/${app.name}.sha1'; + return '${_getDeviceDataPath(app)}/${app.name}.sha1'; } String _getDeviceApkSha1(ApplicationPackage app) { @@ -728,14 +732,8 @@ class AndroidDevice extends Device { print('Installing ${app.name} on device.'); runCheckedSync([adbPath, 'install', '-r', app.localPath]); - - Directory tempDir = Directory.systemTemp; - String sha1Path = path.join( - tempDir.path, (app.localPath + '.sha1').replaceAll(path.separator, '_')); - File sha1TempFile = new File(sha1Path); - sha1TempFile.writeAsStringSync(_getSourceSha1(app), flush: true); - runCheckedSync([adbPath, 'push', sha1Path, _getDeviceSha1Path(app)]); - sha1TempFile.deleteSync(); + runCheckedSync([adbPath, 'shell', 'run-as', app.id, 'chmod', '777', _getDeviceDataPath(app)]); + runCheckedSync([adbPath, 'shell', 'echo', '-n', _getSourceSha1(app), '>', _getDeviceSha1Path(app)]); return true; } @@ -897,16 +895,7 @@ class AndroidDevice extends Device { } if (tracePath != null) { - // adb root exits with 0 even if the command fails, - // so check the output string - String output = runSync([adbPath, 'root']); - if (new RegExp(r'.*cannot run as root.*').hasMatch(output)) { - _logging - .severe('Unable to download trace "${path.basename(tracePath)}"\n' - 'You need to be able to run adb as root ' - 'on your android device'); - return null; - } + runSync([adbPath, 'shell', 'run-as', apk.id, 'chmod', '777', tracePath]); runSync([adbPath, 'pull', tracePath]); runSync([adbPath, 'shell', 'rm', tracePath]); return path.basename(tracePath); From abe69c7f063caadf05301d5f1f587b76c248807a Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 14 Oct 2015 15:19:19 -0700 Subject: [PATCH 130/188] Rev pub spec --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 33f0d83fe0..b3799f04ab 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.21 +version: 0.0.22 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From dff0edbc97731ddb0e41dc90283838e1ecc4b6dd Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 14 Oct 2015 15:25:58 -0700 Subject: [PATCH 131/188] init command errors out We were checking for a pubspec.yaml too early. --- .../src/commands/flutter_command_runner.dart | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index 65dc778a29..c33f5b1099 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -85,7 +85,14 @@ class FlutterCommandRunner extends CommandRunner { help: 'Path to your packages directory.', defaultsTo: 'packages'); } - List buildConfigurations; + List get buildConfigurations { + if (_buildConfigurations == null) + _buildConfigurations = _createBuildConfigurations(_globalResults); + return _buildConfigurations; + } + List _buildConfigurations; + + ArgResults _globalResults; Future runCommand(ArgResults globalResults) async { if (globalResults['verbose']) @@ -94,6 +101,11 @@ class FlutterCommandRunner extends CommandRunner { if (globalResults['very-verbose']) Logger.root.level = Level.FINE; + _globalResults = globalResults; + return super.runCommand(globalResults); + } + + List _createBuildConfigurations(ArgResults globalResults) { ArtifactStore.packageRoot = globalResults['package-root']; if (!FileSystemEntity.isDirectorySync(ArtifactStore.packageRoot)) { String message = '${ArtifactStore.packageRoot} is not a valid directory.'; @@ -104,15 +116,9 @@ class FlutterCommandRunner extends CommandRunner { message += '\nDid you run this command from the same directory as your pubspec.yaml file?'; } _logging.severe(message); - return 2; + exit(2); } - buildConfigurations = _createBuildConfigurations(globalResults); - - return super.runCommand(globalResults); - } - - List _createBuildConfigurations(ArgResults globalResults) { String enginePath = globalResults['engine-src-path']; bool isDebug = globalResults['debug']; bool isRelease = globalResults['release']; From db6a7abdcac1bcad0b1074139f51caa23cfc914c Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 14 Oct 2015 15:26:37 -0700 Subject: [PATCH 132/188] Rev pub spec --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index b3799f04ab..5a6a31e38f 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.22 +version: 0.0.23 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 3c9c31332abd3996f24801f87d268037aaa0a18d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 15 Oct 2015 20:06:30 -0700 Subject: [PATCH 133/188] run_mojo can't find ArtifactStore.packageRoot The `run_mojo` command doesn't integrate with `FlutterCommand` and doesn't understand how to download its toolchain components ahead of time. Eventually we should teach `run_mojo` how to integrate with the `Toolchain` class, but until then, we can fix the regression by eagerly setting `ArtifactStore.packageRoot` again. Fixes https://github.com/domokit/mojo/issues/475 --- .../flutter_tools/lib/src/commands/flutter_command_runner.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index c33f5b1099..0c19f83a12 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -102,11 +102,12 @@ class FlutterCommandRunner extends CommandRunner { Logger.root.level = Level.FINE; _globalResults = globalResults; + ArtifactStore.packageRoot = globalResults['package-root']; + return super.runCommand(globalResults); } List _createBuildConfigurations(ArgResults globalResults) { - ArtifactStore.packageRoot = globalResults['package-root']; if (!FileSystemEntity.isDirectorySync(ArtifactStore.packageRoot)) { String message = '${ArtifactStore.packageRoot} is not a valid directory.'; if (ArtifactStore.packageRoot == 'packages') { From bb1da703a61d4f4c5d54a33b8fdc9b239a511535 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 17 Oct 2015 11:50:23 -0700 Subject: [PATCH 134/188] return exit codes on failures --- packages/flutter_tools/bin/sky_server.dart | 4 +-- packages/flutter_tools/bin/sky_test.dart | 2 +- packages/flutter_tools/lib/executable.dart | 23 ++++++++++------ .../flutter_tools/lib/src/commands/build.dart | 27 ++++++++++--------- .../src/commands/flutter_command_runner.dart | 2 +- .../flutter_tools/lib/src/commands/list.dart | 2 +- .../flutter_tools/lib/src/commands/logs.dart | 2 +- .../lib/src/commands/run_mojo.dart | 14 +++++----- .../flutter_tools/lib/src/commands/start.dart | 8 ++++++ .../flutter_tools/lib/src/commands/trace.dart | 2 +- packages/flutter_tools/lib/src/device.dart | 8 +++--- packages/flutter_tools/test/stop_test.dart | 2 -- packages/flutter_tools/test/trace_test.dart | 2 -- 13 files changed, 55 insertions(+), 43 deletions(-) diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart index 486591a64f..04bb6e960c 100644 --- a/packages/flutter_tools/bin/sky_server.dart +++ b/packages/flutter_tools/bin/sky_server.dart @@ -5,10 +5,10 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:shelf_static/shelf_static.dart'; -import 'package:shelf/shelf_io.dart' as io; import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as io; import 'package:shelf_route/shelf_route.dart' as shelf_route; +import 'package:shelf_static/shelf_static.dart'; void printUsage(parser) { print('Usage: sky_server [-v] PORT'); diff --git a/packages/flutter_tools/bin/sky_test.dart b/packages/flutter_tools/bin/sky_test.dart index 30082368d6..a5e08f626a 100644 --- a/packages/flutter_tools/bin/sky_test.dart +++ b/packages/flutter_tools/bin/sky_test.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:test/src/executable.dart' as executable; import 'package:sky_tools/src/test/loader.dart' as loader; +import 'package:test/src/executable.dart' as executable; main(List args) { loader.installHook(); diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index db06747998..cdecdd03e3 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -8,9 +8,9 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; -import 'src/commands/flutter_command_runner.dart'; import 'src/commands/build.dart'; import 'src/commands/cache.dart'; +import 'src/commands/flutter_command_runner.dart'; import 'src/commands/init.dart'; import 'src/commands/install.dart'; import 'src/commands/list.dart'; @@ -25,13 +25,18 @@ import 'src/commands/trace.dart'; /// /// This function is intended to be used from the [flutter] command line tool. Future main(List args) async { - Logger.root.level = Level.WARNING; + // This level can be adjusted by users through the `--verbose` option. + Logger.root.level = Level.SEVERE; Logger.root.onRecord.listen((LogRecord record) { - print('${record.level.name}: ${record.message}'); + if (record.level >= Level.WARNING) { + stderr.writeln(record.message); + } else { + print(record.message); + } if (record.error != null) - print(record.error); + stderr.writeln(record.error); if (record.stackTrace != null) - print(record.stackTrace); + stderr.writeln(record.stackTrace); }); FlutterCommandRunner runner = new FlutterCommandRunner() @@ -48,9 +53,11 @@ Future main(List args) async { ..addCommand(new TraceCommand()); try { - await runner.run(args); + dynamic result = await runner.run(args); + if (result is int) + exit(result); } on UsageException catch (e) { - print(e); - exit(1); + stderr.writeln(e); + exit(4); } } diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index af0b64fe86..1a09adabdb 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -81,24 +81,24 @@ Iterable<_MaterialAsset> _parseMaterialAssets(Map manifestDescriptor) sync* { } } -Future _loadManifest(String manifestPath) async { +dynamic _loadManifest(String manifestPath) { if (manifestPath == null) return null; - String manifestDescriptor = await new File(manifestPath).readAsString(); + String manifestDescriptor = new File(manifestPath).readAsStringSync(); return loadYaml(manifestDescriptor); } -Future _createFile(String key, String assetBase) async { +ArchiveFile _createFile(String key, String assetBase) { File file = new File('${assetBase}/${key}'); - if (!await file.exists()) + if (!file.existsSync()) return null; - List content = await file.readAsBytes(); + List content = file.readAsBytesSync(); return new ArchiveFile.noCompress(key, content.length, content); } -Future _createSnapshotFile(String snapshotPath) async { +ArchiveFile _createSnapshotFile(String snapshotPath) { File file = new File(snapshotPath); - List content = await file.readAsBytes(); + List content = file.readAsBytesSync(); return new ArchiveFile(_kSnapshotKey, content.length, content); } @@ -145,7 +145,7 @@ class BuildCommand extends FlutterCommand { String outputPath: _kDefaultOutputPath, String snapshotPath: _kDefaultSnapshotPath }) async { - Map manifestDescriptor = await _loadManifest(manifestPath); + Map manifestDescriptor = _loadManifest(manifestPath); Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath); Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor); @@ -156,20 +156,21 @@ class BuildCommand extends FlutterCommand { if (result != 0) return result; - archive.addFile(await _createSnapshotFile(snapshotPath)); + archive.addFile(_createSnapshotFile(snapshotPath)); for (_Asset asset in assets) - archive.addFile(await _createFile(asset.key, asset.base)); + archive.addFile(_createFile(asset.key, asset.base)); for (_MaterialAsset asset in materialAssets) { - ArchiveFile file = await _createFile(asset.key, assetBase); + ArchiveFile file = _createFile(asset.key, assetBase); if (file != null) archive.addFile(file); } File outputFile = new File(outputPath); - await outputFile.writeAsString('#!mojo mojo:sky_viewer\n'); - await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND, flush: true); + outputFile.writeAsStringSync('#!mojo mojo:sky_viewer\n'); + outputFile.writeAsBytesSync( + new ZipEncoder().encode(archive), mode: FileMode.APPEND, flush: true); return 0; } } diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index 0c19f83a12..4c73b484f3 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -94,7 +94,7 @@ class FlutterCommandRunner extends CommandRunner { ArgResults _globalResults; - Future runCommand(ArgResults globalResults) async { + Future runCommand(ArgResults globalResults) { if (globalResults['verbose']) Logger.root.level = Level.INFO; diff --git a/packages/flutter_tools/lib/src/commands/list.dart b/packages/flutter_tools/lib/src/commands/list.dart index e81ca0b86e..379f733b6e 100644 --- a/packages/flutter_tools/lib/src/commands/list.dart +++ b/packages/flutter_tools/lib/src/commands/list.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'package:logging/logging.dart'; -import 'flutter_command.dart'; import '../device.dart'; +import 'flutter_command.dart'; final Logger _logging = new Logger('sky_tools.list'); diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart index 68a49798fc..62ee900f20 100644 --- a/packages/flutter_tools/lib/src/commands/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'package:logging/logging.dart'; -import 'flutter_command.dart'; import '../device.dart'; +import 'flutter_command.dart'; final Logger _logging = new Logger('sky_tools.logs'); diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index 48679f8ccd..4080782f02 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -31,17 +31,17 @@ class RunMojoCommand extends Command { } // TODO(abarth): Why not use path.absolute? - Future _makePathAbsolute(String relativePath) async { + String _makePathAbsolute(String relativePath) { File file = new File(relativePath); - if (!await file.exists()) { + if (!file.existsSync()) { throw new Exception("Path \"${relativePath}\" does not exist"); } return file.absolute.path; } - Future _runAndroid(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { + Future _runAndroid(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) { String skyViewerUrl = ArtifactStore.googleStorageUrl('viewer', 'android-arm'); - String command = await _makePathAbsolute(path.join(mojoPath, 'mojo', 'devtools', 'common', 'mojo_run')); + String command = _makePathAbsolute(path.join(mojoPath, 'mojo', 'devtools', 'common', 'mojo_run')); String appName = path.basename(appPath); String appDir = path.dirname(appPath); String buildFlag = mojoConfig == _MojoConfig.Debug ? '--debug' : '--release'; @@ -65,9 +65,9 @@ class RunMojoCommand extends Command { } Future _runLinux(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { - String viewerPath = await _makePathAbsolute(await ArtifactStore.getPath(Artifact.skyViewerMojo)); + String viewerPath = _makePathAbsolute(await ArtifactStore.getPath(Artifact.skyViewerMojo)); String mojoBuildType = mojoConfig == _MojoConfig.Debug ? 'Debug' : 'Release'; - String mojoShellPath = await _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); + String mojoShellPath = _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); List cmd = [ mojoShellPath, 'file://${appPath}', @@ -93,7 +93,7 @@ class RunMojoCommand extends Command { } String mojoPath = argResults['mojo-path']; _MojoConfig mojoConfig = argResults['mojo-debug'] ? _MojoConfig.Debug : _MojoConfig.Release; - String appPath = await _makePathAbsolute(argResults['app']); + String appPath = _makePathAbsolute(argResults['app']); args.addAll(argResults.rest); if (argResults['android']) { diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index 77e3c69e60..434fa77f2b 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -67,6 +67,14 @@ class StartCommand extends FlutterCommand { } } + if (!startedSomething) { + if (!devices.all.any((device) => device.isConnected())) { + _logging.severe('Unable to run application - no connected devices.'); + } else { + _logging.severe('Unable to run application.'); + } + } + return startedSomething ? 0 : 2; } } diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart index 7ad257ffe8..31582a84be 100644 --- a/packages/flutter_tools/lib/src/commands/trace.dart +++ b/packages/flutter_tools/lib/src/commands/trace.dart @@ -6,9 +6,9 @@ import 'dart:async'; import 'package:logging/logging.dart'; -import 'flutter_command.dart'; import '../application_package.dart'; import '../device.dart'; +import 'flutter_command.dart'; final Logger _logging = new Logger('sky_tools.trace'); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 84d52fcf40..193aca226b 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -277,7 +277,7 @@ class IOSDevice extends Device { return 2; } return runCommandAndStreamOutput([loggerPath], - prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*')); + prefix: 'iOS dev: ', filter: new RegExp(r'.*SkyShell.*')); } } @@ -499,7 +499,7 @@ class IOSSimulator extends Device { runSync(['rm', logFilePath]); } return runCommandAndStreamOutput(['tail', '-f', logFilePath], - prefix: 'IOS SIM: ', filter: new RegExp(r'.*SkyShell.*')); + prefix: 'iOS sim: ', filter: new RegExp(r'.*SkyShell.*')); } } @@ -575,7 +575,7 @@ class AndroidDevice extends Device { _hasValidAndroid = _checkForLollipopOrLater(); if (!_hasAdb || !_hasValidAndroid) { - _logging.severe('Unable to run on Android.'); + _logging.warning('Unable to run on Android.'); } } @@ -855,7 +855,7 @@ class AndroidDevice extends Device { '-s', 'sky', 'chromium', - ], prefix: 'ANDROID: '); + ], prefix: 'android: '); } void startTracing(AndroidApk apk) { diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart index 79ea1fb5f9..53da532aa5 100644 --- a/packages/flutter_tools/test/stop_test.dart +++ b/packages/flutter_tools/test/stop_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library stop_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/stop.dart'; diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart index dc7c48d600..83f5a1b18f 100644 --- a/packages/flutter_tools/test/trace_test.dart +++ b/packages/flutter_tools/test/trace_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library trace_test; - import 'package:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/trace.dart'; From 1999db29734203b3cf58a2ed581b9e9eb31e6554 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sun, 18 Oct 2015 12:37:59 -0700 Subject: [PATCH 135/188] 0.0.24 --- packages/flutter_tools/README.md | 3 ++- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 618586a699..7c2e17dd73 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,6 +1,7 @@ # tools [![Build Status](https://travis-ci.org/flutter/tools.svg)](https://travis-ci.org/flutter/tools) +[![pub package](https://img.shields.io/pub/v/sky_tools.svg)](https://pub.dartlang.org/packages/sky_tools) Tools for building Flutter applications. @@ -13,7 +14,7 @@ To install, run: or, depend on this package in your pubspec: ```yaml -dependencies: +dev_dependencies: sky_tools: any ``` diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 5a6a31e38f..bb3186e5b7 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.23 +version: 0.0.24 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 5670243b53c77718b36c7642c6303070a1d195de Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 19 Oct 2015 09:52:56 -0700 Subject: [PATCH 136/188] Sanity check --local-build directory Fixes https://github.com/flutter/engine/issues/1613 --- .../lib/src/commands/flutter_command_runner.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index 4c73b484f3..8749a2470f 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -129,8 +129,10 @@ class FlutterCommandRunner extends CommandRunner { String realFlutterPath = flutterDir.resolveSymbolicLinksSync(); enginePath = path.dirname(path.dirname(path.dirname(path.dirname(realFlutterPath)))); - if (enginePath == '/' || enginePath.isEmpty) - enginePath = null; + if (enginePath == '/' || enginePath.isEmpty || !FileSystemEntity.isDirectorySync(path.join(enginePath, 'out'))) { + _logging.severe('Unable to detect local build in $enginePath.\nDo you have a dependency override for the flutter package?'); + exit(2); + } } List configs = []; From b458935b84f7bb0ca9c82db48a3ec880e46adf9d Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 19 Oct 2015 16:08:53 -0400 Subject: [PATCH 137/188] Support signing flx packages with ECDSA key pair Adds a --private-key option to the build command, which specifies an ECDSA private key. When this is provided along with a manifest, the manifest is prepended to the .flx package and signed with the private key. The manifest also includes a SHA-256 hash of the zipped content portion of the .flx package. This is used by the Flutter updater package, to verify that updates are from the right publisher. --- .../flutter_tools/lib/src/commands/build.dart | 34 ++++++- packages/flutter_tools/lib/src/signing.dart | 94 +++++++++++++++++++ packages/flutter_tools/pubspec.yaml | 2 + 3 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 packages/flutter_tools/lib/src/signing.dart diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 1a09adabdb..04e9335018 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -4,10 +4,14 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'package:archive/archive.dart'; +import 'package:cipher/cipher.dart'; +import 'package:cipher/impl/client.dart'; import 'package:yaml/yaml.dart'; +import '../signing.dart'; import '../toolchain.dart'; import 'flutter_command.dart'; @@ -96,6 +100,16 @@ ArchiveFile _createFile(String key, String assetBase) { return new ArchiveFile.noCompress(key, content.length, content); } +// Writes a 32-bit length followed by the content of [bytes]. +void _writeBytesWithLength(File outputFile, List bytes) { + if (bytes == null) + bytes = new Uint8List(0); + assert(bytes.length < 0xffffffff); + ByteData length = new ByteData(4)..setUint32(0, bytes.length, Endianness.LITTLE_ENDIAN); + outputFile.writeAsBytesSync(length.buffer.asUint8List(), mode: FileMode.APPEND); + outputFile.writeAsBytesSync(bytes, mode: FileMode.APPEND); +} + ArchiveFile _createSnapshotFile(String snapshotPath) { File file = new File(snapshotPath); List content = file.readAsBytesSync(); @@ -106,6 +120,7 @@ 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'; +const String _kDefaultPrivateKeyPath = 'privatekey.der'; class BuildCommand extends FlutterCommand { final String name = 'build'; @@ -113,15 +128,18 @@ class BuildCommand extends FlutterCommand { BuildCommand() { argParser.addOption('asset-base', defaultsTo: _kDefaultAssetBase); + argParser.addOption('compiler'); argParser.addOption('main', defaultsTo: _kDefaultMainPath); argParser.addOption('manifest'); + argParser.addOption('private-key', defaultsTo: _kDefaultPrivateKeyPath); argParser.addOption('output-file', abbr: 'o', defaultsTo: _kDefaultOutputPath); argParser.addOption('snapshot', defaultsTo: _kDefaultSnapshotPath); } @override Future run() async { + initCipher(); String compilerPath = argResults['compiler']; if (compilerPath == null) @@ -134,7 +152,8 @@ class BuildCommand extends FlutterCommand { mainPath: argResults['main'], manifestPath: argResults['manifest'], outputPath: argResults['output-file'], - snapshotPath: argResults['snapshot'] + snapshotPath: argResults['snapshot'], + privateKeyPath: argResults['private-key'] ); } @@ -143,7 +162,8 @@ class BuildCommand extends FlutterCommand { String mainPath: _kDefaultMainPath, String manifestPath, String outputPath: _kDefaultOutputPath, - String snapshotPath: _kDefaultSnapshotPath + String snapshotPath: _kDefaultSnapshotPath, + String privateKeyPath: _kDefaultPrivateKeyPath }) async { Map manifestDescriptor = _loadManifest(manifestPath); @@ -167,10 +187,16 @@ class BuildCommand extends FlutterCommand { archive.addFile(file); } + ECPrivateKey privateKey = await loadPrivateKey(privateKeyPath); + ECPublicKey publicKey = publicKeyFromPrivateKey(privateKey); + File outputFile = new File(outputPath); outputFile.writeAsStringSync('#!mojo mojo:sky_viewer\n'); - outputFile.writeAsBytesSync( - new ZipEncoder().encode(archive), mode: FileMode.APPEND, flush: true); + Uint8List zipBytes = new Uint8List.fromList(new ZipEncoder().encode(archive)); + Uint8List manifestBytes = serializeManifest(manifestDescriptor, publicKey, zipBytes); + _writeBytesWithLength(outputFile, signManifest(manifestBytes, privateKey)); + _writeBytesWithLength(outputFile, manifestBytes); + outputFile.writeAsBytesSync(zipBytes, mode: FileMode.APPEND, flush: true); return 0; } } diff --git a/packages/flutter_tools/lib/src/signing.dart b/packages/flutter_tools/lib/src/signing.dart new file mode 100644 index 0000000000..d3390a6d59 --- /dev/null +++ b/packages/flutter_tools/lib/src/signing.dart @@ -0,0 +1,94 @@ +// 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 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:asn1lib/asn1lib.dart'; +import 'package:bignum/bignum.dart'; +import 'package:cipher/cipher.dart'; + +// The ECDSA algorithm parameters we're using. These match the parameters used +// by the Flutter updater package. +final ECDomainParameters _ecDomain = new ECDomainParameters('prime256v1'); +final String kSignerAlgorithm = 'SHA-256/ECDSA'; +final String kHashAlgorithm = 'SHA-256'; + +final SecureRandom _random = _initRandom(); + +SecureRandom _initRandom() { + // TODO(mpcomplete): Provide a better seed here. External entropy source? + final Uint8List key = new Uint8List(16); + final KeyParameter keyParam = new KeyParameter(key); + final ParametersWithIV params = new ParametersWithIV(keyParam, new Uint8List(16)); + SecureRandom random = new SecureRandom('AES/CTR/AUTO-SEED-PRNG') + ..seed(params); + return random; +} + +// Returns a serialized manifest, with the public key and hash of the content +// included. +Uint8List serializeManifest(Map manifestDescriptor, ECPublicKey publicKey, Uint8List zipBytes) { + if (manifestDescriptor == null) + return null; + final List kSavedKeys = [ + 'name', + 'version', + 'update-url' + ]; + Map outputManifest = new Map(); + manifestDescriptor.forEach((key, value) { + if (kSavedKeys.contains(key)) + outputManifest[key] = value; + }); + + if (publicKey != null) + outputManifest['key'] = BASE64.encode(publicKey.Q.getEncoded()); + + Uint8List zipHash = new Digest(kHashAlgorithm).process(zipBytes); + BigInteger zipHashInt = new BigInteger.fromBytes(1, zipHash); + outputManifest['content-hash'] = zipHashInt.intValue(); + + return new Uint8List.fromList(UTF8.encode(JSON.encode(outputManifest))); +} + +// Returns the ASN.1 encoded signature of the input manifestBytes. +List signManifest(Uint8List manifestBytes, ECPrivateKey privateKey) { + if (manifestBytes == null || privateKey == null) + return []; + Signer signer = new Signer(kSignerAlgorithm); + PrivateKeyParameter params = new PrivateKeyParameter(privateKey); + signer.init(true, new ParametersWithRandom(params, _random)); + ECSignature signature = signer.generateSignature(manifestBytes); + ASN1Sequence asn1 = new ASN1Sequence() + ..add(new ASN1Integer(signature.r)) + ..add(new ASN1Integer(signature.s)); + return asn1.encodedBytes; +} + +ECPrivateKey _asn1ParsePrivateKey(ECDomainParameters ecDomain, Uint8List privateKey) { + ASN1Parser parser = new ASN1Parser(privateKey); + ASN1Sequence seq = parser.nextObject(); + assert(seq.elements.length >= 2); + ASN1OctetString keyOct = seq.elements[1]; + BigInteger d = new BigInteger.fromBytes(1, keyOct.octets); + return new ECPrivateKey(d, ecDomain); +} + +Future loadPrivateKey(String privateKeyPath) async { + File file = new File(privateKeyPath); + if (!file.existsSync()) + return null; + List bytes = file.readAsBytesSync(); + return _asn1ParsePrivateKey(_ecDomain, new Uint8List.fromList(bytes)); +} + +ECPublicKey publicKeyFromPrivateKey(ECPrivateKey privateKey) { + if (privateKey == null) + return null; + ECPoint Q = privateKey.parameters.G * privateKey.d; + return new ECPublicKey(Q, privateKey.parameters); +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index bb3186e5b7..1de2b20e85 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -10,6 +10,8 @@ environment: dependencies: archive: ^1.0.20 args: ^0.13.0 + asn1lib: ^0.4.1 + cipher: ^0.7.1 mustache4dart: ^1.0.0 path: ^1.3.0 shelf_route: ^0.13.4 From b53e7264045d056339668f1ef6b513b6ea0c85bf Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 19 Oct 2015 16:33:54 -0400 Subject: [PATCH 138/188] Update flutter_tools version to 0.0.25. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 1de2b20e85..e8150a9f46 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.24 +version: 0.0.25 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 9a67954b666a73fbd30166bc68a87f9e62240bd0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Oct 2015 18:50:19 -0700 Subject: [PATCH 139/188] Avoid script snapshot creation if the --precompiled flag is set during builds --- .../flutter_tools/lib/src/commands/build.dart | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 04e9335018..cc421b5ac6 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -127,8 +127,8 @@ class BuildCommand extends FlutterCommand { final String description = 'Create a Flutter app.'; BuildCommand() { + argParser.addFlag('precompiled', negatable: false); argParser.addOption('asset-base', defaultsTo: _kDefaultAssetBase); - argParser.addOption('compiler'); argParser.addOption('main', defaultsTo: _kDefaultMainPath); argParser.addOption('manifest'); @@ -153,7 +153,8 @@ class BuildCommand extends FlutterCommand { manifestPath: argResults['manifest'], outputPath: argResults['output-file'], snapshotPath: argResults['snapshot'], - privateKeyPath: argResults['private-key'] + privateKeyPath: argResults['private-key'], + precompiledSnapshot: argResults['precompiled'] ); } @@ -163,7 +164,8 @@ class BuildCommand extends FlutterCommand { String manifestPath, String outputPath: _kDefaultOutputPath, String snapshotPath: _kDefaultSnapshotPath, - String privateKeyPath: _kDefaultPrivateKeyPath + String privateKeyPath: _kDefaultPrivateKeyPath, + bool precompiledSnapshot: false }) async { Map manifestDescriptor = _loadManifest(manifestPath); @@ -172,11 +174,15 @@ class BuildCommand extends FlutterCommand { Archive archive = new Archive(); - int result = await toolchain.compiler.compile(mainPath: mainPath, snapshotPath: snapshotPath); - if (result != 0) - return result; + if (!precompiledSnapshot) { + // In a precompiled snapshot, the instruction buffer contains script + // content equivalents + int result = await toolchain.compiler.compile(mainPath: mainPath, snapshotPath: snapshotPath); + if (result != 0) + return result; - archive.addFile(_createSnapshotFile(snapshotPath)); + archive.addFile(_createSnapshotFile(snapshotPath)); + } for (_Asset asset in assets) archive.addFile(_createFile(asset.key, asset.base)); From d4940c5cd4becee292186a03a72138ff5d54bad7 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 20 Oct 2015 19:01:05 -0700 Subject: [PATCH 140/188] set a min. dep of sdk 1.13 --- packages/flutter_tools/lib/src/commands/init.dart | 2 +- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index f968246ac4..91608e2f50 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -24,7 +24,7 @@ class InitCommand extends Command { Future run() async { if (!argResults.wasParsed('out')) { print('No option specified for the output directory.'); - print(argParser.getUsage()); + print(argParser.usage); return 2; } diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index e8150a9f46..133f3a6f4b 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -5,7 +5,7 @@ homepage: http://flutter.io author: Flutter Authors environment: - sdk: ">=1.8.0 <2.0.0" + sdk: '>=1.13.0-dev.1 <2.0.0' dependencies: archive: ^1.0.20 From 24fe7efa28d8c57e95f897dc1bfa00a83df7bcfc Mon Sep 17 00:00:00 2001 From: Nathan Kerr Date: Fri, 23 Oct 2015 09:30:03 -0700 Subject: [PATCH 141/188] Don't rely on external shasum program to calculate sum of the APK. --- packages/flutter_tools/lib/src/device.dart | 8 +++++--- packages/flutter_tools/pubspec.yaml | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 193aca226b..684f9095b9 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -10,6 +10,7 @@ import 'dart:math'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; +import 'package:crypto/crypto.dart'; import 'application_package.dart'; import 'build_configuration.dart'; @@ -695,9 +696,10 @@ class AndroidDevice extends Device { } String _getSourceSha1(ApplicationPackage app) { - String sha1 = - runCheckedSync(['shasum', '-a', '1', '-p', app.localPath]).split(' ')[0]; - return sha1; + var sha1 = new SHA1(); + var file = new File(app.localPath); + sha1.add(file.readAsBytesSync()); + return CryptoUtils.bytesToHex(sha1.close()); } @override diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 133f3a6f4b..c787f37f8d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: shelf: ^0.6.2 test: ">=0.12.4+5 <0.12.5" yaml: ^2.1.3 + crypto: ^0.9.1 dev_dependencies: mockito: "^0.10.1" From 54e45e2b56616215624c7c4786d3143879b6aa26 Mon Sep 17 00:00:00 2001 From: Nathan Kerr Date: Fri, 23 Oct 2015 09:30:03 -0700 Subject: [PATCH 142/188] Don't rely on external shasum program to calculate sum of the APK. Windows has no direct way to kill a process based on port. Uses netstats and loops through the results to find the correct process to kill. Also modify Process.run for the server to runInShell if on Windows. Style nits. --- packages/flutter_tools/lib/src/device.dart | 30 +++++++++++++++++++--- packages/flutter_tools/pubspec.yaml | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 193aca226b..02a1a417f3 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -10,6 +10,7 @@ import 'dart:math'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; +import 'package:crypto/crypto.dart'; import 'application_package.dart'; import 'build_configuration.dart'; @@ -695,9 +696,10 @@ class AndroidDevice extends Device { } String _getSourceSha1(ApplicationPackage app) { - String sha1 = - runCheckedSync(['shasum', '-a', '1', '-p', app.localPath]).split(' ')[0]; - return sha1; + var sha1 = new SHA1(); + var file = new File(app.localPath); + sha1.add(file.readAsBytesSync()); + return CryptoUtils.bytesToHex(sha1.close()); } @override @@ -765,7 +767,7 @@ class AndroidDevice extends Device { // Actually start the server. await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort], - workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); + workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED, runInShell: Platform.isWindows); // Set up reverse port-forwarding so that the Android app can reach the // server running on localhost. @@ -824,6 +826,26 @@ class AndroidDevice extends Device { // equivalent command when doing verbose logging. _logging.info('kill $pid'); Process.killPid(int.parse(pid)); + } else if (Platform.isWindows) { + //Get list of network processes and split on newline + List processes = runSync(['netstat.exe','-ano']).split("\r"); + + //List entries from netstat is formatted like so + // TCP 192.168.2.11:50945 192.30.252.90:443 LISTENING 1304 + //This regexp is to find process where the the port exactly matches + RegExp pattern = new RegExp(':$_serverPort[ ]+'); + + //Split the columns by 1 or more spaces + RegExp columnPattern = new RegExp('[ ]+'); + processes.forEach((String process){ + if (process.contains(pattern)) { + //The last column is the Process ID + String processId = process.split(columnPattern).last; + //Force and Tree kill the process + _logging.info('kill $processId'); + runSync(['TaskKill.exe', '/F', '/T', '/PID', processId]); + } + }); } else { runSync(['fuser', '-k', '$_serverPort/tcp']); } diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 133f3a6f4b..c787f37f8d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: shelf: ^0.6.2 test: ">=0.12.4+5 <0.12.5" yaml: ^2.1.3 + crypto: ^0.9.1 dev_dependencies: mockito: "^0.10.1" From bfeaaa8fb78831901019e513631f122867521aa3 Mon Sep 17 00:00:00 2001 From: Nathan Kerr Date: Fri, 23 Oct 2015 13:34:39 -0700 Subject: [PATCH 143/188] Web paths should always replace \ with / --- packages/flutter_tools/lib/src/device.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 02a1a417f3..19ab6cdd26 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -702,6 +702,13 @@ class AndroidDevice extends Device { return CryptoUtils.bytesToHex(sha1.close()); } + /** + * Since Window's paths have backslashes, we need to convert those to forward slashes to make a valid URL + */ + String _convertToURL(String path) { + return path.replaceAll('\\', '/'); + } + @override bool isAppInstalled(ApplicationPackage app) { if (!isConnected()) { @@ -775,7 +782,7 @@ class AndroidDevice extends Device { runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]); } - String relativeDartMain = path.relative(mainDart, from: serverRoot); + String relativeDartMain = _convertToURL(path.relative(mainDart, from: serverRoot)); String url = 'http://localhost:$_serverPort/$relativeDartMain'; if (poke) { url += '?rand=${new Random().nextDouble()}'; @@ -837,7 +844,7 @@ class AndroidDevice extends Device { //Split the columns by 1 or more spaces RegExp columnPattern = new RegExp('[ ]+'); - processes.forEach((String process){ + processes.forEach((String process) { if (process.contains(pattern)) { //The last column is the Process ID String processId = process.split(columnPattern).last; From 6b2d6fdc8c7b7be0e3b5a165c5b6342b90f27eb1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 23 Oct 2015 14:54:03 -0700 Subject: [PATCH 144/188] Update pubspec and changelog for 0.0.26 --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index c787f37f8d..f2fe4cf6b0 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.25 +version: 0.0.26 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 5dbeb8f018c4c247b37373a0a0bc155c2f5f0303 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 23 Oct 2015 16:01:09 -0700 Subject: [PATCH 145/188] fix tests on windows remove a runInShell arg on windows when launching pub --- packages/flutter_tools/lib/src/commands/init.dart | 6 ++++-- packages/flutter_tools/lib/src/device.dart | 4 ++-- packages/flutter_tools/lib/src/process.dart | 6 ++++++ packages/flutter_tools/pubspec.yaml | 2 +- packages/flutter_tools/test/init_test.dart | 3 ++- packages/flutter_tools/test/list_test.dart | 14 +++++++++----- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 91608e2f50..4e3512df71 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -9,6 +9,8 @@ import 'package:args/command_runner.dart'; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; +import '../process.dart'; + class InitCommand extends Command { final String name = 'init'; final String description = 'Create a new Flutter project.'; @@ -43,8 +45,8 @@ class InitCommand extends Command { if (argResults['pub']) { print("Running pub get..."); - Process process = - await Process.start('pub', ['get'], workingDirectory: out.path); + Process process = await Process.start( + sdkBinaryName('pub'), ['get'], workingDirectory: out.path); stdout.addStream(process.stdout); stderr.addStream(process.stderr); int code = await process.exitCode; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 19ab6cdd26..f3c22afc24 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -773,8 +773,8 @@ class AndroidDevice extends Device { [adbPath, 'forward', observatoryPortString, observatoryPortString]); // Actually start the server. - await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort], - workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED, runInShell: Platform.isWindows); + await Process.start(sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort], + workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); // Set up reverse port-forwarding so that the Android app can reach the // server running on localhost. diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 7e22f52598..a23c3db5b3 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -64,6 +64,12 @@ String runCheckedSync(List cmd) => /// Run cmd and return stdout. String runSync(List cmd) => _runWithLoggingSync(cmd); +/// Return the platform specific name for the given Dart SDK binary. So, `pub` +/// ==> `pub.bat`. +String sdkBinaryName(String name) { + return Platform.isWindows ? '${name}.bat' : name; +} + String _runWithLoggingSync(List cmd, {bool checked: false}) { _logging.info(cmd.join(' ')); ProcessResult results = diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index f2fe4cf6b0..b992a472c5 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: args: ^0.13.0 asn1lib: ^0.4.1 cipher: ^0.7.1 + crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 shelf_route: ^0.13.4 @@ -19,7 +20,6 @@ dependencies: shelf: ^0.6.2 test: ">=0.12.4+5 <0.12.5" yaml: ^2.1.3 - crypto: ^0.9.1 dev_dependencies: mockito: "^0.10.1" diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index b69eaa74fc..2d55c3f600 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -11,6 +11,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:path/path.dart' as p; import 'package:sky_tools/src/commands/init.dart'; +import 'package:sky_tools/src/process.dart'; import 'package:test/test.dart'; main() => defineTests(); @@ -38,7 +39,7 @@ defineTests() { String path = p.join(temp.path, 'lib', 'main.dart'); expect(new File(path).existsSync(), true); ProcessResult exec = Process.runSync( - 'dartanalyzer', ['--fatal-warnings', path], + sdkBinaryName('dartanalyzer'), ['--fatal-warnings', path], workingDirectory: temp.path); if (exec.exitCode != 0) { print(exec.stdout); diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index 83bf713fd0..0b01b075ff 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -2,6 +2,8 @@ // 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:args/command_runner.dart'; import 'package:mockito/mockito.dart'; import 'package:sky_tools/src/commands/list.dart'; @@ -14,23 +16,25 @@ main() => defineTests(); defineTests() { group('list', () { test('returns 0 when called', () { + final String mockCommand = Platform.isWindows ? 'cmd /c echo' : 'echo'; + ListCommand command = new ListCommand(); applyMocksToCommand(command); MockDeviceStore mockDevices = command.devices; // Avoid relying on adb being installed on the test system. // Instead, cause the test to run the echo command. - when(mockDevices.android.adbPath).thenReturn('echo'); + when(mockDevices.android.adbPath).thenReturn(mockCommand); // Avoid relying on idevice* being installed on the test system. // Instead, cause the test to run the echo command. - when(mockDevices.iOS.informerPath).thenReturn('echo'); - when(mockDevices.iOS.installerPath).thenReturn('echo'); - when(mockDevices.iOS.listerPath).thenReturn('echo'); + when(mockDevices.iOS.informerPath).thenReturn(mockCommand); + when(mockDevices.iOS.installerPath).thenReturn(mockCommand); + when(mockDevices.iOS.listerPath).thenReturn(mockCommand); // Avoid relying on xcrun being installed on the test system. // Instead, cause the test to run the echo command. - when(mockDevices.iOSSimulator.xcrunPath).thenReturn('echo'); + when(mockDevices.iOSSimulator.xcrunPath).thenReturn(mockCommand); CommandRunner runner = new CommandRunner('test_flutter', '') From e7da41afd5ffd04885285a455871cfe0e68c9e09 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 24 Oct 2015 11:42:19 -0700 Subject: [PATCH 146/188] Update init_test.dart Increase the test timeout for the `init` test to 2 minutes (test was timing out downloading assets on appveyor). --- packages/flutter_tools/test/init_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 2d55c3f600..c10ceb6717 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This test can take a while due to network requests -@Timeout(const Duration(seconds: 60)) +// This test can take a while due to network requests. +@Timeout(const Duration(minutes: 2)) library init_test; import 'dart:io'; From 98d61fc18132033761d963680e0c14749f215ed0 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 24 Oct 2015 12:28:05 -0700 Subject: [PATCH 147/188] create an all.dart test script to work around an issue with pub run test on windows --- packages/flutter_tools/test/all.dart | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/flutter_tools/test/all.dart diff --git a/packages/flutter_tools/test/all.dart b/packages/flutter_tools/test/all.dart new file mode 100644 index 0000000000..0b2dcfe97f --- /dev/null +++ b/packages/flutter_tools/test/all.dart @@ -0,0 +1,25 @@ +// 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 'android_device_test.dart' as android_device_test; +import 'init_test.dart' as init_test; +import 'install_test.dart' as install_test; +import 'listen_test.dart' as listen_test; +import 'list_test.dart' as list_test; +import 'logs_test.dart' as logs_test; +import 'start_test.dart' as start_test; +import 'stop_test.dart' as stop_test; +import 'trace_test.dart' as trace_test; + +main() { + android_device_test.defineTests(); + init_test.defineTests(); + install_test.defineTests(); + listen_test.defineTests(); + list_test.defineTests(); + logs_test.defineTests(); + start_test.defineTests(); + stop_test.defineTests(); + trace_test.defineTests(); +} From b7f918c92bff5a91003aaf6d3f6a6cc808605fc8 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 24 Oct 2015 12:33:17 -0700 Subject: [PATCH 148/188] change how we specify test timeout --- packages/flutter_tools/test/init_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index c10ceb6717..1b44b01f17 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This test can take a while due to network requests. -@Timeout(const Duration(minutes: 2)) -library init_test; - import 'dart:io'; import 'package:args/command_runner.dart'; @@ -46,6 +42,8 @@ defineTests() { print(exec.stderr); } expect(exec.exitCode, 0); - }); + }, + // This test can take a while due to network requests. + timeout: new Timeout(new Duration(minutes: 2))); }); } From 828b861fcec6238b91f2e33630d841c1e27d0508 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 26 Oct 2015 15:14:01 -0400 Subject: [PATCH 149/188] Change signing code to use shared flx package. --- .../flutter_tools/lib/src/commands/build.dart | 33 ++----- packages/flutter_tools/lib/src/signing.dart | 94 ------------------- packages/flutter_tools/pubspec.yaml | 1 + 3 files changed, 11 insertions(+), 117 deletions(-) delete mode 100644 packages/flutter_tools/lib/src/signing.dart diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index cc421b5ac6..1360f39ee9 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -7,11 +7,10 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:archive/archive.dart'; -import 'package:cipher/cipher.dart'; -import 'package:cipher/impl/client.dart'; import 'package:yaml/yaml.dart'; -import '../signing.dart'; +import 'package:flx/bundle.dart'; +import 'package:flx/signing.dart'; import '../toolchain.dart'; import 'flutter_command.dart'; @@ -100,16 +99,6 @@ ArchiveFile _createFile(String key, String assetBase) { return new ArchiveFile.noCompress(key, content.length, content); } -// Writes a 32-bit length followed by the content of [bytes]. -void _writeBytesWithLength(File outputFile, List bytes) { - if (bytes == null) - bytes = new Uint8List(0); - assert(bytes.length < 0xffffffff); - ByteData length = new ByteData(4)..setUint32(0, bytes.length, Endianness.LITTLE_ENDIAN); - outputFile.writeAsBytesSync(length.buffer.asUint8List(), mode: FileMode.APPEND); - outputFile.writeAsBytesSync(bytes, mode: FileMode.APPEND); -} - ArchiveFile _createSnapshotFile(String snapshotPath) { File file = new File(snapshotPath); List content = file.readAsBytesSync(); @@ -139,7 +128,6 @@ class BuildCommand extends FlutterCommand { @override Future run() async { - initCipher(); String compilerPath = argResults['compiler']; if (compilerPath == null) @@ -193,16 +181,15 @@ class BuildCommand extends FlutterCommand { archive.addFile(file); } - ECPrivateKey privateKey = await loadPrivateKey(privateKeyPath); - ECPublicKey publicKey = publicKeyFromPrivateKey(privateKey); - - File outputFile = new File(outputPath); - outputFile.writeAsStringSync('#!mojo mojo:sky_viewer\n'); + KeyPair keyPair = KeyPair.readFromPrivateKeySync(privateKeyPath); Uint8List zipBytes = new Uint8List.fromList(new ZipEncoder().encode(archive)); - Uint8List manifestBytes = serializeManifest(manifestDescriptor, publicKey, zipBytes); - _writeBytesWithLength(outputFile, signManifest(manifestBytes, privateKey)); - _writeBytesWithLength(outputFile, manifestBytes); - outputFile.writeAsBytesSync(zipBytes, mode: FileMode.APPEND, flush: true); + Bundle bundle = new Bundle.fromContent( + path: outputPath, + manifest: manifestDescriptor, + contentBytes: zipBytes, + keyPair: keyPair + ); + bundle.writeSync(); return 0; } } diff --git a/packages/flutter_tools/lib/src/signing.dart b/packages/flutter_tools/lib/src/signing.dart deleted file mode 100644 index d3390a6d59..0000000000 --- a/packages/flutter_tools/lib/src/signing.dart +++ /dev/null @@ -1,94 +0,0 @@ -// 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 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:asn1lib/asn1lib.dart'; -import 'package:bignum/bignum.dart'; -import 'package:cipher/cipher.dart'; - -// The ECDSA algorithm parameters we're using. These match the parameters used -// by the Flutter updater package. -final ECDomainParameters _ecDomain = new ECDomainParameters('prime256v1'); -final String kSignerAlgorithm = 'SHA-256/ECDSA'; -final String kHashAlgorithm = 'SHA-256'; - -final SecureRandom _random = _initRandom(); - -SecureRandom _initRandom() { - // TODO(mpcomplete): Provide a better seed here. External entropy source? - final Uint8List key = new Uint8List(16); - final KeyParameter keyParam = new KeyParameter(key); - final ParametersWithIV params = new ParametersWithIV(keyParam, new Uint8List(16)); - SecureRandom random = new SecureRandom('AES/CTR/AUTO-SEED-PRNG') - ..seed(params); - return random; -} - -// Returns a serialized manifest, with the public key and hash of the content -// included. -Uint8List serializeManifest(Map manifestDescriptor, ECPublicKey publicKey, Uint8List zipBytes) { - if (manifestDescriptor == null) - return null; - final List kSavedKeys = [ - 'name', - 'version', - 'update-url' - ]; - Map outputManifest = new Map(); - manifestDescriptor.forEach((key, value) { - if (kSavedKeys.contains(key)) - outputManifest[key] = value; - }); - - if (publicKey != null) - outputManifest['key'] = BASE64.encode(publicKey.Q.getEncoded()); - - Uint8List zipHash = new Digest(kHashAlgorithm).process(zipBytes); - BigInteger zipHashInt = new BigInteger.fromBytes(1, zipHash); - outputManifest['content-hash'] = zipHashInt.intValue(); - - return new Uint8List.fromList(UTF8.encode(JSON.encode(outputManifest))); -} - -// Returns the ASN.1 encoded signature of the input manifestBytes. -List signManifest(Uint8List manifestBytes, ECPrivateKey privateKey) { - if (manifestBytes == null || privateKey == null) - return []; - Signer signer = new Signer(kSignerAlgorithm); - PrivateKeyParameter params = new PrivateKeyParameter(privateKey); - signer.init(true, new ParametersWithRandom(params, _random)); - ECSignature signature = signer.generateSignature(manifestBytes); - ASN1Sequence asn1 = new ASN1Sequence() - ..add(new ASN1Integer(signature.r)) - ..add(new ASN1Integer(signature.s)); - return asn1.encodedBytes; -} - -ECPrivateKey _asn1ParsePrivateKey(ECDomainParameters ecDomain, Uint8List privateKey) { - ASN1Parser parser = new ASN1Parser(privateKey); - ASN1Sequence seq = parser.nextObject(); - assert(seq.elements.length >= 2); - ASN1OctetString keyOct = seq.elements[1]; - BigInteger d = new BigInteger.fromBytes(1, keyOct.octets); - return new ECPrivateKey(d, ecDomain); -} - -Future loadPrivateKey(String privateKeyPath) async { - File file = new File(privateKeyPath); - if (!file.existsSync()) - return null; - List bytes = file.readAsBytesSync(); - return _asn1ParsePrivateKey(_ecDomain, new Uint8List.fromList(bytes)); -} - -ECPublicKey publicKeyFromPrivateKey(ECPrivateKey privateKey) { - if (privateKey == null) - return null; - ECPoint Q = privateKey.parameters.G * privateKey.d; - return new ECPublicKey(Q, privateKey.parameters); -} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index b992a472c5..8c782bda8b 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: args: ^0.13.0 asn1lib: ^0.4.1 cipher: ^0.7.1 + flx: ^0.0.1 crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From a0886508c9378b06d07de155045ec1f4c9489865 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 26 Oct 2015 15:29:44 -0400 Subject: [PATCH 150/188] Update flutter_tools to version 0.0.27. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 8c782bda8b..47b88f0112 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.26 +version: 0.0.27 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 672976aa0a5b09905ce98422f2a95f5baec643f8 Mon Sep 17 00:00:00 2001 From: Hixie Date: Fri, 23 Oct 2015 14:29:07 -0700 Subject: [PATCH 151/188] Rev the Dart SDK We also have to require a newer test package, since the old one depends on a version of the analyzer that uses dart:profiler, which is gone and replaced by dart:developer. --- packages/flutter_tools/pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 47b88f0112..202bccec15 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,11 +1,11 @@ name: sky_tools -version: 0.0.27 +version: 0.0.28 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors environment: - sdk: '>=1.13.0-dev.1 <2.0.0' + sdk: '>=1.13.0-dev.7.4 <2.0.0' dependencies: archive: ^1.0.20 @@ -19,7 +19,7 @@ dependencies: shelf_route: ^0.13.4 shelf_static: ^0.2.3 shelf: ^0.6.2 - test: ">=0.12.4+5 <0.12.5" + test: ^0.12.5 yaml: ^2.1.3 dev_dependencies: From 9992a3e0bb29fc7d6e0b222a3e1a3bf312ea2f6c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 28 Oct 2015 11:18:13 -0700 Subject: [PATCH 152/188] Update README.md Add a badge for AppVeyor. (TBR) --- packages/flutter_tools/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 7c2e17dd73..9c41e2fc70 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -1,6 +1,7 @@ # tools [![Build Status](https://travis-ci.org/flutter/tools.svg)](https://travis-ci.org/flutter/tools) +[![Build status](https://ci.appveyor.com/api/projects/status/fpokp26jprqddfms/branch/master?svg=true)](https://ci.appveyor.com/project/devoncarew/tools/branch/master) [![pub package](https://img.shields.io/pub/v/sky_tools.svg)](https://pub.dartlang.org/packages/sky_tools) Tools for building Flutter applications. From 678af9c0451a592f6e7593802c31f9289361656e Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 28 Oct 2015 13:42:46 -0700 Subject: [PATCH 153/188] fix some issues found by strong mode --- packages/flutter_tools/lib/src/commands/logs.dart | 4 ++-- packages/flutter_tools/lib/src/device.dart | 6 ++++-- packages/flutter_tools/lib/src/test/remote_listener.dart | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart index 62ee900f20..5785db8351 100644 --- a/packages/flutter_tools/lib/src/commands/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -12,8 +12,8 @@ import 'flutter_command.dart'; final Logger _logging = new Logger('sky_tools.logs'); class LogsCommand extends FlutterCommand { - final name = 'logs'; - final description = 'Show logs for running Sky apps.'; + final String name = 'logs'; + final String description = 'Show logs for running Sky apps.'; LogsCommand() { argParser.addFlag('clear', diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index f3c22afc24..bb5ea05347 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -810,13 +810,15 @@ class AndroidDevice extends Device { } @override - Future startApp(AndroidApk apk) async { + Future startApp(ApplicationPackage app) async { // Android currently has to be started with startServer(...). assert(false); return false; } - Future stopApp(AndroidApk apk) async { + Future stopApp(ApplicationPackage app) async { + final AndroidApk apk = app; + // Turn off reverse port forwarding runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); // Stop the app diff --git a/packages/flutter_tools/lib/src/test/remote_listener.dart b/packages/flutter_tools/lib/src/test/remote_listener.dart index 838e21d5eb..ae2c60244b 100644 --- a/packages/flutter_tools/lib/src/test/remote_listener.dart +++ b/packages/flutter_tools/lib/src/test/remote_listener.dart @@ -69,7 +69,7 @@ class RemoteListener { return; } - var declarer = new Declarer(); + Declarer declarer = new Declarer(); try { await runZoned(() => new Future.sync(main), zoneValues: { #test.declarer: declarer From 494d1e0140f9b5c66d6bd91f4d92162085ed2f20 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 28 Oct 2015 20:57:51 -0700 Subject: [PATCH 154/188] verify that we're running from the root of a project remove an unused import review comments rename st --> stack --- packages/flutter_tools/lib/executable.dart | 13 ++++++++++++- .../flutter_tools/lib/src/commands/build.dart | 2 +- .../lib/src/commands/flutter_command.dart | 18 ++++++++++++++++++ .../src/commands/flutter_command_runner.dart | 13 ++++++++----- .../flutter_tools/lib/src/commands/init.dart | 3 +-- .../lib/src/commands/install.dart | 2 +- .../flutter_tools/lib/src/commands/list.dart | 2 +- .../flutter_tools/lib/src/commands/listen.dart | 2 +- .../flutter_tools/lib/src/commands/logs.dart | 2 +- .../flutter_tools/lib/src/commands/start.dart | 2 +- .../flutter_tools/lib/src/commands/stop.dart | 2 +- .../flutter_tools/lib/src/commands/trace.dart | 2 +- packages/flutter_tools/lib/src/process.dart | 7 +++++++ packages/flutter_tools/pubspec.yaml | 2 +- 14 files changed, 55 insertions(+), 17 deletions(-) diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index cdecdd03e3..4643ccb0a4 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -20,6 +20,7 @@ import 'src/commands/run_mojo.dart'; import 'src/commands/start.dart'; import 'src/commands/stop.dart'; import 'src/commands/trace.dart'; +import 'src/process.dart'; /// Main entry point for commands. /// @@ -58,6 +59,16 @@ Future main(List args) async { exit(result); } on UsageException catch (e) { stderr.writeln(e); - exit(4); + // Args error exit code. + exit(64); + } catch (e, stack) { + if (e is ProcessExit) { + // We've caught an exit code. + exit(e.exitCode); + } + + stderr.writeln(e); + Logger.root.log(Level.SEVERE, '\nException:', null, stack); + exit(1); } } diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 1360f39ee9..6abcf13cdf 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -127,7 +127,7 @@ class BuildCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { String compilerPath = argResults['compiler']; if (compilerPath == null) diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart index d56cdcc9f7..917ece301f 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:args/command_runner.dart'; @@ -14,6 +15,9 @@ import 'flutter_command_runner.dart'; abstract class FlutterCommand extends Command { FlutterCommandRunner get runner => super.runner; + /// Whether this command needs to be run from the root of a project. + bool get requiresProjectRoot => true; + Future downloadApplicationPackages() async { if (applicationPackages == null) applicationPackages = await ApplicationPackageStore.forConfigs(runner.buildConfigurations); @@ -40,6 +44,20 @@ abstract class FlutterCommand extends Command { devices = other.devices; } + Future run() async { + if (requiresProjectRoot) { + if (!FileSystemEntity.isFileSync('pubspec.yaml')) { + stderr.writeln('No pubspec.yaml file found. ' + 'This command should be run from the root of a project.'); + return 1; + } + } + + return runInProject(); + } + + Future runInProject(); + ApplicationPackageStore applicationPackages; Toolchain toolchain; DeviceStore devices; diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index 8749a2470f..b12c25703e 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -12,6 +12,7 @@ import 'package:path/path.dart' as path; import '../artifacts.dart'; import '../build_configuration.dart'; +import '../process.dart'; final Logger _logging = new Logger('sky_tools.flutter_command_runner'); @@ -116,8 +117,8 @@ class FlutterCommandRunner extends CommandRunner { else message += '\nDid you run this command from the same directory as your pubspec.yaml file?'; } - _logging.severe(message); - exit(2); + stderr.writeln(message); + throw new ProcessExit(2); } String enginePath = globalResults['engine-src-path']; @@ -129,9 +130,11 @@ class FlutterCommandRunner extends CommandRunner { String realFlutterPath = flutterDir.resolveSymbolicLinksSync(); enginePath = path.dirname(path.dirname(path.dirname(path.dirname(realFlutterPath)))); - if (enginePath == '/' || enginePath.isEmpty || !FileSystemEntity.isDirectorySync(path.join(enginePath, 'out'))) { - _logging.severe('Unable to detect local build in $enginePath.\nDo you have a dependency override for the flutter package?'); - exit(2); + bool dirExists = FileSystemEntity.isDirectorySync(path.join(enginePath, 'out')); + if (enginePath == '/' || enginePath.isEmpty || !dirExists) { + stderr.writeln('Unable to detect local build in $enginePath.\n' + 'Do you have a dependency override for the flutter package?'); + throw new ProcessExit(2); } } diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 4e3512df71..57eac43411 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -160,8 +160,7 @@ class FlutterDemo extends StatelessComponent { ), floatingActionButton: new FloatingActionButton( child: new Icon( - type: 'content/add', - size: 24 + type: 'content/add' ) ) ); diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart index a8cb776ac3..235243e1e4 100644 --- a/packages/flutter_tools/lib/src/commands/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -18,7 +18,7 @@ class InstallCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { await downloadApplicationPackagesAndConnectToDevices(); return install(boot: argResults['boot']) ? 0 : 2; } diff --git a/packages/flutter_tools/lib/src/commands/list.dart b/packages/flutter_tools/lib/src/commands/list.dart index 379f733b6e..498f0a5e47 100644 --- a/packages/flutter_tools/lib/src/commands/list.dart +++ b/packages/flutter_tools/lib/src/commands/list.dart @@ -23,7 +23,7 @@ class ListCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { connectToDevices(); bool details = argResults['details']; diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index 64dc314677..93bd19e6bf 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -38,7 +38,7 @@ class ListenCommand extends FlutterCommand { static const String _remoteFlutterBundle = 'Documents/app.flx'; @override - Future run() async { + Future runInProject() async { await downloadApplicationPackagesAndConnectToDevices(); await downloadToolchain(); diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart index 5785db8351..992187f128 100644 --- a/packages/flutter_tools/lib/src/commands/logs.dart +++ b/packages/flutter_tools/lib/src/commands/logs.dart @@ -22,7 +22,7 @@ class LogsCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { connectToDevices(); bool clear = argResults['clear']; diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index 434fa77f2b..33f9f46a6e 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -36,7 +36,7 @@ class StartCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { await downloadApplicationPackagesAndConnectToDevices(); bool poke = argResults['poke']; diff --git a/packages/flutter_tools/lib/src/commands/stop.dart b/packages/flutter_tools/lib/src/commands/stop.dart index 6118b9b639..9117443594 100644 --- a/packages/flutter_tools/lib/src/commands/stop.dart +++ b/packages/flutter_tools/lib/src/commands/stop.dart @@ -17,7 +17,7 @@ class StopCommand extends FlutterCommand { final String description = 'Stop your Flutter app on all attached devices.'; @override - Future run() async { + Future runInProject() async { await downloadApplicationPackagesAndConnectToDevices(); return await stop() ? 0 : 2; } diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart index 31582a84be..b30f570312 100644 --- a/packages/flutter_tools/lib/src/commands/trace.dart +++ b/packages/flutter_tools/lib/src/commands/trace.dart @@ -28,7 +28,7 @@ class TraceCommand extends FlutterCommand { } @override - Future run() async { + Future runInProject() async { await downloadApplicationPackagesAndConnectToDevices(); if (!devices.android.isConnected()) { diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index a23c3db5b3..2332b58aab 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -89,3 +89,10 @@ String _runWithLoggingSync(List cmd, {bool checked: false}) { _logging.fine(results.stdout.trim()); return results.stdout; } + +class ProcessExit implements Exception { + final int exitCode; + ProcessExit(this.exitCode); + String get message => 'ProcessExit: ${exitCode}'; + String toString() => message; +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 202bccec15..3577ccbb72 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: shelf_route: ^0.13.4 shelf_static: ^0.2.3 shelf: ^0.6.2 - test: ^0.12.5 + test: 0.12.4+9 yaml: ^2.1.3 dev_dependencies: From b15bcc5c6431a32563d8e578ce7d5206f3ab5aae Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 29 Oct 2015 11:32:54 -0700 Subject: [PATCH 155/188] 0.0.29 --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 3577ccbb72..3bb3216172 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.28 +version: 0.0.29 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 0da7c7c6ac68f14c4c43eeada734bf5bb15b1c68 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 29 Oct 2015 11:42:02 -0700 Subject: [PATCH 156/188] upgrade test --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 3bb3216172..6d33c75acb 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: shelf_route: ^0.13.4 shelf_static: ^0.2.3 shelf: ^0.6.2 - test: 0.12.4+9 + test: ^0.12.5 yaml: ^2.1.3 dev_dependencies: From 30ad6ebaae41d691dbd84a0987ab0c3fb13953a4 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 29 Oct 2015 16:40:13 -0400 Subject: [PATCH 157/188] Depend on exact version of flx package so I can make breaking changes. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 47b88f0112..55dc809830 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: args: ^0.13.0 asn1lib: ^0.4.1 cipher: ^0.7.1 - flx: ^0.0.1 + flx: 0.0.1 crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From 79ceee73b2875ecc03597cc35260547c4dc997ac Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 29 Oct 2015 12:16:47 -0700 Subject: [PATCH 158/188] Port our testing framework to new test library --- .../flutter_tools/lib/src/test/loader.dart | 77 ++++++++++--------- .../lib/src/test/remote_listener.dart | 20 +++-- .../lib/src/test/remote_test.dart | 20 +++-- packages/flutter_tools/tool/travis.sh | 3 + 4 files changed, 68 insertions(+), 52 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index fe755a6c29..a1ea50b867 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -10,12 +10,14 @@ import 'package:path/path.dart' as p; import 'package:sky_tools/src/test/json_socket.dart'; import 'package:sky_tools/src/test/remote_test.dart'; import 'package:stack_trace/stack_trace.dart'; +import 'package:test/src/backend/group.dart'; +import 'package:test/src/backend/group_entry.dart'; import 'package:test/src/backend/metadata.dart'; import 'package:test/src/backend/test_platform.dart'; import 'package:test/src/runner/configuration.dart'; +import 'package:test/src/runner/hack_load_vm_file_hook.dart' as hack; import 'package:test/src/runner/load_exception.dart'; import 'package:test/src/runner/runner_suite.dart'; -import 'package:test/src/runner/hack_load_vm_file_hook.dart' as hack; import 'package:test/src/runner/vm/environment.dart'; import 'package:test/src/util/io.dart'; import 'package:test/src/util/remote_exception.dart'; @@ -82,7 +84,7 @@ void main() { } '''); - Completer completer = new Completer(); + Completer> completer = new Completer>(); Process process = await _startProcess(listenerFile.path, packageRoot: p.absolute(config.packageRoot)); @@ -105,44 +107,43 @@ void main() { } }); - Future socket = (() async { - return new JSONSocket(await info.socket); - })(); + JSONSocket socket = new JSONSocket(await info.socket); - socket.then((JSONSocket socket) async { - await cleanupTempDirectory(); + await cleanupTempDirectory(); - StreamSubscription subscription; - subscription = socket.stream.listen((response) { - if (response["type"] == "print") { - print(response["line"]); - } else if (response["type"] == "loadException") { - process.kill(); - completer.completeError( - new LoadException(path, response["message"]), - new Trace.current()); - } else if (response["type"] == "error") { - process.kill(); - AsyncError asyncError = RemoteException.deserialize(response["error"]); - completer.completeError( - new LoadException(path, asyncError.error), - asyncError.stackTrace); - } else { - assert(response["type"] == "success"); - subscription.cancel(); - completer.complete(response["tests"]); - } - }); - }); - - return new RunnerSuite(const VMEnvironment(), - (await completer.future).map((test) { + StreamSubscription subscription; + subscription = socket.stream.listen((response) { + if (response["type"] == "print") { + print(response["line"]); + } else if (response["type"] == "loadException") { + process.kill(); + completer.completeError( + new LoadException(path, response["message"]), + new Trace.current()); + } else if (response["type"] == "error") { + process.kill(); + AsyncError asyncError = RemoteException.deserialize(response["error"]); + completer.completeError( + new LoadException(path, asyncError.error), + asyncError.stackTrace); + } else { + assert(response["type"] == "success"); + subscription.cancel(); + completer.complete(response["tests"].map((test) { var testMetadata = new Metadata.deserialize(test['metadata']); return new RemoteTest(test['name'], testMetadata, socket, test['index']); - }), - metadata: metadata, - path: path, - platform: TestPlatform.vm, - os: currentOS, - onClose: process.kill); + })); + } + }); + + Iterable entries = await completer.future; + + return new RunnerSuite( + const VMEnvironment(), + new Group.root(entries, metadata: metadata), + path: path, + platform: TestPlatform.vm, + os: currentOS, + onClose: process.kill + ); } diff --git a/packages/flutter_tools/lib/src/test/remote_listener.dart b/packages/flutter_tools/lib/src/test/remote_listener.dart index ae2c60244b..e53df8f985 100644 --- a/packages/flutter_tools/lib/src/test/remote_listener.dart +++ b/packages/flutter_tools/lib/src/test/remote_listener.dart @@ -41,7 +41,7 @@ class RemoteListener { Isolate.current.setErrorsFatal(false); Isolate.current.addErrorListener(errorPort.sendPort); errorPort.listen((message) { - // Masquerade as an IsoalteSpawnException because that's what this would + // Masquerade as an IsolateSpawnException because that's what this would // be if the error had been detected statically. var error = new IsolateSpawnException(message[0]); var stackTrace = @@ -69,7 +69,7 @@ class RemoteListener { return; } - Declarer declarer = new Declarer(); + Declarer declarer = new Declarer(metadata); try { await runZoned(() => new Future.sync(main), zoneValues: { #test.declarer: declarer @@ -84,8 +84,8 @@ class RemoteListener { return; } - Suite suite = new Suite(declarer.tests, - platform: TestPlatform.vm, os: currentOS, metadata: metadata); + Suite suite = new Suite(declarer.build(), + platform: TestPlatform.vm, os: currentOS); new RemoteListener._(suite, socket)._listen(); } @@ -101,8 +101,10 @@ class RemoteListener { void _listen() { List tests = []; - for (var i = 0; i < _suite.tests.length; i++) { - Test test = _suite.tests[i]; + for (var i = 0; i < _suite.group.entries.length; i++) { + // TODO(ianh): entries[] might return a Group instead of a Test. We don't + // currently support nested groups. + Test test = _suite.group.entries[i]; tests.add({ "name": test.name, "metadata": test.metadata.serialize(), @@ -118,7 +120,9 @@ class RemoteListener { var message = JSON.decode(data); if (message['command'] == 'run') { assert(_liveTest == null); - Test test = _suite.tests[message['index']]; + // TODO(ianh): entries[] might return a Group instead of a Test. We don't + // currently support nested groups. + Test test = _suite.group.entries[message['index']]; _liveTest = test.load(_suite); _liveTest.onStateChange.listen((state) { @@ -148,6 +152,8 @@ class RemoteListener { } else if (message['command'] == 'close') { _liveTest.close(); _liveTest = null; + } else { + print('remote_listener.dart: ignoring command "${message["command"]}" from test harness'); } } } diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart index a89471b74c..a6ac36f970 100644 --- a/packages/flutter_tools/lib/src/test/remote_test.dart +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -7,18 +7,20 @@ import 'dart:async'; import 'package:test/src/backend/live_test.dart'; import 'package:test/src/backend/live_test_controller.dart'; import 'package:test/src/backend/metadata.dart'; +import 'package:test/src/backend/operating_system.dart'; import 'package:test/src/backend/state.dart'; import 'package:test/src/backend/suite.dart'; import 'package:test/src/backend/test.dart'; +import 'package:test/src/backend/test_platform.dart'; import 'package:test/src/util/remote_exception.dart'; import 'package:sky_tools/src/test/json_socket.dart'; -class RemoteTest implements Test { +class RemoteTest extends Test { final String name; final Metadata metadata; - final Future _socket; + final JSONSocket _socket; final int _index; RemoteTest(this.name, this.metadata, this._socket, this._index); @@ -30,10 +32,9 @@ class RemoteTest implements Test { controller = new LiveTestController(suite, this, () async { controller.setState(const State(Status.running, Result.success)); - JSONSocket socket = await _socket; - socket.send({'command': 'run', 'index': _index}); + _socket.send({'command': 'run', 'index': _index}); - subscription = socket.stream.listen((message) { + subscription = _socket.stream.listen((message) { if (message['type'] == 'error') { AsyncError asyncError = RemoteException.deserialize(message['error']); controller.addError(asyncError.error, asyncError.stackTrace); @@ -52,8 +53,7 @@ class RemoteTest implements Test { } }); }, () async { - JSONSocket socket = await _socket; - socket.send({'command': 'close'}); + _socket.send({'command': 'close'}); if (subscription != null) { subscription.cancel(); subscription = null; @@ -68,4 +68,10 @@ class RemoteTest implements Test { if (metadata == null) metadata = this.metadata; return new RemoteTest(name, metadata, _socket, _index); } + + // TODO(ianh): Implement this if we need it. + Test forPlatform(TestPlatform platform, {OperatingSystem os}) { + assert(false); + return this; + } } diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh index 13a64ae8bb..ed16ec2999 100755 --- a/packages/flutter_tools/tool/travis.sh +++ b/packages/flutter_tools/tool/travis.sh @@ -7,6 +7,9 @@ # Fast fail the script on failures. set -e +# Fetch all our dependencies +pub get + # Verify that the libraries are error free. pub global activate tuneup pub global run tuneup check From a57109beba4d3ce38055f59da915dfd64dc8f0f6 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Thu, 29 Oct 2015 13:56:34 -0700 Subject: [PATCH 159/188] Wait for sky server to start before starting device Instead of just waiting for the sky server process to start before we start the activity on the device, this causes us to wait for the sky server to actually start listening on its port Fixes #141 --- packages/flutter_tools/lib/src/device.dart | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index bb5ea05347..830f37bc19 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -5,6 +5,7 @@ library sky_tools.device; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'dart:math'; @@ -512,6 +513,9 @@ class AndroidDevice extends Device { static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default_android_device'; + static const String _kFlutterServerStartMessage = 'Serving'; + static const Duration _kFlutterServerTimeout = const Duration(seconds: 3); + String productID; String modelID; String deviceCodeName; @@ -773,8 +777,14 @@ class AndroidDevice extends Device { [adbPath, 'forward', observatoryPortString, observatoryPortString]); // Actually start the server. - await Process.start(sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort], - workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED); + Process server = await Process.start( + sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort], + workingDirectory: serverRoot, + mode: ProcessStartMode.DETACHED_WITH_STDIO + ); + await server.stdout.transform(UTF8.decoder) + .firstWhere((String value) => value.startsWith(_kFlutterServerStartMessage)) + .timeout(_kFlutterServerTimeout); // Set up reverse port-forwarding so that the Android app can reach the // server running on localhost. From 7aa05d4bce079b3f39b0a2af1b8d5be995ebc2ce Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 29 Oct 2015 14:24:35 -0700 Subject: [PATCH 160/188] Release 0.0.30. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index a14a20bc9b..51eedf53cf 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.29 +version: 0.0.30 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From ef2e038b11b685c1caf43d238475ca066bb77ea8 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 29 Oct 2015 14:27:08 -0700 Subject: [PATCH 161/188] Generalize path handling in flutter init This will make "-o ." work as expected to initialize a project in the current directory --- packages/flutter_tools/lib/src/commands/init.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 57eac43411..0290d1e093 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -67,7 +67,8 @@ abstract class Template { Template(this.name, this.description); void generateInto(Directory dir) { - String projectName = _normalizeProjectName(p.basename(dir.path)); + String dirPath = p.normalize(dir.absolute.path); + String projectName = _normalizeProjectName(p.basename(dirPath)); print('Creating ${p.basename(projectName)}...'); dir.createSync(recursive: true); From f069ac2427a6bc2eb3b19f7b14027274428ec1e4 Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 29 Oct 2015 14:32:40 -0700 Subject: [PATCH 162/188] Loosen the sky_tools dependency on flx --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 51eedf53cf..63e2d8a41d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: args: ^0.13.0 asn1lib: ^0.4.1 cipher: ^0.7.1 - flx: 0.0.1 + flx: ">=0.0.1" crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From 184ab7c9aa112ccdc8efef1a3a6558bd40ba256f Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 29 Oct 2015 14:35:55 -0700 Subject: [PATCH 163/188] Put an upper bound on flx dependency In case we run into trouble, this at least gives us an escape hatch. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 63e2d8a41d..69e483142a 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: args: ^0.13.0 asn1lib: ^0.4.1 cipher: ^0.7.1 - flx: ">=0.0.1" + flx: ">=0.0.1 <0.1.0" crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From 587f6e27feafe8d4d81f74dda8ab3b74418ea62d Mon Sep 17 00:00:00 2001 From: Alhaad Gokhale Date: Thu, 29 Oct 2015 14:36:25 -0700 Subject: [PATCH 164/188] Modify run_mojo command to take path to devtools. Made changes as discussed. R=@jamesr --- .../lib/src/commands/run_mojo.dart | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index 4080782f02..79bd8ccffd 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -27,7 +27,8 @@ class RunMojoCommand extends Command { argParser.addFlag('mojo-release', negatable: false, help: 'Use Release build of mojo (default)'); argParser.addOption('app', defaultsTo: 'app.flx'); - argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services'); + argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services. This is required for a Linux build.'); + argParser.addOption('devtools-path', help: 'Path to the mojo_run script in mojo devtools. This is required for an Android build.'); } // TODO(abarth): Why not use path.absolute? @@ -39,9 +40,9 @@ class RunMojoCommand extends Command { return file.absolute.path; } - Future _runAndroid(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) { + Future _runAndroid(String devtoolsPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) { String skyViewerUrl = ArtifactStore.googleStorageUrl('viewer', 'android-arm'); - String command = _makePathAbsolute(path.join(mojoPath, 'mojo', 'devtools', 'common', 'mojo_run')); + String command = _makePathAbsolute(devtoolsPath); String appName = path.basename(appPath); String appDir = path.dirname(appPath); String buildFlag = mojoConfig == _MojoConfig.Debug ? '--debug' : '--release'; @@ -79,8 +80,13 @@ class RunMojoCommand extends Command { @override Future run() async { - if (argResults['mojo-path'] == null) { - _logging.severe('Must specify --mojo-path to mojo_run'); + if (argResults['mojo-path'] == null && !argResults['android']) { + _logging.severe('Must specify --mojo-path for Linux.'); + return 1; + } + + if (argResults['devtools-path'] == null && argResults['android']) { + _logging.severe('Must specify --devtools-path for Android.'); return 1; } if (argResults['mojo-debug'] && argResults['mojo-release']) { @@ -97,7 +103,7 @@ class RunMojoCommand extends Command { args.addAll(argResults.rest); if (argResults['android']) { - return _runAndroid(mojoPath, mojoConfig, appPath, args); + return _runAndroid(argResults['devtools-path'], mojoConfig, appPath, args); } else { return _runLinux(mojoPath, mojoConfig, appPath, args); } From dde6bd20340de3c1bb8f81f5836fcf2fc9965778 Mon Sep 17 00:00:00 2001 From: Alhaad Gokhale Date: Thu, 29 Oct 2015 15:29:44 -0700 Subject: [PATCH 165/188] Use a common flag for both android and linux. --- .../flutter_tools/lib/src/commands/run_mojo.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index 79bd8ccffd..bbab6ac43f 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -27,8 +27,7 @@ class RunMojoCommand extends Command { argParser.addFlag('mojo-release', negatable: false, help: 'Use Release build of mojo (default)'); argParser.addOption('app', defaultsTo: 'app.flx'); - argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services. This is required for a Linux build.'); - argParser.addOption('devtools-path', help: 'Path to the mojo_run script in mojo devtools. This is required for an Android build.'); + argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services for Linux and to mojo devtools from Android.'); } // TODO(abarth): Why not use path.absolute? @@ -80,15 +79,11 @@ class RunMojoCommand extends Command { @override Future run() async { - if (argResults['mojo-path'] == null && !argResults['android']) { - _logging.severe('Must specify --mojo-path for Linux.'); + if (argResults['mojo-path'] == null) { + _logging.severe('Must specify --mojo-path.'); return 1; } - if (argResults['devtools-path'] == null && argResults['android']) { - _logging.severe('Must specify --devtools-path for Android.'); - return 1; - } if (argResults['mojo-debug'] && argResults['mojo-release']) { _logging.severe('Cannot specify both --mojo-debug and --mojo-release'); return 1; @@ -103,7 +98,7 @@ class RunMojoCommand extends Command { args.addAll(argResults.rest); if (argResults['android']) { - return _runAndroid(argResults['devtools-path'], mojoConfig, appPath, args); + return _runAndroid(mojoPath, mojoConfig, appPath, args); } else { return _runLinux(mojoPath, mojoConfig, appPath, args); } From 64c96e0dfa55cc6f8dbb2408fc58d39c54bb4c6f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 29 Oct 2015 16:17:13 -0700 Subject: [PATCH 166/188] Stop requiring customers to use a dev SDK --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 69e483142a..d1b408245f 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -5,7 +5,7 @@ homepage: http://flutter.io author: Flutter Authors environment: - sdk: '>=1.13.0-dev.7.4 <2.0.0' + sdk: '>=1.12.0 <2.0.0' dependencies: archive: ^1.0.20 From 15acf8ef6bc3f2a8068a1ff69bb8f035b44ad2b5 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 29 Oct 2015 16:45:11 -0700 Subject: [PATCH 167/188] increase test timeout --- packages/flutter_tools/test/init_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 1b44b01f17..f29166ca40 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -44,6 +44,6 @@ defineTests() { expect(exec.exitCode, 0); }, // This test can take a while due to network requests. - timeout: new Timeout(new Duration(minutes: 2))); + timeout: new Timeout(new Duration(minutes: 3))); }); } From 7115ff2674a09e629407929dd9e84e497f568500 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 30 Oct 2015 10:08:21 -0700 Subject: [PATCH 168/188] Rev pub package --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index d1b408245f..306ab929f6 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.30 +version: 0.0.31 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 39cf5218968d5fb40b6ee4b48fa2968e83d60815 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 30 Oct 2015 13:13:58 -0700 Subject: [PATCH 169/188] skip a test on windows --- packages/flutter_tools/test/init_test.dart | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index f29166ca40..4cb74fefe7 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -24,26 +24,30 @@ defineTests() { temp.deleteSync(recursive: true); }); - // Verify that we create a project that is well-formed. - test('init flutter-simple', () async { - InitCommand command = new InitCommand(); - CommandRunner runner = new CommandRunner('test_flutter', '') - ..addCommand(command); - await runner.run(['init', '--out', temp.path]) - .then((int code) => expect(code, equals(0))); + // This test consistently times out on our windows bot. The code is already + // covered on the linux one. + if (!Platform.isWindows) { + // Verify that we create a project that is well-formed. + test('init flutter-simple', () async { + InitCommand command = new InitCommand(); + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + await runner.run(['init', '--out', temp.path]) + .then((int code) => expect(code, equals(0))); - String path = p.join(temp.path, 'lib', 'main.dart'); - expect(new File(path).existsSync(), true); - ProcessResult exec = Process.runSync( - sdkBinaryName('dartanalyzer'), ['--fatal-warnings', path], - workingDirectory: temp.path); - if (exec.exitCode != 0) { - print(exec.stdout); - print(exec.stderr); - } - expect(exec.exitCode, 0); - }, - // This test can take a while due to network requests. - timeout: new Timeout(new Duration(minutes: 3))); + String path = p.join(temp.path, 'lib', 'main.dart'); + expect(new File(path).existsSync(), true); + ProcessResult exec = Process.runSync( + sdkBinaryName('dartanalyzer'), ['--fatal-warnings', path], + workingDirectory: temp.path); + if (exec.exitCode != 0) { + print(exec.stdout); + print(exec.stderr); + } + expect(exec.exitCode, 0); + }, + // This test can take a while due to network requests. + timeout: new Timeout(new Duration(minutes: 2))); + } }); } From 48c3d0158752a05a7176965c06fdffbcec51153b Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 30 Oct 2015 12:11:14 -0700 Subject: [PATCH 170/188] fix the stop command --- packages/flutter_tools/lib/src/device.dart | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 830f37bc19..75c994eb5d 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -835,16 +835,23 @@ class AndroidDevice extends Device { runSync([adbPath, 'shell', 'am', 'force-stop', apk.id]); // Kill the server if (Platform.isMacOS) { - String pid = runSync(['lsof', '-i', ':$_serverPort', '-t']); - if (pid.isEmpty) { + String pids = runSync(['lsof', '-i', ':$_serverPort', '-t']).trim(); + if (pids.isEmpty) { _logging.fine('No process to kill for port $_serverPort'); return true; } - // Killing a pid with a shell command from within dart is hard, - // so use a library command, but it's still nice to give the - // equivalent command when doing verbose logging. - _logging.info('kill $pid'); - Process.killPid(int.parse(pid)); + + // Handle multiple returned pids. + for (String pidString in pids.split('\n')) { + // Killing a pid with a shell command from within dart is hard, so use a + // library command, but it's still nice to give the equivalent command + // when doing verbose logging. + _logging.info('kill $pidString'); + + int pid = int.parse(pidString, onError: (_) => null); + if (pid != null) + Process.killPid(pid); + } } else if (Platform.isWindows) { //Get list of network processes and split on newline List processes = runSync(['netstat.exe','-ano']).split("\r"); From 94b472ff6767df5674509706bbcd01f7a2f87360 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 30 Oct 2015 22:57:39 -0700 Subject: [PATCH 171/188] Add a --no-http flag to start command This flag builds a local FLX file and pushes that to the device instead of using an HTTP server. --- .../lib/src/application_package.dart | 36 +-- packages/flutter_tools/lib/src/artifacts.dart | 238 ++++++++++++------ .../lib/src/build_configuration.dart | 34 ++- .../src/commands/flutter_command_runner.dart | 42 ++-- .../lib/src/commands/run_mojo.dart | 6 +- .../flutter_tools/lib/src/commands/start.dart | 28 ++- packages/flutter_tools/lib/src/device.dart | 86 ++++--- packages/flutter_tools/lib/src/toolchain.dart | 22 +- packages/flutter_tools/test/src/mocks.dart | 6 +- 9 files changed, 337 insertions(+), 161 deletions(-) diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 09a2061ba3..f705622d57 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -66,16 +66,15 @@ class ApplicationPackageStore { ApplicationPackageStore({ this.android, this.iOS, this.iOSSimulator }); - ApplicationPackage getPackageForPlatform(BuildPlatform platform) { + ApplicationPackage getPackageForPlatform(TargetPlatform platform) { switch (platform) { - case BuildPlatform.android: + case TargetPlatform.android: return android; - case BuildPlatform.iOS: + case TargetPlatform.iOS: return iOS; - case BuildPlatform.iOSSimulator: + case TargetPlatform.iOSSimulator: return iOSSimulator; - case BuildPlatform.mac: - case BuildPlatform.linux: + case TargetPlatform.linux: return null; } } @@ -86,31 +85,32 @@ class ApplicationPackageStore { IOSApp iOSSimulator; for (BuildConfiguration config in configs) { - switch (config.platform) { - case BuildPlatform.android: + switch (config.targetPlatform) { + case TargetPlatform.android: assert(android == null); - String localPath = config.type == BuildType.prebuilt ? - await ArtifactStore.getPath(Artifact.flutterShell) : - path.join(config.buildDir, 'apks', AndroidApk._defaultName); - android = new AndroidApk(localPath: localPath); + if (config.type != BuildType.prebuilt) { + String localPath = path.join(config.buildDir, 'apks', AndroidApk._defaultName); + android = new AndroidApk(localPath: localPath); + } else { + Artifact artifact = ArtifactStore.getArtifact( + type: ArtifactType.shell, targetPlatform: TargetPlatform.android); + android = new AndroidApk(localPath: await ArtifactStore.getPath(artifact)); + } break; - case BuildPlatform.iOS: + case TargetPlatform.iOS: assert(iOS == null); assert(config.type != BuildType.prebuilt); iOS = new IOSApp(localPath: path.join(config.buildDir, IOSApp._defaultName)); break; - case BuildPlatform.iOSSimulator: + case TargetPlatform.iOSSimulator: assert(iOSSimulator == null); assert(config.type != BuildType.prebuilt); iOSSimulator = new IOSApp(localPath: path.join(config.buildDir, IOSApp._defaultName)); break; - case BuildPlatform.mac: - case BuildPlatform.linux: - // TODO(abarth): Support mac and linux targets. - assert(false); + case TargetPlatform.linux: break; } } diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 9bd6a06351..5e39e47d27 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -10,15 +10,147 @@ import 'dart:io'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; +import 'build_configuration.dart'; + final Logger _logging = new Logger('sky_tools.artifacts'); -enum Artifact { - flutterCompiler, - flutterShell, - skyViewerMojo, +const String _kShellCategory = 'shell'; +const String _kViewerCategory = 'viewer'; + +String _getNameForHostPlatform(HostPlatform platform) { + switch (platform) { + case HostPlatform.linux: + return 'linux-x64'; + case HostPlatform.mac: + return 'darwin-x64'; + } +} + +String _getNameForTargetPlatform(TargetPlatform platform) { + switch (platform) { + case TargetPlatform.android: + return 'android-arm'; + case TargetPlatform.iOS: + return 'ios-arm'; + case TargetPlatform.iOSSimulator: + return 'ios-x64'; + case TargetPlatform.linux: + return 'linux-x64'; + } +} + +// Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 +String _getCloudStorageBaseUrl({String category, String platform, String revision}) { + if (platform == 'darwin-x64') { + // In the fullness of time, we'll have a consistent URL pattern for all of + // our artifacts, but, for the time being, darwin artifacts are stored in a + // different cloud storage bucket. + return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/'; + } + return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/'; +} + +enum ArtifactType { + snapshot, + shell, + viewer, +} + +class Artifact { + const Artifact._({ + this.name, + this.fileName, + this.category, + this.type, + this.hostPlatform, + this.targetPlatform + }); + + final String name; + final String fileName; + final String category; // TODO(abarth): Remove categories. + final ArtifactType type; + final HostPlatform hostPlatform; + final TargetPlatform targetPlatform; + + String get platform { + if (targetPlatform != null) + return _getNameForTargetPlatform(targetPlatform); + if (hostPlatform != null) + return _getNameForHostPlatform(hostPlatform); + assert(false); + return null; + } + + String getUrl(String revision) { + return _getCloudStorageBaseUrl(category: category, platform: platform, revision: revision) + fileName; + } + + // Whether the artifact needs to be marked as executable on disk. + bool get executable => type == ArtifactType.snapshot; } class ArtifactStore { + static const List knownArtifacts = const [ + const Artifact._( + name: 'Sky Shell', + fileName: 'SkyShell.apk', + category: _kShellCategory, + type: ArtifactType.shell, + targetPlatform: TargetPlatform.android + ), + const Artifact._( + name: 'Sky Snapshot', + fileName: 'sky_snapshot', + category: _kShellCategory, + type: ArtifactType.snapshot, + hostPlatform: HostPlatform.linux + ), + const Artifact._( + name: 'Sky Snapshot', + fileName: 'sky_snapshot', + category: _kShellCategory, + type: ArtifactType.snapshot, + hostPlatform: HostPlatform.mac + ), + const Artifact._( + name: 'Sky Viewer', + fileName: 'sky_viewer.mojo', + category: _kViewerCategory, + type: ArtifactType.viewer, + targetPlatform: TargetPlatform.android + ), + const Artifact._( + name: 'Sky Viewer', + fileName: 'sky_viewer.mojo', + category: _kViewerCategory, + type: ArtifactType.viewer, + targetPlatform: TargetPlatform.linux + ), + ]; + + static Artifact getArtifact({ + ArtifactType type, + HostPlatform hostPlatform, + TargetPlatform targetPlatform + }) { + for (Artifact artifact in ArtifactStore.knownArtifacts) { + if (type != null && + type != artifact.type) + continue; + if (hostPlatform != null && + artifact.hostPlatform != null && + hostPlatform != artifact.hostPlatform) + continue; + if (targetPlatform != null && + artifact.targetPlatform != null && + targetPlatform != artifact.targetPlatform) + continue; + return artifact; + } + return null; + } + static String packageRoot; static String _engineRevision; @@ -31,102 +163,68 @@ class ArtifactStore { return _engineRevision; } - // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 - static String googleStorageUrl(String category, String platform) { - return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${engineRevision}/'; + static String getCloudStorageBaseUrl(String category, String platform) { + return _getCloudStorageBaseUrl(category: category, platform: platform, revision: engineRevision); } static Future _downloadFile(String url, File file) async { - print('Downloading $url to ${file.path}.'); + _logging.info('Downloading $url to ${file.path}.'); HttpClient httpClient = new HttpClient(); HttpClientRequest request = await httpClient.getUrl(Uri.parse(url)); HttpClientResponse response = await request.close(); - _logging.fine('Received response'); - if (response.statusCode != 200) throw new Exception(response.reasonPhrase); + _logging.fine('Received response statusCode=${response.statusCode}'); + if (response.statusCode != 200) + throw new Exception(response.reasonPhrase); IOSink sink = file.openWrite(); await sink.addStream(response); await sink.close(); _logging.fine('Wrote file'); } - static Future _cacheDir() async { + static Directory _getBaseCacheDir() { Directory cacheDir = new Directory(path.join(packageRoot, 'sky_tools', 'cache')); - if (!await cacheDir.exists()) { - await cacheDir.create(recursive: true); - } + if (!cacheDir.existsSync()) + cacheDir.createSync(recursive: true); return cacheDir; } - static Future _engineSpecificCacheDir() async { - Directory cacheDir = await _cacheDir(); + static Directory _getCacheDirForArtifact(Artifact artifact) { + Directory baseDir = _getBaseCacheDir(); // For now, all downloaded artifacts are release mode host binaries so use // a path that mirrors a local release build. // TODO(jamesr): Add support for more configurations. String config = 'Release'; - Directory engineSpecificDir = new Directory(path.join(cacheDir.path, 'sky_engine', engineRevision, config)); - - if (!await engineSpecificDir.exists()) { - await engineSpecificDir.create(recursive: true); - } - return engineSpecificDir; - } - - // Whether the artifact needs to be marked as executable on disk. - static bool _needsToBeExecutable(Artifact artifact) { - return artifact == Artifact.flutterCompiler; + Directory artifactSpecificDir = new Directory(path.join( + baseDir.path, 'sky_engine', engineRevision, config, artifact.platform)); + if (!artifactSpecificDir.existsSync()) + artifactSpecificDir.createSync(recursive: true); + return artifactSpecificDir; } static Future getPath(Artifact artifact) async { - Directory cacheDir = await _engineSpecificCacheDir(); - - String category; - String platform; - String name; - - switch (artifact) { - case Artifact.flutterCompiler: - category = 'shell'; - name = 'sky_snapshot'; - break; - case Artifact.flutterShell: - category = 'shell'; - platform = 'android-arm'; - name = 'SkyShell.apk'; - break; - case Artifact.skyViewerMojo: - category = 'viewer'; - name = 'sky_viewer.mojo'; - break; - } - - File cachedFile = new File(path.join(cacheDir.path, name)); - if (!await cachedFile.exists()) { - _logging.info('Downloading ${name} from the cloud, one moment please...'); - if (platform == null) { - if (!Platform.isLinux) - throw new Exception('Platform unsupported.'); - platform = 'linux-x64'; - } - String url = googleStorageUrl(category, platform) + name; - await _downloadFile(url, cachedFile); - if (_needsToBeExecutable(artifact)) { - ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]); - if (result.exitCode != 0) throw new Exception(result.stderr); + Directory cacheDir = _getCacheDirForArtifact(artifact); + File cachedFile = new File(path.join(cacheDir.path, artifact.fileName)); + if (!cachedFile.existsSync()) { + print('Downloading ${artifact.name} from the cloud, one moment please...'); + await _downloadFile(artifact.getUrl(engineRevision), cachedFile); + if (artifact.executable) { + // TODO(abarth): We should factor this out into a separate function that + // can have a platform-specific implementation. + ProcessResult result = Process.runSync('chmod', ['u+x', cachedFile.path]); + if (result.exitCode != 0) + throw new Exception(result.stderr); } } return cachedFile.path; } - static Future clear() async { - Directory cacheDir = await _cacheDir(); + static void clear() { + Directory cacheDir = _getBaseCacheDir(); _logging.fine('Clearing cache directory ${cacheDir.path}'); - await cacheDir.delete(recursive: true); + cacheDir.deleteSync(recursive: true); } - static Future populate() async { - for (Artifact artifact in Artifact.values) { - _logging.fine('Populating cache with $artifact'); - await getPath(artifact); - } + static Future populate() { + return Future.wait(knownArtifacts.map((artifact) => getPath(artifact))); } } diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart index 0db0299e7c..5fc0356495 100644 --- a/packages/flutter_tools/lib/src/build_configuration.dart +++ b/packages/flutter_tools/lib/src/build_configuration.dart @@ -2,29 +2,48 @@ // 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:logging/logging.dart'; import 'package:path/path.dart' as path; +final Logger _logging = new Logger('sky_tools.build_configuration'); + enum BuildType { prebuilt, release, debug, } -enum BuildPlatform { - android, - iOS, - iOSSimulator, +enum HostPlatform { mac, linux, } +enum TargetPlatform { + android, + iOS, + iOSSimulator, + linux, +} + +HostPlatform getCurrentHostPlatform() { + if (Platform.isMacOS) + return HostPlatform.mac; + if (Platform.isLinux) + return HostPlatform.linux; + _logging.warning('Unsupported host platform, defaulting to Linux'); + return HostPlatform.linux; +} + class BuildConfiguration { - BuildConfiguration.prebuilt({ this.platform }) + BuildConfiguration.prebuilt({ this.hostPlatform, this.targetPlatform }) : type = BuildType.prebuilt, buildDir = null; BuildConfiguration.local({ this.type, - this.platform, + this.hostPlatform, + this.targetPlatform, String enginePath, String buildPath }) : buildDir = path.normalize(path.join(enginePath, buildPath)) { @@ -32,6 +51,7 @@ class BuildConfiguration { } final BuildType type; - final BuildPlatform platform; + final HostPlatform hostPlatform; + final TargetPlatform targetPlatform; final String buildDir; } diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart index b12c25703e..8f0b3891c9 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart @@ -27,8 +27,10 @@ class FlutterCommandRunner extends CommandRunner { negatable: false, help: 'Very noisy logging, including the output of all ' 'shell commands executed.'); + argParser.addOption('package-root', + help: 'Path to your packages directory.', defaultsTo: 'packages'); - argParser.addSeparator('Global build selection options:'); + argParser.addSeparator('Local build selection options:'); argParser.addFlag('debug', negatable: false, help: @@ -48,42 +50,40 @@ class FlutterCommandRunner extends CommandRunner { 'Automatically detect your engine src directory from an overridden Flutter package.' 'Useful if you are building Flutter locally and are using a dependency_override for' 'the Flutter package that points to your engine src directory.'); - argParser.addOption('engine-src-path', + argParser.addOption('engine-src-path', hide: true, help: 'Path to your engine src directory, if you are building Flutter locally. ' 'Ignored if neither debug nor release is set. Not normally required.'); - argParser.addOption('android-debug-build-path', + argParser.addOption('android-debug-build-path', hide: true, help: 'Path to your Android Debug out directory, if you are building Flutter locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/android_Debug/'); - argParser.addOption('android-release-build-path', + argParser.addOption('android-release-build-path', hide: true, help: 'Path to your Android Release out directory, if you are building Flutter locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/android_Release/'); - argParser.addOption('ios-debug-build-path', + argParser.addOption('ios-debug-build-path', hide: true, help: 'Path to your iOS Debug out directory, if you are building Flutter locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_Debug/'); - argParser.addOption('ios-release-build-path', + argParser.addOption('ios-release-build-path', hide: true, help: 'Path to your iOS Release out directory, if you are building Flutter locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_Release/'); - argParser.addOption('ios-sim-debug-build-path', + argParser.addOption('ios-sim-debug-build-path', hide: true, help: 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Debug/'); - argParser.addOption('ios-sim-release-build-path', + argParser.addOption('ios-sim-release-build-path', hide: true, help: 'Path to your iOS Simulator Release out directory, if you are building Sky locally. ' 'This path is relative to engine-src-path. Not normally required.', defaultsTo: 'out/ios_sim_Release/'); - argParser.addOption('package-root', - help: 'Path to your packages directory.', defaultsTo: 'packages'); } List get buildConfigurations { @@ -124,6 +124,7 @@ class FlutterCommandRunner extends CommandRunner { String enginePath = globalResults['engine-src-path']; bool isDebug = globalResults['debug']; bool isRelease = globalResults['release']; + HostPlatform hostPlatform = getCurrentHostPlatform(); if (enginePath == null && globalResults['local-build']) { Directory flutterDir = new Directory(path.join(globalResults['package-root'], 'flutter')); @@ -141,7 +142,8 @@ class FlutterCommandRunner extends CommandRunner { List configs = []; if (enginePath == null) { - configs.add(new BuildConfiguration.prebuilt(platform: BuildPlatform.android)); + configs.add(new BuildConfiguration.prebuilt( + hostPlatform: hostPlatform, targetPlatform: TargetPlatform.android)); } else { if (!FileSystemEntity.isDirectorySync(enginePath)) _logging.warning('$enginePath is not a valid directory'); @@ -152,7 +154,8 @@ class FlutterCommandRunner extends CommandRunner { if (isDebug) { configs.add(new BuildConfiguration.local( type: BuildType.debug, - platform: BuildPlatform.android, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.android, enginePath: enginePath, buildPath: globalResults['android-debug-build-path'] )); @@ -160,14 +163,16 @@ class FlutterCommandRunner extends CommandRunner { if (Platform.isMacOS) { configs.add(new BuildConfiguration.local( type: BuildType.debug, - platform: BuildPlatform.iOS, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.iOS, enginePath: enginePath, buildPath: globalResults['ios-debug-build-path'] )); configs.add(new BuildConfiguration.local( type: BuildType.debug, - platform: BuildPlatform.iOSSimulator, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.iOSSimulator, enginePath: enginePath, buildPath: globalResults['ios-sim-debug-build-path'] )); @@ -177,7 +182,8 @@ class FlutterCommandRunner extends CommandRunner { if (isRelease) { configs.add(new BuildConfiguration.local( type: BuildType.release, - platform: BuildPlatform.android, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.android, enginePath: enginePath, buildPath: globalResults['android-release-build-path'] )); @@ -185,14 +191,16 @@ class FlutterCommandRunner extends CommandRunner { if (Platform.isMacOS) { configs.add(new BuildConfiguration.local( type: BuildType.release, - platform: BuildPlatform.iOS, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.iOS, enginePath: enginePath, buildPath: globalResults['ios-release-build-path'] )); configs.add(new BuildConfiguration.local( type: BuildType.release, - platform: BuildPlatform.iOSSimulator, + hostPlatform: hostPlatform, + targetPlatform: TargetPlatform.iOSSimulator, enginePath: enginePath, buildPath: globalResults['ios-sim-release-build-path'] )); diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index bbab6ac43f..5853204ee7 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -9,6 +9,7 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; +import '../build_configuration.dart'; import '../artifacts.dart'; import '../process.dart'; @@ -40,7 +41,7 @@ class RunMojoCommand extends Command { } Future _runAndroid(String devtoolsPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) { - String skyViewerUrl = ArtifactStore.googleStorageUrl('viewer', 'android-arm'); + String skyViewerUrl = ArtifactStore.getCloudStorageBaseUrl('viewer', 'android-arm'); String command = _makePathAbsolute(devtoolsPath); String appName = path.basename(appPath); String appDir = path.dirname(appPath); @@ -65,7 +66,8 @@ class RunMojoCommand extends Command { } Future _runLinux(String mojoPath, _MojoConfig mojoConfig, String appPath, List additionalArgs) async { - String viewerPath = _makePathAbsolute(await ArtifactStore.getPath(Artifact.skyViewerMojo)); + Artifact artifact = ArtifactStore.getArtifact(type: ArtifactType.viewer, targetPlatform: TargetPlatform.linux); + String viewerPath = _makePathAbsolute(await ArtifactStore.getPath(artifact)); String mojoBuildType = mojoConfig == _MojoConfig.Debug ? 'Debug' : 'Release'; String mojoShellPath = _makePathAbsolute(path.join(mojoPath, 'out', mojoBuildType, 'mojo_shell')); List cmd = [ diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index 33f9f46a6e..2fb4701f42 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -3,17 +3,21 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import '../application_package.dart'; import '../device.dart'; +import 'build.dart'; import 'flutter_command.dart'; import 'install.dart'; import 'stop.dart'; final Logger _logging = new Logger('sky_tools.start'); +const String _localBundlePath = 'app.flx'; +const bool _kUseServer = true; class StartCommand extends FlutterCommand { final String name = 'start'; @@ -31,13 +35,20 @@ class StartCommand extends FlutterCommand { defaultsTo: '.', abbr: 't', help: 'Target app path or filename to start.'); + argParser.addFlag('http', + negatable: true, + defaultsTo: true, + help: 'Use a local HTTP server to serve your app to your device.'); argParser.addFlag('boot', help: 'Boot the iOS Simulator if it isn\'t already running.'); } @override Future runInProject() async { - await downloadApplicationPackagesAndConnectToDevices(); + await Future.wait([ + downloadToolchain(), + downloadApplicationPackagesAndConnectToDevices(), + ]); bool poke = argResults['poke']; if (!poke) { @@ -59,8 +70,19 @@ class StartCommand extends FlutterCommand { continue; if (device is AndroidDevice) { String target = path.absolute(argResults['target']); - if (await device.startServer(target, poke, argResults['checked'], package)) - startedSomething = true; + if (argResults['http']) { + if (await device.startServer(target, poke, argResults['checked'], package)) + startedSomething = true; + } else { + String mainPath = target; + if (FileSystemEntity.isDirectorySync(target)) + mainPath = path.join(target, 'lib', 'main.dart'); + BuildCommand builder = new BuildCommand(); + builder.inheritFromParent(this); + await builder.build(outputPath: _localBundlePath, mainPath: mainPath); + if (device.startBundle(package, _localBundlePath, poke, argResults['checked'])) + startedSomething = true; + } } else { if (await device.startApp(package)) startedSomething = true; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 75c994eb5d..fa13654a22 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -66,7 +66,7 @@ abstract class Device { /// Check if the current version of the given app is already installed bool isAppInstalled(ApplicationPackage app); - BuildPlatform get platform; + TargetPlatform get platform; Future logs({bool clear: false}); @@ -271,7 +271,7 @@ class IOSDevice extends Device { } @override - BuildPlatform get platform => BuildPlatform.iOS; + TargetPlatform get platform => TargetPlatform.iOS; /// Note that clear is not supported on iOS at this time. Future logs({bool clear: false}) async { @@ -487,7 +487,7 @@ class IOSSimulator extends Device { } @override - BuildPlatform get platform => BuildPlatform.iOSSimulator; + TargetPlatform get platform => TargetPlatform.iOSSimulator; Future logs({bool clear: false}) async { if (!isConnected()) { @@ -695,6 +695,10 @@ class AndroidDevice extends Device { return '${_getDeviceDataPath(app)}/${app.name}.sha1'; } + String _getDeviceBundlePath(ApplicationPackage app) { + return '${_getDeviceDataPath(app)}/dev.flx'; + } + String _getDeviceApkSha1(ApplicationPackage app) { return runCheckedSync([adbPath, 'shell', 'cat', _getDeviceSha1Path(app)]); } @@ -750,12 +754,45 @@ class AndroidDevice extends Device { return true; } + void _forwardObservatoryPort() { + // Set up port forwarding for observatory. + String observatoryPortString = 'tcp:$_observatoryPort'; + runCheckedSync( + [adbPath, 'forward', observatoryPortString, observatoryPortString]); + } + + bool startBundle(AndroidApk apk, String bundlePath, bool poke, bool checked) { + if (!FileSystemEntity.isFileSync(bundlePath)) { + _logging.severe('Cannot find $bundlePath'); + return false; + } + + if (!poke) + _forwardObservatoryPort(); + + String deviceTmpPath = '/data/local/tmp/dev.flx'; + String deviceBundlePath = _getDeviceBundlePath(apk); + runCheckedSync([adbPath, 'push', bundlePath, deviceTmpPath]); + runCheckedSync([adbPath, 'shell', 'mv', deviceTmpPath, deviceBundlePath]); + List cmd = [ + adbPath, + 'shell', 'am', 'start', + '-a', 'android.intent.action.RUN', + '-d', deviceBundlePath, + ]; + if (checked) + cmd.addAll(['--ez', 'enable-checked-mode', 'true']); + cmd.add(apk.launchActivity); + runCheckedSync(cmd); + return true; + } + Future startServer( String target, bool poke, bool checked, AndroidApk apk) async { String serverRoot = ''; String mainDart = ''; String missingMessage = ''; - if (await FileSystemEntity.isDirectory(target)) { + if (FileSystemEntity.isDirectorySync(target)) { serverRoot = target; mainDart = path.join(serverRoot, 'lib', 'main.dart'); missingMessage = 'Missing lib/main.dart in project: $serverRoot'; @@ -765,16 +802,13 @@ class AndroidDevice extends Device { missingMessage = '$mainDart does not exist.'; } - if (!await FileSystemEntity.isFile(mainDart)) { + if (!FileSystemEntity.isFileSync(mainDart)) { _logging.severe(missingMessage); return false; } if (!poke) { - // Set up port forwarding for observatory. - String observatoryPortString = 'tcp:$_observatoryPort'; - runCheckedSync( - [adbPath, 'forward', observatoryPortString, observatoryPortString]); + _forwardObservatoryPort(); // Actually start the server. Process server = await Process.start( @@ -794,28 +828,20 @@ class AndroidDevice extends Device { String relativeDartMain = _convertToURL(path.relative(mainDart, from: serverRoot)); String url = 'http://localhost:$_serverPort/$relativeDartMain'; - if (poke) { + if (poke) url += '?rand=${new Random().nextDouble()}'; - } // Actually launch the app on Android. List cmd = [ adbPath, - 'shell', - 'am', - 'start', - '-a', - 'android.intent.action.VIEW', - '-d', - url, + 'shell', 'am', 'start', + '-a', 'android.intent.action.VIEW', + '-d', url, ]; - if (checked) { + if (checked) cmd.addAll(['--ez', 'enable-checked-mode', 'true']); - } cmd.add(apk.launchActivity); - runCheckedSync(cmd); - return true; } @@ -880,7 +906,7 @@ class AndroidDevice extends Device { } @override - BuildPlatform get platform => BuildPlatform.android; + TargetPlatform get platform => TargetPlatform.android; void clearLogs() { runSync([adbPath, 'logcat', '-c']); @@ -985,24 +1011,20 @@ class DeviceStore { IOSSimulator iOSSimulator; for (BuildConfiguration config in configs) { - switch (config.platform) { - case BuildPlatform.android: + switch (config.targetPlatform) { + case TargetPlatform.android: assert(android == null); android = new AndroidDevice(); break; - case BuildPlatform.iOS: + case TargetPlatform.iOS: assert(iOS == null); iOS = new IOSDevice(); break; - case BuildPlatform.iOSSimulator: + case TargetPlatform.iOSSimulator: assert(iOSSimulator == null); iOSSimulator = new IOSSimulator(); break; - - case BuildPlatform.mac: - case BuildPlatform.linux: - // TODO(abarth): Support mac and linux targets. - assert(false); + case TargetPlatform.linux: break; } } diff --git a/packages/flutter_tools/lib/src/toolchain.dart b/packages/flutter_tools/lib/src/toolchain.dart index 655e6b6b98..34301ec9ae 100644 --- a/packages/flutter_tools/lib/src/toolchain.dart +++ b/packages/flutter_tools/lib/src/toolchain.dart @@ -11,16 +11,16 @@ import 'build_configuration.dart'; import 'process.dart'; class Compiler { - Compiler(this._compilerPath); + Compiler(this._path); - String _compilerPath; + String _path; Future compile({ String mainPath, String snapshotPath }) { return runCommandAndStreamOutput([ - _compilerPath, + _path, mainPath, '--package-root=${ArtifactStore.packageRoot}', '--snapshot=$snapshotPath' @@ -28,18 +28,22 @@ class Compiler { } } +Future _getCompilerPath(BuildConfiguration config) async { + if (config.type != BuildType.prebuilt) + return path.join(config.buildDir, 'clang_x64', 'sky_snapshot'); + Artifact artifact = ArtifactStore.getArtifact( + type: ArtifactType.snapshot, hostPlatform: config.hostPlatform); + return await ArtifactStore.getPath(artifact); +} + class Toolchain { Toolchain({ this.compiler }); final Compiler compiler; static Future forConfigs(List 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'); - + // TODO(abarth): Shouldn't we consider all the configs? + String compilerPath = await _getCompilerPath(configs.first); return new Toolchain(compiler: new Compiler(compilerPath)); } } diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index b971bd1831..94466d4984 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -26,21 +26,21 @@ class MockToolchain extends Toolchain { } class MockAndroidDevice extends Mock implements AndroidDevice { - BuildPlatform get platform => BuildPlatform.android; + TargetPlatform get platform => TargetPlatform.android; @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } class MockIOSDevice extends Mock implements IOSDevice { - BuildPlatform get platform => BuildPlatform.iOS; + TargetPlatform get platform => TargetPlatform.iOS; @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } class MockIOSSimulator extends Mock implements IOSSimulator { - BuildPlatform get platform => BuildPlatform.iOSSimulator; + TargetPlatform get platform => TargetPlatform.iOSSimulator; @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); From 492090360a1282bdfdd606f92a3a5ea614f2966b Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Sat, 31 Oct 2015 12:43:26 -0700 Subject: [PATCH 172/188] Remove some unnessary dependencies --- packages/flutter_tools/pubspec.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 306ab929f6..93919a5bfc 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -10,8 +10,6 @@ environment: dependencies: archive: ^1.0.20 args: ^0.13.0 - asn1lib: ^0.4.1 - cipher: ^0.7.1 flx: ">=0.0.1 <0.1.0" crypto: ^0.9.1 mustache4dart: ^1.0.0 From 70e20153ab9b459fa54a64e155800309f0684da1 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 2 Nov 2015 00:20:22 -0800 Subject: [PATCH 173/188] use stack_trace to create more readable exception traces --- packages/flutter_tools/lib/executable.dart | 27 +++++++++++----------- packages/flutter_tools/pubspec.yaml | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 4643ccb0a4..64458eea8d 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'package:stack_trace/stack_trace.dart'; import 'src/commands/build.dart'; import 'src/commands/cache.dart'; @@ -53,22 +54,22 @@ Future main(List args) async { ..addCommand(new StopCommand()) ..addCommand(new TraceCommand()); - try { + return Chain.capture(() async { dynamic result = await runner.run(args); if (result is int) exit(result); - } on UsageException catch (e) { - stderr.writeln(e); - // Args error exit code. - exit(64); - } catch (e, stack) { - if (e is ProcessExit) { + }, onError: (error, Chain chain) { + if (error is UsageException) { + stderr.writeln(error); + // Argument error exit code. + exit(64); + } else if (error is ProcessExit) { // We've caught an exit code. - exit(e.exitCode); + exit(error.exitCode); + } else { + stderr.writeln(error); + Logger.root.log(Level.SEVERE, '\nException:', null, chain.terse.toTrace()); + exit(1); } - - stderr.writeln(e); - Logger.root.log(Level.SEVERE, '\nException:', null, stack); - exit(1); - } + }); } diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 93919a5bfc..48d7a86546 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: shelf_route: ^0.13.4 shelf_static: ^0.2.3 shelf: ^0.6.2 + stack_trace: ^1.4.0 test: ^0.12.5 yaml: ^2.1.3 From 297b90e25015b73effb34c371e8cc1c53b047fec Mon Sep 17 00:00:00 2001 From: Hixie Date: Fri, 30 Oct 2015 14:57:31 -0700 Subject: [PATCH 174/188] Try to fix the test framework better than before The previous attempt to port the 'test' framework to the new framework wasn't super-successful. This does a better job, hopefully. --- .../flutter_tools/lib/src/test/loader.dart | 66 +++++++++++++++---- .../lib/src/test/remote_listener.dart | 31 +++++---- .../lib/src/test/remote_test.dart | 17 +++-- 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index a1ea50b867..b649d911dd 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -11,7 +11,6 @@ import 'package:sky_tools/src/test/json_socket.dart'; import 'package:sky_tools/src/test/remote_test.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:test/src/backend/group.dart'; -import 'package:test/src/backend/group_entry.dart'; import 'package:test/src/backend/metadata.dart'; import 'package:test/src/backend/test_platform.dart'; import 'package:test/src/runner/configuration.dart'; @@ -30,6 +29,12 @@ final String _kSkyShell = Platform.environment['SKY_SHELL']; const String _kHost = '127.0.0.1'; const String _kPath = '/runner'; +// Right now a bunch of our tests crash or assert after the tests have finished running. +// Mostly this is just because the test puts the framework in an inconsistent state with +// a scheduled microtask that verifies that state. Eventually we should fix all these +// problems but for now we'll just paper over them. +const bool kExpectAllTestsToCloseCleanly = false; + class _ServerInfo { final String url; final Future socket; @@ -84,10 +89,12 @@ void main() { } '''); - Completer> completer = new Completer>(); + Completer> completer = new Completer>(); - Process process = await _startProcess(listenerFile.path, - packageRoot: p.absolute(config.packageRoot)); + Process process = await _startProcess( + listenerFile.path, + packageRoot: p.absolute(config.packageRoot) + ); Future cleanupTempDirectory() async { if (tempDir == null) @@ -98,12 +105,43 @@ void main() { } process.exitCode.then((int exitCode) async { - info.server.close(force: true); - await cleanupTempDirectory(); - if (!completer.isCompleted) { - String error = await process.stderr.transform(UTF8.decoder).first; - completer.completeError( - new LoadException(path, error), new Trace.current()); + try { + info.server.close(force: true); + await cleanupTempDirectory(); + String output = ''; + if (exitCode < 0) { + // Abnormal termination (high bit of signed 8-bit exitCode is set) + switch (exitCode) { + case -0x0f: // ProcessSignal.SIGTERM + break; // we probably killed it ourselves + case -0x0b: // ProcessSignal.SIGSEGV + output += 'Segmentation fault in subprocess for: $path\n'; + break; + default: + output += 'Unexpected exit code $exitCode from subprocess for: $path\n'; + } + } + String stdout = await process.stdout.transform(UTF8.decoder).join('\n'); + String stderr = await process.stderr.transform(UTF8.decoder).join('\n'); + if (stdout != '') + output += '\nstdout:\n$stdout'; + if (stderr != '') + output += '\nstderr:\n$stderr'; + if (!completer.isCompleted) { + if (output == '') + output = 'No output.'; + completer.completeError( + new LoadException(path, output), + new Trace.current() + ); + } else { + if (kExpectAllTestsToCloseCleanly && output != '') + print('Unexpected failure after test claimed to pass:\n$output'); + } + } catch (e) { + // Throwing inside this block causes all kinds of hard-to-debug issues + // like stack overflows and hangs. So catch everything just in case. + print("exception while handling subprocess termination: $e"); } }); @@ -116,12 +154,12 @@ void main() { if (response["type"] == "print") { print(response["line"]); } else if (response["type"] == "loadException") { - process.kill(); + process.kill(ProcessSignal.SIGTERM); completer.completeError( new LoadException(path, response["message"]), new Trace.current()); } else if (response["type"] == "error") { - process.kill(); + process.kill(ProcessSignal.SIGTERM); AsyncError asyncError = RemoteException.deserialize(response["error"]); completer.completeError( new LoadException(path, asyncError.error), @@ -136,7 +174,7 @@ void main() { } }); - Iterable entries = await completer.future; + Iterable entries = await completer.future; return new RunnerSuite( const VMEnvironment(), @@ -144,6 +182,6 @@ void main() { path: path, platform: TestPlatform.vm, os: currentOS, - onClose: process.kill + onClose: () { process.kill(ProcessSignal.SIGTERM); } ); } diff --git a/packages/flutter_tools/lib/src/test/remote_listener.dart b/packages/flutter_tools/lib/src/test/remote_listener.dart index e53df8f985..6f0e283c7f 100644 --- a/packages/flutter_tools/lib/src/test/remote_listener.dart +++ b/packages/flutter_tools/lib/src/test/remote_listener.dart @@ -28,9 +28,11 @@ final OperatingSystem currentOS = (() { typedef AsyncFunction(); class RemoteListener { + RemoteListener._(this._suite, this._socket); + final Suite _suite; final WebSocket _socket; - LiveTest _liveTest; + final Set _liveTests = new Set(); static Future start(String server, Metadata metadata, Function getMain()) async { WebSocket socket = await WebSocket.connect(server); @@ -93,8 +95,6 @@ class RemoteListener { socket.add(JSON.encode({"type": "loadException", "message": message})); } - RemoteListener._(this._suite, this._socket); - void _send(data) { _socket.add(JSON.encode(data)); } @@ -119,13 +119,13 @@ class RemoteListener { void _handleCommand(String data) { var message = JSON.decode(data); if (message['command'] == 'run') { - assert(_liveTest == null); // TODO(ianh): entries[] might return a Group instead of a Test. We don't // currently support nested groups. Test test = _suite.group.entries[message['index']]; - _liveTest = test.load(_suite); + LiveTest liveTest = test.load(_suite); + _liveTests.add(liveTest); - _liveTest.onStateChange.listen((state) { + liveTest.onStateChange.listen((state) { _send({ "type": "state-change", "status": state.status.name, @@ -133,25 +133,30 @@ class RemoteListener { }); }); - _liveTest.onError.listen((asyncError) { + liveTest.onError.listen((asyncError) { _send({ "type": "error", "error": RemoteException.serialize( - asyncError.error, asyncError.stackTrace) + asyncError.error, + asyncError.stackTrace + ) }); }); - _liveTest.onPrint.listen((line) { + liveTest.onPrint.listen((line) { _send({"type": "print", "line": line}); }); - _liveTest.run().then((_) { + liveTest.run().then((_) { _send({"type": "complete"}); - _liveTest = null; + _liveTests.remove(liveTest); }); } else if (message['command'] == 'close') { - _liveTest.close(); - _liveTest = null; + if (_liveTests.isNotEmpty) + print('closing with ${_liveTests.length} live tests'); + for (LiveTest liveTest in _liveTests) + liveTest.close(); + _liveTests.clear(); } else { print('remote_listener.dart: ignoring command "${message["command"]}" from test harness'); } diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart index a6ac36f970..9c7b11cd69 100644 --- a/packages/flutter_tools/lib/src/test/remote_test.dart +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -17,14 +17,13 @@ import 'package:test/src/util/remote_exception.dart'; import 'package:sky_tools/src/test/json_socket.dart'; class RemoteTest extends Test { + RemoteTest(this.name, this.metadata, this._socket, this._index); + final String name; final Metadata metadata; - final JSONSocket _socket; final int _index; - RemoteTest(this.name, this.metadata, this._socket, this._index); - LiveTest load(Suite suite) { LiveTestController controller; StreamSubscription subscription; @@ -71,7 +70,13 @@ class RemoteTest extends Test { // TODO(ianh): Implement this if we need it. Test forPlatform(TestPlatform platform, {OperatingSystem os}) { - assert(false); - return this; - } + if (!metadata.testOn.evaluate(platform, os: os)) + return null; + return new RemoteTest( + name, + metadata.forPlatform(platform, os: os), + _socket, + _index + ); + } } From b43722e79fabe23106e034555d9ebd472eadbcc3 Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 2 Nov 2015 11:17:33 -0800 Subject: [PATCH 175/188] Handle crashing engine. When the engine dies unexpectedly during test execution, we have to terminate any tests running in that engine. Previously, they would just hang. For some reason that I was never able to satisfactorily explain, the WebSocket doesn't die in a way I can detect in this case. So instead, we hand in a future that we only complete when we detect the server subprocess ends. --- .../flutter_tools/lib/src/test/json_socket.dart | 3 ++- packages/flutter_tools/lib/src/test/loader.dart | 4 +++- .../flutter_tools/lib/src/test/remote_test.dart | 14 +++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/json_socket.dart b/packages/flutter_tools/lib/src/test/json_socket.dart index 56ee44f61f..8d29f0fd48 100644 --- a/packages/flutter_tools/lib/src/test/json_socket.dart +++ b/packages/flutter_tools/lib/src/test/json_socket.dart @@ -7,11 +7,12 @@ import 'dart:convert'; import 'dart:io'; class JSONSocket { - JSONSocket(WebSocket socket) + JSONSocket(WebSocket socket, this.unusualTermination) : _socket = socket, stream = socket.map(JSON.decode).asBroadcastStream(); final WebSocket _socket; final Stream stream; + final Future unusualTermination; void send(dynamic data) { _socket.add(JSON.encode(data)); diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index b649d911dd..635fc60e27 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -90,6 +90,7 @@ void main() { '''); Completer> completer = new Completer>(); + Completer deathCompleter = new Completer(); Process process = await _startProcess( listenerFile.path, @@ -138,6 +139,7 @@ void main() { if (kExpectAllTestsToCloseCleanly && output != '') print('Unexpected failure after test claimed to pass:\n$output'); } + deathCompleter.complete(); } catch (e) { // Throwing inside this block causes all kinds of hard-to-debug issues // like stack overflows and hangs. So catch everything just in case. @@ -145,7 +147,7 @@ void main() { } }); - JSONSocket socket = new JSONSocket(await info.socket); + JSONSocket socket = new JSONSocket(await info.socket, deathCompleter.future); await cleanupTempDirectory(); diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart index 9c7b11cd69..261604ea1c 100644 --- a/packages/flutter_tools/lib/src/test/remote_test.dart +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:stack_trace/stack_trace.dart'; import 'package:test/src/backend/live_test.dart'; import 'package:test/src/backend/live_test_controller.dart'; import 'package:test/src/backend/metadata.dart'; @@ -29,8 +30,8 @@ class RemoteTest extends Test { StreamSubscription subscription; controller = new LiveTestController(suite, this, () async { - controller.setState(const State(Status.running, Result.success)); + controller.setState(const State(Status.running, Result.success)); _socket.send({'command': 'run', 'index': _index}); subscription = _socket.stream.listen((message) { @@ -51,6 +52,17 @@ class RemoteTest extends Test { controller.completer.complete(); } }); + + _socket.unusualTermination.then((_) { + if (subscription != null) { + controller.addError(new Exception('Unexpected subprocess termination.'), new Trace.current()); + controller.setState(new State(Status.complete, Result.error)); + subscription.cancel(); + subscription = null; + controller.completer.complete(); + } + }); + }, () async { _socket.send({'command': 'close'}); if (subscription != null) { From fe5f98e33b775f0eca804bb6e1808fe107713207 Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 2 Nov 2015 11:25:58 -0800 Subject: [PATCH 176/188] Rev sky_tools to 0.0.32. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 48d7a86546..3af2fc90fa 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.31 +version: 0.0.32 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 27118f39ea94b5ab3452cfa5af1a0f6f3f6d399f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 2 Nov 2015 12:54:05 -0800 Subject: [PATCH 177/188] Add a default maniest path for build --- packages/flutter_tools/lib/src/commands/build.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 6abcf13cdf..b82402cc3e 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -85,7 +85,7 @@ Iterable<_MaterialAsset> _parseMaterialAssets(Map manifestDescriptor) sync* { } dynamic _loadManifest(String manifestPath) { - if (manifestPath == null) + if (manifestPath == null || !FileSystemEntity.isFileSync(manifestPath)) return null; String manifestDescriptor = new File(manifestPath).readAsStringSync(); return loadYaml(manifestDescriptor); @@ -107,6 +107,7 @@ ArchiveFile _createSnapshotFile(String snapshotPath) { const String _kDefaultAssetBase = 'packages/material_design_icons/icons'; const String _kDefaultMainPath = 'lib/main.dart'; +const String _kDefaultManifestPath = 'flutter.yaml'; const String _kDefaultOutputPath = 'app.flx'; const String _kDefaultSnapshotPath = 'snapshot_blob.bin'; const String _kDefaultPrivateKeyPath = 'privatekey.der'; @@ -120,7 +121,7 @@ class BuildCommand extends FlutterCommand { argParser.addOption('asset-base', defaultsTo: _kDefaultAssetBase); argParser.addOption('compiler'); argParser.addOption('main', defaultsTo: _kDefaultMainPath); - argParser.addOption('manifest'); + argParser.addOption('manifest', defaultsTo: _kDefaultManifestPath); argParser.addOption('private-key', defaultsTo: _kDefaultPrivateKeyPath); argParser.addOption('output-file', abbr: 'o', defaultsTo: _kDefaultOutputPath); argParser.addOption('snapshot', defaultsTo: _kDefaultSnapshotPath); @@ -149,7 +150,7 @@ class BuildCommand extends FlutterCommand { Future build({ String assetBase: _kDefaultAssetBase, String mainPath: _kDefaultMainPath, - String manifestPath, + String manifestPath: _kDefaultManifestPath, String outputPath: _kDefaultOutputPath, String snapshotPath: _kDefaultSnapshotPath, String privateKeyPath: _kDefaultPrivateKeyPath, From 5dc4a7cce4ce97c7b62870f54191566cfa728db6 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 2 Nov 2015 01:50:40 -0800 Subject: [PATCH 178/188] abstract some OS operations --- packages/flutter_tools/lib/src/artifacts.dart | 19 ++-- .../flutter_tools/lib/src/commands/build.dart | 4 +- .../flutter_tools/lib/src/commands/init.dart | 4 +- .../lib/src/commands/run_mojo.dart | 2 +- packages/flutter_tools/lib/src/device.dart | 59 ++---------- packages/flutter_tools/lib/src/os_utils.dart | 91 +++++++++++++++++++ packages/flutter_tools/lib/src/process.dart | 2 - packages/flutter_tools/test/all.dart | 2 + .../flutter_tools/test/os_utils_test.dart | 66 ++++++++++++++ 9 files changed, 185 insertions(+), 64 deletions(-) create mode 100644 packages/flutter_tools/lib/src/os_utils.dart create mode 100644 packages/flutter_tools/test/os_utils_test.dart diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 5e39e47d27..24d97b7555 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.artifacts; - import 'dart:async'; import 'dart:io'; @@ -11,6 +9,7 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'build_configuration.dart'; +import 'os_utils.dart'; final Logger _logging = new Logger('sky_tools.artifacts'); @@ -83,7 +82,11 @@ class Artifact { } String getUrl(String revision) { - return _getCloudStorageBaseUrl(category: category, platform: platform, revision: revision) + fileName; + return _getCloudStorageBaseUrl( + category: category, + platform: platform, + revision: revision + ) + fileName; } // Whether the artifact needs to be marked as executable on disk. @@ -164,7 +167,11 @@ class ArtifactStore { } static String getCloudStorageBaseUrl(String category, String platform) { - return _getCloudStorageBaseUrl(category: category, platform: platform, revision: engineRevision); + return _getCloudStorageBaseUrl( + category: category, + platform: platform, + revision: engineRevision + ); } static Future _downloadFile(String url, File file) async { @@ -208,9 +215,7 @@ class ArtifactStore { print('Downloading ${artifact.name} from the cloud, one moment please...'); await _downloadFile(artifact.getUrl(engineRevision), cachedFile); if (artifact.executable) { - // TODO(abarth): We should factor this out into a separate function that - // can have a platform-specific implementation. - ProcessResult result = Process.runSync('chmod', ['u+x', cachedFile.path]); + ProcessResult result = osUtils.makeExecutable(cachedFile); if (result.exitCode != 0) throw new Exception(result.stderr); } diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 6abcf13cdf..62f1880c34 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -7,10 +7,10 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:archive/archive.dart'; -import 'package:yaml/yaml.dart'; - import 'package:flx/bundle.dart'; import 'package:flx/signing.dart'; +import 'package:yaml/yaml.dart'; + import '../toolchain.dart'; import 'flutter_command.dart'; diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 0290d1e093..756dc9562b 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -158,10 +158,10 @@ class FlutterDemo extends StatelessComponent { child: new Center( child: new Text("Hello world!") ) - ), + ), floatingActionButton: new FloatingActionButton( child: new Icon( - type: 'content/add' + icon: 'content/add' ) ) ); diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index 5853204ee7..3cbd597dfd 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -9,8 +9,8 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import '../build_configuration.dart'; import '../artifacts.dart'; +import '../build_configuration.dart'; import '../process.dart'; final Logger _logging = new Logger('sky_tools.run_mojo'); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index fa13654a22..5fadb74871 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -2,19 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.device; - import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; +import 'package:crypto/crypto.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import 'package:crypto/crypto.dart'; import 'application_package.dart'; import 'build_configuration.dart'; +import 'os_utils.dart'; import 'process.dart'; final Logger _logging = new Logger('sky_tools.device'); @@ -507,8 +506,8 @@ class IOSSimulator extends Device { class AndroidDevice extends Device { static const String _ADB_PATH = 'adb'; - static const String _observatoryPort = '8181'; - static const String _serverPort = '9888'; + static const int _observatoryPort = 8181; + static const int _serverPort = 9888; static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default_android_device'; @@ -756,9 +755,8 @@ class AndroidDevice extends Device { void _forwardObservatoryPort() { // Set up port forwarding for observatory. - String observatoryPortString = 'tcp:$_observatoryPort'; - runCheckedSync( - [adbPath, 'forward', observatoryPortString, observatoryPortString]); + String portString = 'tcp:$_observatoryPort'; + runCheckedSync([adbPath, 'forward', portString, portString]); } bool startBundle(AndroidApk apk, String bundlePath, bool poke, bool checked) { @@ -812,7 +810,7 @@ class AndroidDevice extends Device { // Actually start the server. Process server = await Process.start( - sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort], + sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort.toString()], workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED_WITH_STDIO ); @@ -859,48 +857,9 @@ class AndroidDevice extends Device { runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']); // Stop the app runSync([adbPath, 'shell', 'am', 'force-stop', apk.id]); + // Kill the server - if (Platform.isMacOS) { - String pids = runSync(['lsof', '-i', ':$_serverPort', '-t']).trim(); - if (pids.isEmpty) { - _logging.fine('No process to kill for port $_serverPort'); - return true; - } - - // Handle multiple returned pids. - for (String pidString in pids.split('\n')) { - // Killing a pid with a shell command from within dart is hard, so use a - // library command, but it's still nice to give the equivalent command - // when doing verbose logging. - _logging.info('kill $pidString'); - - int pid = int.parse(pidString, onError: (_) => null); - if (pid != null) - Process.killPid(pid); - } - } else if (Platform.isWindows) { - //Get list of network processes and split on newline - List processes = runSync(['netstat.exe','-ano']).split("\r"); - - //List entries from netstat is formatted like so - // TCP 192.168.2.11:50945 192.30.252.90:443 LISTENING 1304 - //This regexp is to find process where the the port exactly matches - RegExp pattern = new RegExp(':$_serverPort[ ]+'); - - //Split the columns by 1 or more spaces - RegExp columnPattern = new RegExp('[ ]+'); - processes.forEach((String process) { - if (process.contains(pattern)) { - //The last column is the Process ID - String processId = process.split(columnPattern).last; - //Force and Tree kill the process - _logging.info('kill $processId'); - runSync(['TaskKill.exe', '/F', '/T', '/PID', processId]); - } - }); - } else { - runSync(['fuser', '-k', '$_serverPort/tcp']); - } + osUtils.killTcpPortListeners(_serverPort); return true; } diff --git a/packages/flutter_tools/lib/src/os_utils.dart b/packages/flutter_tools/lib/src/os_utils.dart new file mode 100644 index 0000000000..caf124d0a9 --- /dev/null +++ b/packages/flutter_tools/lib/src/os_utils.dart @@ -0,0 +1,91 @@ +// 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:io'; + +import 'package:logging/logging.dart'; + +import 'process.dart'; + +final OperatingSystemUtils osUtils = new OperatingSystemUtils._(); + +final Logger _logging = new Logger('sky_tools.os'); + +abstract class OperatingSystemUtils { + factory OperatingSystemUtils._() { + if (Platform.isWindows) { + return new _WindowsUtils(); + } else if (Platform.isMacOS) { + return new _MacUtils(); + } else { + return new _LinuxUtils(); + } + } + + /// Make the given file executable. This may be a no-op on some platforms. + ProcessResult makeExecutable(File file); + + /// A best-effort attempt to kill all listeners on the given TCP port. + void killTcpPortListeners(int tcpPort); +} + +abstract class _PosixUtils implements OperatingSystemUtils { + ProcessResult makeExecutable(File file) { + return Process.runSync('chmod', ['u+x', file.path]); + } +} + +class _WindowsUtils implements OperatingSystemUtils { + // This is a no-op. + ProcessResult makeExecutable(File file) { + return new ProcessResult(0, 0, null, null); + } + + void killTcpPortListeners(int tcpPort) { + // Get list of network processes and split on newline + List processes = runSync(['netstat.exe','-ano']).split("\r"); + + // List entries from netstat is formatted like so: + // TCP 192.168.2.11:50945 192.30.252.90:443 LISTENING 1304 + // This regexp is to find process where the the port exactly matches + RegExp pattern = new RegExp(':$tcpPort[ ]+'); + + // Split the columns by 1 or more spaces + RegExp columnPattern = new RegExp('[ ]+'); + processes.forEach((String process) { + if (process.contains(pattern)) { + // The last column is the Process ID + String processId = process.split(columnPattern).last; + // Force and Tree kill the process + _logging.info('kill $processId'); + runSync(['TaskKill.exe', '/F', '/T', '/PID', processId]); + } + }); + } +} + +class _MacUtils extends _PosixUtils { + void killTcpPortListeners(int tcpPort) { + String pids = runSync(['lsof', '-i', ':$tcpPort', '-t']).trim(); + if (pids.isNotEmpty) { + // Handle multiple returned pids. + for (String pidString in pids.split('\n')) { + // Killing a pid with a shell command from within dart is hard, so use a + // library command, but it's still nice to give the equivalent command + // when doing verbose logging. + _logging.info('kill $pidString'); + + int pid = int.parse(pidString, onError: (_) => null); + if (pid != null) + Process.killPid(pid); + } + } + } +} + +class _LinuxUtils extends _PosixUtils { + void killTcpPortListeners(int tcpPort) { + runSync(['fuser', '-k', '$tcpPort/tcp']); + } +} diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 2332b58aab..2fd8a7a360 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library sky_tools.process; - import 'dart:async'; import 'dart:convert'; import 'dart:io'; diff --git a/packages/flutter_tools/test/all.dart b/packages/flutter_tools/test/all.dart index 0b2dcfe97f..91450afa86 100644 --- a/packages/flutter_tools/test/all.dart +++ b/packages/flutter_tools/test/all.dart @@ -8,6 +8,7 @@ import 'install_test.dart' as install_test; import 'listen_test.dart' as listen_test; import 'list_test.dart' as list_test; import 'logs_test.dart' as logs_test; +import 'os_utils_test.dart' as os_utils_test; import 'start_test.dart' as start_test; import 'stop_test.dart' as stop_test; import 'trace_test.dart' as trace_test; @@ -19,6 +20,7 @@ main() { listen_test.defineTests(); list_test.defineTests(); logs_test.defineTests(); + os_utils_test.defineTests(); start_test.defineTests(); stop_test.defineTests(); trace_test.defineTests(); diff --git a/packages/flutter_tools/test/os_utils_test.dart b/packages/flutter_tools/test/os_utils_test.dart new file mode 100644 index 0000000000..999fc9cc5a --- /dev/null +++ b/packages/flutter_tools/test/os_utils_test.dart @@ -0,0 +1,66 @@ +// 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:io'; + +import 'package:sky_tools/src/os_utils.dart'; +import 'package:test/test.dart'; +import 'package:path/path.dart' as p; + +main() => defineTests(); + +defineTests() { + group('OperatingSystemUtils', () { + Directory temp; + + setUp(() { + temp = Directory.systemTemp.createTempSync('sky_tools'); + }); + + tearDown(() { + temp.deleteSync(recursive: true); + }); + + test('makeExecutable', () { + File file = new File(p.join(temp.path, 'foo.script')); + file.writeAsStringSync('hello world'); + osUtils.makeExecutable(file); + + // Skip this test on windows. + if (!Platform.isWindows) { + String mode = file.statSync().modeString(); + // rwxr--r-- + expect(mode.substring(0, 3), endsWith('x')); + } + }); + + /// Start a script listening on a port, try and kill that process. + test('killTcpPortListeners', () async { + final int port = 40170; + + File file = new File(p.join(temp.path, 'script.dart')); + file.writeAsStringSync(''' +import 'dart:io'; + +void main() async { + ServerSocket serverSocket = await ServerSocket.bind( + InternetAddress.LOOPBACK_IP_V4, ${port}); + // wait... + print('listening on port ${port}...'); +} +'''); + Process process = await Process.start('dart', [file.path]); + await process.stdout.first; + + osUtils.killTcpPortListeners(40170); + int exitCode = await process.exitCode; + expect(exitCode, isNot(equals(0))); + }); + + /// Try and kill with a port that no process is listening to. + test('killTcpPortListeners none', () { + osUtils.killTcpPortListeners(40171); + }); + }); +} From 466a91ab41a7ccb574110fc319e87daea4fd337f Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 2 Nov 2015 13:09:55 -0800 Subject: [PATCH 179/188] fix the 'dart:profiler' bug ...by requiring a new version of analyzer. Also, when the subprocess is terminated unexpectedly, report the actual problem in more detail. --- packages/flutter_tools/lib/src/test/json_socket.dart | 2 +- packages/flutter_tools/lib/src/test/loader.dart | 4 ++-- packages/flutter_tools/lib/src/test/remote_test.dart | 3 ++- packages/flutter_tools/pubspec.yaml | 11 ++++++++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/flutter_tools/lib/src/test/json_socket.dart b/packages/flutter_tools/lib/src/test/json_socket.dart index 8d29f0fd48..2edc71db8c 100644 --- a/packages/flutter_tools/lib/src/test/json_socket.dart +++ b/packages/flutter_tools/lib/src/test/json_socket.dart @@ -12,7 +12,7 @@ class JSONSocket { final WebSocket _socket; final Stream stream; - final Future unusualTermination; + final Future unusualTermination; void send(dynamic data) { _socket.add(JSON.encode(data)); diff --git a/packages/flutter_tools/lib/src/test/loader.dart b/packages/flutter_tools/lib/src/test/loader.dart index 635fc60e27..0b6e090f9f 100644 --- a/packages/flutter_tools/lib/src/test/loader.dart +++ b/packages/flutter_tools/lib/src/test/loader.dart @@ -90,7 +90,7 @@ void main() { '''); Completer> completer = new Completer>(); - Completer deathCompleter = new Completer(); + Completer deathCompleter = new Completer(); Process process = await _startProcess( listenerFile.path, @@ -139,7 +139,7 @@ void main() { if (kExpectAllTestsToCloseCleanly && output != '') print('Unexpected failure after test claimed to pass:\n$output'); } - deathCompleter.complete(); + deathCompleter.complete(output); } catch (e) { // Throwing inside this block causes all kinds of hard-to-debug issues // like stack overflows and hangs. So catch everything just in case. diff --git a/packages/flutter_tools/lib/src/test/remote_test.dart b/packages/flutter_tools/lib/src/test/remote_test.dart index 261604ea1c..d3abea3492 100644 --- a/packages/flutter_tools/lib/src/test/remote_test.dart +++ b/packages/flutter_tools/lib/src/test/remote_test.dart @@ -53,8 +53,9 @@ class RemoteTest extends Test { } }); - _socket.unusualTermination.then((_) { + _socket.unusualTermination.then((String message) { if (subscription != null) { + controller.print('Unexpected subprocess termination: $message'); controller.addError(new Exception('Unexpected subprocess termination.'), new Trace.current()); controller.setState(new State(Status.complete, Result.error)); subscription.cancel(); diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 3af2fc90fa..7fe13dfa7e 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.32 +version: 0.0.33 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors @@ -8,6 +8,7 @@ environment: sdk: '>=1.12.0 <2.0.0' dependencies: + analyzer: ">=0.26.1+17" # see note below archive: ^1.0.20 args: ^0.13.0 flx: ">=0.0.1 <0.1.0" @@ -21,6 +22,14 @@ dependencies: test: ^0.12.5 yaml: ^2.1.3 +# A note about 'analyzer': +# We don't actually depend on 'analyzer', but 'test' does. We aren't +# compatible with some older versions of 'analyzer'. We lie here, +# saying we do depend on it, so that we constrain the version that +# 'test' will get to a version that we'll probably be ok with. (This +# is why there's no upper bound on our dependency.) +# See also https://github.com/dart-lang/pub/issues/1356 + dev_dependencies: mockito: "^0.10.1" From 8ab21d7a9cb66e8e1a2b826aeafadb47eefe0baa Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 3 Nov 2015 15:28:55 -0500 Subject: [PATCH 180/188] Depend on the new flx 0.0.5 and remove use of deprecated KeyPair. --- packages/flutter_tools/lib/src/commands/build.dart | 2 +- packages/flutter_tools/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 586fc872b2..2fb9541621 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -182,7 +182,7 @@ class BuildCommand extends FlutterCommand { archive.addFile(file); } - KeyPair keyPair = KeyPair.readFromPrivateKeySync(privateKeyPath); + AsymmetricKeyPair keyPair = keyPairFromPrivateKeyFileSync(privateKeyPath); Uint8List zipBytes = new Uint8List.fromList(new ZipEncoder().encode(archive)); Bundle bundle = new Bundle.fromContent( path: outputPath, diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 7fe13dfa7e..ccb0e8326d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: analyzer: ">=0.26.1+17" # see note below archive: ^1.0.20 args: ^0.13.0 - flx: ">=0.0.1 <0.1.0" + flx: ">=0.0.5 <0.1.0" crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From 071ebda56f4f63bcf18b8f16a1b39b10bc6b322f Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 3 Nov 2015 15:41:47 -0500 Subject: [PATCH 181/188] Change flutter_tools to version 0.0.34. --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index ccb0e8326d..617e36dd44 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.33 +version: 0.0.34 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 7d0a298b0b7b7d55eb42e0ccf8265aa5170ca75f Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 2 Nov 2015 13:05:05 -0800 Subject: [PATCH 182/188] start a daemon command wrap up first daemon implementation --- packages/flutter_tools/lib/executable.dart | 4 +- .../lib/src/commands/daemon.dart | 214 ++++++++++++++++++ packages/flutter_tools/lib/src/device.dart | 6 +- packages/flutter_tools/test/all.dart | 2 + packages/flutter_tools/test/daemon_test.dart | 80 +++++++ packages/flutter_tools/test/init_test.dart | 4 +- packages/flutter_tools/test/list_test.dart | 1 - .../flutter_tools/tool/daemon_client.dart | 49 ++++ 8 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 packages/flutter_tools/lib/src/commands/daemon.dart create mode 100644 packages/flutter_tools/test/daemon_test.dart create mode 100644 packages/flutter_tools/tool/daemon_client.dart diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 64458eea8d..276ec02294 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -11,6 +11,7 @@ import 'package:stack_trace/stack_trace.dart'; import 'src/commands/build.dart'; import 'src/commands/cache.dart'; +import 'src/commands/daemon.dart'; import 'src/commands/flutter_command_runner.dart'; import 'src/commands/init.dart'; import 'src/commands/install.dart'; @@ -28,7 +29,7 @@ import 'src/process.dart'; /// This function is intended to be used from the [flutter] command line tool. Future main(List args) async { // This level can be adjusted by users through the `--verbose` option. - Logger.root.level = Level.SEVERE; + Logger.root.level = Level.WARNING; Logger.root.onRecord.listen((LogRecord record) { if (record.level >= Level.WARNING) { stderr.writeln(record.message); @@ -44,6 +45,7 @@ Future main(List args) async { FlutterCommandRunner runner = new FlutterCommandRunner() ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) + ..addCommand(new DaemonCommand()) ..addCommand(new InitCommand()) ..addCommand(new InstallCommand()) ..addCommand(new ListCommand()) diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart new file mode 100644 index 0000000000..78872bd322 --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -0,0 +1,214 @@ +// 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 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; + +import 'flutter_command.dart'; +import 'start.dart'; +import 'stop.dart'; + +const String protocolVersion = '0.0.1'; + +/// A @domain annotation. +const String domain = 'domain'; + +/// A domain @command annotation. +const String command = 'command'; + +final Logger _logging = new Logger('sky_tools.daemon'); + +// TODO: Create a `device` domain in order to list devices and fire events when +// devices are added or removed. + +// TODO: Is this the best name? Server? Daemon? + +/// A server process command. This command will start up a long-lived server. +/// It reads JSON-RPC based commands from stdin, executes them, and returns +/// JSON-RPC based responses and events to stdout. +/// +/// It can be shutdown with a `daemon.shutdown` command (or by killing the +/// process). +class DaemonCommand extends FlutterCommand { + final String name = 'daemon'; + final String description = + 'Run a persistent, JSON-RPC based server to communicate with devices.'; + final String usageFooter = + '\nThis command is intended to be used by tooling environments that need ' + 'a programatic interface into launching Flutter applications.'; + + @override + Future runInProject() async { + print('Starting device daemon...'); + + Stream commandStream = stdin + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .where((String line) => line.startsWith('[{') && line.endsWith('}]')) + .map((String line) { + line = line.substring(1, line.length - 1); + return JSON.decode(line); + }); + + await downloadApplicationPackagesAndConnectToDevices(); + + Daemon daemon = new Daemon(commandStream, (Map command) { + stdout.writeln('[${JSON.encode(command)}]'); + }, daemonCommand: this); + + return daemon.onExit; + } +} + +typedef void DispatchComand(Map command); + +typedef Future CommandHandler(dynamic args); + +class Daemon { + final DispatchComand sendCommand; + final DaemonCommand daemonCommand; + + final Completer _onExitCompleter = new Completer(); + final Map _domains = {}; + + Daemon(Stream commandStream, this.sendCommand, {this.daemonCommand}) { + // Set up domains. + _registerDomain(new DaemonDomain(this)); + _registerDomain(new AppDomain(this)); + + // Start listening. + commandStream.listen( + (Map command) => _handleCommand(command), + onDone: () => _onExitCompleter.complete(0) + ); + } + + void _registerDomain(Domain domain) { + _domains[domain.name] = domain; + } + + Future get onExit => _onExitCompleter.future; + + void _handleCommand(Map command) { + // {id, event, params} + var id = command['id']; + + if (id == null) { + _logging.severe('no id for command: ${command}'); + return; + } + + try { + String event = command['event']; + if (event.indexOf('.') == -1) + throw 'command not understood: ${event}'; + + String prefix = event.substring(0, event.indexOf('.')); + String name = event.substring(event.indexOf('.') + 1); + if (_domains[prefix] == null) + throw 'no domain for command: ${command}'; + + _domains[prefix].handleEvent(name, id, command['params']); + } catch (error, trace) { + _send({'id': id, 'error': _toJsonable(error)}); + _logging.warning('error handling ${command['event']}', error, trace); + } + } + + void _send(Map map) => sendCommand(map); + + void shutdown() { + if (!_onExitCompleter.isCompleted) + _onExitCompleter.complete(0); + } +} + +abstract class Domain { + final Daemon daemon; + final String name; + final Map _handlers = {}; + + Domain(this.daemon, this.name); + + void registerHandler(String name, CommandHandler handler) { + _handlers[name] = handler; + } + + String toString() => name; + + void handleEvent(String name, dynamic id, dynamic args) { + new Future.sync(() { + if (_handlers.containsKey(name)) + return _handlers[name](args); + throw 'command not understood: ${name}'; + }).then((result) { + if (result == null) { + _send({'id': id}); + } else { + _send({'id': id, 'result': _toJsonable(result)}); + } + }).catchError((error, trace) { + _send({'id': id, 'error': _toJsonable(error)}); + _logging.warning('error handling ${name}', error, trace); + }); + } + + void _send(Map map) => daemon._send(map); +} + +/// This domain responds to methods like [version] and [shutdown]. +@domain +class DaemonDomain extends Domain { + DaemonDomain(Daemon daemon) : super(daemon, 'daemon') { + registerHandler('version', version); + registerHandler('shutdown', shutdown); + } + + @command + Future version(dynamic args) { + return new Future.value(protocolVersion); + } + + @command + Future shutdown(dynamic args) { + Timer.run(() => daemon.shutdown()); + return new Future.value(); + } +} + +/// This domain responds to methods like [start] and [stopAll]. +/// +/// It'll be extended to fire events for when applications start, stop, and +/// log data. +@domain +class AppDomain extends Domain { + AppDomain(Daemon daemon) : super(daemon, 'app') { + registerHandler('start', start); + registerHandler('stopAll', stopAll); + } + + @command + Future start(dynamic args) { + // TODO: Add the ability to pass args: target, http, checked + StartCommand startComand = new StartCommand(); + startComand.inheritFromParent(daemon.daemonCommand); + return startComand.runInProject().then((_) => null); + } + + @command + Future stopAll(dynamic args) { + StopCommand stopCommand = new StopCommand(); + stopCommand.inheritFromParent(daemon.daemonCommand); + return stopCommand.stop(); + } +} + +dynamic _toJsonable(dynamic obj) { + if (obj is String || obj is int || obj is bool || obj is Map || obj is List || obj == null) + return obj; + return '${obj}'; +} diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 5fadb74871..4063e86167 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -886,8 +886,10 @@ class AndroidDevice extends Device { '-v', 'tag', // Only log the tag and the message '-s', - 'sky', - 'chromium', + 'sky:V', + 'chromium:D', + 'ActivityManager:W', + '*:F', ], prefix: 'android: '); } diff --git a/packages/flutter_tools/test/all.dart b/packages/flutter_tools/test/all.dart index 91450afa86..924f8485ff 100644 --- a/packages/flutter_tools/test/all.dart +++ b/packages/flutter_tools/test/all.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'android_device_test.dart' as android_device_test; +import 'daemon_test.dart' as daemon_test; import 'init_test.dart' as init_test; import 'install_test.dart' as install_test; import 'listen_test.dart' as listen_test; @@ -15,6 +16,7 @@ import 'trace_test.dart' as trace_test; main() { android_device_test.defineTests(); + daemon_test.defineTests(); init_test.defineTests(); install_test.defineTests(); listen_test.defineTests(); diff --git a/packages/flutter_tools/test/daemon_test.dart b/packages/flutter_tools/test/daemon_test.dart new file mode 100644 index 0000000000..352bb71f91 --- /dev/null +++ b/packages/flutter_tools/test/daemon_test.dart @@ -0,0 +1,80 @@ +// 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:mockito/mockito.dart'; +import 'package:sky_tools/src/commands/daemon.dart'; +import 'package:test/test.dart'; + +import 'src/mocks.dart'; + +main() => defineTests(); + +defineTests() { + group('daemon', () { + Daemon daemon; + + tearDown(() { + if (daemon != null) + return daemon.shutdown(); + }); + + test('daemon.version', () async { + StreamController commands = new StreamController(); + StreamController responses = new StreamController(); + daemon = new Daemon( + commands.stream, + (Map result) => responses.add(result) + ); + commands.add({'id': 0, 'event': 'daemon.version'}); + Map response = await responses.stream.first; + expect(response['id'], 0); + expect(response['result'], isNotEmpty); + expect(response['result'] is String, true); + }); + + test('daemon.shutdown', () async { + StreamController commands = new StreamController(); + StreamController responses = new StreamController(); + daemon = new Daemon( + commands.stream, + (Map result) => responses.add(result) + ); + commands.add({'id': 0, 'event': 'daemon.shutdown'}); + return daemon.onExit.then((int code) { + expect(code, 0); + }); + }); + + test('daemon.stopAll', () async { + DaemonCommand command = new DaemonCommand(); + applyMocksToCommand(command); + + StreamController commands = new StreamController(); + StreamController responses = new StreamController(); + daemon = new Daemon( + commands.stream, + (Map result) => responses.add(result), + daemonCommand: command + ); + + MockDeviceStore mockDevices = command.devices; + + when(mockDevices.android.isConnected()).thenReturn(true); + when(mockDevices.android.stopApp(any)).thenReturn(true); + + when(mockDevices.iOS.isConnected()).thenReturn(false); + when(mockDevices.iOS.stopApp(any)).thenReturn(false); + + when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); + when(mockDevices.iOSSimulator.stopApp(any)).thenReturn(false); + + commands.add({'id': 0, 'event': 'app.stopAll'}); + Map response = await responses.stream.first; + expect(response['id'], 0); + expect(response['result'], true); + }); + }); +} diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart index 4cb74fefe7..c14e4f8d4b 100644 --- a/packages/flutter_tools/test/init_test.dart +++ b/packages/flutter_tools/test/init_test.dart @@ -13,7 +13,7 @@ import 'package:test/test.dart'; main() => defineTests(); defineTests() { - group('', () { + group('init', () { Directory temp; setUp(() { @@ -28,7 +28,7 @@ defineTests() { // covered on the linux one. if (!Platform.isWindows) { // Verify that we create a project that is well-formed. - test('init flutter-simple', () async { + test('flutter-simple', () async { InitCommand command = new InitCommand(); CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); diff --git a/packages/flutter_tools/test/list_test.dart b/packages/flutter_tools/test/list_test.dart index 0b01b075ff..c2fec2183a 100644 --- a/packages/flutter_tools/test/list_test.dart +++ b/packages/flutter_tools/test/list_test.dart @@ -36,7 +36,6 @@ defineTests() { // Instead, cause the test to run the echo command. when(mockDevices.iOSSimulator.xcrunPath).thenReturn(mockCommand); - CommandRunner runner = new CommandRunner('test_flutter', '') ..addCommand(command); runner.run(['list']).then((int code) => expect(code, equals(0))); diff --git a/packages/flutter_tools/tool/daemon_client.dart b/packages/flutter_tools/tool/daemon_client.dart new file mode 100644 index 0000000000..bb21f2008e --- /dev/null +++ b/packages/flutter_tools/tool/daemon_client.dart @@ -0,0 +1,49 @@ +// 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:convert'; +import 'dart:io'; + +Process daemon; + +main() async { + daemon = await Process.start('dart', ['bin/sky_tools.dart', 'daemon']); + print('daemon process started, pid: ${daemon.pid}'); + + daemon.stdout + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .listen((String line) => print('<== ${line}')); + daemon.stderr.listen((data) => stderr.add(data)); + + stdout.write('> '); + stdin.transform(UTF8.decoder).transform(const LineSplitter()).listen((String line) { + if (line == 'version' || line == 'v') { + _send({'event': 'daemon.version'}); + } else if (line == 'shutdown' || line == 'q') { + _send({'event': 'daemon.shutdown'}); + } else if (line == 'start') { + _send({'event': 'app.start'}); + } else if (line == 'stopAll') { + _send({'event': 'app.stopAll'}); + } else { + print('command not understood: ${line}'); + } + stdout.write('> '); + }); + + daemon.exitCode.then((int code) { + print('daemon exiting (${code})'); + exit(code); + }); +} + +int id = 0; + +void _send(Map map) { + map['id'] = id++; + String str = '[${JSON.encode(map)}]'; + daemon.stdin.writeln(str); + print('==> ${str}'); +} From 3804107e2ddf94fddab8a461d321f58a4ef34180 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 3 Nov 2015 19:35:17 -0800 Subject: [PATCH 183/188] Enable --no-http by default We still have the --http option as a fallback for now. Once we're confident the --no-http version works, we'll drop the --http support. Also, create the FLX in a temp directory and then delete the temp directory when we're done. Finally, pull the Linux artifacts from the cloud storage bucket that the buildbot is uploading to. --- packages/flutter_tools/lib/src/artifacts.dart | 11 +++++----- .../flutter_tools/lib/src/commands/start.dart | 22 ++++++++++++++----- packages/flutter_tools/test/start_test.dart | 4 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 24d97b7555..e8309f6444 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -38,15 +38,16 @@ String _getNameForTargetPlatform(TargetPlatform platform) { } } -// Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50 +// Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/release_engine.py +// and https://github.com/flutter/buildbot/blob/master/travis/build.sh String _getCloudStorageBaseUrl({String category, String platform, String revision}) { - if (platform == 'darwin-x64') { + if (platform == 'android-arm') { // In the fullness of time, we'll have a consistent URL pattern for all of - // our artifacts, but, for the time being, darwin artifacts are stored in a + // our artifacts, but, for the time being, Android artifacts are stored in a // different cloud storage bucket. - return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/'; + return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/'; } - return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/'; + return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/'; } enum ArtifactType { diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index 2fb4701f42..d851593373 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -16,8 +16,8 @@ import 'install.dart'; import 'stop.dart'; final Logger _logging = new Logger('sky_tools.start'); -const String _localBundlePath = 'app.flx'; -const bool _kUseServer = true; +const String _localBundleName = 'app.flx'; +const String _localSnapshotName = 'snapshot_blob.bin'; class StartCommand extends FlutterCommand { final String name = 'start'; @@ -37,7 +37,6 @@ class StartCommand extends FlutterCommand { help: 'Target app path or filename to start.'); argParser.addFlag('http', negatable: true, - defaultsTo: true, help: 'Use a local HTTP server to serve your app to your device.'); argParser.addFlag('boot', help: 'Boot the iOS Simulator if it isn\'t already running.'); @@ -79,9 +78,20 @@ class StartCommand extends FlutterCommand { mainPath = path.join(target, 'lib', 'main.dart'); BuildCommand builder = new BuildCommand(); builder.inheritFromParent(this); - await builder.build(outputPath: _localBundlePath, mainPath: mainPath); - if (device.startBundle(package, _localBundlePath, poke, argResults['checked'])) - startedSomething = true; + + Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools'); + try { + String localBundlePath = path.join(tempDir.path, _localBundleName); + String localSnapshotPath = path.join(tempDir.path, _localSnapshotName); + await builder.build( + snapshotPath: localSnapshotPath, + outputPath: localBundlePath, + mainPath: mainPath); + if (device.startBundle(package, localBundlePath, poke, argResults['checked'])) + startedSomething = true; + } finally { + tempDir.deleteSync(recursive: true); + } } } else { if (await device.startApp(package)) diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart index f9719c5e82..f3868ccffb 100644 --- a/packages/flutter_tools/test/start_test.dart +++ b/packages/flutter_tools/test/start_test.dart @@ -21,7 +21,7 @@ defineTests() { when(mockDevices.android.isConnected()).thenReturn(true); when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(true); - when(mockDevices.android.startServer(any, any, any, any)).thenReturn(true); + when(mockDevices.android.startBundle(any, any, any, any)).thenReturn(true); when(mockDevices.android.stopApp(any)).thenReturn(true); when(mockDevices.iOS.isConnected()).thenReturn(false); @@ -49,7 +49,7 @@ defineTests() { when(mockDevices.android.isConnected()).thenReturn(false); when(mockDevices.android.isAppInstalled(any)).thenReturn(false); when(mockDevices.android.installApp(any)).thenReturn(false); - when(mockDevices.android.startServer(any, any, any, any)).thenReturn(false); + when(mockDevices.android.startBundle(any, any, any, any)).thenReturn(false); when(mockDevices.android.stopApp(any)).thenReturn(false); when(mockDevices.iOS.isConnected()).thenReturn(true); From 297e9c33fa338e39d50c57c3d2cd8edbc6096209 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 4 Nov 2015 08:57:33 -0800 Subject: [PATCH 184/188] Support Jelly Bean --- packages/flutter_tools/lib/src/device.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 4063e86167..3dfe573bfb 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -674,9 +674,9 @@ class AndroidDevice extends Device { _logging.severe('Unexpected response from getprop: "$sdkVersion"'); return false; } - if (sdkVersionParsed < 22) { + if (sdkVersionParsed < 19) { _logging.severe('Version "$sdkVersion" of the Android SDK is too old. ' - 'Please install Lollipop (version 22) or later.'); + 'Please install Jelly Bean (version 19) or later.'); return false; } return true; From 76c54f148b437d8a2fb04d487d4b6df43c0f8364 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 4 Nov 2015 09:07:29 -0800 Subject: [PATCH 185/188] Rev pubspec --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 617e36dd44..d13051339f 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.34 +version: 0.0.35 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors From 4ba8a7f077ae452e37bda5ca2dd60e44ba0d2c62 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Wed, 4 Nov 2015 14:08:24 -0500 Subject: [PATCH 186/188] Seed the RNG before signing the package. --- packages/flutter_tools/lib/src/commands/build.dart | 2 ++ packages/flutter_tools/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 2fb9541621..1d47dfc7d4 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -182,6 +182,8 @@ class BuildCommand extends FlutterCommand { archive.addFile(file); } + await CipherParameters.get().seedRandom(); + AsymmetricKeyPair keyPair = keyPairFromPrivateKeyFileSync(privateKeyPath); Uint8List zipBytes = new Uint8List.fromList(new ZipEncoder().encode(archive)); Bundle bundle = new Bundle.fromContent( diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index d13051339f..0b631c68d6 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.35 +version: 0.0.36 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors @@ -11,7 +11,7 @@ dependencies: analyzer: ">=0.26.1+17" # see note below archive: ^1.0.20 args: ^0.13.0 - flx: ">=0.0.5 <0.1.0" + flx: ">=0.0.7 <0.1.0" crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 From b4fa3fbbf0da34b3f2ea526e8edec983cae976d4 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 4 Nov 2015 14:25:47 -0800 Subject: [PATCH 187/188] Return to the mojo artifacts for Linux --- packages/flutter_tools/lib/src/artifacts.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index e8309f6444..bc2a8502e6 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -41,13 +41,13 @@ String _getNameForTargetPlatform(TargetPlatform platform) { // Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/release_engine.py // and https://github.com/flutter/buildbot/blob/master/travis/build.sh String _getCloudStorageBaseUrl({String category, String platform, String revision}) { - if (platform == 'android-arm') { + if (platform == 'darwin-x64') { // In the fullness of time, we'll have a consistent URL pattern for all of - // our artifacts, but, for the time being, Android artifacts are stored in a + // our artifacts, but, for the time being, Mac OS X artifacts are stored in a // different cloud storage bucket. - return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/'; + return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/'; } - return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/'; + return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/'; } enum ArtifactType { From 01a6356230cdaff5b3c46a262fec6e4f3a68cc63 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 4 Nov 2015 14:26:07 -0800 Subject: [PATCH 188/188] Rev pubspec --- packages/flutter_tools/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 0b631c68d6..1ee0e87690 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: sky_tools -version: 0.0.36 +version: 0.0.37 description: Tools for building Flutter applications homepage: http://flutter.io author: Flutter Authors