[build] simplify bootfs inclusion

Change-Id: I3273bf80c2a371283eb67328bd06a153eb570448
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/380602
Commit-Queue: Derek Gonyeo <dgonyeo@google.com>
Reviewed-by: Bryan Henry <bryanhenry@google.com>
diff --git a/build/images/BUILD.gn b/build/images/BUILD.gn
index 476841b..1d7e08b 100644
--- a/build/images/BUILD.gn
+++ b/build/images/BUILD.gn
@@ -11,6 +11,7 @@
 import("//build/config/fuchsia/zircon_legacy_vars.gni")
 import("//build/images/args.gni")
 import("//build/images/boot.gni")
+import("//build/images/bootfs_manifest_additions.gni")
 import("//build/images/collect_blob_manifest.gni")
 import("//build/images/custom_signing.gni")
 import("//build/images/filesystem_limits.gni")
@@ -550,24 +551,12 @@
   contents = zxcrypt_key_source
 }
 
-coordinator_label = "//src/devices:devices.bootfs"
-coordinator_target_dir = get_label_info(coordinator_label, "target_out_dir")
-coordinator_target_name = get_label_info(coordinator_label, "name")
-coordinator_manifest = coordinator_target_dir + "/" + coordinator_target_name
-
-component_manager_label = "//src/sys/component_manager:component_manager.bootfs"
-component_manager_target_dir =
-    get_label_info(component_manager_label, "target_out_dir")
-component_manager_target_name = get_label_info(component_manager_label, "name")
-component_manager_manifest =
-    component_manager_target_dir + "/" + component_manager_target_name
-
-root_manifests_label = "//src/sys/bootstrap:root_manifests.bootfs"
-root_manifests_target_dir =
-    get_label_info(root_manifests_label, "target_out_dir")
-root_manifests_target_name = get_label_info(root_manifests_label, "name")
-root_manifests_manifest =
-    root_manifests_target_dir + "/" + root_manifests_target_name
+additional_manifests = []
+foreach(label, bootfs_manifest_additions) {
+  target_dir = get_label_info(label, "target_out_dir")
+  target_name = get_label_info(label, "name")
+  additional_manifests += [ target_dir + "/" + target_name ]
+}
 
 # The main bootable image, which requires `blob.blk` to appear on some
 # attached storage device at runtime.
@@ -575,19 +564,13 @@
   testonly = true
 
   deps = [
-    ":devmgr_config.txt",
-    ":system_image.manifest",
-    component_manager_label,
-    coordinator_label,
-    root_manifests_label,
-  ]
+           ":devmgr_config.txt",
+           ":system_image.manifest",
+         ] + bootfs_manifest_additions
   inputs = [
-    boot_manifest,
-    component_manager_manifest,
-    root_manifests_manifest,
-    coordinator_manifest,
-    zircon_kernel_zbi,
-  ]
+             boot_manifest,
+             zircon_kernel_zbi,
+           ] + additional_manifests
   manifest = []
   if (!include_devmgr_config_in_vbmeta) {
     manifest += [
diff --git a/build/images/assemble_system.gni b/build/images/assemble_system.gni
index 15cea44..3fdd08e 100644
--- a/build/images/assemble_system.gni
+++ b/build/images/assemble_system.gni
@@ -7,6 +7,7 @@
 import("//build/config/fuchsia/zbi.gni")
 import("//build/config/fuchsia/zircon.gni")
 import("//build/config/fuchsia/zircon_images.gni")
+import("//build/images/bootfs_manifest_additions.gni")
 import("//build/images/collect_blob_manifest.gni")
 import("//build/images/fvm.gni")
 import("//build/images/pkgfs.gni")
@@ -333,26 +334,12 @@
     contents = zxcrypt_key_source
   }
 
-  component_manager_label =
-      "//src/sys/component_manager:component_manager.bootfs"
-  component_manager_target_dir =
-      get_label_info(component_manager_label, "target_out_dir")
-  component_manager_target_name =
-      get_label_info(component_manager_label, "name")
-  component_manager_manifest =
-      component_manager_target_dir + "/" + component_manager_target_name
-
-  coordinator_label = "//src/devices:devices.bootfs"
-  coordinator_target_dir = get_label_info(coordinator_label, "target_out_dir")
-  coordinator_target_name = get_label_info(coordinator_label, "name")
-  coordinator_manifest = coordinator_target_dir + "/" + coordinator_target_name
-
-  root_manifests_label = "//src/sys/bootstrap:root_manifests.bootfs"
-  root_manifests_target_dir =
-      get_label_info(root_manifests_label, "target_out_dir")
-  root_manifests_target_name = get_label_info(root_manifests_label, "name")
-  root_manifests_manifest =
-      root_manifests_target_dir + "/" + root_manifests_target_name
+  additional_manifests = []
+  foreach(label, bootfs_manifest_additions) {
+    target_dir = get_label_info(label, "target_out_dir")
+    target_name = get_label_info(label, "name")
+    additional_manifests += [ target_dir + "/" + target_name ]
+  }
 
   zbi(image_name) {
     metadata = {
@@ -382,21 +369,15 @@
 
     testonly = true
     deps = [
-      ":${image_name}_devmgr_config.txt",
-      ":${image_name}_fvm.blk",
-      ":${image_name}_system_image.manifest",
-      ":${image_name}_zxcrypt_config.txt",
-      component_manager_label,
-      coordinator_label,
-      root_manifests_label,
-    ]
+             ":${image_name}_devmgr_config.txt",
+             ":${image_name}_fvm.blk",
+             ":${image_name}_system_image.manifest",
+             ":${image_name}_zxcrypt_config.txt",
+           ] + bootfs_manifest_additions
     inputs = [
-      boot_manifest,
-      component_manager_manifest,
-      coordinator_manifest,
-      root_manifests_manifest,
-      zircon_kernel_zbi,
-    ]
+               boot_manifest,
+               zircon_kernel_zbi,
+             ] + additional_manifests
     manifest = [
       {
         outputs = [ "config/devmgr" ]
diff --git a/build/images/bootfs_manifest.gni b/build/images/bootfs_manifest.gni
new file mode 100644
index 0000000..8e0a6bb
--- /dev/null
+++ b/build/images/bootfs_manifest.gni
@@ -0,0 +1,159 @@
+# Copyright 2020 The Fuchsia 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("//build/images/manifest.gni")
+import("//src/sys/cmc/build/cml.gni")
+
+# Helper template to create manifests for including items in bootfs.
+#
+# Parameters
+#
+#   binaries (optional)
+#     [list of scopes] Defines binaries in the ZBI. A binary is typically
+#     produced by the build system and is always placed in the `bin/` directory
+#     of the ZBI.
+#
+#     Entries in a scope in the binaries list:
+#
+#       name (required)
+#         [string] Name of the binary.
+#
+#       source (optional)
+#         [path] Location of the binary in the build directory if it is not
+#         at `$root_out_dir/$name`.
+#
+#       dest (optional)
+#         [path] Location the binary will be placed within `bin/`.
+#
+#   drivers (optional)
+#     [list of scopes] Defines drivers in the ZBI. A driver is typically
+#     produced by the build system and is always placed in the `driver/`
+#     directory of the ZBI.
+#
+#     Entries in a scope in the binaries list:
+#
+#       name (required)
+#         [string] Name of the driver.
+#
+#       source (optional)
+#         [path] Location of the driver in the build directory if it is not
+#         at `$root_out_dir/$name`.
+#
+#       dest (optional)
+#         [path] Location the driver will be placed within `driver/`.
+#
+#   meta (optional)
+#     [list of scopes] Defines metadata entries in the ZBI. A metadata entry is
+#     typically a source file and is always placed in the `meta/` directory of
+#     the assembled package. Only CML files may be listed here.
+#
+#     Entries in a scope in the meta list:
+#
+#       path (required)
+#         [path] Location of entry in source or build directory. If the
+#         resource is checked in, this will typically be specified as a
+#         path relative to the BUILD.gn file containing the `bootfs_manifest()`
+#         target. If the resource is generated, this will typically be
+#         specified relative to `$target_gen_dir`.
+#
+#       dest (optional)
+#         [path] Location the resource will be placed within `meta/`.
+#
+#   deps (optional)
+#     Usual GN meanings.
+#
+template("bootfs_manifest") {
+  forward_variables_from(invoker,
+                         [
+                           "binaries",
+                           "drivers",
+                           "meta",
+                         ])
+  if (!defined(binaries)) {
+    binaries = []
+  }
+  if (!defined(drivers)) {
+    drivers = []
+  }
+  if (!defined(meta)) {
+    meta = []
+  }
+
+  manifest_deps = []
+  if (defined(invoker.deps)) {
+    manifest_deps = invoker.deps
+  }
+
+  manifest_args = []
+  foreach(binary, binaries) {
+    if (defined(binary.dest)) {
+      dest = binary.dest
+    } else {
+      dest = binary.name
+    }
+    dest = "bin/" + dest
+
+    if (defined(binary.source)) {
+      source = binary.source
+    } else {
+      source = binary.name
+    }
+    source = rebase_path(source, "", root_out_dir)
+
+    manifest_args += [ "--entry=" + dest + "=" + source ]
+  }
+
+  foreach(driver, drivers) {
+    if (defined(driver.dest)) {
+      dest = driver.dest
+    } else {
+      dest = driver.name
+    }
+    dest = "driver/" + dest
+
+    if (defined(driver.source)) {
+      source = driver.source
+    } else {
+      source = driver.name
+    }
+    source = rebase_path(source, "", root_out_dir)
+
+    manifest_args += [ "--entry=" + dest + "=" + source ]
+  }
+
+  foreach(m, meta) {
+    assert(defined(m.path), "path is required")
+
+    assert(get_path_info(m.path, "extension") == "cml",
+           "only component manifests are supported in /boot/meta")
+    if (defined(m.dest)) {
+      assert(get_path_info(m.dest, "extension") == "cm",
+             "only component manifests are supported in /boot/meta")
+      dest = m.dest
+    } else {
+      filename = get_path_info(m.path, "file")
+      dest = string_replace(filename, ".cml", ".cm")
+    }
+
+    compile_label = "compile_boot_" + get_path_info(dest, "file")
+    cm(compile_label) {
+      data = m.path
+    }
+    manifest_deps += [ ":$compile_label" ]
+
+    dest = "meta/" + dest
+
+    cm_out = []
+    cm_out = get_target_outputs(":$compile_label")
+
+    source = rebase_path(cm_out[0])
+
+    manifest_args += [ "--entry=" + dest + "=" + source ]
+  }
+
+  generate_manifest(target_name) {
+    deps = manifest_deps
+    args = manifest_args
+  }
+}
diff --git a/build/images/bootfs_manifest_additions.gni b/build/images/bootfs_manifest_additions.gni
new file mode 100644
index 0000000..897b5e2
--- /dev/null
+++ b/build/images/bootfs_manifest_additions.gni
@@ -0,0 +1,13 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# The labels in this list are bootfs manifests (created with the template in
+# //build/images/bootfs_manifest.gni) that describe files from the GN build that
+# are to be included in the ZBI on every build.
+bootfs_manifest_additions = [
+  "//src/devices/bin/driver_manager:driver_manager.manifest",
+  "//src/devices:devices.manifest",
+  "//src/sys/bootstrap:root_manifests.manifest",
+  "//src/sys/component_manager:component_manager_bootfs.manifest",
+]
diff --git a/build/images/bringup/BUILD.gn b/build/images/bringup/BUILD.gn
index af88a84..8e4c01f8 100644
--- a/build/images/bringup/BUILD.gn
+++ b/build/images/bringup/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/fuchsia/zbi.gni")
 import("//build/images/args.gni")
 import("//build/images/boot.gni")
+import("//build/images/bootfs_manifest_additions.gni")
 import("//build/images/custom_signing.gni")
 import("//build/images/manifest.gni")
 import("//build/images/ta.gni")
@@ -49,43 +50,24 @@
   }
 }
 
-coordinator_label = "//src/devices:devices.bootfs"
-coordinator_target_dir = get_label_info(coordinator_label, "target_out_dir")
-coordinator_target_name = get_label_info(coordinator_label, "name")
-coordinator_manifest = coordinator_target_dir + "/" + coordinator_target_name
-
-component_manager_label = "//src/sys/component_manager:component_manager.bootfs"
-component_manager_target_dir =
-    get_label_info(component_manager_label, "target_out_dir")
-component_manager_target_name = get_label_info(component_manager_label, "name")
-component_manager_manifest =
-    component_manager_target_dir + "/" + component_manager_target_name
-
-root_manifests_label = "//src/sys/bootstrap:root_manifests.bootfs"
-root_manifests_target_dir =
-    get_label_info(root_manifests_label, "target_out_dir")
-root_manifests_target_name = get_label_info(root_manifests_label, "name")
-root_manifests_manifest =
-    root_manifests_target_dir + "/" + root_manifests_target_name
+additional_manifests = []
+foreach(label, bootfs_manifest_additions) {
+  target_dir = get_label_info(label, "target_out_dir")
+  target_name = get_label_info(label, "name")
+  additional_manifests += [ target_dir + "/" + target_name ]
+}
 
 zbi("bringup") {
   testonly = true
 
   deps = [
-    ":boot.manifest",
-    ":devmgr_config.txt",
-    component_manager_label,
-    coordinator_label,
-    root_manifests_label,
-  ]
+           ":boot.manifest",
+           ":devmgr_config.txt",
+         ] + bootfs_manifest_additions
 
   boot_manifest_outputs = get_target_outputs(":boot.manifest")
-  inputs = [ boot_manifest_outputs[0] ] + [
-             component_manager_manifest,
-             coordinator_manifest,
-             root_manifests_manifest,
-             zircon_kernel_zbi,
-           ]
+  inputs = [ boot_manifest_outputs[0] ] + [ zircon_kernel_zbi ] +
+           additional_manifests
 
   manifest = [
     {
diff --git a/build/images/zedboot/BUILD.gn b/build/images/zedboot/BUILD.gn
index cdf4498..c3a6e17 100644
--- a/build/images/zedboot/BUILD.gn
+++ b/build/images/zedboot/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/fuchsia/zbi.gni")
 import("//build/images/args.gni")
 import("//build/images/boot.gni")
+import("//build/images/bootfs_manifest_additions.gni")
 import("//build/images/custom_signing.gni")
 import("//build/images/custom_signing.gni")
 import("//build/images/manifest.gni")
@@ -116,50 +117,27 @@
   contents = zxcrypt_key_source
 }
 
-component_manager_label = "//src/sys/component_manager:component_manager.bootfs"
-component_manager_target_dir =
-    get_label_info(component_manager_label, "target_out_dir")
-component_manager_target_name = get_label_info(component_manager_label, "name")
-component_manager_manifest =
-    component_manager_target_dir + "/" + component_manager_target_name
+bootfs_manifest_additions += [ "//src/sys/installer:installer.manifest" ]
 
-coordinator_label = "//src/devices:devices.bootfs"
-coordinator_target_dir = get_label_info(coordinator_label, "target_out_dir")
-coordinator_target_name = get_label_info(coordinator_label, "name")
-coordinator_manifest = coordinator_target_dir + "/" + coordinator_target_name
-
-installer_label = "//src/sys/installer:installer.bootfs"
-installer_target_dir = get_label_info(installer_label, "target_out_dir")
-installer_target_name = get_label_info(installer_label, "name")
-installer_manifest = installer_target_dir + "/" + installer_target_name
-
-root_manifests_label = "//src/sys/bootstrap:root_manifests.bootfs"
-root_manifests_target_dir =
-    get_label_info(root_manifests_label, "target_out_dir")
-root_manifests_target_name = get_label_info(root_manifests_label, "name")
-root_manifests_manifest =
-    root_manifests_target_dir + "/" + root_manifests_target_name
+additional_manifests = []
+foreach(label, bootfs_manifest_additions) {
+  target_dir = get_label_info(label, "target_out_dir")
+  target_name = get_label_info(label, "name")
+  additional_manifests += [ target_dir + "/" + target_name ]
+}
 
 # Note: This output is also used by a script in //scripts/verify_cmdline_params.
 zbi("zbi") {
   output_name = "zedboot"
   deps = [
-    ":devmgr_config.txt",
-    ":zedboot.manifest",
-    ":zxcrypt_config.txt",
-    component_manager_label,
-    coordinator_label,
-    installer_label,
-    root_manifests_label,
-  ]
+           ":devmgr_config.txt",
+           ":zedboot.manifest",
+           ":zxcrypt_config.txt",
+         ] + bootfs_manifest_additions
   inputs = [
-    component_manager_manifest,
-    coordinator_manifest,
-    installer_manifest,
-    root_manifests_manifest,
-    zircon_kernel_zbi,
-    manifest_file,
-  ]
+             zircon_kernel_zbi,
+             manifest_file,
+           ] + additional_manifests
   manifest =
       [ "config/zxcrypt=" +
         rebase_path("$target_out_dir/zxcrypt_config.txt", root_build_dir) ]
diff --git a/src/devices/BUILD.gn b/src/devices/BUILD.gn
index 6e51ae4..20ed830 100644
--- a/src/devices/BUILD.gn
+++ b/src/devices/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/images/bootfs_manifest.gni")
 import("//build/package.gni")
 
 group("devices") {
@@ -39,7 +40,7 @@
   ]
 }
 
-generate_manifest("devices.bootfs") {
+bootfs_manifest("devices.manifest") {
   deps = [
     "bin",
     "lib/driver",
@@ -48,19 +49,22 @@
   ]
 
   # TODO(bwb): match the file in the image to the respective build name
-  args = [
-    "--entry=bin/driver_manager=" +
-        rebase_path("$root_build_dir/driver_manager", root_build_dir),
-    "--entry=bin/driver_host=" +
-        rebase_path("$root_build_dir/driver_host", root_build_dir),
+  binaries = [
+    {
+      name = "driver_host"
+    },
 
     # Inject Virtcon into the ZBI
     # TODO(41410): Remove when unified build exists.
-    "--entry=bin/virtual-console=" +
-        rebase_path("$root_build_dir/virtual-console", root_build_dir),
+    {
+      name = "virtual-console"
+    },
+  ]
 
+  drivers = [
     # Drivers for ZBI injection
-    "--entry=driver/hid-input-report.so=" +
-        rebase_path("$root_build_dir/hid-input-report.so", root_build_dir),
+    {
+      name = "hid-input-report.so"
+    },
   ]
 }
diff --git a/src/devices/bin/driver_manager/BUILD.gn b/src/devices/bin/driver_manager/BUILD.gn
index 0c5157d..83e61cd 100644
--- a/src/devices/bin/driver_manager/BUILD.gn
+++ b/src/devices/bin/driver_manager/BUILD.gn
@@ -2,11 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/images/bootfs_manifest.gni")
 import("//build/test.gni")
 import("//build/test/test_package.gni")
 import("//build/test/test_package.gni")
 import("//build/testing/environments.gni")
-import("//src/sys/cmc/build/cml.gni")
 
 group("tests") {
   testonly = true
@@ -109,10 +109,6 @@
   defines = [ "_ALL_SOURCE=1" ]
 }
 
-cm("driver_manager.cm") {
-  data = rebase_path("meta/driver_manager.cml")
-}
-
 test("driver_manager_test") {
   sources = [
     "binding_test.cc",
@@ -158,3 +154,17 @@
     },
   ]
 }
+
+bootfs_manifest("driver_manager.manifest") {
+  deps = [ ":driver_manager" ]
+  binaries = [
+    {
+      name = "driver_manager"
+    },
+  ]
+  meta = [
+    {
+      path = rebase_path("meta/driver_manager.cml")
+    },
+  ]
+}
diff --git a/src/sys/bootstrap/BUILD.gn b/src/sys/bootstrap/BUILD.gn
index a750e12..3c846eb 100644
--- a/src/sys/bootstrap/BUILD.gn
+++ b/src/sys/bootstrap/BUILD.gn
@@ -2,60 +2,30 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/images/manifest.gni")
-import("//src/sys/cmc/build/cml.gni")
+import("//build/images/bootfs_manifest.gni")
 
-cm("root.cm") {
-  data = rebase_path("meta/root.cml")
-}
-cm("bootstrap.cm") {
-  data = rebase_path("meta/bootstrap.cml")
-}
+bootfs_manifest("root_manifests.manifest") {
+  meta = [
+    {
+      path = rebase_path("meta/root.cml")
+    },
+    {
+      path = rebase_path("meta/bootstrap.cml")
+    },
 
-# TODO(BLD-448): These should move into their appropriate packages that are
-# included in bootfs along with the binaries and other resources/libraries.
-cm("console.cm") {
-  data = rebase_path("meta/console.cml")
-}
-cm("ptysvc.cm") {
-  data = rebase_path("meta/ptysvc.cml")
-}
-cm("sysinfo.cm") {
-  data = rebase_path("meta/sysinfo.cml")
-}
-cm("fshost.cm") {
-  data = rebase_path("meta/fshost.cml")
-}
-
-driver_manager_cm = "//src/devices/bin/driver_manager:driver_manager.cm"
-
-# TODO(BLD-448): Should become a package just containing the root manifests.
-generate_manifest("root_manifests.bootfs") {
-  deps = [
-    ":bootstrap.cm",
-    ":console.cm",
-    ":fshost.cm",
-    ":ptysvc.cm",
-    ":root.cm",
-    ":sysinfo.cm",
-    driver_manager_cm,
-  ]
-
-  root_cm_out = get_target_outputs(":root.cm")
-  bootstrap_cm_out = get_target_outputs(":bootstrap.cm")
-  devc_cm_out =
-      get_label_info(driver_manager_cm, "target_out_dir") + "/driver_manager.cm"
-  fshost_cm_out = get_target_outputs(":fshost.cm")
-  console_cm_out = get_target_outputs(":console.cm")
-  ptysvc_cm_out = get_target_outputs(":ptysvc.cm")
-  sysinfo_cm_out = get_target_outputs(":sysinfo.cm")
-  args = [
-    "--entry=meta/root.cm=" + rebase_path(root_cm_out[0]),
-    "--entry=meta/bootstrap.cm=" + rebase_path(bootstrap_cm_out[0]),
-    "--entry=meta/driver_manager.cm=" + rebase_path(devc_cm_out),
-    "--entry=meta/console.cm=" + rebase_path(console_cm_out[0]),
-    "--entry=meta/ptysvc.cm=" + rebase_path(ptysvc_cm_out[0]),
-    "--entry=meta/sysinfo.cm=" + rebase_path(sysinfo_cm_out[0]),
-    "--entry=meta/fshost.cm=" + rebase_path(fshost_cm_out[0]),
+    # TODO(BLD-448): These should move into their appropriate packages that are
+    # included in bootfs along with the binaries and other resources/libraries.
+    {
+      path = rebase_path("meta/console.cml")
+    },
+    {
+      path = rebase_path("meta/ptysvc.cml")
+    },
+    {
+      path = rebase_path("meta/sysinfo.cml")
+    },
+    {
+      path = rebase_path("meta/fshost.cml")
+    },
   ]
 }
diff --git a/src/sys/component_manager/BUILD.gn b/src/sys/component_manager/BUILD.gn
index 517c8c4..f5d2279 100644
--- a/src/sys/component_manager/BUILD.gn
+++ b/src/sys/component_manager/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/images/bootfs_manifest.gni")
 import("//build/package.gni")
 import("//build/rust/rustc_binary.gni")
 import("//build/rust/rustc_library.gni")
@@ -131,11 +132,14 @@
 }
 
 # This manifest is consumed by the ZBI rule in //build/images to add component_manager to bootfs.
-generate_manifest("component_manager.bootfs") {
+bootfs_manifest("component_manager_bootfs.manifest") {
   deps = [ ":bin" ]
 
-  args = [ "--entry=bin/component_manager=" +
-           rebase_path("$root_build_dir/component_manager", root_build_dir) ]
+  binaries = [
+    {
+      name = "component_manager"
+    },
+  ]
 }
 
 # Note that this package and the component it contains wrap component_manager
diff --git a/src/sys/installer/BUILD.gn b/src/sys/installer/BUILD.gn
index 552b461f..f6e28b5 100644
--- a/src/sys/installer/BUILD.gn
+++ b/src/sys/installer/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/images/bootfs_manifest.gni")
 import("//build/package.gni")
 import("//build/rust/rustc_binary.gni")
 import("//build/test/test_package.gni")
@@ -34,11 +35,14 @@
   ]
 }
 
-generate_manifest("installer.bootfs") {
+bootfs_manifest("installer.manifest") {
   deps = [ ":bin" ]
 
-  args = [ "--entry=bin/installer=" +
-           rebase_path("$root_build_dir/installer", root_build_dir) ]
+  binaries = [
+    {
+      name = "installer"
+    },
+  ]
 }
 
 test_package("installer-tests") {