diff --git a/bin/flutter b/bin/flutter index 9d96ad347d..f879c22fa4 100755 --- a/bin/flutter +++ b/bin/flutter @@ -12,16 +12,10 @@ SCRIPT_PATH="$FLUTTER_TOOLS_DIR/bin/flutter_tools.dart" # TODO(abarth): We shouldn't require dart to be on the user's path. 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)` - -if [ ! -f "$SNAPSHOT_PATH" ] || [ ! -f "$STAMP_PATH" ] || [ `cat "$STAMP_PATH"` != $REVISION ]; then +if [ ! -f "$SNAPSHOT_PATH" ] || [ ! -f "$STAMP_PATH" ] || [ `cat "$STAMP_PATH"` != $REVISION ] || [ "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]; then + echo Updating flutter tool... + (cd "$FLUTTER_TOOLS_DIR"; pub get) $DART --snapshot="$SNAPSHOT_PATH" --package-root="$FLUTTER_TOOLS_DIR/packages" "$SCRIPT_PATH" echo -n $REVISION > "$STAMP_PATH" fi diff --git a/bin/flutter.bat b/bin/flutter.bat index 2cb79e220f..4b0ebbb5d7 100644 --- a/bin/flutter.bat +++ b/bin/flutter.bat @@ -29,6 +29,7 @@ GOTO :after_snapshot :do_snapshot CD "%flutter_tools_dir%" +ECHO Updating flutter tool... CALL pub.bat get CD "%flutter_root%" CALL %dart% --snapshot="%snapshot_path%" --package-root="%flutter_tools_dir%\packages" "%script_path%" @@ -42,4 +43,4 @@ CALL %dart% "%snapshot_path%" %* IF /I "%ERRORLEVEL%" EQU "253" ( CALL %dart% --snapshot="%snapshot_path%" --package-root="%flutter_tools_dir%\packages" "%script_path%" CALL %dart% "%snapshot_path%" %* -) \ No newline at end of file +) diff --git a/examples/fitness/lib/main.dart b/examples/fitness/lib/main.dart index a8feff0c02..029b877acd 100644 --- a/examples/fitness/lib/main.dart +++ b/examples/fitness/lib/main.dart @@ -124,7 +124,7 @@ class FitnessAppState extends State { }); } - Widget build(BuildContext) { + Widget build(BuildContext context) { return new MaterialApp( theme: new ThemeData( brightness: ThemeBrightness.light, diff --git a/examples/game/example_effect_line.dart b/examples/game/example_effect_line.dart index c34ebebf75..090f50831a 100644 --- a/examples/game/example_effect_line.dart +++ b/examples/game/example_effect_line.dart @@ -38,7 +38,7 @@ final ThemeData _theme = new ThemeData( ); class TestAppState extends State { - TestApp() { + TestAppState() { _testBed = new TestBed(_labelTexts[_selectedLine]); } diff --git a/examples/widgets/tabs.dart b/examples/widgets/tabs.dart index c34dab9ea2..cd9c0679e0 100644 --- a/examples/widgets/tabs.dart +++ b/examples/widgets/tabs.dart @@ -44,10 +44,10 @@ class TabbedNavigatorAppState extends State { TabNavigator _buildIconLabelsTabNavigator(int n) { Iterable views = ["event", "home", "android", "alarm", "face", "language"] - .map((icon_name) { + .map((String iconName) { return new TabNavigatorView( - label: new TabLabel(icon: "action/$icon_name"), - builder: (BuildContext context) => _buildContent(icon_name) + label: new TabLabel(icon: "action/$iconName"), + builder: (BuildContext context) => _buildContent(iconName) ); }); return _buildTabNavigator(n, views.toList(), const ValueKey('iconLabelsTabNavigator')); diff --git a/packages/cassowary/lib/constraint.dart b/packages/cassowary/lib/constraint.dart index 83a5946cc2..97820d484a 100644 --- a/packages/cassowary/lib/constraint.dart +++ b/packages/cassowary/lib/constraint.dart @@ -21,20 +21,20 @@ class Constraint { switch (relation) { case Relation.equalTo: - buffer.write(" == 0 "); + buffer.write(' == 0 '); break; case Relation.greaterThanOrEqualTo: - buffer.write(" >= 0 "); + buffer.write(' >= 0 '); break; case Relation.lessThanOrEqualTo: - buffer.write(" <= 0 "); + buffer.write(' <= 0 '); break; } - buffer.write(" | priority = ${priority}"); + buffer.write(' | priority = $priority'); if (priority == Priority.required) { - buffer.write(" (required)"); + buffer.write(' (required)'); } return buffer.toString(); diff --git a/packages/cassowary/lib/expression.dart b/packages/cassowary/lib/expression.dart index 0c7f1e9ce2..44fee17512 100644 --- a/packages/cassowary/lib/expression.dart +++ b/packages/cassowary/lib/expression.dart @@ -58,7 +58,7 @@ class Expression extends _EquationMember { _createConstraint(value, Relation.lessThanOrEqualTo); operator ==(_EquationMember value) => - _createConstraint(value, Relation.equalTo); + _createConstraint(value, Relation.equalTo); // analyzer says "Type check failed" // analyzer says "The return type 'Constraint' is not a 'bool', as defined by the method '=='" Expression operator +(_EquationMember m) { if (m is ConstantMember) { @@ -140,7 +140,7 @@ class Expression extends _EquationMember { if (args == null) { throw new ParserException( - "Could not find constant multiplicand or multiplier", [this, m]); + 'Could not find constant multiplicand or multiplier', [this, m]); return null; } @@ -150,7 +150,7 @@ class Expression extends _EquationMember { _EquationMember operator /(_EquationMember m) { if (!m.isConstant) { throw new ParserException( - "The divisor was not a constant expression", [this, m]); + 'The divisor was not a constant expression', [this, m]); return null; } @@ -160,10 +160,10 @@ class Expression extends _EquationMember { String toString() { StringBuffer buffer = new StringBuffer(); - terms.forEach((t) => buffer.write("${t}")); + terms.forEach((t) => buffer.write('$t')); if (constant != 0.0) { - buffer.write(constant.sign > 0.0 ? "+" : "-"); + buffer.write(constant.sign > 0.0 ? '+' : '-'); buffer.write(constant.abs()); } diff --git a/packages/cassowary/lib/solver.dart b/packages/cassowary/lib/solver.dart index 249560def1..3569c7b277 100644 --- a/packages/cassowary/lib/solver.dart +++ b/packages/cassowary/lib/solver.dart @@ -410,10 +410,10 @@ class Solver { double temp = row.coefficientForSymbol(entering); if (temp < 0.0) { - double temp_ratio = -row.constant / temp; + double tempRatio = -row.constant / temp; - if (temp_ratio < ratio) { - ratio = temp_ratio; + if (tempRatio < ratio) { + ratio = tempRatio; result.first = symbol; result.second = row; } diff --git a/packages/cassowary/lib/symbol.dart b/packages/cassowary/lib/symbol.dart index cd7bcf491c..ab0531adb7 100644 --- a/packages/cassowary/lib/symbol.dart +++ b/packages/cassowary/lib/symbol.dart @@ -13,24 +13,24 @@ class _Symbol { _Symbol(this.type, this.tick); String toString() { - String typeString = "unknown"; + String typeString = 'unknown'; switch (type) { case _SymbolType.invalid: - typeString = "i"; + typeString = 'i'; break; case _SymbolType.external: - typeString = "v"; + typeString = 'v'; break; case _SymbolType.slack: - typeString = "s"; + typeString = 's'; break; case _SymbolType.error: - typeString = "e"; + typeString = 'e'; break; case _SymbolType.dummy: - typeString = "d"; + typeString = 'd'; break; } - return "${typeString}${tick}"; + return '$typeString$tick'; } } diff --git a/packages/cassowary/lib/variable.dart b/packages/cassowary/lib/variable.dart index 93850e716e..1b278fc45b 100644 --- a/packages/cassowary/lib/variable.dart +++ b/packages/cassowary/lib/variable.dart @@ -21,7 +21,7 @@ class Variable { return res; } - String get debugName => _elvis(name, "variable${_tick}"); + String get debugName => _elvis(name, 'variable$_tick'); String toString() => debugName; } diff --git a/packages/flutter/lib/src/http/http.dart b/packages/flutter/lib/src/http/http.dart index d6b492f085..e70a70451c 100644 --- a/packages/flutter/lib/src/http/http.dart +++ b/packages/flutter/lib/src/http/http.dart @@ -114,7 +114,7 @@ Future read(url) => Future readBytes(url) => _withClient((client) => client.readBytes(url)); -Future _withClient(Future fn(MojoClient)) { +Future _withClient(Future fn(MojoClient client)) { var client = new MojoClient(); var future = fn(client); return future.whenComplete(client.close); diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index c69da99db1..bd5fbd43f9 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -132,7 +132,11 @@ class _PointerEventConverter { return 'mouse'; case PointerKind.STYLUS: return 'stylus'; + case PointerKind.INVERTED_STYLUS: + return 'invertedStylus'; } + assert(false); + return ''; } } diff --git a/packages/flutter_sprites/lib/src/sound.dart b/packages/flutter_sprites/lib/src/sound.dart index aadf317143..1ebd43d40a 100644 --- a/packages/flutter_sprites/lib/src/sound.dart +++ b/packages/flutter_sprites/lib/src/sound.dart @@ -3,7 +3,7 @@ part of flutter_sprites; // TODO: The sound effects should probably use Android's SoundPool instead of // MediaPlayer as it is more efficient and flexible for playing back sound effects -typedef void SoundEffectStreamCallback(SoundEffectStream); +typedef void SoundEffectStreamCallback(SoundEffectStream stream); class SoundEffect { SoundEffect(this._pipeFuture); @@ -137,8 +137,8 @@ class SoundEffectPlayer { } } -typedef void SoundTrackCallback(SoundTrack); -typedef void SoundTrackBufferingCallback(SoundTrack, int); +typedef void SoundTrackCallback(SoundTrack soundTrack); +typedef void SoundTrackBufferingCallback(SoundTrack soundTrack, int index); class SoundTrack { MediaPlayerProxy _player; diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 39b4e26910..44cabd491b 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -9,6 +9,7 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; import 'package:stack_trace/stack_trace.dart'; +import 'src/commands/analyze.dart'; import 'src/commands/build.dart'; import 'src/commands/cache.dart'; import 'src/commands/daemon.dart'; @@ -46,6 +47,7 @@ Future main(List args) async { }); FlutterCommandRunner runner = new FlutterCommandRunner() + ..addCommand(new AnalyzeCommand()) ..addCommand(new BuildCommand()) ..addCommand(new CacheCommand()) ..addCommand(new DaemonCommand()) diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 6310851bbf..c0e49388f6 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -48,9 +48,9 @@ String _getCloudStorageBaseUrl({String category, String platform, String revisio // In the fullness of time, we'll have a consistent URL pattern for all of // 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_infra/flutter/${platform}/${revision}/'; + 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/'; } enum ArtifactType { diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart new file mode 100644 index 0000000000..f4fa293240 --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/analyze.dart @@ -0,0 +1,302 @@ +// 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 'package:path/path.dart' as path; + +import '../artifacts.dart'; +import '../build_configuration.dart'; +import '../process.dart'; +import 'flutter_command.dart'; + +final Logger _logging = new Logger('sky_tools.analyze'); + +class AnalyzeCommand extends FlutterCommand { + String get name => 'analyze'; + String get description => 'Runs a carefully configured dartanalyzer over the current project\'s dart code.'; + + AnalyzeCommand() { + argParser.addFlag('flutter-repo', help: 'Include all the examples and tests from the Flutter repository.', defaultsTo: false); + argParser.addFlag('current-directory', help: 'Include all the Dart files in the current directory, if any.', defaultsTo: true); + argParser.addFlag('current-package', help: 'Include the lib/main.dart file from the current directory, if any.', defaultsTo: true); + argParser.addFlag('congratulate', help: 'Show output even when there are no errors, warnings, hints, or lints.', defaultsTo: true); + } + + bool get requiresProjectRoot => false; + + @override + Future runInProject() async { + Set pubSpecDirectories = new Set(); + List dartFiles = argResults.rest.toList(); + + for (String file in dartFiles) { + // TODO(ianh): figure out how dartanalyzer decides which .packages file to use when given a random file + pubSpecDirectories.add(path.dirname(file)); + } + + if (argResults['flutter-repo']) { + // .../examples/*/*.dart + // .../examples/*/lib/main.dart + Directory examples = new Directory(path.join(ArtifactStore.flutterRoot, 'examples')); + for (FileSystemEntity entry in examples.listSync()) { + if (entry is Directory) { + bool foundOne = false; + for (FileSystemEntity subentry in entry.listSync()) { + if (subentry is File && subentry.path.endsWith('.dart')) { + dartFiles.add(subentry.path); + foundOne = true; + } else if (subentry is Directory && path.basename(subentry.path) == 'lib') { + String mainPath = path.join(subentry.path, 'main.dart'); + if (FileSystemEntity.isFileSync(mainPath)) { + dartFiles.add(mainPath); + foundOne = true; + } + } + } + if (foundOne) + pubSpecDirectories.add(entry.path); + } + } + + bool foundTest = false; + Directory flutterDir = new Directory(path.join(ArtifactStore.flutterRoot, 'packages/unit')); // See https://github.com/flutter/flutter/issues/50 + + // .../packages/unit/test/*/*_test.dart + Directory tests = new Directory(path.join(flutterDir.path, 'test')); + for (FileSystemEntity entry in tests.listSync()) { + if (entry is Directory) { + for (FileSystemEntity subentry in entry.listSync()) { + if (subentry is File && subentry.path.endsWith('_test.dart')) { + dartFiles.add(subentry.path); + foundTest = true; + } + } + } + } + + // .../packages/unit/benchmark/*/*_bench.dart + Directory benchmarks = new Directory(path.join(flutterDir.path, 'benchmark')); + for (FileSystemEntity entry in benchmarks.listSync()) { + if (entry is Directory) { + for (FileSystemEntity subentry in entry.listSync()) { + if (subentry is File && subentry.path.endsWith('_bench.dart')) { + dartFiles.add(subentry.path); + foundTest = true; + } + } + } + } + + if (foundTest) + pubSpecDirectories.add(flutterDir.path); + + // .../packages/*/bin/*.dart + Directory packages = new Directory(path.join(ArtifactStore.flutterRoot, 'packages')); + for (FileSystemEntity entry in packages.listSync()) { + if (entry is Directory) { + bool foundOne = false; + Directory binDirectory = new Directory(path.join(entry.path, 'bin')); + if (binDirectory.existsSync()) { + for (FileSystemEntity subentry in binDirectory.listSync()) { + if (subentry is File && subentry.path.endsWith('.dart')) { + dartFiles.add(subentry.path); + foundOne = true; + } + } + } + if (foundOne) + pubSpecDirectories.add(entry.path); + } + } + } + + if (argResults['current-directory']) { + // ./*.dart + Directory currentDirectory = new Directory('.'); + bool foundOne = false; + for (FileSystemEntity entry in currentDirectory.listSync()) { + if (entry is File && entry.path.endsWith('.dart')) { + dartFiles.add(entry.path); + foundOne = true; + } + } + if (foundOne) + pubSpecDirectories.add('.'); + } + + if (argResults['current-package']) { + // ./lib/main.dart + String mainPath = 'lib/main.dart'; + if (FileSystemEntity.isFileSync(mainPath)) { + dartFiles.add(mainPath); + pubSpecDirectories.add('.'); + } + } + + // prepare a Dart file that references all the above Dart files + StringBuffer mainBody = new StringBuffer(); + for (int index = 0; index < dartFiles.length; index += 1) { + mainBody.writeln('import \'${path.normalize(path.absolute(dartFiles[index]))}\' as file$index;'); + } + mainBody.writeln('void main() { }'); + + // prepare a union of all the .packages files + Map packages = {}; + bool hadInconsistentRequirements = false; + for (Directory directory in pubSpecDirectories.map((path) => new Directory(path))) { + File dotPackages = new File(path.join(directory.path, '.packages')); + if (dotPackages.existsSync()) { + Map dependencies = {}; + dotPackages + .readAsStringSync() + .split('\n') + .where((line) => !line.startsWith(new RegExp(r'^ *#'))) + .forEach((line) { + int colon = line.indexOf(':'); + if (colon > 0) + dependencies[line.substring(0, colon)] = path.normalize(path.absolute(directory.path, path.fromUri(line.substring(colon+1)))); + }); + for (String package in dependencies.keys) { + if (packages.containsKey(package)) { + if (packages[package] != dependencies[package]) { + _logging.warning('Inconsistent requirements for $package; using ${packages[package]} (and not ${dependencies[package]}).'); + hadInconsistentRequirements = true; + } + } else { + packages[package] = dependencies[package]; + } + } + } + } + if (hadInconsistentRequirements) { + if (argResults['flutter-repo']) + _logging.warning('You may need to run "dart ${path.normalize(path.relative(path.join(ArtifactStore.flutterRoot, 'dev/update_packages.dart')))}".'); + if (argResults['current-directory'] || argResults['current-package']) + _logging.warning('You may need to run "pub get".'); + } + + String buildDir = buildConfigurations.firstWhere((BuildConfiguration config) => config.testable, orElse: () => null)?.buildDir; + if (buildDir != null) { + packages['sky_engine'] = path.join(buildDir, 'gen/dart-pkg/sky_engine/lib'); + packages['sky_services'] = path.join(buildDir, 'gen/dart-pkg/sky_services/lib'); + } + + StringBuffer packagesBody = new StringBuffer(); + for (String package in packages.keys) + packagesBody.writeln('$package:${path.toUri(packages[package])}'); + + // save the Dart file and the .packages file to disk + Directory host = Directory.systemTemp.createTempSync('flutter-analyze-'); + File mainFile = new File(path.join(host.path, 'main.dart'))..writeAsStringSync(mainBody.toString()); + File packagesFile = new File(path.join(host.path, '.packages'))..writeAsStringSync(packagesBody.toString()); + + List cmd = [ + sdkBinaryName('dartanalyzer'), + // do not set '--warnings', since that will include the entire Dart SDK + '--ignore-unrecognized-flags', + '--supermixin', + '--enable-strict-call-checks', + '--enable_type_checks', + '--strong', + '--package-warnings', + '--fatal-warnings', + '--strong-hints', + '--fatal-hints', + '--lints', + '--packages', packagesFile.path, + mainFile.path + ]; + + _logging.info(cmd.join(' ')); + Process process = await Process.start( + cmd[0], + cmd.sublist(1), + workingDirectory: host.path + ); + int errorCount = 0; + StringBuffer output = new StringBuffer(); + process.stdout.transform(UTF8.decoder).listen((String data) { + output.write(data); + }); + process.stderr.transform(UTF8.decoder).listen((String data) { + // dartanalyzer doesn't seem to ever output anything on stderr + errorCount += 1; + print(data); + }); + + // host.deleteSync(); + + int exitCode = await process.exitCode; + + List patternsToSkip = [ + 'Analyzing [${mainFile.path}]...', + new RegExp('^\\[hint\\] Unused import \\(${mainFile.path},'), + new RegExp(r'^\[.+\] .+ \(.+/\.pub-cache/.+'), + new RegExp(r'^\[error\] Invalid override\. The type of [^ ]+ \(.+\) is not a subtype of [^ ]+ \(.+\)\.'), // we allow type narrowing + new RegExp(r'^\[warning\] .+ will need runtime check to cast to type .+'), // https://github.com/dart-lang/sdk/issues/24542 + new RegExp(r'^\[error\] Type check failed: .*\(dynamic\) is not of type'), // allow unchecked casts from dynamic + new RegExp('^\\[error\\] Target of URI does not exist: \'dart:ui_internals\''), // https://github.com/flutter/flutter/issues/83 + new RegExp(r'\[lint\] Prefer using lowerCamelCase for constant names.'), // sometimes we have no choice (e.g. when matching other platforms) + new RegExp(r'\[lint\] Avoid defining a one-member abstract class when a simple function will do.'), // too many false-positives; code review should catch real instances + new RegExp(r'\[0-9]+ (error|warning|hint|lint).+found\.'), + '', + ]; + + RegExp generalPattern = new RegExp(r'^\[(error|warning|hint|lint)\] (.+) \(([^(),]+), line ([0-9]+), col ([0-9]+)\)$'); + RegExp ignorePattern = new RegExp(r'// analyzer says "([^"]+)"'); + RegExp constructorTearOffsPattern = new RegExp('.+#.+// analyzer doesn\'t like constructor tear-offs'); + RegExp allowedIdentifiers = new RegExp(r'_?([A-Z]|_+)\b'); + + List errorLines = output.toString().split('\n'); + for (String errorLine in errorLines) { + if (patternsToSkip.every((Pattern pattern) => pattern.allMatches(errorLine).isEmpty)) { + Match groups = generalPattern.firstMatch(errorLine); + if (groups != null) { + String level = groups[1]; + String filename = groups[3]; + String errorMessage = groups[2]; + int lineNumber = int.parse(groups[4]); + int colNumber = int.parse(groups[5]); + File source = new File(filename); + List sourceLines = source.readAsLinesSync(); + String sourceLine = sourceLines[lineNumber-1]; + bool shouldIgnore = false; + if (filename.endsWith('.mojom.dart')) { + shouldIgnore = true; + } else if (level == 'lint' && errorMessage == 'Name non-constant identifiers using lowerCamelCase.') { + if (allowedIdentifiers.matchAsPrefix(sourceLine, colNumber-1) != null) + shouldIgnore = true; + } else if (constructorTearOffsPattern.allMatches(sourceLine).isNotEmpty) { + shouldIgnore = true; + } else { + Iterable ignoreGroups = ignorePattern.allMatches(sourceLine); + for (Match ignoreGroup in ignoreGroups) { + if (errorMessage.contains(ignoreGroup[1])) { + shouldIgnore = true; + break; + } + } + } + if (shouldIgnore) + continue; + } + print(errorLine); + errorCount += 1; + } + } + + if (exitCode < 0 || exitCode > 3) // 1 = hints, 2 = warnings, 3 = errors + return exitCode; + + if (errorCount > 1) + return 1; + if (argResults['congratulate']) + print('No analyzer warnings!'); + return 0; + } +} \ No newline at end of file diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index caee7e6939..b892c10d59 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -93,7 +93,7 @@ dynamic _loadManifest(String manifestPath) { } ArchiveFile _createFile(String key, String assetBase) { - File file = new File('${assetBase}/${key}'); + File file = new File('$assetBase/$key'); if (!file.existsSync()) return null; List content = file.readAsBytesSync(); diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index cd82c5fab0..e39129699d 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -60,7 +60,7 @@ class DaemonCommand extends FlutterCommand { stdout.writeln('[${JSON.encode(command)}]'); }, daemonCommand: this); - return daemon.onExit; + return await daemon.onExit; } } @@ -98,19 +98,19 @@ class Daemon { var id = command['id']; if (id == null) { - _logging.severe('no id for command: ${command}'); + _logging.severe('no id for command: $command'); return; } try { String event = command['event']; if (event.indexOf('.') == -1) - throw 'command not understood: ${event}'; + 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}'; + throw 'no domain for command: $command'; _domains[prefix].handleEvent(name, id, command['params']); } catch (error, trace) { @@ -144,7 +144,7 @@ abstract class Domain { new Future.sync(() { if (_handlers.containsKey(name)) return _handlers[name](args); - throw 'command not understood: ${name}'; + throw 'command not understood: $name'; }).then((result) { if (result == null) { _send({'id': id}); @@ -153,7 +153,7 @@ abstract class Domain { } }).catchError((error, trace) { _send({'id': id, 'error': _toJsonable(error)}); - _logging.warning('error handling ${name}', error, trace); + _logging.warning('error handling $name', error, trace); }); } @@ -210,5 +210,5 @@ class AppDomain extends Domain { 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}'; + return '$obj'; } diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart index f31ad565c9..4ff335a0d7 100644 --- a/packages/flutter_tools/lib/src/commands/flutter_command.dart +++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart @@ -56,7 +56,7 @@ abstract class FlutterCommand extends Command { } } - return runInProject(); + return await runInProject(); } Future runInProject(); diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart index 85ef4415cd..3c705315e4 100644 --- a/packages/flutter_tools/lib/src/commands/init.dart +++ b/packages/flutter_tools/lib/src/commands/init.dart @@ -6,12 +6,15 @@ import 'dart:async'; import 'dart:io'; import 'package:args/command_runner.dart'; +import 'package:logging/logging.dart'; import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:path/path.dart' as p; import '../artifacts.dart'; import '../process.dart'; +final Logger _logging = new Logger('sky_tools.init'); + class InitCommand extends Command { final String name = 'init'; final String description = 'Create a new Flutter project.'; @@ -20,7 +23,7 @@ class InitCommand extends Command { 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.'); + help: 'Whether to run "pub get" after the project has been created.'); } @override @@ -40,7 +43,7 @@ class InitCommand extends Command { String flutterPackagePath = p.join(flutterRoot, 'packages', 'flutter'); if (!FileSystemEntity.isFileSync(p.join(flutterPackagePath, 'pubspec.yaml'))) { - print('Unable to find package:flutter in ${flutterPackagePath}'); + print('Unable to find package:flutter in $flutterPackagePath'); return 2; } @@ -58,11 +61,7 @@ class InitCommand extends Command { '''; if (argResults['pub']) { - print("Running pub get..."); - int code = await runCommandAndStreamOutput( - [sdkBinaryName('pub'), 'get'], - workingDirectory: out.path - ); + int code = await pubGet(directory: out.path); if (code != 0) return code; } @@ -70,6 +69,41 @@ class InitCommand extends Command { print(message); return 0; } + + Future pubGet({ + String directory: '', + bool skipIfAbsent: false, + bool verbose: true + }) async { + File pubSpecYaml = new File(p.join(directory, 'pubspec.yaml')); + File pubSpecLock = new File(p.join(directory, 'pubspec.lock')); + File dotPackages = new File(p.join(directory, '.packages')); + + if (!pubSpecYaml.existsSync()) { + if (skipIfAbsent) + return 0; + _logging.severe('$directory: no pubspec.yaml found'); + return 1; + } + + if (!pubSpecLock.existsSync() || pubSpecYaml.lastModifiedSync().isAfter(pubSpecLock.lastModifiedSync())) { + if (verbose) + print("Running pub get in $directory..."); + int code = await runCommandAndStreamOutput( + [sdkBinaryName('pub'), 'get'], + workingDirectory: directory + ); + if (code != 0) + return code; + } + + if ((pubSpecLock.existsSync() && pubSpecLock.lastModifiedSync().isAfter(pubSpecYaml.lastModifiedSync())) && + (dotPackages.existsSync() && dotPackages.lastModifiedSync().isAfter(pubSpecYaml.lastModifiedSync()))) + return 0; + + _logging.severe('$directory: pubspec.yaml, pubspec.lock, and .packages are in an inconsistent state'); + return 1; + } } abstract class Template { diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart index ce0a187547..516673ef6e 100644 --- a/packages/flutter_tools/lib/src/commands/run_mojo.dart +++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart @@ -34,7 +34,7 @@ class RunMojoCommand extends Command { String _makePathAbsolute(String relativePath) { File file = new File(relativePath); if (!file.existsSync()) { - throw new Exception("Path \"${relativePath}\" does not exist"); + throw new Exception('Path "$relativePath" does not exist'); } return file.absolute.path; } @@ -79,8 +79,8 @@ class RunMojoCommand extends Command { final appPath = _makePathAbsolute(argResults['app']); Artifact artifact = ArtifactStore.getArtifact(type: ArtifactType.viewer, targetPlatform: TargetPlatform.linux); final viewerPath = _makePathAbsolute(await ArtifactStore.getPath(artifact)); - args.add('file://${appPath}'); - args.add('--url-mappings=mojo:sky_viewer=file://${viewerPath}'); + args.add('file://$appPath'); + args.add('--url-mappings=mojo:sky_viewer=file://$viewerPath'); } if (useDevtools) { @@ -115,6 +115,6 @@ class RunMojoCommand extends Command { return 1; } - return runCommandAndStreamOutput(await _getShellConfig()); + return await runCommandAndStreamOutput(await _getShellConfig()); } } diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart index ce60e438c5..842d909b39 100644 --- a/packages/flutter_tools/lib/src/commands/start.dart +++ b/packages/flutter_tools/lib/src/commands/start.dart @@ -68,7 +68,7 @@ class StartCommand extends FlutterCommand { if (FileSystemEntity.isDirectorySync(target)) mainPath = path.join(target, 'lib', 'main.dart'); if (!FileSystemEntity.isFileSync(mainPath)) { - String message = 'Tried to run ${mainPath}, but that file does not exist.'; + String message = 'Tried to run $mainPath, but that file does not exist.'; if (!argResults.wasParsed('target')) message += '\nConsider using the -t option to specify that Dart file to start.'; stderr.writeln(message); diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 8c3202c60d..5d268345c9 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -11,7 +11,6 @@ import 'package:test/src/executable.dart' as executable; import '../artifacts.dart'; import '../build_configuration.dart'; -import '../process.dart'; import '../test/loader.dart' as loader; import 'flutter_command.dart'; @@ -49,25 +48,6 @@ class TestCommand extends FlutterCommand { testArgs.insert(0, '--no-color'); List configs = buildConfigurations; bool foundOne = false; - - File pubSpecYaml = new File(path.join(flutterDir.path, 'pubspec.yaml')); - File pubSpecLock = new File(path.join(flutterDir.path, 'pubspec.lock')); - - if (!pubSpecYaml.existsSync()) { - print('${flutterDir.path} has no pubspec.yaml'); - return 1; - } - - if (!pubSpecLock.existsSync() || pubSpecYaml.lastModifiedSync().isAfter(pubSpecLock.lastModifiedSync())) { - print("Running pub get..."); - int code = await runCommandAndStreamOutput( - [sdkBinaryName('pub'), 'get'], - workingDirectory: flutterDir.path - ); - if (code != 0) - return code; - } - String currentDirectory = Directory.current.path; Directory.current = flutterDir.path; loader.installHook(); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index d0ef982a83..47831e83f1 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -19,36 +19,8 @@ abstract class Device { final String id; static Map _deviceCache = {}; - factory Device._unique(String className, [String id = null]) { - if (id == null) { - if (className == AndroidDevice.className) { - 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'; - } - } - - return _deviceCache.putIfAbsent(id, () { - if (className == AndroidDevice.className) { - 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 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'; - } - }); + static Device _unique(String id, Device constructor(String id)) { + return _deviceCache.putIfAbsent(id, () => constructor(id)); } Device._(this.id); @@ -74,7 +46,6 @@ abstract class Device { } class IOSDevice extends Device { - static const String className = 'IOSDevice'; static final String defaultDeviceID = 'default_ios_id'; static const String _macInstructions = @@ -108,7 +79,7 @@ class IOSDevice extends Device { String get name => _name; factory IOSDevice({String id, String name}) { - IOSDevice device = new Device._unique(className, id); + IOSDevice device = Device._unique(id ?? defaultDeviceID, new IOSDevice#_); // analyzer doesn't like constructor tear-offs device._name = name; return device; } @@ -224,14 +195,17 @@ 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.id], new Duration(seconds: 3)).then( - (_) { - return true; - }, onError: (e) { - _logging.info('Failure running $debuggerPath: ', e); - return false; - }); + return await runAndKill( + [debuggerPath, 'run', app.id], + new Duration(seconds: 3) + ).then( + (_) { + return true; + }, onError: (e) { + _logging.info('Failure running $debuggerPath: ', e); + return false; + } + ); } @override @@ -274,13 +248,12 @@ class IOSDevice extends Device { if (!isConnected()) { return 2; } - return runCommandAndStreamOutput([loggerPath], + return await 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 = @@ -299,7 +272,7 @@ class IOSSimulator extends Device { String get name => _name; factory IOSSimulator({String id, String name, String iOSSimulatorPath}) { - IOSSimulator device = new Device._unique(className, id); + IOSSimulator device = Device._unique(id ?? defaultDeviceID, new IOSSimulator#_); // analyzer doesn't like constructor tear-offs device._name = name; if (iOSSimulatorPath == null) { iOSSimulatorPath = path.join('/Applications', 'iOS Simulator.app', @@ -309,7 +282,7 @@ class IOSSimulator extends Device { return device; } - IOSSimulator._(String id) : super._(id) {} + IOSSimulator._(String id) : super._(id); static String _getRunningSimulatorID([IOSSimulator mockIOS]) { String xcrunPath = mockIOS != null ? mockIOS.xcrunPath : _xcrunPath; @@ -380,7 +353,6 @@ class IOSSimulator extends Device { } 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.'); @@ -388,12 +360,12 @@ class IOSSimulator extends Device { } if (!isConnected()) { _logging.info('Waiting for iOS Simulator $id to boot...'); - return new Future.delayed(new Duration(milliseconds: 500), + return await new Future.delayed(new Duration(milliseconds: 500), () => checkConnection(attempts - 1)); } return true; } - return checkConnection(); + return await checkConnection(); } else { try { runCheckedSync([xcrunPath, 'simctl', 'boot', id]); @@ -496,7 +468,7 @@ class IOSSimulator extends Device { if (clear) { runSync(['rm', logFilePath]); } - return runCommandAndStreamOutput(['tail', '-f', logFilePath], + return await runCommandAndStreamOutput(['tail', '-f', logFilePath], prefix: 'iOS sim: ', filter: new RegExp(r'.*SkyShell.*')); } } @@ -505,7 +477,6 @@ class AndroidDevice extends Device { static const String _ADB_PATH = 'adb'; static const int _observatoryPort = 8181; - static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default_android_device'; String productID; @@ -522,7 +493,7 @@ class AndroidDevice extends Device { String productID: null, String modelID: null, String deviceCodeName: null}) { - AndroidDevice device = new Device._unique(className, id); + AndroidDevice device = Device._unique(id ?? defaultDeviceID, new AndroidDevice#_); // analyzer doesn't like constructor tear-offs device.productID = productID; device.modelID = modelID; device.deviceCodeName = deviceCodeName; @@ -813,7 +784,7 @@ class AndroidDevice extends Device { clearLogs(); } - return runCommandAndStreamOutput(adbCommandForDevice([ + return await runCommandAndStreamOutput(adbCommandForDevice([ 'logcat', '-v', 'tag', // Only log the tag and the message diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart index 3259b69e00..c0a547ff80 100644 --- a/packages/flutter_tools/lib/src/process.dart +++ b/packages/flutter_tools/lib/src/process.dart @@ -18,33 +18,35 @@ Future runCommandAndStreamOutput(List cmd, { String workingDirectory }) async { _logging.info(cmd.join(' ')); - Process proc = await Process.start( + Process process = await Process.start( cmd[0], - cmd.getRange(1, cmd.length).toList(), + cmd.sublist(1), workingDirectory: workingDirectory ); - proc.stdout.transform(UTF8.decoder).listen((String data) { + process.stdout.transform(UTF8.decoder).listen((String data) { List dataLines = data.trimRight().split('\n'); if (filter != null) { + // TODO(ianh): This doesn't handle IO buffering (where the data might be split half-way through a line) dataLines = dataLines.where((String s) => filter.hasMatch(s)).toList(); } if (dataLines.length > 0) { stdout.write('$prefix${dataLines.join('\n$prefix')}\n'); } }); - proc.stderr.transform(UTF8.decoder).listen((String data) { + process.stderr.transform(UTF8.decoder).listen((String data) { List dataLines = data.trimRight().split('\n'); if (filter != null) { + // TODO(ianh): This doesn't handle IO buffering (where the data might be split half-way through a line) dataLines = dataLines.where((String s) => filter.hasMatch(s)); } if (dataLines.length > 0) { stderr.write('$prefix${dataLines.join('\n$prefix')}\n'); } }); - return proc.exitCode; + return await process.exitCode; } -Future runAndKill(List cmd, Duration timeout) async { +Future runAndKill(List cmd, Duration timeout) { Future proc = runDetached(cmd); return new Future.delayed(timeout, () async { _logging.info('Intentionally killing ${cmd[0]}'); @@ -52,7 +54,7 @@ Future runAndKill(List cmd, Duration timeout) async { }); } -Future runDetached(List cmd) async { +Future runDetached(List cmd) { _logging.info(cmd.join(' ')); Future proc = Process.start( cmd[0], cmd.getRange(1, cmd.length).toList(), @@ -97,6 +99,6 @@ String _runWithLoggingSync(List cmd, {bool checked: false}) { class ProcessExit implements Exception { final int exitCode; ProcessExit(this.exitCode); - String get message => 'ProcessExit: ${exitCode}'; + String get message => 'ProcessExit: $exitCode'; String toString() => message; } diff --git a/packages/newton/lib/src/friction_simulation.dart b/packages/newton/lib/src/friction_simulation.dart index 38671b99d3..17d1ccc014 100644 --- a/packages/newton/lib/src/friction_simulation.dart +++ b/packages/newton/lib/src/friction_simulation.dart @@ -50,8 +50,9 @@ class BoundedFrictionSimulation extends FrictionSimulation { double drag, double position, double velocity, - double this._minX, - double this._maxX) : super(drag, position, velocity); + this._minX, + this._maxX + ) : super(drag, position, velocity); final double _minX; final double _maxX; diff --git a/packages/newton/lib/src/spring_simulation.dart b/packages/newton/lib/src/spring_simulation.dart index 143f96c77f..f0f216a6c5 100644 --- a/packages/newton/lib/src/spring_simulation.dart +++ b/packages/newton/lib/src/spring_simulation.dart @@ -17,7 +17,8 @@ class SpringDescription { final double damping; SpringDescription( - {double this.mass, double this.springConstant, double this.damping}) { + { this.mass, this.springConstant, this.damping } + ) { assert(mass != null); assert(springConstant != null); assert(damping != null); diff --git a/packages/unit/benchmark/gestures/velocity_tracker_bench.dart b/packages/unit/benchmark/gestures/velocity_tracker_bench.dart index cf89561a0c..94c6a15ca3 100644 --- a/packages/unit/benchmark/gestures/velocity_tracker_bench.dart +++ b/packages/unit/benchmark/gestures/velocity_tracker_bench.dart @@ -1,5 +1,3 @@ -import 'dart:ui' as ui; - import 'package:flutter/gestures.dart'; import 'package:test/test.dart'; import 'velocity_tracker_data.dart'; @@ -45,20 +43,4 @@ void main() { watch.stop(); print("Dart tracker: " + watch.elapsed.toString()); }); - - test('Native velocity tracker performance', () { - ui.VelocityTracker tracker = new ui.VelocityTracker(); - Stopwatch watch = new Stopwatch(); - watch.start(); - for (int i = 0; i < kNumIters; i++) { - for (PointerInputEvent event in events) { - if (event.type == 'pointerdown' || event.type == 'pointermove') - tracker.addPosition((event.timeStamp*1000.0).toInt(), event.x, event.y); - if (event.type == 'pointerup') - tracker.getVelocity(); - } - } - watch.stop(); - print("Native tracker: " + watch.elapsed.toString()); - }); } diff --git a/packages/unit/test/rendering/image_test.dart b/packages/unit/test/rendering/image_test.dart index b794f84a18..48996734d0 100644 --- a/packages/unit/test/rendering/image_test.dart +++ b/packages/unit/test/rendering/image_test.dart @@ -8,16 +8,19 @@ import 'rendering_tester.dart'; class SquareImage implements ui.Image { int get width => 10; int get height => 10; + void dispose() { } } class WideImage implements ui.Image { int get width => 20; int get height => 10; + void dispose() { } } class TallImage implements ui.Image { int get width => 10; int get height => 20; + void dispose() { } } void main() { diff --git a/travis/test.sh b/travis/test.sh index 2e1597bea8..fa5e48b779 100755 --- a/travis/test.sh +++ b/travis/test.sh @@ -1,12 +1,16 @@ #!/bin/bash set -ex -(cd packages/cassowary; pub global run tuneup check; pub run test -j1) -(cd packages/flutter_sprites; pub global run tuneup check) # No tests to run. -(cd packages/flutter_tools; pub global run tuneup check; pub run test -j1) -(cd packages/flx; pub global run tuneup check; pub run test -j1) -(cd packages/newton; pub global run tuneup check; pub run test -j1) -(cd packages/playfair; pub global run tuneup check) # No tests to run. -(cd packages/updater; pub global run tuneup check) # No tests to run. +# analyze all the Dart code in the repo +./bin/flutter analyze --flutter-repo --no-current-directory --no-current-package --congratulate +# flutter package tests ./bin/flutter test --engine-src-path bin/cache/travis + +(cd packages/cassowary; pub run test -j1) +# (cd packages/flutter_sprites; ) # No tests to run. +(cd packages/flutter_tools; pub run test -j1) +(cd packages/flx; pub run test -j1) +(cd packages/newton; pub run test -j1) +# (cd packages/playfair; ) # No tests to run. +# (cd packages/updater; ) # No tests to run.