Revert "[build][scripts] Integrate Zircon via new GN build rather than old make build"

This reverts commit 9d72346ea9b2cbb5aa2a918512405b36c9baac13.

Reason for revert: Landed accidentally via topic/CQ snafu!

Original change's description:
> [build][scripts] Integrate Zircon via new GN build rather than old make build
> 
> build-zircon.sh and thus `fx build-zircon` now runs gn (if necessary) and
> ninja (just once) rather than running `make` several times.  The extra
> arguments previously taken as make arguments are now taken as either GN
> build arguments or ninja arguments.
> 
> `fx set` now runs Zircon's `gn gen` before Fuchsia's `gn gen`, which
> consumes files generated by the former.
> 
> Zircon-related GN build arguments are gone.  Zircon configuration
> can be controlled via arguments to build-zircon.sh instead.
> 
> Note this also means that `zircon_boot_groups="all"` behavior is now
> always on.  That is, the BOOTFS in the Fuchsia boot images will be
> fatter and contain all of the Zircon tools and tests in `/boot`.
> Zircon tests now appear in `/boot/test` rather than `/system/test`.
> 
> Bug: BLD-325 #comment Fuchsia GN integration adapted to Zircon GN legacy support
> Test: manual
> Change-Id: I6e0b49b65c78a27e71078d32e40d0b22d366d666

TBR=kulakowski@google.com,pylaligand@google.com,mcgrathr@google.com,abarth@google.com,juliehockett@google.com

Change-Id: I81bb2c5892707cd7a7563d4db9faf392cdeda5fc
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: BLD-325 #comment Fuchsia GN integration adapted to Zircon GN legacy support
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn
index 2580aa0c..4864667 100644
--- a/build/config/fuchsia/BUILD.gn
+++ b/build/config/fuchsia/BUILD.gn
@@ -114,16 +114,12 @@
 }
 
 config("fdio_config") {
+  libs = [ "fdio" ]
+
   # 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.
-  foreach(image, zircon_images) {
-    if (image.name == "fdio" && image.cpu == current_cpu) {
-      assert(image.type == "so")
-      libs = [ "$zircon_root_build_dir/${image.path}" ]
-    }
-  }
-  assert(defined(libs), "fdio.so missing from Zircon images.json")
+  lib_dirs = [ "$zircon_build_dir/system/ulib/fdio" ]
 }
 
 config("executable_config") {
diff --git a/build/config/fuchsia/zircon.gni b/build/config/fuchsia/zircon.gni
index 75064ab..27a700e1 100644
--- a/build/config/fuchsia/zircon.gni
+++ b/build/config/fuchsia/zircon.gni
@@ -4,19 +4,59 @@
 
 import("//build/config/clang/clang.gni")
 
-# This is $root_build_dir in the Zircon GN build.
-zircon_root_build_dir = "//out/build-zircon"
+declare_args() {
+  # Where to find Zircon's host-side tools that are run as part of the build.
+  zircon_tools_dir = "//out/build-zircon/tools"
 
-# The top-level `tools` Ninja target in Zircon puts the tool binaries here.
-zircon_tools_dir = "$zircon_root_build_dir/tools"
+  # Zircon build directory for `target_cpu`, containing link-time `.so.abi`
+  # files that GN `deps` on //zircon/public libraries will link against.
+  # This should not be a sanitizer build.
+  zircon_build_abi_dir = "//out/build-zircon/build-$target_cpu"
 
-# The `gn gen` stage of the Zircon GN build writes this file.
-# It's a list of {name=... path=... type=...} scopes.
-zircon_images = read_file("$zircon_root_build_dir/images.json", "json")
+  # Zircon build directory for `target_cpu`, containing `.manifest` and
+  # `.zbi` files for Zircon's BOOTFS and kernel.  This provides the kernel
+  # and Zircon components used in the boot image.  It also provides the
+  # Zircon shared libraries used at runtime in Fuchsia packages.
+  #
+  # If left `""` (the default), then this is computed from
+  # [`zircon_build_abi_dir`](#zircon_build_abi_dir) and
+  # [`zircon_use_asan`](#zircon_use_asan).
+  zircon_build_dir = ""
 
-foreach(image, zircon_images) {
-  if (image.name == "kernel" && image.type == "zbi" && image.cpu == target_cpu) {
-    zircon_kernel_zbi = "$zircon_root_build_dir/${image.path}"
+  # Zircon `USE_ASAN=true` build directory for `target_cpu` containing
+  # `bootfs.manifest` with libraries and `devhost.asan`.
+  #
+  # If left `""` (the default), then this is computed from
+  # [`zircon_build_dir`](#zircon_build_dir) and
+  # [`zircon_use_asan`](#zircon_use_asan).
+  zircon_asan_build_dir = ""
+
+  # Set this if [`zircon_build_dir`](#zircon_build_dir) was built with
+  # `USE_ASAN=true`, e.g. `//scripts/build-zircon.sh -A`.  This mainly
+  # affects the defaults for [`zircon_build_dir`](#zircon_build_dir) and
+  # [`zircon_build_abi_dir`](#zircon_build_abi_dir).  It also gets noticed
+  # by //scripts/fx commands that rebuild Zircon so that they use `-A`
+  # again next time.
+  zircon_use_asan = false
+
+  # Path to `make` binary. By default, `make` is assumed to be in the
+  # path. Used in the script that generates the Zircon build rules. N.B. this
+  # path is *not* rebased, just used as is.
+  zircon_make_path = "make"
+}
+
+if (zircon_build_dir == "") {
+  zircon_build_dir = zircon_build_abi_dir
+  if (zircon_use_asan) {
+    zircon_build_dir += "-asan"
+  }
+}
+
+if (zircon_asan_build_dir == "") {
+  if (zircon_use_asan) {
+    zircon_asan_build_dir = zircon_build_dir
+  } else {
+    zircon_asan_build_dir = "${zircon_build_dir}-asan"
   }
 }
 
diff --git a/build/gn/BUILD.gn b/build/gn/BUILD.gn
index 3b74bcc..26b64c6 100644
--- a/build/gn/BUILD.gn
+++ b/build/gn/BUILD.gn
@@ -112,10 +112,14 @@
             [
               "--out",
               rebase_path("//zircon/public"),
-              "--zircon-build",
-              rebase_path(zircon_root_build_dir),
-              "--zircon-manifest",
-              rebase_path("$zircon_root_build_dir/export/manifest-$target_cpu"),
+              "--staging",
+              rebase_path("$root_out_dir/zircon-gn"),
+              "--zircon-user-build",
+              rebase_path(zircon_build_abi_dir),
+              "--zircon-tool-build",
+              rebase_path("$zircon_tools_dir/.."),
+              "--make",
+              zircon_make_path,
             ],
             "",
             zircon_files + supporting_templates)
@@ -130,6 +134,24 @@
   "FUCHSIA_BUILD_DIR='${_relative_build_dir}'",
   "FUCHSIA_ARCH='${target_cpu}'",
 ]
+if (use_goma) {
+  _fx_config_lines += [
+    "# This will affect Zircon's make via //scripts/build-zircon.sh.",
+    "export GOMACC='${goma_dir}/gomacc'",
+  ]
+}
+_fx_build_zircon_args = ""
+if (zircon_use_asan) {
+  _fx_build_zircon_args += " -A"
+}
+foreach(selector, select_variant) {
+  if (selector == "host_asan") {
+    _fx_build_zircon_args += " -H"
+  }
+}
+if (_fx_build_zircon_args != "") {
+  _fx_config_lines += [ "FUCHSIA_BUILD_ZIRCON_ARGS=($_fx_build_zircon_args)" ]
+}
 write_file("$root_build_dir/fx.config", _fx_config_lines)
 
 # Generates breakpad symbol data for unstripped binaries.
diff --git a/build/images/BUILD.gn b/build/images/BUILD.gn
index 4458773..e0f790f 100644
--- a/build/images/BUILD.gn
+++ b/build/images/BUILD.gn
@@ -19,11 +19,40 @@
 import("//garnet/go/src/pm/pm.gni")
 
 declare_args() {
+  # Groups to include from the Zircon /boot manifest into /boot.
+  # This is either "all" or a comma-separated list of one or more of:
+  #   core -- necessary to boot
+  #   misc -- utilities in /bin
+  #   test -- test binaries in /bin and /test
+  zircon_boot_groups = "core"
+
   # Path to manifest file containing data to place into the initial /data
   # partition.
   data_partition_manifest = ""
 }
 
+declare_args() {
+  # Groups to include from the Zircon /boot manifest into /system
+  # (instead of into /boot like Zircon's own bootdata.bin does).
+  # Should not include any groups that are also in zircon_boot_groups,
+  # which see.  If zircon_boot_groups is "all" then this should be "".
+  # **TODO(mcgrathr)**: _Could default to "" for `!is_debug`, or "production
+  # build".  Note including `"test"` here places all of Zircon's tests into
+  # `/system/test`, which means that Fuchsia bots run those tests too._
+  zircon_system_groups = "misc,test"
+  if (zircon_boot_groups == "all") {
+    zircon_system_groups = ""
+  }
+}
+
+if (zircon_boot_groups == "all") {
+  assert(zircon_system_groups == "",
+         "zircon_boot_groups already has everything")
+} else {
+  assert(zircon_system_groups != "all" && zircon_system_groups != "core",
+         "zircon_system_groups cannot include core (or all)")
+}
+
 # This will collect a list of scopes describing each image exported.
 # See json.gni.
 images = [
@@ -261,10 +290,10 @@
   # /system manifest files can assume that the /boot files are visible at
   # runtime, so dependencies already in /boot won't be copied into /system.
   bootfs_manifest = boot_manifest
-  bootfs_zircon_groups = "all"
+  bootfs_zircon_groups = zircon_boot_groups
 
   # Collect whatever we want from Zircon that didn't go into /boot.
-  zircon_groups = ""
+  zircon_groups = zircon_system_groups
 
   # Now each package() target in the build contributes manifest entries.
   # For system_image packages, these contain binaries that need their
@@ -440,12 +469,14 @@
     }
     sdk = "qemu-kernel.bin"
     deps = []
-    foreach(image, zircon_images) {
-      if (image.name == "qemu-kernel" && image.cpu == target_cpu) {
-        sources = [
-          "$zircon_root_build_dir/${image.path}",
-        ]
-      }
+    if (current_cpu == "arm64") {
+      sources = [
+        "$zircon_build_dir/qemu-boot-shim.bin",
+      ]
+    } else if (current_cpu == "x64") {
+      sources = [
+        "$zircon_build_dir/multiboot.bin",
+      ]
     }
   },
 ]
@@ -532,7 +563,7 @@
     ":system_image.manifest",
   ]
   inputs = [
-    zircon_kernel_zbi,
+    "${zircon_build_dir}/kernel.zbi",
     boot_manifest,
   ]
   manifest = [
@@ -1471,9 +1502,6 @@
 ###
 ### Build ID maps.
 ###
-### TODO(TC-303): ids.txt is deprecated and will be removed.
-### The toolchain rules already populate $root_build_dir/.build-id/.
-###
 
 # Combine the /boot, /system, and package build ID maps into one.
 # Nothing in the build uses this, but top-level targets always update
@@ -1484,6 +1512,8 @@
   deps = [
     ":kernel-ids.txt",
     ":system_image.manifest",
+    ":zircon-asan-build-id",
+    ":zircon-build-id",
   ]
   sources = [
     "$target_out_dir/kernel-ids.txt",
@@ -1519,7 +1549,7 @@
 action("kernel-ids.txt") {
   script = "manifest.py"
   sources = [
-    "$zircon_root_build_dir/ids-$current_cpu.txt",
+    "$zircon_build_dir/ids.txt",
   ]
   outputs = [
     "$target_out_dir/kernel-ids.txt",
@@ -1527,12 +1557,45 @@
   args = [
     "--separator= ",
     "--output=" + rebase_path(outputs[0], root_build_dir),
-    "--include-source=*/libzircon.so.debug",
+    "--include-source=*/libzircon.so",
     "--include-source=*/zircon.elf",
     "--manifest=" + rebase_path(sources[0], root_build_dir),
   ]
 }
 
+# TODO(TC-303): This is a temporary hack to get all of Zircon's debug files
+# into the $root_build_dir/.build-id hierarchy.  The Zircon build produces
+# its own .build-id hierarchy under $zircon_build_dir, but using its ids.txt
+# is the simpler way to populate the one in this build.  When ids.txt is fully
+# obsolete, hopefully Zircon will be in the unified build anyway.
+foreach(target,
+        [
+          {
+            name = "zircon-build-id"
+            sources = [
+              "$zircon_build_dir/ids.txt",
+            ]
+          },
+          {
+            name = "zircon-asan-build-id"
+            sources = [
+              "$zircon_asan_build_dir/ids.txt",
+            ]
+          },
+        ]) {
+  action(target.name) {
+    visibility = [ ":ids.txt" ]
+    script = "//scripts/build_id_conv.py"
+    sources = target.sources
+    outputs = [
+      "$root_build_dir/${target_name}.stamp",
+    ]
+    args =
+        [ "--stamp=" + rebase_path(outputs[0], root_build_dir) ] +
+        rebase_path(sources + [ "$root_build_dir/.build-id" ], root_build_dir)
+  }
+}
+
 images += [
   {
     deps = [
diff --git a/build/images/boot.gni b/build/images/boot.gni
index 07c6905..f66f0e8 100644
--- a/build/images/boot.gni
+++ b/build/images/boot.gni
@@ -65,11 +65,7 @@
     # The Multiboot trampoline is the "kernel" (`--vmlinuz` switch) and the
     # ZBI is the RAM disk (`--bootloader` switch).
     assert(current_cpu == "x64")
-    foreach(image, zircon_images) {
-      if (image.name == "multiboot-kernel" && image.cpu == target_cpu) {
-        kernel = "$zircon_root_build_dir/${image.path}"
-      }
-    }
+    kernel = "${zircon_build_dir}/multiboot.bin"
     inputs += [ kernel ]
 
     args = [
@@ -186,12 +182,7 @@
     }
 
     if (target_cpu == "x64") {
-      foreach(image, zircon_images) {
-        if (image.name == "bootloader" && image.type == "efi" &&
-            image.cpu == target_cpu) {
-          gigaboot_bin = "$zircon_root_build_dir/${image.path}"
-        }
-      }
+      gigaboot_bin = "${zircon_build_dir}/bootloader/bootx64.efi"
       args += [
         "--efi-bootloader",
         rebase_path(gigaboot_bin),
diff --git a/build/images/custom_signing.gni b/build/images/custom_signing.gni
index ff8085e..6a895a4 100644
--- a/build/images/custom_signing.gni
+++ b/build/images/custom_signing.gni
@@ -93,7 +93,7 @@
       "-v",
       rebase_path(vbmeta_file, root_build_dir),
       "-B",
-      rebase_path(zircon_root_build_dir, root_build_dir),
+      rebase_path(zircon_build_dir, root_build_dir),
     ]
   }
 }
diff --git a/build/images/finalize_manifests.py b/build/images/finalize_manifests.py
index 0163dae..cb05b79 100755
--- a/build/images/finalize_manifests.py
+++ b/build/images/finalize_manifests.py
@@ -258,15 +258,11 @@
 
     def find_debug_file(filename):
         # In the Zircon makefile build, the file to be installed is called
-        # foo.strip and the unstripped file is called foo.  In the new Zircon
-        # GN build, the file to be installed is called foo and the unstripped
-        # file is called foo.debug.  In the Fuchsia GN build, the file to be
-        # installed is called foo and the unstripped file has the same name in
-        # the exe.unstripped or lib.unstripped subdirectory.
+        # foo.strip and the unstripped file is called foo.  In the GN build,
+        # the file to be installed is called foo and the unstripped file has
+        # the same name in the exe.unstripped or lib.unstripped subdirectory.
         if filename.endswith('.strip'):
             debugfile = filename[:-6]
-        elif os.path.exists(filename + '.debug'):
-            debugfile = filename + '.debug'
         else:
             dir, file = os.path.split(filename)
             if file.endswith('.so') or '.so.' in file:
diff --git a/build/images/guest/BUILD.gn b/build/images/guest/BUILD.gn
index c4c5fbc..6812232 100644
--- a/build/images/guest/BUILD.gn
+++ b/build/images/guest/BUILD.gn
@@ -53,7 +53,7 @@
   testonly = true
 
   bootfs_manifest = boot_manifest
-  bootfs_zircon_groups = "all"
+  bootfs_zircon_groups = "misc,test"
 
   args = []
   deps = []
@@ -224,15 +224,10 @@
     ":devmgr_config.txt",
     ":guest.manifest",
   ]
-  foreach(image, zircon_images) {
-    if (image.cpu == current_cpu && image.type == "zbi" &&
-        image.name == current_cpu) {
-      inputs = [
-        "$zircon_root_build_dir/${image.path}",
-      ]
-    }
-  }
-  inputs += [ boot_manifest ]
+  inputs = [
+    "${zircon_build_dir}/zircon.zbi",
+    boot_manifest,
+  ]
   manifest = [
     {
       outputs = [
diff --git a/build/images/manifest.gni b/build/images/manifest.gni
index 0c2451d..e13f2cf 100644
--- a/build/images/manifest.gni
+++ b/build/images/manifest.gni
@@ -15,20 +15,38 @@
                 root_build_dir),
   ]
 
+  # Manifest files describing extra libraries from a Zircon build
+  # not included in `zircon_boot_manifests`, such as an ASan build.
+  # Can be either // source paths or absolute system paths.
+  #
+  # Since Zircon manifest files are relative to a Zircon source directory
+  # rather than to the directory containing the manifest, these are assumed
+  # to reside in a build directory that's a direct subdirectory of the
+  # Zircon source directory and thus their contents can be taken as
+  # relative to `get_path_info(entry, "dir") + "/.."`.
+  # TODO(mcgrathr): Make Zircon manifests self-relative too and then
+  # merge this and toolchain_manifests into generic aux_manifests.
+  if (zircon_use_asan) {
+    zircon_aux_manifests = [ "$zircon_build_abi_dir/bootfs.manifest" ]
+  } else {
+    zircon_aux_manifests = [ "$zircon_asan_build_dir/bootfs.manifest" ]
+  }
+
+  # Manifest files describing files to go into the `/boot` filesystem.
+  # Can be either // source paths or absolute system paths.
+  # `zircon_boot_groups` controls which files are actually selected.
+  #
+  # Since Zircon manifest files are relative to a Zircon source directory
+  # rather than to the directory containing the manifest, these are assumed
+  # to reside in a build directory that's a direct subdirectory of the
+  # Zircon source directory and thus their contents can be taken as
+  # relative to `get_path_info(entry, "dir") + "/.."`.
+  zircon_boot_manifests = [ "$zircon_build_dir/bootfs.manifest" ]
+
   # Extra args to globally apply to the manifest generation script.
   extra_manifest_args = []
 }
 
-foreach(image, zircon_images) {
-  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) {
-      zircon_aux_manifests = [ "$zircon_root_build_dir/${image.path}" ]
-    }
-  }
-}
-
 # Action target that generates a response file in GN's "shlex" format.
 #
 # Parameters
@@ -175,7 +193,7 @@
     }
     sources += zircon_aux_manifests + zircon_boot_manifests
     foreach(manifest, zircon_aux_manifests + zircon_boot_manifests) {
-      manifest_cwd = rebase_path(zircon_root_build_dir)
+      manifest_cwd = get_path_info(rebase_path(manifest), "dir") + "/.."
       response_file_contents += [
         "--cwd=$manifest_cwd",
         "--manifest=" + rebase_path(manifest),
@@ -202,10 +220,6 @@
       response_file_contents += [
         "--exclude=bin/devhost",
         "--exclude=bin/devhost.asan",
-
-        # Elide the empty file deposited by the Zircon build, since
-        # we will add our own.
-        "--exclude=config/devmgr",
       ]
     }
 
@@ -229,7 +243,7 @@
         response_file_contents += [ "--groups=${manifest.groups}" ]
         sources += zircon_boot_manifests
         foreach(manifest, zircon_boot_manifests) {
-          manifest_cwd = rebase_path(zircon_root_build_dir)
+          manifest_cwd = get_path_info(rebase_path(manifest), "dir") + "/.."
           response_file_contents += [
             "--cwd=$manifest_cwd",
             "--manifest=" + rebase_path(manifest),
diff --git a/build/images/zedboot/BUILD.gn b/build/images/zedboot/BUILD.gn
index cc1b270..1bb2a68 100644
--- a/build/images/zedboot/BUILD.gn
+++ b/build/images/zedboot/BUILD.gn
@@ -100,7 +100,7 @@
     ":zedboot.manifest",
   ]
   inputs = [
-    zircon_kernel_zbi,
+    "${zircon_build_dir}/kernel.zbi",
     manifest_file,
   ]
   manifest = [
diff --git a/build/zircon/create_gn_rules.py b/build/zircon/create_gn_rules.py
index 85a0c1e..8c2df11 100755
--- a/build/zircon/create_gn_rules.py
+++ b/build/zircon/create_gn_rules.py
@@ -92,10 +92,8 @@
             return
         if current_list and current_map:
             raise Exception('Found both map-style and list-style section')
-        if current_map:
-            result[current_section] = current_map
-        elif current_list:
-            result[current_section] = current_list
+        result[current_section] = (current_map if current_map
+                                   else current_list)
     for line in lines:
         section_match = section_exp.match(line)
         if section_match:
@@ -120,8 +118,9 @@
     # name: foo/bar.h
     # path: <SOURCE|BUILD>/somewhere/under/zircon/foo/bar.h
     (full_path, changes) = re.subn('^SOURCE', context.source_base, path)
+    build_base = context.tool_build_base if is_tool else context.user_build_base
     if not changes:
-        (full_path, changes) = re.subn('^BUILD', context.build_base, path)
+        (full_path, changes) = re.subn('^BUILD', build_base, path)
     if not changes:
         raise Exception('Unknown pattern type: %s' % path)
     folder = None
@@ -445,10 +444,12 @@
 class GenerationContext(object):
     '''Describes the context in which GN rules should be generated.'''
 
-    def __init__(self, out_dir, source_base, build_base, templates):
+    def __init__(self, out_dir, source_base, user_build_base, tool_build_base,
+                 templates):
         self.out_dir = out_dir
         self.source_base = source_base
-        self.build_base = build_base
+        self.user_build_base = user_build_base
+        self.tool_build_base = tool_build_base
         self.templates = templates
 
 
@@ -457,15 +458,21 @@
     parser.add_argument('--out',
                         help='Path to the output directory',
                         required=True)
-    parser.add_argument('--zircon-build',
-                        help='Path to the Zircon build directory',
+    parser.add_argument('--staging',
+                        help='Path to the staging directory',
                         required=True)
-    parser.add_argument('--zircon-manifest',
-                        help='Path to the Zircon export/manifest file',
+    parser.add_argument('--zircon-user-build',
+                        help='Path to the Zircon "user" build directory',
+                        required=True)
+    parser.add_argument('--zircon-tool-build',
+                        help='Path to the Zircon "tools" build directory',
                         required=True)
     parser.add_argument('--debug',
                         help='Whether to print out debug information',
                         action='store_true')
+    parser.add_argument('--make',
+                        help='Path to make binary',
+                        required=True)
     args = parser.parse_args()
 
     out_dir = os.path.abspath(args.out)
@@ -477,13 +484,29 @@
     shutil.rmtree(os.path.join(out_dir, 'tool'), True)
     debug = args.debug
 
-    # Parse package definitions from Zircon's build.
+    # Generate package descriptions through Zircon's build.
+    zircon_dir = os.path.abspath(args.staging)
+    shutil.rmtree(zircon_dir, True)
+    if debug:
+        print('Building Zircon in: %s' % zircon_dir)
+    make_args = [
+        args.make,
+        'packages',
+        'BUILDDIR=%s' % zircon_dir,
+    ]
+
+    env = {}
+    env['PATH'] = os.environ['PATH']
+    if not debug:
+        env['QUIET'] = '1'
+    subprocess.check_call(make_args, cwd=ZIRCON_ROOT, env=env)
+    # Parse package definitions.
     packages = []
-    with open(args.zircon_manifest, 'r') as manifest:
-        for file in manifest:
-            file = file.strip()
-            with open(os.path.join(args.zircon_build, 'export', file), 'r') as pkg_file:
-                packages.append(parse_package(pkg_file.readlines()))
+    with open(os.path.join(zircon_dir, 'export', 'manifest'), 'r') as manifest:
+        package_files = map(lambda line: line.strip(), manifest.readlines())
+    for file in package_files:
+        with open(os.path.join(zircon_dir, 'export', file), 'r') as pkg_file:
+            packages.append(parse_package(pkg_file.readlines()))
     if debug:
         print('Found %s packages:' % len(packages))
         names = sorted(map(lambda p: p['package']['name'], packages))
@@ -494,7 +517,8 @@
     context = GenerationContext(
         out_dir,
         ZIRCON_ROOT,
-        os.path.abspath(args.zircon_build),
+        os.path.abspath(args.zircon_user_build),
+        os.path.abspath(args.zircon_tool_build),
         TemplateLookup(directories=[SCRIPT_DIR]),
     )
     for package in packages:
@@ -528,14 +552,10 @@
         if debug:
             print('Processed %s (%s)' % (name, type))
 
-    board_file_lines = []
-    for cpu in ['arm64', 'x64']:
-        board_path = os.path.join(args.zircon_build, 'export',
-                                  'boards-%s.list' % cpu)
-        with open(board_path, 'r') as board_file:
-            board_file_lines += [ '[%s]' % cpu ] + board_file.readlines()
-    package = parse_package(board_file_lines)
-    generate_board_list(package, context)
+    board_path = os.path.join(zircon_dir, 'export', 'all-boards.list')
+    with open(board_path, 'r') as board_file:
+        package = parse_package(board_file.readlines())
+        generate_board_list(package, context)
 
 
 if __name__ == '__main__':
diff --git a/scripts/build-zircon.sh b/scripts/build-zircon.sh
index 6c20f37..f18edf4 100755
--- a/scripts/build-zircon.sh
+++ b/scripts/build-zircon.sh
@@ -6,41 +6,32 @@
 readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
 readonly ROOT_DIR="$(dirname "${SCRIPT_DIR}")"
 
+JOBS=`getconf _NPROCESSORS_ONLN` || {
+  Cannot get number of processors
+  exit 1
+}
+
 set -eo pipefail; [[ "${TRACE}" ]] && set -x
 
 usage() {
-  echo "$0 <options> [<gn_build_arg>=<value> ...] [<ninja_arg> ...]"
+  echo "$0 <options> <extra-make-arguments>"
   echo ""
   echo "Options:"
   echo "  -c: Clean before building"
-  echo "  -g: Run GN but not Ninja"
-  echo "  -G: Run Ninja but not GN (Ninja will re-run GN as needed)"
   echo "  -v: Level 1 verbosity"
   echo "  -V: Level 2 verbosity"
   echo "  -A: Build with ASan"
   echo "  -H: Build host tools with ASan"
-  echo "  -n: Just print build commands to STDOUT."
+  echo "  -n: Just print make recipes to STDOUT."
   echo "  -l: Only build tools; do not build zircon."
   echo "  -j N: Passed along to make (number of parallel jobs)"
   echo "  -t <target>: Architecture (GN style) to build, instead of all"
   echo "  -o <outdir>: Directory in which to put the build-zircon directory."
   echo ""
-  echo 'Additional arguments containing `=` will be used as GN build arguments.'
-  echo "Other additional arguments will be passed as extra Ninja arguments."
-  echo "e.g., $0 use_goma=true core-tests-x64"
-  echo ""
-  echo 'Note that -A and -H translate into a `variants=...` build argument.'
-  echo "You can't use those switches and also such a build argument."
-  echo "Note that if GN no build arguments (nor -c) are specified and the file"
-  echo "<outdir>/build-zircon/args.gn already exists, it will be reused."
-  echo "For nontrivial configuration changes, edit the args.gn file by hand"
-  echo "either from scratch or after a run of this script with switches;"
-  echo "then run this script with neither build arguments nor -A or -H."
+  echo "Additional arguments may be passed to make using standard FOO=bar syntax."
+  echo "E.g., build-zircon.sh USE_CLANG=true"
 }
 
-readonly GN="${ROOT_DIR}/zircon/prebuilt/downloads/gn"
-readonly NINJA="${ROOT_DIR}/zircon/prebuilt/downloads/ninja"
-
 declare ASAN="false"
 declare CLEAN="false"
 declare DRY_RUN="false"
@@ -49,16 +40,11 @@
 declare OUTDIR="${ROOT_DIR}/out"
 declare VERBOSE="0"
 declare -a ARCHLIST=(arm64 x64)
-declare JOBS=0
-declare RUN_GN="true"
-declare RUN_NINJA="true"
 
-while getopts "AcgGHhlnj:t:p:o:vV" opt; do
+while getopts "AcHhlnj:t:p:o:vV" opt; do
   case "${opt}" in
     A) ASAN="true" ;;
     c) CLEAN="true" ;;
-    g) RUN_NINJA="false" ;;
-    G) RUN_GN="false" ;;
     H) HOST_ASAN="true" ;;
     h) usage ; exit 0 ;;
     n) DRY_RUN="true" ;;
@@ -81,90 +67,51 @@
   rm -rf -- "${ZIRCON_BUILDROOT}"
 fi
 
-GN_CMD=("$GN" gen "$ZIRCON_BUILDROOT" --root="${ROOT_DIR}/zircon")
-NINJA_CMD=("$NINJA" -C "$ZIRCON_BUILDROOT")
-VARIANTS=()
-GN_ARGS=()
+# These variables are picked up by make from the environment.
+case "${VERBOSE}" in
+  1) QUIET=0 ; V=0 ;;
+  2) QUIET=0 ; V=1 ;;
+  *) QUIET=1 ; V=0 ;;
+esac
+export QUIET V
 
-if $ASAN; then
-  VARIANTS+=(asan)
-fi
-if $HOST_ASAN; then
-  VARIANTS+=(host_asan)
-fi
-if [ ${#VARIANTS[@]} -gt 0 ]; then
-  VARIANTS_ARG='variants = ['
-  for variant in "${VARIANTS[@]}"; do
-    VARIANTS_ARG+="\"${variant}\", "
-  done
-  VARIANTS_ARG+=']'
-  GN_ARGS+=("$VARIANTS_ARG")
-fi
-
-if [[ $VERBOSE -lt 1 ]]; then
-  GN_CMD+=(-q)
-  # Ninja doesn't have a --quiet switch.
-fi
-if [[ $VERBOSE -ge 2 ]]; then
-  NINJA_CMD+=(-v)
-fi
-
-if $DRY_RUN; then
-  NINJA_CMD+=(-n)
-fi
-
-if [[ "$JOBS" != 0 ]]; then
-   NINJA_CMD+=(-j "$JOBS")
-fi
-
-if $TOOLS_ONLY; then
-  NINJA_CMD+=(tools)
+if [[ "${ASAN}" = "true" ]]; then
+  readonly NOT_ASAN=false
 else
-  NINJA_CMD+=(legacy-host_tests)
-  for arch in "${ARCHLIST[@]}"; do
-    NINJA_CMD+=("manifest-$arch")
-  done
+  readonly NOT_ASAN=true
 fi
 
-for arg in "$@"; do
-  # TODO(BLD-325): Special case for argument used by bot recipes.
-  # Remove this after infra transitions.
-  if [[ "$arg" == GOMACC=* ]]; then
-    goma_dir="$(dirname "${arg#GOMACC=}")"
-    GN_ARGS+=(use_goma=true "goma_dir=\"$goma_dir\"")
-  elif [[ "$arg" == *=* ]]; then
-    GN_ARGS+=("$arg")
-  elif $RUN_NINJA; then
-    NINJA_CMD+=("$arg")
-  else
-    echo >&2 "Extra Ninja arguments given when -g says not to run Ninja"
-    exit 2
-  fi
-done
-
-if [[ ${#GN_ARGS[@]} -gt 0 ]]; then
-  if ! $RUN_GN; then
-    echo >&2 "GN build arguments specified when -G says not to run GN"
-    echo >&2 "Consider editting $ZIRCON_BUILDROOT/args.gn by hand instead."
-    exit 2
-  fi
-  GN_CMD+=("--args=${GN_ARGS[*]}")
-elif $RUN_GN && [[ $VERBOSE -gt 0 && -r "$ZIRCON_BUILDROOT/args.gn" ]]; then
-  echo >&2 "Reusing existing $ZIRCON_BUILDROOT/args.gn file."
+if [[ "${DRY_RUN}" = "true" ]]; then
+  readonly DRY_RUN_ARGS="-Bnwk"
 fi
 
-run_cmd() {
-  if [[ $VERBOSE -gt 0 ]]; then
-    (set -x; "$@")
-  else
-    "$@"
-  fi
+make_zircon_common() {
+  (test $QUIET -ne 0 || set -x
+   exec make ${DRY_RUN_ARGS} --no-print-directory -C "${ROOT_DIR}/zircon" \
+             -j ${JOBS} DEBUG_BUILDROOT=../../zircon "$@")
 }
 
-if ! $RUN_GN && ! $RUN_NINJA; then
-  echo >&2 "Doing nothing since -g and -G say not to."
+# Build host tools.
+make_zircon_common \
+  BUILDDIR=${OUTDIR}/build-zircon HOST_USE_ASAN="${HOST_ASAN}" tools "$@"
+
+if [[ "${TOOLS_ONLY}" = "true" ]]; then
   exit 0
 fi
 
-! $RUN_GN || run_cmd "${GN_CMD[@]}"
-! $RUN_NINJA || run_cmd "${NINJA_CMD[@]}"
+make_zircon_target() {
+  make_zircon_common \
+    BUILDROOT=${ZIRCON_BUILDROOT} TOOLS=${OUTDIR}/build-zircon/tools "$@"
+}
+
+for ARCH in "${ARCHLIST[@]}"; do
+    # Build without ASan for sysroot.  If all of userland will be ASan,
+    # then this build is only user libraries.
+    make_zircon_target PROJECT="${ARCH}" \
+        BUILDDIR_SUFFIX= ENABLE_ULIB_ONLY="${ASAN}" "$@"
+
+    # Always build at least the libraries with ASan, but never the sysroot.
+    make_zircon_target PROJECT="${ARCH}" \
+        BUILDDIR_SUFFIX=-asan USE_ASAN=true ENABLE_BUILD_SYSROOT=false \
+        ENABLE_ULIB_ONLY="${NOT_ASAN}"
+done
diff --git a/scripts/devshell/build b/scripts/devshell/build
index 7d0609f..97ae565 100755
--- a/scripts/devshell/build
+++ b/scripts/devshell/build
@@ -3,16 +3,63 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-### Run Ninja to build Fuchsia
+### build Fuchsia
 
-## usage: fx build [ninja arguments ...]
-## Run `fx build -h` to see Ninja argument details.
+## usage: fx build [ninja option,...] [target,...]
 
 source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
 fx-config-read
 
 function main {
-  fx-run-ninja "${FUCHSIA_DIR}/buildtools/ninja" -C "${FUCHSIA_BUILD_DIR}" "$@"
+  local args=()
+  local have_load=false
+  local have_jobs=false
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+    -l) have_load=true ;;
+    -j) have_jobs=true ;;
+    esac
+    args+=("$1")
+    shift
+  done
+
+  if ! $have_load; then
+    if [[ "$(uname -s)" == "Darwin" ]]; then
+      # Load level on Darwin is quite different from that of Linux, wherein a
+      # load level of 1 per CPU is not necessarily a prohibitive load level. An
+      # unscientific study of build side effects suggests that cpus*20 is a
+      # reaosnable value to prevent catastrophic load (i.e. user can not kill
+      # the build, can not lock the screen, etc).
+      local cpus="$(fx-cpu-count)"
+      args=("-l" $(($cpus * 20)) "${args[@]}")
+    fi
+  fi
+
+  if ! $have_jobs; then
+    local concurrency="$(fx-choose-build-concurrency)"
+    # macOS in particular has a low default for number of open file descriptors
+    # per process, which is prohibitive for higher job counts. Here we raise
+    # the number of allowed file descriptors per process if it appears to be
+    # low in order to avoid failures due to the limit. See `getrlimit(2)` for
+    # more information.
+    local min_limit=$((${concurrency} * 2))
+    if [[ $(ulimit -n) -lt "${min_limit}" ]]; then
+      ulimit -n "${min_limit}"
+    fi
+    args=("-j" "${concurrency}" "${args[@]}")
+  fi
+
+
+  # TERM is passed for the pretty ninja UI
+  # PATH is passed as some tools are referenced via $PATH due to platform differences.
+  # TMPDIR is passed for Goma on macOS. TMPDIR must be set, or unset, not
+  # empty. Some Dart build tools have been observed writing into source paths
+  # when TMPDIR="" - it is deliberately unquoted and using the ${+} expansion
+  # expression).
+  fx-try-locked env -i TERM="${TERM}" PATH="${PATH}" \
+    ${NINJA_STATUS+"NINJA_STATUS=${NINJA_STATUS}"} \
+    ${TMPDIR+"TMPDIR=$TMPDIR"} \
+    "${FUCHSIA_DIR}/buildtools/ninja" -C "${FUCHSIA_BUILD_DIR}" "${args[@]}"
 }
 
 main "$@"
diff --git a/scripts/devshell/build-zircon b/scripts/devshell/build-zircon
index 12f5507..2071072 100755
--- a/scripts/devshell/build-zircon
+++ b/scripts/devshell/build-zircon
@@ -3,19 +3,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-### Run //scripts/build-zircon.sh to build Zircon.
-## Arguments are passed to build-zircon.sh; run with -h for details.
+### build the kernel
 
 source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
 fx-config-read
 
-# If `fx set` or `fx build-zircon` has already been run, then the explicit
-# `gn gen` run is superfluous and Ninja will do it as needed.
-if [[ -r "${ZIRCON_BUILDROOT}/args.gn" ]]; then
-  set -- -G "$@"
-fi
-
-fx-run-ninja "${FUCHSIA_DIR}/scripts/build-zircon.sh" \
+echo "Building zircon..."
+"${FUCHSIA_DIR}/scripts/build-zircon.sh" \
   -t "${FUCHSIA_ARCH}" \
   -j "$(fx-choose-build-concurrency)" \
-  "$@"
+  "${FUCHSIA_BUILD_ZIRCON_ARGS[@]}" "$@"
diff --git a/scripts/devshell/full-build b/scripts/devshell/full-build
index 5218479..3fb0c5a 100755
--- a/scripts/devshell/full-build
+++ b/scripts/devshell/full-build
@@ -4,8 +4,7 @@
 # found in the LICENSE file.
 
 ### build zircon, then build fuchsia
-## Runs exactly `fx build-zircon` followed by `fx build`.
-## Arguments are ignored.  To pass arguments, run each step separately.
+## build zircon, then build fuchsia
 
 set -e
 
diff --git a/scripts/devshell/lib/vars.sh b/scripts/devshell/lib/vars.sh
index b5d8628..4c13d10 100644
--- a/scripts/devshell/lib/vars.sh
+++ b/scripts/devshell/lib/vars.sh
@@ -115,14 +115,22 @@
   local -r build_dir="$1"
   if [[ "$build_dir" == /* ]]; then
     local -r args_file="${build_dir}/args.gn"
+    local -r zircon_args_file="${build_dir}.zircon-args"
   else
     local -r args_file="${FUCHSIA_DIR}/${build_dir}/args.gn"
+    local -r zircon_args_file="${FUCHSIA_DIR}/${build_dir}.zircon-args"
   fi
-  local arch="$(fx-config-glean-arch "$args_file")" || return
+  local arch zircon_args
+  arch="$(fx-config-glean-arch "$args_file")" || return
+  if [[ -f "${zircon_args_file}" ]]; then
+    zircon_args=$(<"${zircon_args_file}")
+  fi
+  shift
   echo > "${FUCHSIA_CONFIG}" "\
 # Generated by \`fx set\` or \`fx use\`.
 FUCHSIA_BUILD_DIR='${build_dir}'
 FUCHSIA_ARCH='${arch}'
+FUCHSIA_BUILD_ZIRCON_ARGS=(${zircon_args})
 "
 }
 
@@ -325,57 +333,3 @@
 function fx-exit-on-failure {
   "$@" || exit $?
 }
-
-# Massage a ninja command line to add default -j and/or -l switches.
-# Note this takes the ninja command itself as first argument.
-# This can be used both to run ninja directly or to run a wrapper script.
-function fx-run-ninja {
-  local args=()
-  local have_load=false
-  local have_jobs=false
-  while [[ $# -gt 0 ]]; do
-    case "$1" in
-    -l) have_load=true ;;
-    -j) have_jobs=true ;;
-    esac
-    args+=("$1")
-    shift
-  done
-
-  if ! $have_load; then
-    if [[ "$(uname -s)" == "Darwin" ]]; then
-      # Load level on Darwin is quite different from that of Linux, wherein a
-      # load level of 1 per CPU is not necessarily a prohibitive load level. An
-      # unscientific study of build side effects suggests that cpus*20 is a
-      # reaosnable value to prevent catastrophic load (i.e. user can not kill
-      # the build, can not lock the screen, etc).
-      local cpus="$(fx-cpu-count)"
-      args+=("-l" $(($cpus * 20)))
-    fi
-  fi
-
-  if ! $have_jobs; then
-    local concurrency="$(fx-choose-build-concurrency)"
-    # macOS in particular has a low default for number of open file descriptors
-    # per process, which is prohibitive for higher job counts. Here we raise
-    # the number of allowed file descriptors per process if it appears to be
-    # low in order to avoid failures due to the limit. See `getrlimit(2)` for
-    # more information.
-    local min_limit=$((${concurrency} * 2))
-    if [[ $(ulimit -n) -lt "${min_limit}" ]]; then
-      ulimit -n "${min_limit}"
-    fi
-    args+=("-j" "${concurrency}")
-  fi
-
-  # TERM is passed for the pretty ninja UI
-  # PATH is passed as some tools are referenced via $PATH due to platform differences.
-  # TMPDIR is passed for Goma on macOS. TMPDIR must be set, or unset, not
-  # empty. Some Dart build tools have been observed writing into source paths
-  # when TMPDIR="" - it is deliberately unquoted and using the ${+} expansion
-  # expression).
-  fx-try-locked env -i TERM="${TERM}" PATH="${PATH}" \
-    ${NINJA_STATUS+"NINJA_STATUS=${NINJA_STATUS}"} \
-    ${TMPDIR+"TMPDIR=$TMPDIR"} \
-    "${args[@]}"
-}
diff --git a/scripts/devshell/run-host-tests b/scripts/devshell/run-host-tests
index 61653ec..3ffd338 100755
--- a/scripts/devshell/run-host-tests
+++ b/scripts/devshell/run-host-tests
@@ -40,7 +40,7 @@
   done
 
   if [[ $ZIRCON -eq 1 ]]; then
-    host_test_dir="${ZIRCON_BUILDROOT}/host_tests"
+    host_test_dir="${ZIRCON_BUILD_DIR}/host_tests"
     fx-command-run build-zircon "-v"
   else
     host_test_dir="${FUCHSIA_BUILD_DIR}/host_tests"
diff --git a/scripts/devshell/set b/scripts/devshell/set
index b4478ab..6b6153d 100755
--- a/scripts/devshell/set
+++ b/scripts/devshell/set
@@ -134,8 +134,8 @@
 ##                         build.
 ##                         This flag is deprecated, please use one of
 ##                         --available, --preinstall, or --monolith.
-##   --zircon-arg ARG      Additional arguments to pass to build-zircon.sh.
-##                         Can be given multiple times.
+##   --zircon-arg ARG      Additional arguments to pass to Zircon make. Can be given
+##                         multiple times.
 ##
 ## Example:
 ##
@@ -238,7 +238,7 @@
   local preinstall=()
   local monolith=()
   local packages=()
-  local zircon_args=(-v -g -t "$arch")
+  local zircon_args=()
   local extra_packages=()
   local build_dir=
   local variant=
@@ -501,7 +501,6 @@
   # Add goma or ccache settings as appropriate.
   if [[ "${use_goma}" -eq 1 ]]; then
     gn_args+=" use_goma=true goma_dir=\"${goma_dir}\""
-    zircon_args+=(use_goma=true "goma_dir=\"${goma_dir}\"")
   elif [[ "${ccache}" -eq 1 ]]; then
     gn_args+=" use_ccache=true"
   fi
@@ -546,14 +545,14 @@
     gn_args+=" select_variant=[${variant}]"
   fi
 
+  mkdir -p "${build_dir}"
+  echo "${zircon_args[@]}" > "${build_dir}.zircon-args"
+
   # Using a subshell with -x prints out the gn command precisely with shell
   # quoting so a cut&paste to the command line works.  Always show the real
   # meaning of what this script does so everyone learns how GN works.
   (
-    set -x -e
-    # Zircon's `gn gen` phase has to be done before Fuchsia's `gn gen`.
-    # The former produces the legacy-$cpu.json file consumed by the latter.
-    "${FUCHSIA_DIR}/scripts/build-zircon.sh" "${zircon_args[@]}"
+    set -x
     "${FUCHSIA_DIR}/buildtools/gn" ${gn_cmd} "${build_dir}" \
                                    "${gn_switches[@]}" --args="${gn_args}" "$@"
   # If GN failed, don't update .config.