[components][flutter][dart] Combine AOT rules

Combine flutter_aot_component, flutter_aot_package, dart_aot_package
into one set of _component and _package build rules that handle both
flutter and dart components.

Saves code, and enables us to write build rules that drop flutter and
dart components into the same package.

TESTED=
Hardcode dart_default_app and flutter_default_app to "dart_aot_app" and
"flutter_aot_app", then:

run -d fuchsia-pkg://fuchsia.com/echo_dart#meta/echo_client_dart.cmx --server fuchsia-pkg://fuchsia.com/echo_dart#meta/echo_server_dart.cmx
tiles_ctl add fuchsia-pkg://fuchsia.com/simple_flutter#meta/simple_flutter.cmx
tiles_ctl add fuchsia-pkg://fuchsia.com/simple_flutter#meta/leaf_flutter.cmx

ls /pkgfs/packages/echo_dart/0/data/echo_server_dart
ls /pkgfs/packages/echo_dart/0/data/echo_client_dart
ls /pkgfs/packages/simple_flutter/0/data/simple_flutter/
ls /pkgfs/packages/simple_flutter/0/data/leaf_flutter/

CP-142 #comment

Change-Id: I255c4a700e9a752d6c0b8002cb5b2f95996730e2
diff --git a/runtime/dart/dart_component.gni b/runtime/dart/dart_component.gni
index a2ff97a..593efcc 100644
--- a/runtime/dart/dart_component.gni
+++ b/runtime/dart/dart_component.gni
@@ -77,9 +77,9 @@
 # future.
 dart_product_app = "flutter_dart_jit_component"
 
-# Defines a Flutter package with JIT runtime components
+# Defines a Flutter and Dart package with JIT runtime components
 #
-# After Flutter JIT runtime components are calculated in
+# After Flutter and Dart JIT runtime components are calculated in
 # flutter_dart_jit_component, put them all into one Fuchsia package.
 #
 # Parameters
@@ -127,7 +127,6 @@
       deps += component.deps
       extra += [ component.kernel_manifest ]
 
-
       if (component.component_type == "flutter") {
         extra += [ component.asset_manifest ]
 
@@ -222,10 +221,10 @@
 
 # Defines JIT runtime components to be further distributed in one package.
 #
-# Takes a set of flutter components and puts them into one fuchsia package with
-# the flutter_jit_runner as its runtime. Also supports legacy calls where the
-# components parameter isn't specified, in which we will create one default
-# component for the package.
+# Takes a set of flutter and dart components and puts them into one fuchsia
+# package with the flutter_jit_runner as its runtime. Also supports legacy
+# calls where the components parameter isn't specified, in which we will create
+# one default component for the package.
 #
 # Parameters
 #
@@ -351,7 +350,6 @@
     kernel_target_name = kernel_name + "_kernel"
     kernel_manifest = "$target_gen_dir/${kernel_target_name}.dilpmanifest"
 
-
     if (component.component_type == "flutter") {
       dart_library_target_name = "${kernel_name}_dart_library"
       dart_target_gen_dir =
@@ -519,3 +517,518 @@
     forward_variables_from(invoker, "*")
   }
 }
+
+# Defines a Flutter and Dart package with AOT runtime components
+#
+# After Flutter and Dart AOT runtime components are calculated in
+# flutter_dart_aot_component, put them all into one Fuchsia package.
+#
+# Parameters
+#
+#   components_with_kernel (required)
+#     [list of scopes] Defines the components in the package. Either main_dart
+#     or components must be defined, but not both.
+#
+#     Entries in a scope in the components_with_kernel list:
+#
+#       kernel_target_name (required)
+#         Name of the kernel target.
+#
+#       kernel_manifest (required)
+#         Manifest file for the dart kernel.
+#
+#       deps (required)
+#         Dependencies of this component.
+#
+#       sources (required)
+#         Source files of this component.
+#
+#       component_resources (required)
+#         Resources of this component.
+#
+#       component_copy_manifest (required)
+#         Copy of the module manifest of this component.
+#
+#       component_verify_manifest (required)
+#         Verify target of the module manifest of this component.
+template("_flutter_dart_aot_package") {
+  package(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "testonly",
+                             "tests",
+                           ])
+
+    package_name = invoker.pkg_name
+    non_dart_deps = []
+    if (defined(invoker.non_dart_deps)) {
+      non_dart_deps = invoker.non_dart_deps
+    }
+    deps = non_dart_deps + invoker.cmx_deps
+    extra = []
+    resources = []
+
+    foreach(component, invoker.components_with_kernel) {
+      deps += [
+             ":${component.snapshot_target_name}",
+             ":${component.stats_target_name}",
+           ] + component.component_shared_snapshot_deps + component.deps
+
+      if (component.component_type == "flutter") {
+        extra += [ component.asset_manifest ]
+        deps += [ ":${component.component_resources}", ]
+        if (defined(invoker.module_manifest)) {
+          deps += [
+            ":${component.component_copy_manifest}",
+            ":${component.component_verify_manifest}",
+          ]
+        }
+      }
+
+      resources_path = "${component.component_name}/"
+      resources += [
+        {
+          path = rebase_path(component.snapshot_data_path)
+          dest = "${resources_path}isolate_snapshot_data.bin"
+        },
+        {
+          path = rebase_path(component.snapshot_instructions_path)
+          dest = "${resources_path}isolate_snapshot_instructions.bin"
+        },
+        {
+          path = rebase_path(component.shared_snapshot_data_path)
+          dest = "${resources_path}shared_snapshot_data.bin"
+        },
+        {
+          path = rebase_path(component.shared_snapshot_instructions_path)
+          dest = "${resources_path}shared_snapshot_instructions.bin"
+        },
+      ]
+      if (component.component_type == "flutter") {
+        resources += [
+          {
+            path = rebase_path(component.vm_snapshot_data_path)
+            dest = "${resources_path}vm_snapshot_data.bin"
+          },
+          {
+            path = rebase_path(component.vm_snapshot_instructions_path)
+            dest = "${resources_path}vm_snapshot_instructions.bin"
+          },
+        ]
+      }
+
+    }
+
+    if (defined(invoker.invoker_resources)) {
+      resources += invoker.invoker_resources
+    }
+    if (defined(invoker.resources)) {
+      resources += invoker.resources
+    }
+
+    meta = invoker.cmx_filtered_meta
+
+    # TODO(CP-140): Support module_manifest per component.
+    if (defined(invoker.module_manifest)) {
+      meta += [
+        {
+          path = rebase_path(invoker.module_manifest)
+          dest = "module.json"
+        },
+      ]
+    }
+  }
+}
+
+# Defines AOT runtime components to be further distributed in one package.
+#
+# Takes a set of flutter and dart components and puts them into one fuchsia
+# package with the flutter_aot_runner as its runtime. Also supports legacy
+# calls where the components parameter isn't specified, in which we will create
+# one default component for the package.
+#
+# Parameters
+#
+#   components (required)
+#     [list of scopes] Defines the components in the package. Either main_dart
+#     or components must be defined, but not both.
+#
+#     Entries in a scope in the resources list:
+#
+#       component_name (required)
+#         Name of the component.
+#
+#       main_dart (required)
+#         File containing the main function of the component.
+#
+#   main_dart (required)
+#     File containing the main function of the application. Either main_dart or
+#     components must be defined, but not both.
+template("flutter_dart_aot_component") {
+  pkg_name = invoker.pkg_name
+  legacy_component = invoker.legacy_component
+  components = []
+  components += invoker.components
+
+  package_meta = []
+  if (defined(invoker.meta)) {
+    package_meta = invoker.meta
+  }
+  # Inject appropriate "runner" into Component manifests
+  cmx_filtered_meta = []
+  cmx_deps = []
+
+  # Build the kernel for each of the components, and bundle them in the same
+  # scope for later packaging.
+  components_with_kernel = []
+  foreach(component, components) {
+    assert(defined(component.main_dart), "Must specify main_dart file")
+
+    if (component.component_type == "flutter") {
+      product = !flutter_profile
+
+    } else if (component.component_type == "dart") {
+      product = !is_debug
+    }
+
+    if (defined(invoker.product)) {
+      product = invoker.product
+    }
+    if (dart_force_product) {
+      product = true
+    }
+    product_suffix = ""
+    if (product) {
+      product_suffix = "_product"
+    }
+
+    if (component.component_type == "flutter") {
+      runtime_meta =
+          "//topaz/runtime/flutter_runner/meta/aot${product_suffix}_runtime"
+    } else if (component.component_type == "dart") {
+      runtime_meta =
+          "//topaz/runtime/dart_runner/meta/aot${product_suffix}_runtime"
+    }
+
+    # Look through the package meta for cmx file for this component, and merge
+    # in the runner.
+    foreach(item, package_meta) {
+      dest = item.path
+      if (defined(item.dest)) {
+        dest = item.dest
+      }
+      if (get_path_info(dest, "file") == "${component.component_name}.cmx") {
+        merged = "merged_" + get_path_info(dest, "file")
+        json_merge(merged) {
+          sources = [
+            item.path,
+            rebase_path(runtime_meta),
+          ]
+        }
+        merged_outputs = []
+        merged_outputs += get_target_outputs(":$merged")
+        item.path = merged_outputs[0]
+        cmx_deps += [ ":$merged" ]
+        cmx_filtered_meta += [ item ]
+      }
+    }
+
+    component_name = component.component_name
+    if (legacy_component) {
+      kernel_name = target_name
+    } else {
+      kernel_name = component_name
+    }
+    kernel_target_name = kernel_name + "_kernel"
+    kernel_path = "$target_gen_dir/${kernel_target_name}.dil"
+
+    if (component.component_type == "flutter") {
+      dart_library_target_name = "${kernel_name}_dart_library"
+    }
+
+    dart_kernel(kernel_name) {
+      if (component.component_type == "flutter") {
+        platform_name = "flutter_runner"
+        platform_deps =
+            [ "//topaz/runtime/flutter_runner/kernel:kernel_platform_files" ]
+        platform_path = "$root_out_dir/flutter_runner_patched_sdk"
+      } else if (component.component_type == "dart") {
+        platform_name = "dart_runner"
+        platform_deps =
+            [ "//topaz/runtime/dart_runner/kernel:kernel_platform_files" ]
+        platform_path = "$root_out_dir/dart_runner_patched_sdk"
+      }
+
+      # TODO(CP-140): These variables should be from the component
+      forward_variables_from(invoker,
+                             [
+                               "disable_analysis",
+                               "non_dart_deps",
+                               "source_dir",
+                               "package_name",
+                             ])
+      deps = component.deps
+      sources = component.sources
+
+      main_dart = component.main_dart
+      args = [ "--aot", "--tfa" ]
+    }
+
+
+    # TODO(rmacnak): VM snapshot is ignored. Allow skipping its generation.
+    vm_snapshot_data_path = "$target_gen_dir/${kernel_name}_vm_data.aotsnapshot"
+    vm_snapshot_instructions_path =
+        "$target_gen_dir/${kernel_name}_vm_instructions.aotsnapshot"
+    snapshot_data_path = "$target_gen_dir/${kernel_name}_data.aotsnapshot"
+    snapshot_instructions_path =
+        "$target_gen_dir/${kernel_name}_instructions.aotsnapshot"
+    snapshot_target_name = kernel_name + "_snapshot"
+
+    if (component.component_type == "flutter" &&
+        flutter_aot_sharing_basis != "" &&
+        get_label_info(":$kernel_name", "label_no_toolchain") !=
+        get_label_info(flutter_aot_sharing_basis, "label_no_toolchain")) {
+      # Note: The use of "label_no_toolchain" is to ensure we are comparing
+      # fully qualified target names. We don't actually care about the
+      # toolchain.
+      shared_snapshot_deps = []
+      shared_snapshot_deps =
+          [ get_label_info(flutter_aot_sharing_basis, "label_no_toolchain") +
+            "_snapshot" ]
+      prefix = get_label_info(flutter_aot_sharing_basis, "target_gen_dir") +
+          "/" + get_label_info(flutter_aot_sharing_basis, "name")
+      shared_snapshot_data_path = "${prefix}_data.aotsnapshot"
+      shared_snapshot_instructions_path = "${prefix}_instructions.aotsnapshot"
+    } else if (component.component_type == "dart" &&
+        dart_aot_sharing_basis != "" &&
+        get_label_info(":$kernel_name", "label_no_toolchain") !=
+        get_label_info(dart_aot_sharing_basis, "label_no_toolchain")) {
+      # Note: The use of "label_no_toolchain" is to ensure we are comparing
+      # fully qualified target names. We don't actually care about the
+      # toolchain.
+      shared_snapshot_deps = []
+      shared_snapshot_deps =
+          [ get_label_info(dart_aot_sharing_basis, "label_no_toolchain") +
+            "_snapshot" ]
+      prefix = get_label_info(dart_aot_sharing_basis, "target_gen_dir") + "/" +
+               get_label_info(dart_aot_sharing_basis, "name")
+      shared_snapshot_data_path = "${prefix}_data.aotsnapshot"
+      shared_snapshot_instructions_path = "${prefix}_instructions.aotsnapshot"
+    } else {
+      shared_snapshot_deps = []
+      shared_snapshot_data_path = "//topaz/runtime/dart_runner/empty"
+      shared_snapshot_instructions_path = "//topaz/runtime/dart_runner/empty"
+    }
+
+    stats_target_name = "${kernel_name}_stats"
+    stats_json_path = "$target_gen_dir/${kernel_name}/stats/symbol_sizes.json"
+    stats_html_dir = "$target_gen_dir/${kernel_name}/stats"
+
+    action(snapshot_target_name) {
+      if (defined(invoker.testonly)) {
+        testonly = invoker.testonly
+      }
+
+      deps = gen_snapshot_deps +
+          shared_snapshot_deps +
+          [ ":$kernel_target_name" ]
+      inputs = [
+                 shared_snapshot_data_path,
+                 shared_snapshot_instructions_path,
+                 kernel_path,
+               ]
+      outputs = [
+        vm_snapshot_data_path,
+        vm_snapshot_instructions_path,
+        snapshot_data_path,
+        snapshot_instructions_path,
+        stats_json_path,
+      ]
+
+      if (product) {
+        script = gen_snapshot_product
+      } else {
+        script = gen_snapshot
+      }
+
+      args = [
+        "--deterministic",
+        "--snapshot_kind=app-aot-blobs",
+        "--vm_snapshot_data=" + rebase_path(vm_snapshot_data_path),
+        "--vm_snapshot_instructions=" +
+            rebase_path(vm_snapshot_instructions_path),
+        "--isolate_snapshot_data=" + rebase_path(snapshot_data_path),
+        "--isolate_snapshot_instructions=" +
+            rebase_path(snapshot_instructions_path),
+        "--shared_data=" + rebase_path(shared_snapshot_data_path),
+        "--shared_instructions=" +
+            rebase_path(shared_snapshot_instructions_path),
+        "--print-instructions-sizes-to=" + rebase_path(stats_json_path),
+      ]
+      if (is_debug && !product) {
+        args += [ "--enable_asserts" ]
+      }
+      args += [ rebase_path(kernel_path) ]
+
+      pool = "//build/dart:dart_pool($dart_toolchain)"
+    }
+
+    dart_action(stats_target_name) {
+      if (defined(invoker.testonly)) {
+        testonly = invoker.testonly
+      }
+
+      deps = [
+        ":$snapshot_target_name",
+      ]
+      script = "//third_party/dart/pkg/vm/bin/run_binary_size_analysis.dart"
+      inputs = [
+        stats_json_path,
+      ]
+      outputs = [
+        "$stats_html_dir/index.html",
+      ]
+      args = [
+        rebase_path(stats_json_path),
+        rebase_path(stats_html_dir),
+      ]
+    }
+
+    if (component.component_type == "flutter") {
+      asset_manifest = "$target_gen_dir/build/${kernel_name}_pkgassets"
+      dart_target_gen_dir =
+          get_label_info(":bogus($dart_toolchain)", "target_gen_dir")
+      dot_packages = "$dart_target_gen_dir/$dart_library_target_name.packages"
+
+      action("${kernel_name}_resources") {
+        script = "//topaz/runtime/flutter_runner/build/asset_package.py"
+
+        args = [
+          "--flutter-root",
+          rebase_path(flutter_base),
+          "--flutter-tools",
+          rebase_path(flutter_asset_tools_bin),
+          "--app-dir",
+          rebase_path("."),
+          "--asset-dir",
+          rebase_path("$target_gen_dir/build"),
+          "--packages",
+          rebase_path(dot_packages),
+          "--asset-manifest-out",
+          rebase_path(asset_manifest),
+          "--component-name",
+          component_name,
+        ]
+
+        if (defined(invoker.manifest)) {
+          args += [
+            "--manifest",
+            rebase_path(invoker.manifest),
+          ]
+        }
+
+        deps = [
+          ":$dart_library_target_name",
+          flutter_asset_tools_label,
+        ]
+
+        outputs = [
+          asset_manifest,
+        ]
+      }
+
+      # The module manifest verify & copy targets.
+      if (defined(invoker.module_manifest)) {
+        verify_module_manifest("${kernel_name}_verify_manifest") {
+          original_target_name = kernel_name
+          module_manifest = invoker.module_manifest
+        }
+        copy_module_manifest("${kernel_name}_copy_manifest") {
+          package_name = kernel_name
+          module_manifest = rebase_path(invoker.module_manifest)
+        }
+      }
+
+      components_with_kernel += [
+        {
+          component_type = component.component_type
+          kernel_target_name = kernel_target_name
+          component_name = component_name
+          asset_manifest = asset_manifest
+
+          snapshot_target_name = snapshot_target_name
+          stats_target_name = stats_target_name
+          vm_snapshot_data_path = vm_snapshot_data_path
+          vm_snapshot_instructions_path = vm_snapshot_instructions_path
+          snapshot_data_path = snapshot_data_path
+          snapshot_instructions_path = snapshot_instructions_path
+          shared_snapshot_data_path = shared_snapshot_data_path
+          shared_snapshot_instructions_path = shared_snapshot_instructions_path
+
+          component_shared_snapshot_deps = shared_snapshot_deps
+          deps = component.deps
+          sources = component.sources
+          component_resources = "${kernel_name}_resources"
+          component_copy_manifest = "${kernel_name}_copy_manifest"
+          component_verify_manifest = "${kernel_name}_verify_manifest"
+        }
+      ]
+    } else if (component.component_type == "dart") {
+      components_with_kernel += [
+        {
+          component_type = component.component_type
+          kernel_target_name = kernel_target_name
+          component_name = component_name
+
+          snapshot_target_name = snapshot_target_name
+          stats_target_name = stats_target_name
+          snapshot_data_path = snapshot_data_path
+          snapshot_instructions_path = snapshot_instructions_path
+          shared_snapshot_data_path = shared_snapshot_data_path
+          shared_snapshot_instructions_path = shared_snapshot_instructions_path
+
+          component_shared_snapshot_deps = shared_snapshot_deps
+          deps = component.deps
+          sources = component.sources
+        }
+      ]
+    }
+  }
+
+  if (defined(invoker.resources)) {
+    invoker_resources = invoker.resources
+  } else {
+    invoker_resources = []
+  }
+
+  found_cmx = false
+  foreach(item, package_meta) {
+    dest = item.path
+    if (defined(item.dest)) {
+      dest = item.dest
+    }
+    if (get_path_info(dest, "extension") == "cmx") {
+      found_cmx = true
+    } else {
+      cmx_filtered_meta += [ item ]
+    }
+  }
+
+  if (!found_cmx) {
+    # No cmx to inject to. Inject the runner aspect as a dangling
+    # deprecated_runtime.
+    # CP-129: deprecate cmx-less components, then delete this.
+    cmx_filtered_meta += [
+      {
+        path = rebase_path(runtime_meta)
+        dest = "deprecated_runtime"
+      },
+    ]
+  }
+
+  # We have all components and their kernels generated now. We call package() to
+  # put everything into a fuchsia package, merging deps, extra, meta etc.
+  _flutter_dart_aot_package(target_name) {
+    forward_variables_from(invoker, "*")
+  }
+}
diff --git a/runtime/dart_runner/dart_app.gni b/runtime/dart_runner/dart_app.gni
index 921e598..cda49e6 100644
--- a/runtime/dart_runner/dart_app.gni
+++ b/runtime/dart_runner/dart_app.gni
@@ -45,37 +45,10 @@
   }
 }
 
-# Defines a Dart package with AOT runtime components
-#
-# Takes a set of dart components and puts them into one fuchsia package with
-# the dart_aot_runner as its runtime. Also supports legacy calls where the
-# components parameter isn't specified, in which we will create one default
-# component for the package.
-#
-# Parameters
-#
-#   components (required)
-#     [list of scopes] Defines the components in the package. Either main_dart
-#     or components must be defined, but not both.
-#
-#     Entries in a scope in the resources list:
-#
-#       component_name (required)
-#         Name of the component.
-#
-#       main_dart (required)
-#         File containing the main function of the component.
-#
-#   main_dart (required)
-#     File containing the main function of the application. Either main_dart or
-#     components must be defined, but not both.
-template("_dart_aot_package") {
+template("_dart_aot_component") {
   legacy_component = false
   pkg_name = target_name
-  components = []
-  if (defined(invoker.components)) {
-    components += invoker.components
-  } else {
+  if (!defined(invoker.components)) {
     # If components is not specified, we are fitting main_dart into a component
     # scope, and using that for the package.
     #
@@ -92,280 +65,19 @@
     if (defined(invoker.sources)) {
       pkg_sources = invoker.sources
     }
-    components += [
+    components = [
       {
         main_dart = invoker.main_dart
         component_name = legacy_component_name
+        component_type = "dart"
         deps = invoker.deps
         sources = pkg_sources
       }
     ]
   }
 
-  # Build the kernel for each of the components, and bundle them in the same
-  # scope for later packaging.
-  components_with_kernel = []
-  foreach(component, components) {
-    assert(defined(component.main_dart), "Must specify main_dart file")
-
-    product = !is_debug
-    if (defined(invoker.product)) {
-      product = invoker.product
-    }
-
-    product_suffix = ""
-    if (product) {
-      product_suffix = "_product"
-    }
-
-    if (dart_force_product) {
-      product = true
-    }
-
-    component_name = component.component_name
-    if (legacy_component) {
-      kernel_name = target_name
-    } else {
-      kernel_name = component_name
-    }
-    kernel_target_name = kernel_name + "_kernel"
-    kernel_path = "$target_gen_dir/${kernel_target_name}.dil"
-
-    dart_kernel(kernel_name) {
-      platform_name = "dart_runner"
-      platform_deps =
-          [ "//topaz/runtime/dart_runner/kernel:kernel_platform_files" ]
-      platform_path = "$root_out_dir/dart_runner_patched_sdk"
-
-      forward_variables_from(invoker,
-                             [
-                               "disable_analysis",
-                               "non_dart_deps",
-                               "source_dir",
-                               "package_name",
-                             ])
-      deps = component.deps
-      sources = component.sources
-
-      main_dart = component.main_dart
-      args = [ "--aot", "--tfa" ]
-    }
-
-
-    # TODO(rmacnak): VM snapshot is ignored. Allow skipping its generation.
-    vm_snapshot_data_path = "$target_gen_dir/${kernel_name}_vm_data.aotsnapshot"
-    vm_snapshot_instructions_path =
-        "$target_gen_dir/${kernel_name}_vm_instructions.aotsnapshot"
-    snapshot_data_path = "$target_gen_dir/${kernel_name}_data.aotsnapshot"
-    snapshot_instructions_path =
-        "$target_gen_dir/${kernel_name}_instructions.aotsnapshot"
-    snapshot_target_name = kernel_name + "_snapshot"
-
-    if (dart_aot_sharing_basis != "" &&
-        get_label_info(":$kernel_name", "label_no_toolchain") !=
-        get_label_info(dart_aot_sharing_basis, "label_no_toolchain")) {
-      # Note: The use of "label_no_toolchain" is to ensure we are comparing fully
-      # qualified target names. We don't actually care about the toolchain.
-      shared_snapshot_deps = []
-      shared_snapshot_deps =
-          [ get_label_info(dart_aot_sharing_basis, "label_no_toolchain") +
-            "_snapshot" ]
-      prefix = get_label_info(dart_aot_sharing_basis, "target_gen_dir") + "/" +
-               get_label_info(dart_aot_sharing_basis, "name")
-      shared_snapshot_data_path = "${prefix}_data.aotsnapshot"
-      shared_snapshot_instructions_path = "${prefix}_instructions.aotsnapshot"
-    } else {
-      shared_snapshot_deps = []
-      shared_snapshot_data_path = "//topaz/runtime/dart_runner/empty"
-      shared_snapshot_instructions_path = "//topaz/runtime/dart_runner/empty"
-    }
-
-    stats_target_name = "${kernel_name}_stats"
-    stats_json_path = "$target_gen_dir/${kernel_name}/stats/symbol_sizes.json"
-    stats_html_dir = "$target_gen_dir/${kernel_name}/stats"
-
-    action(snapshot_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-
-      deps = gen_snapshot_deps + shared_snapshot_deps + [ ":$kernel_target_name" ]
-      inputs = [
-                 shared_snapshot_data_path,
-                 shared_snapshot_instructions_path,
-                 kernel_path,
-               ]
-      outputs = [
-        vm_snapshot_data_path,
-        vm_snapshot_instructions_path,
-        snapshot_data_path,
-        snapshot_instructions_path,
-        stats_json_path,
-      ]
-
-      if (product) {
-        script = gen_snapshot_product
-      } else {
-        script = gen_snapshot
-      }
-
-      args = [
-        "--deterministic",
-        "--snapshot_kind=app-aot-blobs",
-        "--vm_snapshot_data=" + rebase_path(vm_snapshot_data_path),
-        "--vm_snapshot_instructions=" +
-            rebase_path(vm_snapshot_instructions_path),
-        "--isolate_snapshot_data=" + rebase_path(snapshot_data_path),
-        "--isolate_snapshot_instructions=" +
-            rebase_path(snapshot_instructions_path),
-        "--shared_data=" + rebase_path(shared_snapshot_data_path),
-        "--shared_instructions=" + rebase_path(shared_snapshot_instructions_path),
-        "--print-instructions-sizes-to=" + rebase_path(stats_json_path),
-      ]
-      if (is_debug && !product) {
-        args += [ "--enable_asserts" ]
-      }
-      args += [ rebase_path(kernel_path) ]
-
-      pool = "//build/dart:dart_pool($dart_toolchain)"
-    }
-
-    dart_action(stats_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-
-      deps = [
-        ":$snapshot_target_name",
-      ]
-      script = "//third_party/dart/pkg/vm/bin/run_binary_size_analysis.dart"
-      inputs = [
-        stats_json_path,
-      ]
-      outputs = [
-        "$stats_html_dir/index.html",
-      ]
-      args = [
-        rebase_path(stats_json_path),
-        rebase_path(stats_html_dir),
-      ]
-    }
-
-    components_with_kernel += [
-      {
-        kernel_target_name = kernel_target_name
-        component_name = component_name
-
-        snapshot_target_name = snapshot_target_name
-        stats_target_name = stats_target_name
-        snapshot_data_path = snapshot_data_path
-        snapshot_instructions_path = snapshot_instructions_path
-        shared_snapshot_data_path = shared_snapshot_data_path
-        shared_snapshot_instructions_path = shared_snapshot_instructions_path
-
-        component_shared_snapshot_deps = shared_snapshot_deps
-        deps = component.deps
-        sources = component.sources
-      }
-    ]
-  }
-
-  # If a component manifest exists, merge in the runtime portion of the
-  # manifest. If it doesn't exist, drop in the deprecated_runtime file.
-  package_meta = []
-  if (defined(invoker.meta)) {
-    package_meta = invoker.meta
-  }
-
-  # Inject appropriate "runner" into Component manifests
-  cmx_filtered_meta = []
-  cmx_deps = []
-  found_cmx = false
-  foreach(item, package_meta) {
-    dest = item.path
-    if (defined(item.dest)) {
-      dest = item.dest
-    }
-    if (get_path_info(dest, "extension") == "cmx") {
-      found_cmx = true
-      merged = "merged_" + get_path_info(dest, "file")
-      json_merge(merged) {
-        sources = [
-          item.path,
-          rebase_path(
-              "//topaz/runtime/dart_runner/meta/aot${product_suffix}_runtime"),
-        ]
-      }
-      merged_outputs = []
-      merged_outputs = get_target_outputs(":$merged")
-      item.path = merged_outputs[0]
-      cmx_deps += [ ":$merged" ]
-    }
-    cmx_filtered_meta += [ item ]
-  }
-
-  if (!found_cmx) {
-    # No cmx to inject to. Inject the runner aspect as a dangling deprecated_runtime.
-    # CP-129: deprecate cmx-less components, then delete this.
-    cmx_filtered_meta += [
-      {
-        path = rebase_path(
-                "//topaz/runtime/dart_runner/meta/aot${product_suffix}_runtime")
-        dest = "deprecated_runtime"
-      },
-    ]
-  }
-
-  # We have all components and their kernels generated now. We call package() to
-  # put everything into a fuchsia package, merging deps, extra, meta etc.
-  package(target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "testonly",
-                             "tests",
-                           ])
-
-    package_name = pkg_name
-
-    non_dart_deps = []
-    if (defined(invoker.non_dart_deps)) {
-      non_dart_deps = invoker.non_dart_deps
-    }
-    deps = non_dart_deps + cmx_deps
-    resources = []
-
-    foreach(component, components_with_kernel) {
-      deps += [
-             ":${component.snapshot_target_name}",
-             ":${component.stats_target_name}",
-           ] + component.component_shared_snapshot_deps + component.deps
-
-      resources_path = "${component.component_name}/"
-      resources += [
-        {
-          path = rebase_path(component.snapshot_data_path)
-          dest = "${resources_path}isolate_snapshot_data.bin"
-        },
-        {
-          path = rebase_path(component.snapshot_instructions_path)
-          dest = "${resources_path}isolate_snapshot_instructions.bin"
-        },
-        {
-          path = rebase_path(component.shared_snapshot_data_path)
-          dest = "${resources_path}shared_snapshot_data.bin"
-        },
-        {
-          path = rebase_path(component.shared_snapshot_instructions_path)
-          dest = "${resources_path}shared_snapshot_instructions.bin"
-        },
-      ]
-    }
-
-    if (defined(invoker.resources)) {
-      resources += invoker.resources
-    }
-
-    meta = cmx_filtered_meta
+  flutter_dart_aot_component(target_name) {
+    forward_variables_from(invoker, "*")
   }
 }
 
@@ -381,7 +93,7 @@
 }
 
 template("dart_aot_app") {
-  template_name = "_dart_aot_package"
+  template_name = "_dart_aot_component"
   if (dart_force_product) {
     template_name = dart_product_app
   }
diff --git a/runtime/flutter_runner/flutter_app.gni b/runtime/flutter_runner/flutter_app.gni
index 4773cf8..2ee44d9 100644
--- a/runtime/flutter_runner/flutter_app.gni
+++ b/runtime/flutter_runner/flutter_app.gni
@@ -71,81 +71,6 @@
   }
 }
 
-template("_flutter_aot_package") {
-  package(target_name) {
-    package_name = invoker.pkg_name
-    deps = invoker.cmx_deps
-    extra = []
-    resources = []
-
-    if (defined(invoker.invoker_resources)) {
-      resources += invoker.invoker_resources
-    }
-
-    foreach(component, invoker.components_with_kernel) {
-      extra += [
-        component.asset_manifest,
-      ]
-
-      deps += [
-             ":${component.snapshot_target_name}",
-             ":${component.component_resources}",
-             ":${component.stats_target_name}",
-           ] + component.component_shared_snapshot_deps + component.deps
-      if (defined(invoker.module_manifest)) {
-        deps += [
-          ":${component.component_copy_manifest}",
-          ":${component.component_verify_manifest}",
-        ]
-      }
-
-      resources_path = "${component.component_name}/"
-      resources += [
-                    {
-                      path = rebase_path(component.vm_snapshot_data_path)
-                      dest = "${resources_path}vm_snapshot_data.bin"
-                    },
-                    {
-                      path = rebase_path(component.vm_snapshot_instructions_path)
-                      dest = "${resources_path}vm_snapshot_instructions.bin"
-                    },
-                    {
-                      path = rebase_path(component.snapshot_data_path)
-                      dest = "${resources_path}isolate_snapshot_data.bin"
-                    },
-                    {
-                      path = rebase_path(component.snapshot_instructions_path)
-                      dest = "${resources_path}isolate_snapshot_instructions.bin"
-                    },
-                    {
-                      path = rebase_path(component.shared_snapshot_data_path)
-                      dest = "${resources_path}shared_snapshot_data.bin"
-                    },
-                    {
-                      path = rebase_path(component.shared_snapshot_instructions_path)
-                      dest = "${resources_path}shared_snapshot_instructions.bin"
-                    },
-                  ]
-    }
-
-    if (defined(invoker.non_dart_deps)) {
-      deps += invoker.non_dart_deps
-    }
-
-    meta = invoker.cmx_filtered_meta
-
-    # TODO(CP-140): Support module_manifest per component.
-    if (defined(invoker.module_manifest)) {
-      meta += [
-        {
-          path = rebase_path(invoker.module_manifest)
-          dest = "module.json"
-        },
-      ]
-    }
-  }
-}
-
 # Defines AOT runtime components to be further distributed in one package.
 #
 # Takes a set of flutter components and puts them into one fuchsia package with
@@ -175,12 +100,9 @@
   if (defined(invoker.flutter_driver_extendable)) {
     not_needed(invoker, ["flutter_driver_extendable",])
   }
-  legacy_component = false
   pkg_name = target_name
-  components = []
-  if (defined(invoker.components)) {
-    components += invoker.components
-  } else {
+  legacy_component = false
+  if (!defined(invoker.components)) {
     # If components is not specified, we are fitting main_dart into a component
     # scope, and using that for the package.
     #
@@ -197,300 +119,17 @@
     if (defined(invoker.sources)) {
       pkg_sources = invoker.sources
     }
-    components += [
+    components = [
       {
         main_dart = invoker.main_dart
         component_name = legacy_component_name
+        component_type = "flutter"
         deps = invoker.deps
         sources = pkg_sources
       }
     ]
   }
-
-  product = !flutter_profile
-  if (defined(invoker.product)) {
-    product = invoker.product
-  }
-
-  if (dart_force_product) {
-    product = true
-  }
-
-  product_suffix = ""
-  if (product) {
-    product_suffix = "_product"
-  }
-
-  # Build the kernel for each of the components, and bundle them in the same
-  # scope for later packaging.
-  components_with_kernel = []
-  foreach(component, components) {
-    assert(defined(component.main_dart), "Must specify main_dart file")
-
-    component_name = component.component_name
-    if (legacy_component) {
-      kernel_name = target_name
-    } else {
-      kernel_name = component_name
-    }
-    dart_library_target_name = kernel_name + "_dart_library"
-    kernel_target_name = kernel_name + "_kernel"
-    kernel_path = "$target_gen_dir/${kernel_target_name}.dil"
-
-    dart_kernel(kernel_name) {
-      platform_name = "flutter_runner"
-      platform_deps =
-          [ "//topaz/runtime/flutter_runner/kernel:kernel_platform_files" ]
-      platform_path = "$root_out_dir/flutter_runner_patched_sdk"
-
-      forward_variables_from(invoker,
-                             [
-                               "disable_analysis",
-                               "non_dart_deps",
-                               "source_dir",
-                               "package_name",
-                             ])
-      deps = component.deps
-      sources = component.sources
-
-      main_dart = component.main_dart
-      args = [ "--aot", "--tfa" ]
-    }
-
-    # TODO(rmacnak): Don't bake the VM service into each app.
-    vm_snapshot_data_path = "$target_gen_dir/${kernel_name}_vm_data.aotsnapshot"
-    vm_snapshot_instructions_path =
-        "$target_gen_dir/${kernel_name}_vm_instructions.aotsnapshot"
-    snapshot_data_path = "$target_gen_dir/${kernel_name}_data.aotsnapshot"
-    snapshot_instructions_path =
-        "$target_gen_dir/${kernel_name}_instructions.aotsnapshot"
-    snapshot_target_name = kernel_name + "_snapshot"
-
-    if (flutter_aot_sharing_basis != "" &&
-        get_label_info(":$kernel_name", "label_no_toolchain") !=
-        get_label_info(flutter_aot_sharing_basis, "label_no_toolchain")) {
-      # Note: The use of "label_no_toolchain" is to ensure we are comparing fully
-      # qualified target names. We don't actually care about the toolchain.
-      shared_snapshot_deps = []
-      shared_snapshot_deps =
-          [ get_label_info(flutter_aot_sharing_basis, "label_no_toolchain") +
-            "_snapshot" ]
-      prefix = get_label_info(flutter_aot_sharing_basis, "target_gen_dir") + "/" +
-               get_label_info(flutter_aot_sharing_basis, "name")
-      shared_snapshot_data_path = "${prefix}_data.aotsnapshot"
-      shared_snapshot_instructions_path = "${prefix}_instructions.aotsnapshot"
-    } else {
-      shared_snapshot_deps = []
-      shared_snapshot_data_path = "//topaz/runtime/dart_runner/empty"
-      shared_snapshot_instructions_path = "//topaz/runtime/dart_runner/empty"
-    }
-
-    stats_target_name = "${kernel_name}_stats"
-    stats_json_path = "$target_gen_dir/${kernel_name}/stats/symbol_sizes.json"
-    stats_html_dir = "$target_gen_dir/${kernel_name}/stats"
-
-    action(snapshot_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-
-      deps = gen_snapshot_deps + shared_snapshot_deps + [ ":$kernel_target_name" ]
-      inputs = [
-                 shared_snapshot_data_path,
-                 shared_snapshot_instructions_path,
-                 kernel_path,
-               ]
-      outputs = [
-        vm_snapshot_data_path,
-        vm_snapshot_instructions_path,
-        snapshot_data_path,
-        snapshot_instructions_path,
-        stats_json_path,
-      ]
-
-      if (product) {
-        script = gen_snapshot_product
-      } else {
-        script = gen_snapshot
-      }
-
-      args = [
-        "--deterministic",
-        "--snapshot_kind=app-aot-blobs",
-        "--vm_snapshot_data=" + rebase_path(vm_snapshot_data_path),
-        "--vm_snapshot_instructions=" +
-            rebase_path(vm_snapshot_instructions_path),
-        "--isolate_snapshot_data=" + rebase_path(snapshot_data_path),
-        "--isolate_snapshot_instructions=" +
-            rebase_path(snapshot_instructions_path),
-        "--shared_data=" + rebase_path(shared_snapshot_data_path),
-        "--shared_instructions=" + rebase_path(shared_snapshot_instructions_path),
-        "--print-instructions-sizes-to=" + rebase_path(stats_json_path),
-      ]
-      if (is_debug && !product) {
-        args += [ "--enable_asserts" ]
-      }
-      args += [ rebase_path(kernel_path) ]
-
-      pool = "//build/dart:dart_pool($dart_toolchain)"
-    }
-
-    dart_action(stats_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-
-      deps = [
-        ":$snapshot_target_name",
-      ]
-      script = "//third_party/dart/pkg/vm/bin/run_binary_size_analysis.dart"
-      inputs = [
-        stats_json_path,
-      ]
-      outputs = [
-        "$stats_html_dir/index.html",
-      ]
-      args = [
-        rebase_path(stats_json_path),
-        rebase_path(stats_html_dir),
-      ]
-    }
-
-    if (defined(invoker.resources)) {
-      invoker_resources = invoker.resources
-    } else {
-      invoker_resources = []
-    }
-
-    asset_manifest = "$target_gen_dir/build/${kernel_name}_pkgassets"
-    dart_target_gen_dir =
-        get_label_info(":bogus($dart_toolchain)", "target_gen_dir")
-    dot_packages = "$dart_target_gen_dir/$dart_library_target_name.packages"
-
-    action("${kernel_name}_resources") {
-      script = "//topaz/runtime/flutter_runner/build/asset_package.py"
-
-      args = [
-        "--flutter-root",
-        rebase_path(flutter_base),
-        "--flutter-tools",
-        rebase_path(flutter_asset_tools_bin),
-        "--app-dir",
-        rebase_path("."),
-        "--asset-dir",
-        rebase_path("$target_gen_dir/build"),
-        "--packages",
-        rebase_path(dot_packages),
-        "--asset-manifest-out",
-        rebase_path(asset_manifest),
-        "--component-name",
-        component_name,
-      ]
-
-      if (defined(invoker.manifest)) {
-        args += [
-          "--manifest",
-          rebase_path(invoker.manifest),
-        ]
-      }
-
-      deps = [
-        ":$dart_library_target_name",
-        flutter_asset_tools_label,
-      ]
-
-      outputs = [
-        asset_manifest,
-      ]
-    }
-
-    # The module manifest verify & copy targets.
-    if (defined(invoker.module_manifest)) {
-      verify_module_manifest("${kernel_name}_verify_manifest") {
-        original_target_name = kernel_name
-        module_manifest = invoker.module_manifest
-      }
-      copy_module_manifest("${kernel_name}_copy_manifest") {
-        package_name = kernel_name
-        module_manifest = rebase_path(invoker.module_manifest)
-      }
-    }
-
-    components_with_kernel += [
-      {
-        kernel_target_name = kernel_target_name
-        component_name = component_name
-        asset_manifest = asset_manifest
-
-        snapshot_target_name = snapshot_target_name
-        stats_target_name = stats_target_name
-        vm_snapshot_data_path = vm_snapshot_data_path
-        vm_snapshot_instructions_path = vm_snapshot_instructions_path
-        snapshot_data_path = snapshot_data_path
-        snapshot_instructions_path = snapshot_instructions_path
-        shared_snapshot_data_path = shared_snapshot_data_path
-        shared_snapshot_instructions_path = shared_snapshot_instructions_path
-
-        component_shared_snapshot_deps = shared_snapshot_deps
-        deps = component.deps
-        sources = component.sources
-        component_resources = "${kernel_name}_resources"
-        component_copy_manifest = "${kernel_name}_copy_manifest"
-        component_verify_manifest = "${kernel_name}_verify_manifest"
-      }
-    ]
-  }
-
-  invoker_resources = []
-  if (defined(invoker.resources)) {
-    invoker_resources += invoker.resources
-  }
-
-  package_meta = []
-  if (defined(invoker.meta)) {
-    package_meta = invoker.meta
-  }
-
-  # Inject appropriate "runner" into Component manifests
-  cmx_filtered_meta = []
-  cmx_deps = []
-  found_cmx = false
-  foreach(item, package_meta) {
-    dest = item.path
-    if (defined(item.dest)) {
-      dest = item.dest
-    }
-    if (get_path_info(dest, "extension") == "cmx") {
-      found_cmx = true
-      merged = "merged_" + get_path_info(dest, "file")
-      json_merge(merged) {
-        sources = [
-          item.path,
-          rebase_path(
-              "//topaz/runtime/flutter_runner/meta/aot${product_suffix}_runtime"),
-        ]
-      }
-      merged_outputs = []
-      merged_outputs = get_target_outputs(":$merged")
-      item.path = merged_outputs[0]
-      cmx_deps += [ ":$merged" ]
-    }
-    cmx_filtered_meta += [ item ]
-  }
-
-  if (!found_cmx) {
-    # No cmx to inject to. Inject the runner aspect as a dangling deprecated_runtime.
-    # CP-129: deprecate cmx-less components, then delete this.
-    cmx_filtered_meta += [
-      {
-        path = rebase_path(
-                "//topaz/runtime/flutter_runner/meta/aot${product_suffix}_runtime")
-        dest = "deprecated_runtime"
-      },
-    ]
-  }
-
-  _flutter_aot_package(target_name) {
+  flutter_dart_aot_component(target_name) {
     forward_variables_from(invoker, "*")
   }
 }