Build fuzzer tests in standalone builds too.

This will avoid compilation to break, and adds standalone reproducers
for the fuzzer_tests.

BUG=chromium:903380
BUG=dawn:34

Change-Id: I9995a852076d9f6d1ebdee5b999989c2d74d4709
Reviewed-on: https://dawn-review.googlesource.com/c/2321
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Dan Sinclair <dsinclair@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 12efda6..cc0fc28 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -17,9 +17,6 @@
 import("//build_overrides/build.gni")
 
 import("//testing/test.gni")
-if (build_with_chromium) {
-  import("//testing/libfuzzer/fuzzer_test.gni")
-}
 
 ###############################################################################
 # Template to wrap the Dawn code generator
@@ -948,100 +945,16 @@
 ###############################################################################
 # Fuzzers
 ###############################################################################
-if (build_with_chromium) {
-  group("dawn_fuzzers") {
-    testonly = true
-    deps = [
-      ":dawn_spirv_cross_glsl_fast_fuzzer",
-      ":dawn_spirv_cross_glsl_full_fuzzer",
-      ":dawn_spirv_cross_hlsl_fast_fuzzer",
-      ":dawn_spirv_cross_hlsl_full_fuzzer",
-      ":dawn_spirv_cross_msl_fast_fuzzer",
-      ":dawn_spirv_cross_msl_full_fuzzer",
-    ]
-  }
 
-  static_library("dawn_spirv_cross_fuzzer_common") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossFuzzer.cpp",
-      "src/fuzzers/DawnSPIRVCrossFuzzer.h",
-    ]
-    public_deps = [
-      "third_party:spirv_cross",
-    ]
-  }
-
-  # Uses Dawn specific options and varies input data
-  fuzzer_test("dawn_spirv_cross_glsl_fast_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  # Varies bother the options and input data
-  fuzzer_test("dawn_spirv_cross_glsl_full_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  # Uses Dawn specific options and varies input data
-  fuzzer_test("dawn_spirv_cross_hlsl_fast_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  # Varies bother the options and input data
-  fuzzer_test("dawn_spirv_cross_hlsl_full_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  # Uses Dawn specific options and varies input data
-  fuzzer_test("dawn_spirv_cross_msl_fast_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  # Varies bother the options and input data
-  fuzzer_test("dawn_spirv_cross_msl_full_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
-    ]
-    deps = [
-      ":dawn_spirv_cross_fuzzer_common",
-    ]
-  }
-
-  fuzzer_test("dawn_wire_server_and_frontend_fuzzer") {
-    sources = [
-      "src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp",
-    ]
-
-    deps = [
-      ":libdawn",
-      ":libdawn_native",
-      ":libdawn_wire",
-    ]
-
-    additional_configs = [ ":dawn_shared_library_public" ]
-  }
+group("dawn_fuzzers") {
+  testonly = true
+  deps = [
+    "src/fuzzers:dawn_spirv_cross_glsl_fast_fuzzer",
+    "src/fuzzers:dawn_spirv_cross_glsl_full_fuzzer",
+    "src/fuzzers:dawn_spirv_cross_hlsl_fast_fuzzer",
+    "src/fuzzers:dawn_spirv_cross_hlsl_full_fuzzer",
+    "src/fuzzers:dawn_spirv_cross_msl_fast_fuzzer",
+    "src/fuzzers:dawn_spirv_cross_msl_full_fuzzer",
+    "src/fuzzers:dawn_wire_server_and_frontend_fuzzer",
+  ]
 }
diff --git a/src/fuzzers/BUILD.gn b/src/fuzzers/BUILD.gn
new file mode 100644
index 0000000..9680d6e
--- /dev/null
+++ b/src/fuzzers/BUILD.gn
@@ -0,0 +1,150 @@
+# Copyright 2018 The Dawn Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build_overrides/build.gni")
+
+dawn_top_level = "../.."
+
+# We only have libfuzzer in Chromium builds but if we build fuzzer targets only
+# there, we would risk breaking fuzzer targets all the time when making changes
+# to Dawn. To avoid that, we make fuzzer targets compile in standalone builds
+# as well with a dawn_fuzzer_test target that acts like Chromium's fuzzer_test.
+#
+# The standalone fuzzer targets are able to run a single fuzzer input which
+# could help reproduce fuzzer crashes more easily because you don't need a
+# whole Chromium checkout.
+
+if (build_with_chromium) {
+  import("//testing/libfuzzer/fuzzer_test.gni")
+
+  # In Chromium build we just proxy everything to the real fuzzer_test
+  template("dawn_fuzzer_test") {
+    fuzzer_test(target_name) {
+      forward_variables_from(invoker, "*")
+    }
+  }
+} else {
+  import("//testing/test.gni")
+
+  # In standalone build we do something similar to fuzzer_test.
+  template("dawn_fuzzer_test") {
+    test(target_name) {
+      forward_variables_from(invoker,
+                             [
+                               "cflags",
+                               "cflags_cc",
+                               "check_includes",
+                               "defines",
+                               "deps",
+                               "include_dirs",
+                               "sources",
+                             ])
+
+      if (!defined(configs)) {
+        configs = []
+      }
+
+      # Weirdly fuzzer_test uses a special variable for additional configs.
+      if (defined(invoker.additional_configs)) {
+        configs += invoker.additional_configs
+      }
+
+      sources += [ "StandaloneFuzzerMain.cpp" ]
+    }
+  }
+}
+
+static_library("dawn_spirv_cross_fuzzer_common") {
+  sources = [
+    "DawnSPIRVCrossFuzzer.cpp",
+    "DawnSPIRVCrossFuzzer.h",
+  ]
+  public_deps = [
+    "${dawn_top_level}/third_party:spirv_cross",
+  ]
+}
+
+# Uses Dawn specific options and varies input data
+dawn_fuzzer_test("dawn_spirv_cross_glsl_fast_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFastFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+# Varies bother the options and input data
+dawn_fuzzer_test("dawn_spirv_cross_glsl_full_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFullFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+# Uses Dawn specific options and varies input data
+dawn_fuzzer_test("dawn_spirv_cross_hlsl_fast_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFastFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+# Varies bother the options and input data
+dawn_fuzzer_test("dawn_spirv_cross_hlsl_full_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFullFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+# Uses Dawn specific options and varies input data
+dawn_fuzzer_test("dawn_spirv_cross_msl_fast_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFastFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+# Varies bother the options and input data
+dawn_fuzzer_test("dawn_spirv_cross_msl_full_fuzzer") {
+  sources = [
+    "DawnSPIRVCrossGLSLFullFuzzer.cpp",
+  ]
+  deps = [
+    ":dawn_spirv_cross_fuzzer_common",
+  ]
+}
+
+dawn_fuzzer_test("dawn_wire_server_and_frontend_fuzzer") {
+  sources = [
+    "DawnWireServerAndFrontendFuzzer.cpp",
+  ]
+
+  deps = [
+    "${dawn_top_level}:libdawn",
+    "${dawn_top_level}:libdawn_native",
+    "${dawn_top_level}:libdawn_wire",
+  ]
+
+  additional_configs = [ "${dawn_top_level}:dawn_shared_library_public" ]
+}
diff --git a/src/fuzzers/StandaloneFuzzerMain.cpp b/src/fuzzers/StandaloneFuzzerMain.cpp
new file mode 100644
index 0000000..ba8d94b
--- /dev/null
+++ b/src/fuzzers/StandaloneFuzzerMain.cpp
@@ -0,0 +1,62 @@
+// Copyright 2018 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <vector>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
+int main(int argc, char** argv) {
+    if (argc != 2) {
+        std::cout << "Usage: <standalone reproducer> [FILE]" << std::endl;
+        return 1;
+    }
+
+    std::cout << "WARNING: this is just a best-effort reproducer for fuzzer issues in standalone "
+              << "Dawn builds. For the real fuzzer, please build inside Chromium." << std::endl;
+
+    const char* filename = argv[1];
+    std::cout << "Reproducing using file: " << filename << std::endl;
+
+    std::vector<char> data;
+    {
+        FILE* file = fopen(filename, "rb");
+        if (!file) {
+            std::cerr << "Failed to open " << filename << std::endl;
+            return 1;
+        }
+
+        fseek(file, 0, SEEK_END);
+        long tellFileSize = ftell(file);
+        if (tellFileSize <= 0) {
+            std::cerr << "Input file of incorrect size: " << filename << std::endl;
+            return 1;
+        }
+        fseek(file, 0, SEEK_SET);
+
+        size_t fileSize = static_cast<size_t>(tellFileSize);
+        data.resize(fileSize);
+
+        size_t bytesRead = fread(data.data(), sizeof(char), fileSize, file);
+        fclose(file);
+        if (bytesRead != fileSize) {
+            std::cerr << "Failed to read " << filename << std::endl;
+            return 1;
+        }
+    }
+
+    return LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(data.data()), data.size());
+}