[fuzzing] Generate cmx files

This CL adds the GN machinery to add component manifests for fuzz
packages.

SEC-102

Tests: Manually ran fuzzers with 'fx fuzz'

Change-Id: I9379e388adcbc5ebdbc2f0678cc43b7da0473772
diff --git a/fuzzing/fuzzer.gni b/fuzzing/fuzzer.gni
index 683fd55..db24c26 100644
--- a/fuzzing/fuzzer.gni
+++ b/fuzzing/fuzzer.gni
@@ -69,6 +69,7 @@
   if (current_toolchain == toolchain_variant.base) {
     fuzz_gen_dir = "${target_gen_dir}/${target_name}"
     fuzz_name = target_name
+
     # Copy the fuzzer dictionary
     if (defined(dictionary)) {
       copy("${fuzz_name}_dictionary") {
@@ -112,6 +113,26 @@
         args += corpora
       }
     }
+
+    # Create component manifest targets for all the sanitizers
+    action("${fuzz_name}_cmx") {
+      script = "//build/fuzzing/gen_fuzzer_cmx.py"
+      outputs = [
+        "${fuzz_gen_dir}/${fuzz_name}.cmx",
+      ]
+      args = [
+        "--out",
+        rebase_path("${fuzz_gen_dir}/${fuzz_name}.cmx"),
+        "--bin",
+        "${fuzz_name}",
+      ]
+      if (defined(invoker.cmx)) {
+        args += [
+          "--cmx",
+          rebase_path(invoker.cmx),
+        ]
+      }
+    }
   } else {
     if (defined(dictionary)) {
       not_needed([ "dictionary" ])
@@ -184,7 +205,7 @@
       fuzz_name = get_label_info(fuzz_target, "name")
 
       # Find the executable variant for the sanitized fuzzer
-      sanitized_target=""
+      sanitized_target = ""
       foreach(sanitizer, fuzz.sanitizers) {
         foreach(selector, select_variant_canonical) {
           if (selector.variant == "${sanitizer}-fuzzer") {
@@ -200,20 +221,21 @@
               selected = selector_output_name[0] == fuzz_name
             }
             if (selected && sanitized_target == "") {
-              sanitized_target="${fuzz_target}(${toolchain_variant.base}-${sanitizer}-fuzzer)"
+              sanitized_target = "${fuzz_target}(${toolchain_variant.base}-${sanitizer}-fuzzer)"
             }
           }
         }
       }
 
-      fuzz_gen_dir = get_label_info(fuzz_target, "target_gen_dir") + "/${fuzz_name}"
+      fuzz_gen_dir =
+          get_label_info(fuzz_target, "target_gen_dir") + "/${fuzz_name}"
 
       # Only add the binary and component manifest if fuzzing
       if (sanitized_target != "") {
         fuzz_out_dir = get_label_info("${sanitized_target}", "root_out_dir")
-
         fuzz.deps += [
           sanitized_target,
+          "${fuzz_target}_cmx",
         ]
 
         fuzz.binaries += [
@@ -223,6 +245,13 @@
             dest = "${fuzz_name}"
           },
         ]
+
+        fuzz.meta += [
+          {
+            path = rebase_path("${fuzz_gen_dir}/${fuzz_name}.cmx")
+            dest = "${fuzz_name}.cmx"
+          },
+        ]
       }
 
       # Resources are common to all variants
diff --git a/fuzzing/gen_fuzzer_cmx.py b/fuzzing/gen_fuzzer_cmx.py
new file mode 100755
index 0000000..4b86fb5
--- /dev/null
+++ b/fuzzing/gen_fuzzer_cmx.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# Copyright 2018 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 argparse
+import json
+import sys
+from collections import defaultdict
+
+def main():
+    parser = argparse.ArgumentParser('Creates a component manifest for a fuzzer')
+    parser.add_argument('--out',
+                        help='Path to the output file',
+                        required=True)
+    parser.add_argument('--bin',
+                        help='Package relative path to the binary',
+                        required=True)
+    parser.add_argument('--cmx',
+                        help='Optional starting manifest')
+    args = parser.parse_args()
+
+    cmx = defaultdict(dict)
+    if args.cmx:
+      with open(args.cmx, 'r') as f:
+        cmx = json.load(f)
+
+    cmx["program"]["binary"] = "bin/" + args.bin
+
+    if "services" not in cmx["sandbox"]:
+      cmx["sandbox"]["services"] = []
+    cmx["sandbox"]["services"].append("fuchsia.sys.Launcher")
+
+    if "features" not in cmx["sandbox"]:
+      cmx["sandbox"]["features"] = []
+    if "persistent-storage" not in cmx["sandbox"]["features"]:
+      cmx["sandbox"]["features"].append("persistent-storage")
+
+    with open(args.out, 'w') as f:
+        f.write(json.dumps(cmx, sort_keys=True, indent=4))
+
+if __name__ == '__main__':
+  sys.exit(main())