Build sky_engine Dart SDK summary bundle on FlutterEngine.download(). (#5460)

diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index b280f8c..c4e041c 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -5,6 +5,8 @@
 import 'dart:async';
 import 'dart:io';
 
+import 'package:flutter_tools/src/dart/pub.dart';
+import 'package:flutter_tools/src/dart/summary.dart';
 import 'package:path/path.dart' as path;
 
 import 'base/context.dart';
@@ -236,13 +238,17 @@
 }
 
 class FlutterEngine {
+
   FlutterEngine(this.cache);
 
   static const String kName = 'engine';
+  static const String kSkyEngine = 'sky_engine';
+  static const String kSkyServices = 'sky_services';
+  static const String kSdkBundle = 'sdk.ds';
 
   final Cache cache;
 
-  List<String> _getPackageDirs() => <String>['sky_engine', 'sky_services'];
+  List<String> _getPackageDirs() => const <String>[kSkyEngine, kSkyServices];
 
   List<String> _getEngineDirs() {
     List<String> dirs = <String>[
@@ -292,11 +298,17 @@
   bool isUpToDate() {
     Directory pkgDir = cache.getCacheDir('pkg');
     for (String pkgName in _getPackageDirs()) {
-      Directory dir = new Directory(path.join(pkgDir.path, pkgName));
-      if (!dir.existsSync())
+      String pkgPath = path.join(pkgDir.path, pkgName);
+      String dotPackagesPath = path.join(pkgPath, '.packages');
+      if (!new Directory(pkgPath).existsSync())
+        return false;
+      if (!new File(dotPackagesPath).existsSync())
         return false;
     }
 
+    if (!new File(path.join(pkgDir.path, kSkyEngine, kSdkBundle)).existsSync())
+      return false;
+
     Directory engineDir = cache.getArtifactDirectory(kName);
     for (String dirName in _getEngineDirs()) {
       Directory dir = new Directory(path.join(engineDir.path, dirName));
@@ -319,10 +331,21 @@
 
     Directory pkgDir = cache.getCacheDir('pkg');
     for (String pkgName in _getPackageDirs()) {
-      Directory dir = new Directory(path.join(pkgDir.path, pkgName));
+      String pkgPath = path.join(pkgDir.path, pkgName);
+      Directory dir = new Directory(pkgPath);
       if (dir.existsSync())
         dir.deleteSync(recursive: true);
       await _downloadItem('Downloading package $pkgName...', url + pkgName + '.zip', pkgDir);
+      await pubGet(directory: pkgPath);
+    }
+
+    Status summaryStatus = logger.startProgress('Building Dart SDK summary...');
+    try {
+      String skyEnginePath = path.join(pkgDir.path, kSkyEngine);
+      String skyServicesPath = path.join(pkgDir.path, kSkyServices);
+      buildSkyEngineSdkSummary(skyEnginePath, skyServicesPath, kSdkBundle);
+    } finally {
+      summaryStatus.stop(showElapsedTime: true);
     }
 
     Directory engineDir = cache.getArtifactDirectory(kName);
diff --git a/packages/flutter_tools/lib/src/dart/summary.dart b/packages/flutter_tools/lib/src/dart/summary.dart
new file mode 100644
index 0000000..b5bf246
--- /dev/null
+++ b/packages/flutter_tools/lib/src/dart/summary.dart
@@ -0,0 +1,76 @@
+import 'dart:io' as io;
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/source/sdk_ext.dart';
+import 'package:analyzer/src/context/builder.dart'; // ignore: implementation_imports
+import 'package:analyzer/src/dart/sdk/sdk.dart'; // ignore: implementation_imports
+import 'package:analyzer/src/generated/engine.dart'; // ignore: implementation_imports
+import 'package:analyzer/src/generated/source.dart'; // ignore: implementation_imports
+import 'package:analyzer/src/summary/summary_file_builder.dart'; // ignore: implementation_imports
+import 'package:flutter_tools/src/globals.dart';
+import 'package:package_config/packages.dart';
+import 'package:path/path.dart' as pathos;
+import 'package:yaml/src/yaml_node.dart'; // ignore: implementation_imports
+
+/// Given the [skyEnginePath] and [skyServicesPath], locate corresponding
+/// `_embedder.yaml` and `_sdkext`, compose the full embedded Dart SDK, and
+/// build the [outBundleName] file with its linked summary.
+void buildSkyEngineSdkSummary(
+    String skyEnginePath, String skyServicesPath, String outBundleName) {
+  ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
+
+  ContextBuilder builder = new ContextBuilder(resourceProvider, null, null);
+  Packages packages = builder.createPackageMap(skyServicesPath);
+  Map<String, List<Folder>> packageMap = builder.convertPackagesToMap(packages);
+  if (packageMap == null) {
+    printError('The expected .packages was not found in $skyServicesPath.');
+    return;
+  }
+  packageMap['sky_engine'] = <Folder>[
+    resourceProvider.getFolder(pathos.join(skyEnginePath, 'lib'))
+  ];
+
+  //
+  // Read the `_embedder.yaml` file.
+  //
+  EmbedderYamlLocator yamlLocator = new EmbedderYamlLocator(packageMap);
+  Map<Folder, YamlMap> embedderYamls = yamlLocator.embedderYamls;
+  if (embedderYamls.length != 1) {
+    printError('Exactly one _embedder.yaml was expected in $packageMap, '
+        'but $embedderYamls found.');
+    return;
+  }
+
+  //
+  // Read the `_sdkext` file.
+  //
+  SdkExtUriResolver extResolver = new SdkExtUriResolver(packageMap);
+  Map<String, String> urlMappings = extResolver.urlMappings;
+  if (embedderYamls.length != 1) {
+    printError('Exactly one extension library was expected in $packageMap, '
+        'but $urlMappings found.');
+    return;
+  }
+
+  //
+  // Create the EmbedderSdk instance.
+  //
+  EmbedderSdk sdk = new EmbedderSdk(resourceProvider, embedderYamls);
+  sdk.addExtensions(urlMappings);
+  sdk.analysisOptions = new AnalysisOptionsImpl()..strongMode = true;
+
+  //
+  // Gather sources.
+  //
+  List<Source> sources = sdk.uris.map(sdk.mapDartUri).toList();
+
+  //
+  // Build.
+  //
+  SummaryBuildConfig config = new SummaryBuildConfig(strongMode: true);
+  BuilderOutput output =
+      new SummaryBuilder(sources, sdk.context, config).build();
+  String outputPath = pathos.join(skyEnginePath, outBundleName);
+  new io.File(outputPath).writeAsBytesSync(output.sum);
+}