[build][rust] Add force_opt and use in component_manager

component_manager is used in the Zircon boot image
and so must always be small, even in debug builds.

Test: built core without --release, saw component_manager
taking up only 0x002eaa10 of ZBI rather than the
0x00a38db8 reported in ZX-4396 (though still larger than
the 0x002ce580 in true release mode due to still building
library dependencies without optimizations).

Fix TC-560.

Change-Id: Ib0820ae9efa1d5761b8fbce0157cd744a441c738
diff --git a/build/rust/config.gni b/build/rust/config.gni
index 3b54283..8901f3d 100644
--- a/build/rust/config.gni
+++ b/build/rust/config.gni
@@ -71,12 +71,6 @@
   }
 }
 
-if (is_debug) {
-  _rust_opt_level = "0"
-} else {
-  _rust_opt_level = "z"
-}
-
 clang_prefix_rebased = rebase_path(clang_prefix, "", root_build_dir)
 
 clang_resource_dir = exec_script(rebase_path("$clang_prefix_rebased/clang"),
@@ -104,8 +98,6 @@
       rebase_path(clang_prefix_rebased, "", root_build_dir),
       "--clang-resource-dir",
       clang_resource_dir,
-      "--opt-level",
-      _rust_opt_level,
       "--symbol-level",
       "$symbol_level",
     ] + _platform_args
diff --git a/build/rust/rustc_artifact.gni b/build/rust/rustc_artifact.gni
index bb23956..00c2e36 100644
--- a/build/rust/rustc_artifact.gni
+++ b/build/rust/rustc_artifact.gni
@@ -194,6 +194,16 @@
 
   group_deps = []
 
+  if (defined(invoker.force_opt)) {
+    rust_opt_level = invoker.force_opt
+  } else {
+    if (is_debug) {
+      rust_opt_level = "0"
+    } else {
+      rust_opt_level = "z"
+    }
+  }
+
   if (type == "bin") {
     if (defined(invoker.with_lto)) {
       with_lto = invoker.with_lto
@@ -416,6 +426,8 @@
     rust_warnings,
     "--remap-path-prefix",
     "$absolute_path=$relative_path",
+    "--opt-level",
+    rust_opt_level,
   ]
 
   if (defined(invoker.features)) {
diff --git a/build/rust/rustc_binary.gni b/build/rust/rustc_binary.gni
index 7e680c7..18c2f5b 100644
--- a/build/rust/rustc_binary.gni
+++ b/build/rust/rustc_binary.gni
@@ -58,6 +58,11 @@
 #     "fat". This value takes precedence over GN args or the default value for the
 #     type of build (debug or release).
 #
+#   force_opt (optional)
+#     Force a particular optimization level for this target (even when building in debug mode).
+#     Values include 0-3, s, or z. This does not change the optimization level of dependencies,
+#     so consider combining with LTO for best results.
+#
 #   features (optional)
 #     A list of conditional compilation flags to enable. This can be used to set features for crates
 #     built in-tree which are also published to crates.io. This would be passed to rustc as
@@ -89,6 +94,7 @@
                              "source_root",
                              "testonly",
                              "with_lto",
+                             "force_opt",
                              "sdk_category",
                              "features",
                              "visibility",
diff --git a/src/sys/component_manager/BUILD.gn b/src/sys/component_manager/BUILD.gn
index 85634a2..a2657d2 100644
--- a/src/sys/component_manager/BUILD.gn
+++ b/src/sys/component_manager/BUILD.gn
@@ -57,6 +57,11 @@
   with_unit_tests = true
   edition = "2018"
 
+  # Component manager must always be small (even in debug builds) since it
+  # is part of the Zircon boot image.
+  force_opt = "z"
+  with_lto = "fat"
+
   deps = [
     ":lib",
     "//garnet/public/lib/fidl/rust/fidl",
diff --git a/third_party/rust_crates/BUILD.gn b/third_party/rust_crates/BUILD.gn
index 1e5e304..7936ea6 100644
--- a/third_party/rust_crates/BUILD.gn
+++ b/third_party/rust_crates/BUILD.gn
@@ -63,6 +63,13 @@
   deps = rust_build_deps
   inputs += rust_build_inputs
 
+  if (is_debug) {
+    rust_opt_level = "0"
+  } else {
+    rust_opt_level = "z"
+  }
+  args += ["--opt-level", rust_opt_level]
+
   outputs = [
     out_deps_data,
   ]