[tint] Add fuzzer target support to 'gen build'

Adds two new target types: 'fuzz' and 'fuzz_cmd'.
Follows the same patterns as test and bench targets.

Change-Id: I3d3fb758b8cff32b06886503e52bd43ea95733e2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/153785
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/docs/tint/gen.md b/docs/tint/gen.md
index 7c7a48a..7d48ac6 100644
--- a/docs/tint/gen.md
+++ b/docs/tint/gen.md
@@ -19,7 +19,7 @@
 
 ### Targets
 
-There are 6 kinds of build target:
+There are 8 kinds of build target:
 
 * `cmd` targets are executables.
 * `lib` targets are libraries used in production code, and can also be used as
@@ -30,6 +30,9 @@
 * `bench` targets are libraries used by Tint benchmarks. Must not be used by
   production code.
 * `bench_cmd` are benchmark executables.
+* `fuzz` targets are libraries used by Tint fuzzers. Must not be used by
+  production code.
+* `fuzz_cmd` are fuzzer executables.
 
 The build generator uses a file naming convention based on the file name before the extension to classify each source file to a single target kind.
 
@@ -37,14 +40,19 @@
   Example: `parser_test.cc`.
 * Source files named `bench` or with a `_bench` suffix are classed as `bench` library targets. \
   Example: `writer_bench.cc`.
+* Source files named `fuzz` or with a `_fuzz` suffix are classed as `fuzz` library targets. \
+  Example: `writer_fuzz.cc`.
 * Source files with the name `main` are classed as executable targets.
   These typically exist under `src/tint/cmd`. \
   Example: `cmd/tint/main.cc`.
 * Source files with the name `main_test` are classed as test executable targets.
-  These typically exist under `src/tint/cmd`. \
+  These typically exist under `src/tint/cmd/test`. \
   Example: `cmd/test/main_test.cc`.
 * Source files with the name `main_bench` are classed as benchmark executable targets.
-  These typically exist under `src/tint/cmd`. \
+  These typically exist under `src/tint/cmd/bench`. \
+  Example: `cmd/benchmark/main_bench.cc`.
+* Source files with the name `main_fuzz` are classed as fuzzer executable targets.
+  These typically exist under `src/tint/cmd/fuzz`. \
   Example: `cmd/benchmark/main_bench.cc`.
 * All other files are considered `lib` targets. \
   Example: `parser.cc`.
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index aa88e1a..0633337 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -203,7 +203,7 @@
   target_link_libraries(${TARGET} PRIVATE benchmark::benchmark)
 endfunction()
 
-function(tint_fuzzer_compile_options TARGET)
+function(tint_fuzz_cmd_compile_options TARGET)
   tint_fuzz_compile_options(${TARGET})
 
   if(NOT "${TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS}" STREQUAL "")
@@ -221,6 +221,12 @@
   target_link_libraries(${TARGET} PRIVATE "tint_api_sanitize_fuzzer")
 endfunction()
 
+# TODO(bclayton): Remove this when fuzzers fully migrated to gen build
+function(tint_fuzzer_compile_options TARGET)
+  tint_fuzz_cmd_compile_options(${TARGET})
+  target_link_libraries(${TARGET} PRIVATE "tint_api_sanitize_fuzzer")
+endfunction()
+
 if(TINT_ENABLE_BREAK_IN_DEBUGGER)
   set_source_files_properties(utils/debug/debugger.cc
     PROPERTIES COMPILE_DEFINITIONS "TINT_ENABLE_BREAK_IN_DEBUGGER=1")
@@ -280,8 +286,11 @@
 #               linking against google-test.
 # 'bench'     - Translates to a CMake object library, configured for compiling and
 #               linking against google-benchmark.
+# 'fuzz'      - Translates to a CMake object library, configured for compiling and
+#               linking against libfuzzer.
 # 'test_cmd'  - Translates to a CMake executable target linked against google-test.
 # 'bench_cmd' - Translates to a CMake executable target linked against google-benchmark.
+# 'fuzz_cmd'  - Translates to a CMake executable target linked against libfuzz.
 # See also: docs/tint/gen.md
 ################################################################################
 
@@ -309,6 +318,10 @@
     if(TINT_BUILD_BENCHMARKS)
       set(IS_ENABLED TRUE PARENT_SCOPE)
     endif()
+  elseif((${KIND} STREQUAL fuzz) OR (${KIND} STREQUAL fuzz_cmd))
+    if(TINT_BUILD_FUZZERS)
+      set(IS_ENABLED TRUE PARENT_SCOPE)
+    endif()
   else()
     message(FATAL_ERROR "unhandled target kind ${KIND}")
   endif()
@@ -350,12 +363,18 @@
   elseif(${KIND} STREQUAL bench_cmd)
     add_executable(${TARGET})
     tint_bench_cmd_compile_options(${TARGET})
+  elseif(${KIND} STREQUAL fuzz_cmd)
+    add_executable(${TARGET})
+    tint_fuzz_cmd_compile_options(${TARGET})
   elseif(${KIND} STREQUAL test)
     add_library(${TARGET} OBJECT EXCLUDE_FROM_ALL)
     tint_test_compile_options(${TARGET})
   elseif(${KIND} STREQUAL bench)
     add_library(${TARGET} OBJECT EXCLUDE_FROM_ALL)
     tint_bench_compile_options(${TARGET})
+  elseif(${KIND} STREQUAL fuzz)
+    add_library(${TARGET} OBJECT EXCLUDE_FROM_ALL)
+    tint_fuzz_compile_options(${TARGET})
   else()
     message(FATAL_ERROR "unhandled target kind ${KIND}")
   endif()
diff --git a/src/tint/tint.gni b/src/tint/tint.gni
index 4376bb3..fc9ffeb 100644
--- a/src/tint/tint.gni
+++ b/src/tint/tint.gni
@@ -72,3 +72,43 @@
     }
   }
 }
+
+###############################################################################
+# Fuzzers
+###############################################################################
+import("//testing/libfuzzer/fuzzer_test.gni")
+template("tint_fuzz_source_set") {
+  source_set(target_name) {
+    forward_variables_from(invoker, "*", [ "configs" ])
+
+    testonly = true
+
+    if (!defined(invoker.deps)) {
+      deps = []
+    }
+
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+
+    configs += [ "${tint_src_dir}:tint_common_config" ]
+
+    if (build_with_chromium) {
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
+    }
+
+    if (!defined(invoker.public_configs)) {
+      public_configs = []
+    }
+
+    public_configs += [ "${tint_src_dir}:tint_public_config" ]
+  }
+}
+
+template("tint_fuzzer_test") {
+  fuzzer_test(target_name) {
+    forward_variables_from(invoker, "*")
+    exclude_main = false
+  }
+}
diff --git a/tools/src/cmd/gen/build/BUILD.cmake.tmpl b/tools/src/cmd/gen/build/BUILD.cmake.tmpl
index 5ee97a3..66250f4 100644
--- a/tools/src/cmd/gen/build/BUILD.cmake.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.cmake.tmpl
@@ -3,8 +3,10 @@
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "cmd")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test_cmd")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "bench_cmd")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "fuzz_cmd")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "bench")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "fuzz")}}
 
 
 {{- /*
diff --git a/tools/src/cmd/gen/build/BUILD.gn.tmpl b/tools/src/cmd/gen/build/BUILD.gn.tmpl
index 369e15d..780855e 100644
--- a/tools/src/cmd/gen/build/BUILD.gn.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.gn.tmpl
@@ -19,6 +19,8 @@
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "test_cmd")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "bench")}}
 {{- Eval "TargetIfNotEmpty" ($.Project.Target $ "bench_cmd")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "fuzz")}}
+{{- Eval "TargetIfNotEmpty" ($.Project.Target $ "fuzz_cmd")}}
 
 
 {{- /*
@@ -59,12 +61,16 @@
 tint_unittests_source_set("unittests") {
 {{- else if $.Kind.IsBench -}}
 tint_unittests_source_set("bench") {
+{{- else if $.Kind.IsFuzz -}}
+tint_fuzz_source_set("fuzz") {
 {{- else if $.Kind.IsTestCmd -}}
 test("test_cmd") {
   testonly = true
 {{- else if $.Kind.IsBenchCmd -}}
 test("bench_cmd") {
   testonly = true
+{{- else if $.Kind.IsFuzzCmd -}}
+tint_fuzzer_test("{{$.Directory.Name}}") {
 {{- else }}{{Error $.Kind}}
 {{- end  }}
 {{- if $.OutputName }}
diff --git a/tools/src/cmd/gen/build/build.go b/tools/src/cmd/gen/build/build.go
index d044663..15e17b5 100644
--- a/tools/src/cmd/gen/build/build.go
+++ b/tools/src/cmd/gen/build/build.go
@@ -307,6 +307,8 @@
 			{cfg.TestCmd, targetTestCmd},
 			{cfg.Bench, targetBench},
 			{cfg.BenchCmd, targetBenchCmd},
+			{cfg.Fuzz, targetFuzz},
+			{cfg.FuzzCmd, targetFuzzCmd},
 			{cfg.Cmd, targetCmd},
 		} {
 			if tc.cfg == nil {
diff --git a/tools/src/cmd/gen/build/directory_config.go b/tools/src/cmd/gen/build/directory_config.go
index dc5a56f..4548ad6 100644
--- a/tools/src/cmd/gen/build/directory_config.go
+++ b/tools/src/cmd/gen/build/directory_config.go
@@ -41,6 +41,10 @@
 	Bench *TargetConfig
 	// Configuration for the 'bench_cmd' target
 	BenchCmd *TargetConfig `json:"bench_cmd"`
+	// Configuration for the 'fuzz' target
+	Fuzz *TargetConfig
+	// Configuration for the 'fuzz_cmd' target
+	FuzzCmd *TargetConfig `json:"fuzz_cmd"`
 	// Configuration for the 'cmd' target
 	Cmd *TargetConfig
 }
diff --git a/tools/src/cmd/gen/build/target_kind.go b/tools/src/cmd/gen/build/target_kind.go
index 7f83f46..a40f608 100644
--- a/tools/src/cmd/gen/build/target_kind.go
+++ b/tools/src/cmd/gen/build/target_kind.go
@@ -28,12 +28,16 @@
 	targetTest TargetKind = "test"
 	// A library target, used for benchmark binaries.
 	targetBench TargetKind = "bench"
+	// A library target, used for fuzzer binaries.
+	targetFuzz TargetKind = "fuzz"
 	// An executable target.
 	targetCmd TargetKind = "cmd"
 	// A test executable target.
 	targetTestCmd TargetKind = "test_cmd"
 	// A benchmark executable target.
 	targetBenchCmd TargetKind = "bench_cmd"
+	// A fuzzer executable target.
+	targetFuzzCmd TargetKind = "fuzz_cmd"
 	// An invalid target.
 	targetInvalid TargetKind = "<invalid>"
 )
@@ -47,6 +51,9 @@
 // IsBench returns true if the TargetKind is 'bench'
 func (k TargetKind) IsBench() bool { return k == targetBench }
 
+// IsBench returns true if the TargetKind is 'fuzz'
+func (k TargetKind) IsFuzz() bool { return k == targetFuzz }
+
 // IsCmd returns true if the TargetKind is 'cmd'
 func (k TargetKind) IsCmd() bool { return k == targetCmd }
 
@@ -56,6 +63,9 @@
 // IsBenchCmd returns true if the TargetKind is 'bench_cmd'
 func (k TargetKind) IsBenchCmd() bool { return k == targetBenchCmd }
 
+// IsFuzzCmd returns true if the TargetKind is 'fuzz_cmd'
+func (k TargetKind) IsFuzzCmd() bool { return k == targetFuzzCmd }
+
 // IsTestOrTestCmd returns true if the TargetKind is 'test' or 'test_cmd'
 func (k TargetKind) IsTestOrTestCmd() bool { return k.IsTest() || k.IsTestCmd() }
 
@@ -67,9 +77,11 @@
 	targetLib,
 	targetTest,
 	targetBench,
+	targetFuzz,
 	targetCmd,
 	targetTestCmd,
 	targetBenchCmd,
+	targetFuzzCmd,
 }
 
 // targetKindFromFilename returns the target kind my pattern matching the filename
@@ -89,10 +101,14 @@
 		return targetTestCmd
 	case filename == "main_bench.cc":
 		return targetBenchCmd
+	case filename == "main_fuzz.cc":
+		return targetFuzzCmd
 	case strings.HasSuffix(noExt, "_test"):
 		return targetTest
 	case noExt == "bench" || strings.HasSuffix(noExt, "_bench"):
 		return targetBench
+	case noExt == "fuzz" || strings.HasSuffix(noExt, "_fuzz"):
+		return targetFuzz
 	case noExt == "main" || strings.HasSuffix(noExt, "_main"):
 		return targetCmd
 	default:
@@ -110,6 +126,8 @@
 		return to == targetLib || to == targetTest
 	case targetBench, targetBenchCmd:
 		return to == targetLib || to == targetBench
+	case targetFuzz, targetFuzzCmd:
+		return to == targetLib || to == targetFuzz
 	default:
 		return false
 	}