[build] Fix integration with Zircon build when it uses ASan

This entailed another substantial cleanup of environment_redirect()
to handle more cases.  It also necessitated reorganizing the deps
in the Zircon build for the legacy integration.  The new arrangement
should be much easier to understand.

Bug: BLD-447 #comment Fixed --variant asan build
Test: fx set bringup.x64 --variant asan && fx build
Change-Id: I2cd8d7aa95eba5f34af421c75d634f31d988a1f8
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn
index a754649..87b9d1c 100644
--- a/build/config/fuchsia/BUILD.gn
+++ b/build/config/fuchsia/BUILD.gn
@@ -91,6 +91,8 @@
       sysroot_libc = entry.libc
     } else if (defined(entry.crt1)) {
       sysroot_crt1 = entry.crt1
+    } else if (defined(entry.vdso)) {
+      sysroot_vdso = entry.vdso
     }
   }
 
@@ -107,17 +109,6 @@
   }
   asmflags = cflags
 
-  # Things use libs = [ "zircon" ] and expect the vDSO headers in the sysroot.
-  # TODO(BLD-353): Clean them up with deps = [ "//zircon/public/lib/zircon" ].
-  foreach(target, zircon_legacy_targets) {
-    if (target.target_name == "zircon") {
-      _libs = target.libs
-      libzircon = _libs[0]
-      assert(_libs == [ libzircon ])
-      lib_dirs = [ get_path_info("$zircon_root_build_dir/$libzircon", "dir") ]
-    }
-  }
-
   # Point the linker at a little directory we populate below.  Plain -L
   # switches (via lib_dirs) would be sufficient for the implicit -lc and -lm
   # from the compiler driver.  But Scrt1.o is found only in the sysroot.
@@ -132,15 +123,18 @@
   # code only changes when touching core Zircon library sources.
   libc = rebase_path(sysroot_libc, "", zircon_root_build_dir)
   crt1 = rebase_path(sysroot_crt1, "", zircon_root_build_dir)
+  vdso = rebase_path(sysroot_vdso, "", zircon_root_build_dir)
   inputs = [
     libc,
     crt1,
+    vdso,
   ]
   write_file("$target_gen_dir/lib/Scrt1.o", [ "INPUT(${crt1})" ])
   write_file("$target_gen_dir/lib/libc.so", [ "INPUT(${libc})" ])
   write_file("$target_gen_dir/lib/libdl.so", [ "/* dummy */" ])
   write_file("$target_gen_dir/lib/libm.so", [ "/* dummy */" ])
   write_file("$target_gen_dir/lib/libpthread.so", [ "/* dummy */" ])
+  write_file("$target_gen_dir/lib/libzircon.so", [ "INPUT(${vdso})" ])
 }
 
 config("compiler_target") {
@@ -173,10 +167,15 @@
   # TODO(pylaligand): find a better way to let executables link in fdio.
   # Ideally their dependencies should be set up in such a way that it would get
   # inherited from them.
-  api_file = "$zircon_root_build_dir/legacy-fdio-$current_cpu.json"
-  libs = rebase_path(read_file(api_file, "json"), ".", zircon_root_build_dir)
-  assert(libs != [], "$api_file empty?")
-  assert(libs == [ libs[0] ], "$api_file has multiple entries?")
+  foreach(target, zircon_legacy_targets) {
+    if (target.target_name == "fdio") {
+      libs = rebase_path(target.libs, "", zircon_root_build_dir)
+    }
+  }
+
+  # TODO(mcgrathr): //build/go/go_build.gni depends on this because it
+  # uses -L... -lfdio rather than passing the file directly.
+  assert(libs == [ libs[0] ], "fdio libs has multiple elements?")
   link_file = libs[0]
   if (get_path_info(link_file, "file") != "libfdio.so") {
     link_file = rebase_path(link_file)
diff --git a/build/config/fuchsia/zircon.gni b/build/config/fuchsia/zircon.gni
index 3ece1f8..8521306 100644
--- a/build/config/fuchsia/zircon.gni
+++ b/build/config/fuchsia/zircon.gni
@@ -96,7 +96,7 @@
 # The `gn gen` stage of the Zircon GN build writes these files.
 # See //build/zircon/template.gn for how they're used.
 zircon_legacy_targets =
-    read_file("$zircon_root_build_dir/legacy-$target_cpu.json", "json")
+    read_file("$zircon_root_build_dir/legacy_targets-$target_cpu.json", "json")
 
 # See //zircon/public/sysroot/BUILD.gn and //build/config/fuchsia/BUILD.gn.
 zircon_legacy_sysroot =
diff --git a/build/images/manifest.gni b/build/images/manifest.gni
index bdfd52a..422847b 100644
--- a/build/images/manifest.gni
+++ b/build/images/manifest.gni
@@ -26,7 +26,7 @@
   if (image.type == "manifest") {
     if (image.name == "legacy-image-$target_cpu") {
       zircon_boot_manifests = [ "$zircon_root_build_dir/${image.path}" ]
-    } else if (image.name == "asan-libs" && image.cpu == target_cpu) {
+    } else if (image.name == "legacy-aux-$target_cpu") {
       zircon_aux_manifests = [ "$zircon_root_build_dir/${image.path}" ]
     }
   }
diff --git a/src/connectivity/telephony/drivers/qmi-usb-transport/BUILD.gn b/src/connectivity/telephony/drivers/qmi-usb-transport/BUILD.gn
index f8c2b7a..ff0c802 100644
--- a/src/connectivity/telephony/drivers/qmi-usb-transport/BUILD.gn
+++ b/src/connectivity/telephony/drivers/qmi-usb-transport/BUILD.gn
@@ -37,7 +37,7 @@
     "//zircon/public/lib/sync",
     "//zircon/public/lib/usb",
     "//zircon/public/lib/zx",
-    "//zircon/system/fidl/fuchsia-hardware-telephony-transport:fuchsia-hardware-telephony-transport_c",
+    "//zircon/public/fidl/fuchsia-hardware-telephony-transport:fuchsia-hardware-telephony-transport_c",
   ]
 
   configs += [ "//build/config/fuchsia:enable_zircon_asserts" ]
diff --git a/src/connectivity/telephony/lib/qmi/BUILD.gn b/src/connectivity/telephony/lib/qmi/BUILD.gn
index 8635fb61..a2738380 100644
--- a/src/connectivity/telephony/lib/qmi/BUILD.gn
+++ b/src/connectivity/telephony/lib/qmi/BUILD.gn
@@ -12,6 +12,6 @@
     "//garnet/public/rust/fuchsia-async",
     "//garnet/public/rust/fuchsia-zircon",
     "//third_party/rust_crates:failure",
-    "//zircon/system/fidl/fuchsia-hardware-telephony-transport:fuchsia-hardware-telephony-transport-rustc",
+    "//zircon/public/fidl/fuchsia-hardware-telephony-transport:fuchsia-hardware-telephony-transport-rustc",
   ]
 }
diff --git a/zircon/BUILD.gn b/zircon/BUILD.gn
index 3294a45..c65e65f 100644
--- a/zircon/BUILD.gn
+++ b/zircon/BUILD.gn
@@ -37,6 +37,7 @@
     environment_label = "$zx/public/gn/toolchain:host"
     cpu = host.cpu
     os = host.os
+    direct = true
     deps = [
       "$zx/system/host:tools",
     ]
@@ -107,6 +108,7 @@
       environment_redirect("$name-$cpu") {
         environment_label = "$zx/public/gn/toolchain:user"
         cpu = cpu
+        direct = true
         deps = [
           name,
         ]
@@ -154,75 +156,47 @@
   # TODO(BLD-353): Temporary hacks for integrating with the legacy Fuchsia
   # GN build.
 
-  legacy_deps = [
-    ":legacy-host_tests",
-    ":tools",
-
-    # Separately listed because they're reached by dependencies only on x64.
-    "$zx/system/banjo/ddk-protocol-acpi",
-    "$zx/system/banjo/ddk-protocol-intelgpucore",
-    "$zx/system/banjo/ddk-protocol-intelhda-codec",
-
-    # Separately listed because they're not reached by any dependencies.
-    "$zx/system/fidl/fuchsia-debugdata",
-    "$zx/system/fidl/fuchsia-inspect",
-    "$zx/system/fidl/fuchsia-tracelink",
-  ]
-  legacy_cpu_deps = [
-    ":ids",
-    ":legacy-deps",
-    ":legacy-fdio",
-    ":legacy-tests",
-    ":all-ulib",
-  ]
-
   build_api_module("legacy_dirs") {
     testonly = true
     data_keys = [ "legacy_dirs" ]
-    deps = legacy_deps
+    walk_keys = [ "legacy_barrier" ]
+    deps = []
     foreach(cpu, standard_fuchsia_cpus) {
-      foreach(dep, legacy_cpu_deps) {
-        deps += [ "$dep-$cpu" ]
-      }
+      deps += [ ":legacy-$cpu" ]
     }
   }
 
   foreach(cpu, standard_fuchsia_cpus) {
-    build_api_module("legacy-$cpu") {
+    group("legacy-$cpu") {
+      testonly = true
+      deps = [
+        ":ids-$cpu",
+        ":legacy-aux-$cpu",
+        ":legacy-host_tests",
+        ":legacy-image-$cpu",
+        ":legacy-tests-$cpu",
+        ":legacy_targets-$cpu",
+      ]
+    }
+
+    build_api_module("legacy_targets-$cpu") {
       testonly = true
       data_keys = [ "legacy_targets" ]
       walk_keys = [ "legacy_barrier" ]
-      deps = legacy_deps + [ ":legacy-image-$cpu" ]
-      foreach(dep, legacy_cpu_deps) {
-        deps += [ "$dep-$cpu" ]
-      }
+      deps = [
+        ":all-ulib-$cpu",
+        ":tools",
+        "$zx/system/banjo",
+        "$zx/system/fidl",
+      ]
     }
 
     build_api_module("legacy_sysroot-$cpu") {
       testonly = true
       data_keys = [ "legacy_sysroot" ]
       walk_keys = [ "legacy_barrier" ]
-      deps = legacy_deps
-      foreach(dep, legacy_cpu_deps) {
-        deps += [ "$dep-$cpu" ]
-      }
-    }
-
-    environment_redirect("all-ulib-$cpu") {
-      testonly = true
-      cpu = cpu
-      environment_label = "$zx/public/gn/toolchain:user"
       deps = [
-        "$zx/system/ulib",
-      ]
-    }
-
-    # This lists just the one file to link against to get fdio.
-    build_api_module("legacy-fdio-$cpu") {
-      data_keys = [ "link_output" ]
-      walk_keys = [ "link_barrier" ]
-      deps = [
-        ":legacy-fdio-redirect-$cpu",
+        ":all-ulib-$cpu",
       ]
     }
   }
@@ -276,7 +250,6 @@
       testonly = true
       cpu = cpu
       deps = [
-        ":asan-manifest-$cpu",
         ":legacy-deps-$cpu",
       ]
       metadata = {
@@ -296,24 +269,16 @@
     }
 
     # The legacy Fuchsia GN build needs to consume a manifest of libraries
-    # built for ASan (and all the libraries themselves).
-    zbi_input("asan-manifest-$cpu") {
+    # built for ASan (and all the libraries themselves).  When the main
+    # build is ASan, it needs to find the non-ASan libraries here too.
+    manifest_file("legacy-aux-$cpu") {
       testonly = true
       deps = [
-        ":asan-libs-$cpu",
+        ":all-ulib-$cpu",
+        ":all-ulib-asan-$cpu",
+        ":driver-deps-$cpu",
       ]
       metadata = {
-        # //build/images/manifest.gni finds this by name and cpu.
-        images = [
-          {
-            cpu = cpu
-            type = "manifest"
-            name = "asan-libs"
-            path = rebase_path("$target_gen_dir/$target_name.manifest",
-                               root_build_dir)
-          },
-        ]
-
         # This prevents build_api_module("legacy-$cpu") from visiting the
         # ASan incarnations of all the libraries.  The legacy build wants
         # only the manifest of ASan shared libraries.  It wants the targets
@@ -323,22 +288,12 @@
     }
   }
 
-  environment_redirect("asan-libs-$cpu") {
-    testonly = true
-    cpu = cpu
-    environment_label = "$zx/public/gn/toolchain:user"
-    variant = "asan"
-    deps = [
-      "$zx/system/core/devmgr:driver_deps.asan",
-      "$zx/system/ulib",
-    ]
-  }
-
   environment_redirect("core-tests-$cpu-deps") {
     visibility = [ ":*" ]
     testonly = true
     cpu = cpu
     environment_label = "$zx/public/gn/toolchain:user"
+    direct = true
     deps = [
       "kernel",
       "system/utest:core-tests",
@@ -360,19 +315,11 @@
       "$zx/system/core",
       "$zx/system/dev",
       "$zx/system/uapp",
-      "$zx/system/ulib",
       "$zx/third_party/uapp",
     ]
-  }
-
-  environment_redirect("legacy-fdio-redirect-$cpu") {
-    visibility = [ ":legacy-fdio-$cpu" ]
-    environment_label = "$zx/public/gn/toolchain:user"
-    cpu = cpu
-    shlib = true
-    deps = [
-      "$zx/system/ulib/fdio",
-    ]
+    metadata = {
+      legacy_barrier = []
+    }
   }
 
   environment_redirect("legacy-tests-deps-$cpu") {
@@ -380,6 +327,7 @@
     testonly = true
     environment_label = "$zx/public/gn/toolchain:user"
     cpu = cpu
+    direct = true
     deps = [
       "$zx/system/utest",
     ]
@@ -390,6 +338,7 @@
     testonly = true
     environment_label = "$zx/public/gn/toolchain:user"
     cpu = cpu
+    direct = true
     deps = [
       "$zx/bootloader",
       "$zx/kernel",
@@ -402,6 +351,37 @@
       deps += [ "$zx/system/utest" ]
     }
   }
+
+  environment_redirect("all-ulib-$cpu") {
+    testonly = true
+    cpu = cpu
+    environment_label = "$zx/public/gn/toolchain:user"
+    exclude_variant_tags = [ "instrumented" ]
+    deps = [
+      "$zx/system/ulib",
+    ]
+  }
+
+  environment_redirect("driver-deps-$cpu") {
+    testonly = true
+    cpu = cpu
+    environment_label = "$zx/public/gn/toolchain:user"
+    exclude_variant_tags = [ "instrumented" ]
+    deps = [
+      "$zx/system/core/devmgr:driver_deps",
+    ]
+  }
+
+  environment_redirect("all-ulib-asan-$cpu") {
+    testonly = true
+    cpu = cpu
+    environment_label = "$zx/public/gn/toolchain:user"
+    variant = "asan"
+    deps = [
+      "$zx/system/core/devmgr:driver_deps.asan",
+      "$zx/system/ulib",
+    ]
+  }
 }
 
 # This is the top-level build API module that just lists all the others.
@@ -500,11 +480,7 @@
     ":tools",
   ]
   foreach(cpu, standard_fuchsia_cpus) {
-    deps += [
-      ":asan-manifest-$cpu",
-      ":legacy-$cpu",
-      ":legacy-image-$cpu",
-    ]
+    deps += [ ":legacy-$cpu" ]
   }
 }
 
diff --git a/zircon/bootloader/BUILD.gn b/zircon/bootloader/BUILD.gn
index b6474f1..afe5060 100644
--- a/zircon/bootloader/BUILD.gn
+++ b/zircon/bootloader/BUILD.gn
@@ -81,6 +81,7 @@
   # In any other toolchain, just redirect to the proper toolchain.
   environment_redirect("bootloader") {
     environment_label = ":efi"
+    direct = true
     deps = [
       ":bootloader",
     ]
diff --git a/zircon/kernel/BUILD.gn b/zircon/kernel/BUILD.gn
index a05490f..74fa946 100644
--- a/zircon/kernel/BUILD.gn
+++ b/zircon/kernel/BUILD.gn
@@ -409,6 +409,7 @@
   # Redirect to the kernel toolchain.
   environment_redirect("kernel") {
     environment_label = ":kernel"
+    direct = true
     deps = [
       ":kernel",
     ]
diff --git a/zircon/kernel/lib/userboot/user/BUILD.gn b/zircon/kernel/lib/userboot/user/BUILD.gn
index 9d6d191..e35b1a5 100644
--- a/zircon/kernel/lib/userboot/user/BUILD.gn
+++ b/zircon/kernel/lib/userboot/user/BUILD.gn
@@ -157,6 +157,7 @@
   # In any other toolchain, just redirect to the proper toolchain.
   environment_redirect("userboot") {
     environment_label = ":userboot"
+    direct = true
     deps = [
       ":userboot",
     ]
diff --git a/zircon/kernel/target/arm64/boot-shim/BUILD.gn b/zircon/kernel/target/arm64/boot-shim/BUILD.gn
index 6bb947f0..83c5ffe 100644
--- a/zircon/kernel/target/arm64/boot-shim/BUILD.gn
+++ b/zircon/kernel/target/arm64/boot-shim/BUILD.gn
@@ -135,10 +135,16 @@
           },
         ]
       }
+
+      # Use the same variant for the extraction that will have built the shim.
+      variant_target = {
+        match = "executable"
+      }
     }
   } else {
     environment_redirect(board) {
       environment_label = "$zx/kernel/arch/arm64:physmem"
+      direct = true
       deps = [
         ":$board",
       ]
diff --git a/zircon/kernel/target/pc/multiboot/BUILD.gn b/zircon/kernel/target/pc/multiboot/BUILD.gn
index 4d251c0..db2d6ec 100644
--- a/zircon/kernel/target/pc/multiboot/BUILD.gn
+++ b/zircon/kernel/target/pc/multiboot/BUILD.gn
@@ -93,6 +93,7 @@
   # In any other toolchain, just redirect to the proper toolchain.
   environment_redirect("multiboot") {
     environment_label = ":multiboot"
+    direct = true
     deps = [
       ":multiboot",
     ]
diff --git a/zircon/public/gn/BUILDCONFIG.gn b/zircon/public/gn/BUILDCONFIG.gn
index 8b888a9..4184d88 100644
--- a/zircon/public/gn/BUILDCONFIG.gn
+++ b/zircon/public/gn/BUILDCONFIG.gn
@@ -401,6 +401,7 @@
     }
 
     legacy_metadata = {
+      legacy_barrier = []
       legacy_dirs = [ "lib/$_library_name" ]
       legacy_targets = [
         {
@@ -476,6 +477,7 @@
     }
   } else if (!is_host && !is_kernel && (static || shared)) {
     legacy_metadata = {
+      legacy_barrier = []
     }
   }
 
@@ -526,18 +528,10 @@
                              "*",
                              _library_params + [
                                    "data_deps",
-                                   "metadata",  # TODO(BLD-353): see below
                                    "install_path",
                                    "public_deps",
                                    "visibility",
                                  ])
-      metadata = {
-        # TODO(BLD-353): temporary hacks
-        forward_variables_from(legacy_metadata, "*")
-        if (defined(invoker.metadata)) {
-          forward_variables_from(invoker.metadata, "*")
-        }
-      }
       if (!defined(deps)) {
         deps = []
       }
@@ -575,6 +569,9 @@
         deps = [
           ":${_library_name}._sources",
         ]
+
+        # TODO(BLD-353): temporary hacks
+        metadata = legacy_metadata
       }
     }
     if (shared) {
@@ -626,6 +623,12 @@
             manifest_lines = [ "${install_path}=" +
                                rebase_path(manifest_inputs[0], root_build_dir) ]
           }
+
+          # TODO(BLD-353): temporary hacks
+          forward_variables_from(legacy_metadata, "*")
+          if (defined(invoker.metadata)) {
+            forward_variables_from(invoker.metadata, [ "legacy_sysroot" ])
+          }
         }
       }
     }
@@ -781,6 +784,10 @@
 #         Optional: Never select a variant with any of these tags.
 #         Type: list(string)
 #         Default: []
+#       variant_suffix_target
+#         Optional: Define $target_name.$variant targets.
+#         Type: bool
+#         Default: true
 #
 #   See executable() or loadable_module() for other parameters.
 #   They will be forwarded to the underlying ${target.type} target.
@@ -792,6 +799,7 @@
     variant_metadata = {
     }
     exclude_variant_tags = []
+    variant_suffix_target = true
     forward_variables_from(invoker.target, "*")
     if (!defined(match)) {
       match = type
@@ -939,24 +947,27 @@
     # "foobin.$variant" so that multiple variants can exist side by side in
     # the same install directory.
 
-    variant_target_name = main_target_name + toolchain.variant_suffix
-
-    # To get this ease of use, a lot more goes on under the covers.  Firstly,
-    # each toolchain in the environment must define a "$target_name.$variant"
-    # target for each *other* variant that just redirects to that toolchain.
     extra_visibility = []
-    foreach(other, toolchain.other_variants) {
-      other_target_name = main_target_name + other.suffix
-      extra_visibility += [ ":other_target_name" ]
-      group(other_target_name) {
-        forward_variables_from(invoker,
-                               [
-                                 "testonly",
-                                 "visibility",
-                               ])
-        public_deps = [
-          ":$other_target_name(${other.label})",
-        ]
+
+    if (target.variant_suffix_target) {
+      variant_target_name = main_target_name + toolchain.variant_suffix
+
+      # To get this ease of use, a lot more goes on under the covers.  Firstly,
+      # each toolchain in the environment must define a "$target_name.$variant"
+      # target for each *other* variant that just redirects to that toolchain.
+      foreach(other, toolchain.other_variants) {
+        other_target_name = main_target_name + other.suffix
+        extra_visibility += [ ":other_target_name" ]
+        group(other_target_name) {
+          forward_variables_from(invoker,
+                                 [
+                                   "testonly",
+                                   "visibility",
+                                 ])
+          public_deps = [
+            ":$other_target_name(${other.label})",
+          ]
+        }
       }
     }
 
@@ -1066,15 +1077,17 @@
     # the main toolchain just redirect to the shlib toolchain.
     if (defined(target.shlib) && target.shlib && defined(toolchain.shlib) &&
         current_toolchain != toolchain.shlib) {
-      group(variant_target_name) {
-        forward_variables_from(invoker,
-                               [
-                                 "testonly",
-                                 "visibility",
-                               ])
-        public_deps = [
-          ":$variant_target_name(${toolchain.shlib})",
-        ]
+      if (target.variant_suffix_target) {
+        group(variant_target_name) {
+          forward_variables_from(invoker,
+                                 [
+                                   "testonly",
+                                   "visibility",
+                                 ])
+          public_deps = [
+            ":$variant_target_name(${toolchain.shlib})",
+          ]
+        }
       }
 
       not_needed(invoker, "*")
@@ -1083,26 +1096,29 @@
                    "output_name",
                  ])
     } else {
-      # The variant-suffixed target redirects to the builder target but also
-      # adds the metadata--different metadata than the main target above.
-      extra_visibility += [ ":$variant_target_name" ]
-      group(variant_target_name) {
-        forward_variables_from(invoker,
-                               [
-                                 "testonly",
-                                 "visibility",
-                               ])
-        public_deps = [
-          ":$builder_target_name",
-        ]
-        metadata = {
-          if (defined(invoker.metadata)) {
-            forward_variables_from(invoker.metadata, "*")
-            assert(!defined(manifest_inputs) && !defined(manifest_lines),
-                   "the `manifest_inputs` and `manifest_lines` metadata keys" +
-                       " are reserved for standard templates; use resource()")
+      if (target.variant_suffix_target) {
+        # The variant-suffixed target redirects to the builder target but also
+        # adds the metadata--different metadata than the main target above.
+        extra_visibility += [ ":$variant_target_name" ]
+        group(variant_target_name) {
+          forward_variables_from(invoker,
+                                 [
+                                   "testonly",
+                                   "visibility",
+                                 ])
+          public_deps = [
+            ":$builder_target_name",
+          ]
+          metadata = {
+            if (defined(invoker.metadata)) {
+              forward_variables_from(invoker.metadata, "*")
+              assert(
+                  !defined(manifest_inputs) && !defined(manifest_lines),
+                  "the `manifest_inputs` and `manifest_lines` metadata keys" +
+                      " are reserved for standard templates; use resource()")
+            }
+            forward_variables_from(target.variant_metadata, "*")
           }
-          forward_variables_from(target.variant_metadata, "*")
         }
       }
 
@@ -1421,6 +1437,7 @@
 
         # TODO(BLD-353): Temporary hacks for integrating with the legacy
         # Fuchsia GN build.
+        legacy_barrier = []
         legacy_dirs = [ "tool/$target_name" ]
         legacy_targets = [
           {
diff --git a/zircon/public/gn/banjo.gni b/zircon/public/gn/banjo.gni
index 2042c3b..1589297 100644
--- a/zircon/public/gn/banjo.gni
+++ b/zircon/public/gn/banjo.gni
@@ -140,6 +140,7 @@
         # TODO(BLD-353): temporary hack
         assert(banjo_target ==
                get_path_info(get_label_info(":$banjo_target", "dir"), "name"))
+        legacy_barrier = []
         legacy_dirs = [ "banjo/$banjo_target" ]
         legacy_targets = [
           {
diff --git a/zircon/public/gn/build_api_module.gni b/zircon/public/gn/build_api_module.gni
index 401087b..20f58c9 100644
--- a/zircon/public/gn/build_api_module.gni
+++ b/zircon/public/gn/build_api_module.gni
@@ -46,14 +46,18 @@
       forward_variables_from(invoker,
                              [
                                "contents",
-                               "deps",
                                "data_keys",
-                               "walk_keys",
+                               "deps",
                                "testonly",
+                               "visibility",
+                               "walk_keys",
                              ])
       output_conversion = "json"
       metadata = {
         build_api_modules = [ target_name ]
+        if (defined(invoker.metadata)) {
+          forward_variables_from(invoker.metadata, "*", [ "build_api_modules" ])
+        }
       }
     }
   } else {
diff --git a/zircon/public/gn/fidl.gni b/zircon/public/gn/fidl.gni
index 56f00a2..029cbc4 100644
--- a/zircon/public/gn/fidl.gni
+++ b/zircon/public/gn/fidl.gni
@@ -241,6 +241,7 @@
         # TODO(BLD-353): temporary hack
         if (fidl_target ==
             get_path_info(get_label_info(":$fidl_target", "dir"), "name")) {
+          legacy_barrier = []
           legacy_dirs = [ "fidl/$fidl_target" ]
           legacy_targets = [
             {
diff --git a/zircon/public/gn/host_tool_action.gni b/zircon/public/gn/host_tool_action.gni
index fc76720..33513c9 100644
--- a/zircon/public/gn/host_tool_action.gni
+++ b/zircon/public/gn/host_tool_action.gni
@@ -26,6 +26,7 @@
     environment_label = "$zx/public/gn/toolchain:host"
     cpu = host_cpu
     os = host_os
+    direct = true
     deps = [
       invoker.tool,
     ]
diff --git a/zircon/public/gn/toolchain/environment_redirect.gni b/zircon/public/gn/toolchain/environment_redirect.gni
index bf55681..50de511 100644
--- a/zircon/public/gn/toolchain/environment_redirect.gni
+++ b/zircon/public/gn/toolchain/environment_redirect.gni
@@ -13,6 +13,47 @@
 # selects an appropriate toolchain for those dependencies based on the other
 # parameters described below.
 #
+# A complexity arises when redirecting to a different environment, cpu, or os.
+# The information necessary to determine what variant will be selected is only
+# available inside a toolchain context that is *some* variant of the same
+# (environment, cpu, os) tuple.  Because of this, environment_redirect() has
+# to first redirect to *some* extant variant of the right environment, cpu,
+# and os and only from there can it then redirect to the correct variant.  To
+# do this, it must redirect to the selfsame environment_redirect() in that
+# other toolchain so that the instantiation in that toolchain can run the code
+# to redirect to the correct variant.  An unfortunate side effect of this is
+# that the entire BUILD.gn file containing the environment_redirect() target
+# will then be instantiated in that other environment.  This can become
+# problematic because not every BUILD.gn file expects to be instantiated in
+# every different environment.  In fact, there are sometimes very good reasons
+# for a BUILD.gn file to have an assert() that it's not instantiated in the
+# "wrong" toolchain.
+#
+# There are two opposing cases with regard to this subtlety:
+#
+# 1. $deps leads to library() or other "non-terminal" targets.
+#    Hence it's crucial to actually select the "right" variant.
+#    There is no way to avoid the "self-reference in other toolchain".
+#    We call this "indirect mode" because it bounces to itself in another
+#    toolchain before going to $deps.
+#
+# 2. $deps leads only to "terminal" targets like executable() or host_tool().
+#    (Intermediate group() targets leading to those are not a problem.)
+#    Since each of those targets does its own variant selection, it doesn't
+#    really matter which variant toolchain environment_redirect() redirects to.
+#    If we know this is the case, then there is no need for the "self-reference
+#    in other toolchain".  This lets us avoid instantiating the invoker's
+#    build file in the target environment, which might be a problem.
+#    We call this "direct mode" because it goes directly to $deps in
+#    another toolchain.
+#
+# There is no way environment_redirect() can figure out which of these cases
+# the $deps fall into.  So the invoker must indicate.  The $direct parameter
+# requests "direct mode", so that the invoker need not worry about their
+# BUILD.gn file being instantiated in the target toolchain.  The default is
+# "indirect mode", which is more "safe" in the sense that it will never go to
+# $deps in the wrong variant.
+#
 # Parameters
 #
 #   cpu
@@ -30,25 +71,35 @@
 #     toolchains defined by that environment() invocation.
 #     Default: ${toolchain.environment_label}
 #
-#   exclude_variant_tags
-#     Optional: Never select a variant that has one of these tags.
-#     Type: list(string)
-#
 #   deps
 #     Required: These must be labels without toolchain suffix.  The
 #     environment_redirect() target redirects its dependents to instead depend
 #     on this list of labels, but in the toolchain selected by the other
 #     parameters.
+#     Type: list(label_no_toolchain)
+#
+#   direct
+#     Optional: $deps reaches only "terminal" targets that do their own variant
+#     selection.  See discussion above.
+#     Type: bool
+#     Default: false
 #
 #   shlib
 #     Optional: Go directly to the environment's ${toolchain.shlib} toolchain.
+#     Type: bool
 #     Default: false
 #
+#   exclude_variant_tags
+#     Optional: Never select a variant that has one of these tags.
+#     This has no effect if $variant is set.
+#     Type: list(string)
+#
 #   variant
 #     Optional: Specific variant toolchain to select.  If omitted, one will be
 #     chosen from $default_variants with the expectation that the
 #     environment() named by $environment_label used a `.variants` list
 #     including $default_variants (as is preset).
+#     Type: string
 #
 template("environment_redirect") {
   forward_variables_from(invoker,
@@ -99,7 +150,8 @@
     }
   }
 
-  foreach(label, invoker.deps) {
+  redirect_deps = invoker.deps
+  foreach(label, redirect_deps) {
     # The original string should not contain a "(toolchain)" suffix.
     # We can't easily tell if it does, but we can tell if it has any
     # suffix other than the expansion of "($current_toolchain)".
@@ -114,8 +166,10 @@
                             current_cpu != cpu || current_os != os)) {
     # If the caller didn't request a specific variant, then use the default
     # most likely to be selected for an arbitrary target in the selected
-    # environment.  That will dispatch to the correct variant for each
-    # particular target.  **NOTE:** environment() has special-case code
+    # environment.  Then redirect to this target in that toolchain, which
+    # will have enough information to use _variant_target() below to do
+    # the final redirect.
+    # **NOTE:** environment() has special-case code
     # based on this logic; see the comments there that mention redirects.
     foreach(default, variants + default_variants) {
       if (!defined(variant)) {
@@ -128,8 +182,13 @@
             }
           }
           if (default != "") {
-            default = {
-              variant = default
+            if (get_path_info(default, "file") == default) {
+              # Ignore "variant/output_name" shorthand selectors.
+              default = {
+                variant = default
+              }
+            } else {
+              default = ""
             }
           }
         }
@@ -140,9 +199,18 @@
              default.environment + [ base_environment ] -
              [ base_environment ] != default.environment)) {
           variant = default.variant
+          if (!defined(invoker.direct) || !invoker.direct) {
+            redirect_deps = []
+            redirect_deps = [ ":$target_name" ]
+          }
+          if (defined(invoker.exclude_variant_tags)) {
+            not_needed(invoker, [ "exclude_variant_tags" ])
+          }
         }
       }
     }
+  } else if (defined(invoker.direct)) {
+    not_needed(invoker, [ "direct" ])
   }
 
   if (defined(variant)) {
@@ -166,19 +234,19 @@
       forward_variables_from(invoker,
                              [
                                "assert_no_deps",
+                               "metadata",
                                "visibility",
                                "testonly",
                              ])
+      if (defined(visibility)) {
+        visibility += [ ":$target_name" ]
+      }
       public_deps = []
-      foreach(label, invoker.deps) {
+      foreach(label, redirect_deps) {
         label = get_label_info(label, "label_no_toolchain")
         public_deps += [ "$label($toolchain_name)" ]
       }
     }
-
-    if (defined(invoker.exclude_variant_tags)) {
-      not_needed(invoker, [ "exclude_variant_tags" ])
-    }
   } else {
     # This does variant selection as if it were an executable called "",
     # so it should get to the configured default variant.
@@ -186,13 +254,18 @@
       forward_variables_from(invoker,
                              [
                                "assert_no_deps",
+                               "metadata",
                                "visibility",
                                "testonly",
                              ])
+      if (defined(visibility)) {
+        visibility += [ ":$target_name" ]
+      }
       target = {
         type = "group"
         match = "executable"
         output_name = ""
+        variant_suffix_target = false
         forward_variables_from(invoker,
                                [
                                  "exclude_variant_tags",
diff --git a/zircon/public/gn/zbi.gni b/zircon/public/gn/zbi.gni
index 9d08d2e..1247f37 100644
--- a/zircon/public/gn/zbi.gni
+++ b/zircon/public/gn/zbi.gni
@@ -5,7 +5,6 @@
 import("$zx/public/gn/host_tool_action.gni")
 import("$zx/public/gn/manifest.gni")
 import("$zx/public/gn/resource.gni")
-import("$zx/public/gn/toolchain/environment_redirect.gni")
 
 # Define inputs to a dependent zbi() target.
 #
diff --git a/zircon/system/banjo/BUILD.gn b/zircon/system/banjo/BUILD.gn
new file mode 100644
index 0000000..ee44729
--- /dev/null
+++ b/zircon/system/banjo/BUILD.gn
@@ -0,0 +1,74 @@
+# Copyright 2019 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.
+
+# TODO(BLD-353): This is all temporary hacks for integrating with
+# the legacy Fuchsia GN build.  This file should not exist at all.
+
+group("banjo") {
+  deps = [
+    "ddk-driver",
+    "ddk-physiter",
+    "ddk-protocol-acpi",
+    "ddk-protocol-amlogiccanvas",
+    "ddk-protocol-badblock",
+    "ddk-protocol-block",
+    "ddk-protocol-block-partition",
+    "ddk-protocol-block-volume",
+    "ddk-protocol-bt-gattsvc",
+    "ddk-protocol-bt-hci",
+    "ddk-protocol-camerasensor",
+    "ddk-protocol-clock",
+    "ddk-protocol-clockimpl",
+    "ddk-protocol-composite",
+    "ddk-protocol-display-controller",
+    "ddk-protocol-dsiimpl",
+    "ddk-protocol-ethernet",
+    "ddk-protocol-ethernet-board",
+    "ddk-protocol-ethernet-mac",
+    "ddk-protocol-goldfish-pipe",
+    "ddk-protocol-gpio",
+    "ddk-protocol-gpioimpl",
+    "ddk-protocol-hidbus",
+    "ddk-protocol-i2c",
+    "ddk-protocol-i2cimpl",
+    "ddk-protocol-intelgpucore",
+    "ddk-protocol-intelhda-codec",
+    "ddk-protocol-iommu",
+    "ddk-protocol-mailbox",
+    "ddk-protocol-mipicsi",
+    "ddk-protocol-nand",
+    "ddk-protocol-pci",
+    "ddk-protocol-pciroot",
+    "ddk-protocol-platform-bus",
+    "ddk-protocol-platform-device",
+    "ddk-protocol-power",
+    "ddk-protocol-powerimpl",
+    "ddk-protocol-rawnand",
+    "ddk-protocol-scpi",
+    "ddk-protocol-sdhci",
+    "ddk-protocol-sdio",
+    "ddk-protocol-sdmmc",
+    "ddk-protocol-serial",
+    "ddk-protocol-serialimpl",
+    "ddk-protocol-sysmem",
+    "ddk-protocol-test",
+    "ddk-protocol-usb",
+    "ddk-protocol-usb-bus",
+    "ddk-protocol-usb-composite",
+    "ddk-protocol-usb-dci",
+    "ddk-protocol-usb-function",
+    "ddk-protocol-usb-hci",
+    "ddk-protocol-usb-hub",
+    "ddk-protocol-usb-modeswitch",
+    "ddk-protocol-usb-request",
+    "zircon-device-audio",
+    "zircon-device-block",
+    "zircon-device-nand",
+    "zircon-device-scpi",
+    "zircon-hw-pci",
+    "zircon-hw-usb",
+    "zircon-hw-usb-hub",
+    "zircon-syscalls-pci",
+  ]
+}
diff --git a/zircon/system/fidl/BUILD.gn b/zircon/system/fidl/BUILD.gn
new file mode 100644
index 0000000..106bb20
--- /dev/null
+++ b/zircon/system/fidl/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2019 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.
+
+# TODO(BLD-353): This is all temporary hacks for integrating with
+# the legacy Fuchsia GN build.  This file should not exist at all.
+
+group("fidl") {
+  deps = [
+    "fuchsia-blobfs",
+    "fuchsia-boot",
+    "fuchsia-cobalt",
+    "fuchsia-crash",
+    "fuchsia-debugdata",
+    "fuchsia-device",
+    "fuchsia-device-manager",
+    "fuchsia-device-test",
+    "fuchsia-device-vsock",
+    "fuchsia-fshost",
+    "fuchsia-hardware-audio",
+    "fuchsia-hardware-backlight",
+    "fuchsia-hardware-block",
+    "fuchsia-hardware-block-partition",
+    "fuchsia-hardware-block-volume",
+    "fuchsia-hardware-bluetooth",
+    "fuchsia-hardware-camera",
+    "fuchsia-hardware-clock",
+    "fuchsia-hardware-display",
+    "fuchsia-hardware-ethernet",
+    "fuchsia-hardware-ethertap",
+    "fuchsia-hardware-goldfish-pipe",
+    "fuchsia-hardware-gpu-clock",
+    "fuchsia-hardware-hidctl",
+    "fuchsia-hardware-i2c",
+    "fuchsia-hardware-input",
+    "fuchsia-hardware-intel-hda",
+    "fuchsia-hardware-light",
+    "fuchsia-hardware-nand",
+    "fuchsia-hardware-power",
+    "fuchsia-hardware-pty",
+    "fuchsia-hardware-ramdisk",
+    "fuchsia-hardware-rtc",
+    "fuchsia-hardware-skipblock",
+    "fuchsia-hardware-tee",
+    "fuchsia-hardware-telephony-transport",
+    "fuchsia-hardware-thermal",
+    "fuchsia-hardware-usb-device",
+    "fuchsia-hardware-usb-fwloader",
+    "fuchsia-hardware-usb-peripheral",
+    "fuchsia-hardware-usb-peripheral-block",
+    "fuchsia-hardware-usb-tester",
+    "fuchsia-hardware-usb-virtual-bus:fuchsia.usb.virtualbus",
+    "fuchsia-hardware-vsock",
+    "fuchsia-hardware-zxcrypt",
+    "fuchsia-inspect",
+    "fuchsia-io",
+    "fuchsia-kernel",
+    "fuchsia-ldsvc",
+    "fuchsia-logger",
+    "fuchsia-mem",
+    "fuchsia-minfs",
+    "fuchsia-nand",
+    "fuchsia-net",
+    "fuchsia-net-stack",
+    "fuchsia-paver",
+    "fuchsia-power",
+    "fuchsia-process",
+    "fuchsia-scheduler",
+    "fuchsia-storage-metrics",
+    "fuchsia-sysinfo",
+    "fuchsia-sysmem",
+    "fuchsia-tee",
+    "fuchsia-tee-manager",
+    "fuchsia-tracelink",
+    "fuchsia-tracing-kernel",
+    "fuchsia-usb-debug",
+    "fuchsia-virtualconsole",
+  ]
+}
diff --git a/zircon/system/fidl/fuchsia-hardware-telephony-transport/BUILD.gn b/zircon/system/fidl/fuchsia-hardware-telephony-transport/BUILD.gn
index 4bb323a..29fdc15 100644
--- a/zircon/system/fidl/fuchsia-hardware-telephony-transport/BUILD.gn
+++ b/zircon/system/fidl/fuchsia-hardware-telephony-transport/BUILD.gn
@@ -2,11 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/fidl/fidl.gni")
+import("$zx/public/gn/fidl.gni")
 
-fidl("fuchsia-hardware-telephony-transport") {
-  name = "fuchsia.hardware.telephony.transport"
-
+fidl_library("fuchsia-hardware-telephony-transport") {
   sources = [
     "qmi.fidl",
   ]
diff --git a/zircon/system/ulib/BUILD.gn b/zircon/system/ulib/BUILD.gn
index 5f88b50..f94baebd 100644
--- a/zircon/system/ulib/BUILD.gn
+++ b/zircon/system/ulib/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("$zx/public/gn/config/standard.gni")
-import("$zx/public/gn/toolchain/environment_redirect.gni")
 import("$zx/public/gn/zbi.gni")
 
 assert(current_toolchain != default_toolchain)
@@ -17,11 +16,16 @@
   deps = [
     "$zx/system/dev/audio/lib/simple-audio-stream",
     "$zx/system/dev/lib/broadcom",
+    "$zx/system/dev/lib/mmio",
+    "$zx/system/dev/lib/operation",
     "$zx/system/dev/lib/usb",
     "$zx/system/fidl/fuchsia-net-stack:c",
     "$zx/third_party/ulib",
     "async",
+    "async:async-cpp",
+    "async:async-default",
     "async-loop",
+    "async-loop:async-loop-cpp",
     "async-testutils",
     "audio-driver-proto",
     "audio-proto-utils",
@@ -34,6 +38,7 @@
     "bootdata",
     "bootfs",
     "c",
+    "c:crt1",
     "chromeos-disk-setup",
     "cmdline",
     "cobalt-client",
@@ -53,7 +58,10 @@
     "fbl",
     "fdio",
     "fidl",
+    "fidl:fidl-llcpp",
+    "fidl:fidl_base",
     "fidl-async",
+    "fidl-async:fidl-async-cpp",
     "fidl-utils",
     "fit",
     "framebuffer",
@@ -85,6 +93,7 @@
     "log-writer-textfile",
     "logger",
     "memfs",
+    "memfs:memfs-cpp",
     "minfs",
     "mini-process",
     "pci",
@@ -92,6 +101,7 @@
     "port",
     "pretty",
     "process-launcher",
+    "ramdevice-client",
     "region-alloc",
     "rtc",
     "runtests-utils",
@@ -101,6 +111,7 @@
     "svc",
     "sync",
     "syslog",
+    "sysmem-connector",
     "task-utils",
     "tee-client-api",
     "test-utils",
@@ -109,6 +120,7 @@
     "trace:trace-with-static-engine",
     "trace-engine",
     "trace-engine:trace-engine-headers-for-reader",
+    "trace-engine:trace-engine-static",
     "trace-provider",
     "trace-provider:trace-provider-fdio-connect",
     "trace-provider:trace-provider-with-static-engine",
diff --git a/zircon/system/ulib/zircon/BUILD.gn b/zircon/system/ulib/zircon/BUILD.gn
index 367d10f..4161cfb 100644
--- a/zircon/system/ulib/zircon/BUILD.gn
+++ b/zircon/system/ulib/zircon/BUILD.gn
@@ -102,6 +102,10 @@
     metadata = {
       legacy_sysroot = [
         {
+          vdso =
+              rebase_path("$target_out_dir/libzircon.so.debug", root_build_dir)
+        },
+        {
           include_dirs = rebase_path([
                                        "include",
                                        root_gen_dir,
@@ -143,6 +147,7 @@
       # is special-cased so no //zircon/public/lib/zircon gets made, but
       # //build/config/fuchsia:compiler_sysroot can look up the metadata to
       # find the right lib_dirs.
+      legacy_barrier = []
       legacy_targets = [
         {
           target_name = "zircon"
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index ff0d2ae..32d31a0 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -232,6 +232,7 @@
     environment_label = "$zx/public/gn/toolchain:host"
     cpu = host.cpu
     os = host.os
+    direct = true
     deps = [
       ":host-tests",
     ]