diff --git a/bin/flutter b/bin/flutter index 5ea94104d6f..a83fb57d173 100755 --- a/bin/flutter +++ b/bin/flutter @@ -14,6 +14,9 @@ DART=dart if [ "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]; then (cd "$FLUTTER_TOOLS_DIR"; pub get) + if [ -f "$SNAPSHOT_PATH" ]; then + rm "$SNAPSHOT_PATH" + fi fi REVISION=`(cd "$FLUTTER_ROOT"; git rev-parse HEAD)` diff --git a/packages/flutter_tools/bin/sky_server.dart b/packages/flutter_tools/bin/sky_server.dart deleted file mode 100644 index 04bb6e960c1..00000000000 --- a/packages/flutter_tools/bin/sky_server.dart +++ /dev/null @@ -1,85 +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:io'; - -import 'package:args/args.dart'; -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'); - print(parser.usage); -} - -void addRoute(var router, String route, String path) { - router.add( - route, - ['GET', 'HEAD'], - 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); - - if (args['help'] || args.rest.length != 1) { - printUsage(parser); - return; - } - - int port; - try { - port = int.parse(args.rest[0]); - } catch(e) { - printUsage(parser); - return; - } - - 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); - - 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); - exit(1); - } - - server.defaultResponseHeaders - ..removeAll('x-content-type-options') - ..removeAll('x-frame-options') - ..removeAll('x-xss-protection') - ..add('cache-control', 'no-store'); -} diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 1d47dfc7d4a..caee7e6939f 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -9,6 +9,7 @@ import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:flx/bundle.dart'; import 'package:flx/signing.dart'; +import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart'; import '../toolchain.dart'; @@ -147,6 +148,27 @@ class BuildCommand extends FlutterCommand { ); } + Future buildInTempDir({ + String mainPath: _kDefaultMainPath, + void onBundleAvailable(String bundlePath) + }) async { + int result; + Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools'); + try { + String localBundlePath = path.join(tempDir.path, 'app.flx'); + String localSnapshotPath = path.join(tempDir.path, 'snapshot_blob.bin'); + result = await build( + snapshotPath: localSnapshotPath, + outputPath: localBundlePath, + mainPath: mainPath + ); + onBundleAvailable(localBundlePath); + } finally { + tempDir.deleteSync(recursive: true); + } + return result; + } + Future build({ String assetBase: _kDefaultAssetBase, String mainPath: _kDefaultMainPath, diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart index 93bd19e6bf0..e384e85259c 100644 --- a/packages/flutter_tools/lib/src/commands/listen.dart +++ b/packages/flutter_tools/lib/src/commands/listen.dart @@ -34,7 +34,6 @@ 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 @@ -53,25 +52,26 @@ class ListenCommand extends FlutterCommand { BuildCommand builder = new BuildCommand(); builder.inheritFromParent(this); - builder.build(outputPath: _localFlutterBundle); - - 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); + await builder.buildInTempDir( + onBundleAvailable: (String localBundlePath) { + for (Device device in devices.all) { + ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); + if (package == null || !device.isConnected()) + continue; + if (device is AndroidDevice) { + device.startBundle(package, localBundlePath, true, argResults['checked']); + } else if (device is IOSDevice) { + device.pushFile(package, localBundlePath, _remoteFlutterBundle); + } else if (device is IOSSimulator) { + // TODO(abarth): Move pushFile up to Device once Android supports + // pushing new bundles. + device.pushFile(package, localBundlePath, _remoteFlutterBundle); + } else { + assert(false); + } + } } - } + ); if (singleRun || !watchDirectory()) break; diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index d8515933730..90a588de213 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -16,8 +16,6 @@ import 'install.dart'; import 'stop.dart'; final Logger _logging = new Logger('sky_tools.start'); -const String _localBundleName = 'app.flx'; -const String _localSnapshotName = 'snapshot_blob.bin'; class StartCommand extends FlutterCommand { final String name = 'start'; @@ -35,9 +33,6 @@ class StartCommand extends FlutterCommand { defaultsTo: '.', abbr: 't', help: 'Target app path or filename to start.'); - argParser.addFlag('http', - negatable: 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.'); } @@ -69,30 +64,18 @@ class StartCommand extends FlutterCommand { continue; if (device is AndroidDevice) { String target = path.absolute(argResults['target']); - 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); - - 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); + String mainPath = target; + if (FileSystemEntity.isDirectorySync(target)) + mainPath = path.join(target, 'lib', 'main.dart'); + BuildCommand builder = new BuildCommand(); + builder.inheritFromParent(this); + await builder.buildInTempDir( + mainPath: mainPath, + onBundleAvailable: (String localBundlePath) { if (device.startBundle(package, localBundlePath, poke, argResults['checked'])) startedSomething = true; - } finally { - tempDir.deleteSync(recursive: 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 c968e3c8590..c3fa641bf97 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -3,9 +3,7 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; import 'dart:io'; -import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:logging/logging.dart'; @@ -13,7 +11,6 @@ import 'package:path/path.dart' as path; import 'application_package.dart'; import 'build_configuration.dart'; -import 'os_utils.dart'; import 'process.dart'; final Logger _logging = new Logger('sky_tools.device'); @@ -507,14 +504,10 @@ class IOSSimulator extends Device { class AndroidDevice extends Device { static const String _ADB_PATH = 'adb'; static const int _observatoryPort = 8181; - static const int _serverPort = 9888; 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; @@ -718,13 +711,6 @@ 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()) { @@ -793,81 +779,16 @@ class AndroidDevice extends Device { return true; } - Future startServer( - String target, bool poke, bool checked, AndroidApk apk) async { - String serverRoot = ''; - String mainDart = ''; - String missingMessage = ''; - if (FileSystemEntity.isDirectorySync(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 (!FileSystemEntity.isFileSync(mainDart)) { - _logging.severe(missingMessage); - return false; - } - - if (!poke) { - _forwardObservatoryPort(); - - // Actually start the server. - Process server = await Process.start( - sdkBinaryName('pub'), ['run', 'sky_tools:sky_server', _serverPort.toString()], - 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. - String serverPortString = 'tcp:$_serverPort'; - runCheckedSync(adbCommandForDevice(['reverse', serverPortString, serverPortString])); - } - - String relativeDartMain = _convertToURL(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 = adbCommandForDevice([ - 'shell', 'am', 'start', - '-a', 'android.intent.action.VIEW', - '-d', url, - ]); - if (checked) - cmd.addAll(['--ez', 'enable-checked-mode', 'true']); - cmd.add(apk.launchActivity); - runCheckedSync(cmd); - return true; - } - @override Future startApp(ApplicationPackage app) async { - // Android currently has to be started with startServer(...). + // Android currently has to be started with startBundle(...). assert(false); return false; } Future stopApp(ApplicationPackage app) async { final AndroidApk apk = app; - - // Turn off reverse port forwarding - runSync(adbCommandForDevice(['reverse', '--remove', 'tcp:$_serverPort'])); - // Stop the app runSync(adbCommandForDevice(['shell', 'am', 'force-stop', apk.id])); - - // Kill the server - 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 index caf124d0a9c..d2006525690 100644 --- a/packages/flutter_tools/lib/src/os_utils.dart +++ b/packages/flutter_tools/lib/src/os_utils.dart @@ -6,8 +6,6 @@ import 'dart:io'; import 'package:logging/logging.dart'; -import 'process.dart'; - final OperatingSystemUtils osUtils = new OperatingSystemUtils._(); final Logger _logging = new Logger('sky_tools.os'); @@ -16,21 +14,16 @@ abstract class OperatingSystemUtils { factory OperatingSystemUtils._() { if (Platform.isWindows) { return new _WindowsUtils(); - } else if (Platform.isMacOS) { - return new _MacUtils(); } else { - return new _LinuxUtils(); + return new _PosixUtils(); } } /// 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 { +class _PosixUtils implements OperatingSystemUtils { ProcessResult makeExecutable(File file) { return Process.runSync('chmod', ['u+x', file.path]); } @@ -41,51 +34,4 @@ class _WindowsUtils implements OperatingSystemUtils { 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/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 1ee0e876900..37c85beb092 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -15,9 +15,6 @@ dependencies: crypto: ^0.9.1 mustache4dart: ^1.0.0 path: ^1.3.0 - 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 diff --git a/packages/flutter_tools/test/os_utils_test.dart b/packages/flutter_tools/test/os_utils_test.dart index 999fc9cc5a8..c38e6386b6d 100644 --- a/packages/flutter_tools/test/os_utils_test.dart +++ b/packages/flutter_tools/test/os_utils_test.dart @@ -34,33 +34,5 @@ defineTests() { 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); - }); }); }