Expand build flags for Tint.

This CL extends the build options to Tint to make the various readers
and writers all optional.

Change-Id: I913e1830b1bb2243eff5deb4b8079ba592dd52e1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16801
Reviewed-by: David Neto <dneto@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb60a1a..2392cc8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,15 +27,21 @@
 endif()
 
 option(TINT_BUILD_DOCS "Build documentation" ON)
-option(TINT_BUILD_SPV_PARSER "Build the SPIR-V input parser" OFF)
-option(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
+option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" ON)
+option(TINT_BUILD_WGSL_READER "Builde the WGSL input reader" ON)
+option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON)
+option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
 
+option(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
 option(TINT_ENABLE_MSAN "Enable memory sanitizer" OFF)
 option(TINT_ENABLE_ASAN "Enable address sanitizer" OFF)
 option(TINT_ENABLE_UBSAN "Enable undefined behaviour sanitizer" OFF)
 
 message(STATUS "Tint build docs: ${TINT_BUILD_DOCS}")
-message(STATUS "Tint build SPIR-V parser: ${TINT_BUILD_SPV_PARSER}")
+message(STATUS "Tint build SPIR-V reader: ${TINT_BUILD_SPV_READER}")
+message(STATUS "Tint build WGSL reader: ${TINT_BUILD_WGSL_READER}")
+message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
+message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
 message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
 message(STATUS "Tint build with ASAN: ${TINT_ENABLE_ASAN}")
 message(STATUS "Tint build with MSAN: ${TINT_ENABLE_MSAN}")
@@ -44,7 +50,7 @@
 message(STATUS "Using python3")
 find_package(PythonInterp 3 REQUIRED)
 
-if (${TINT_BUILD_SPV_PARSER})
+if (${TINT_BUILD_SPV_READER})
   include_directories("${PROJECT_SOURCE_DIR}/third_party/spirv-tools/include")
 endif()
 
@@ -72,7 +78,13 @@
   include_directories("${PROJECT_SOURCE_DIR}")
 
   target_compile_definitions(${TARGET} PRIVATE
-      -DTINT_BUILD_SPV_PARSER=$<BOOL:${TINT_BUILD_SPV_PARSER}>)
+      -DTINT_BUILD_SPV_READER=$<BOOL:${TINT_BUILD_SPV_READER}>)
+  target_compile_definitions(${TARGET} PRIVATE
+      -DTINT_BUILD_WGSL_READER=$<BOOL:${TINT_BUILD_WGSL_READER}>)
+  target_compile_definitions(${TARGET} PRIVATE
+      -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
+  target_compile_definitions(${TARGET} PRIVATE
+      -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
 
   if (${COMPILER_IS_LIKE_GNU})
     target_compile_options(${TARGET} PRIVATE
@@ -140,6 +152,10 @@
 add_subdirectory(samples)
 
 if (${TINT_BUILD_FUZZERS})
+  if (NOT ${TINT_BUILD_WGSL_READER)
+    message(ERROR, "Fuzzers require WGSL reader to be enabled")
+  endif()
+
   add_subdirectory(fuzz)
 endif()
 
diff --git a/README.md b/README.md
index 10a9e69..f69ea92 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,10 @@
  * Python, for fetching dependencies
 
 ## Build options
- * `TINT_BUILD_SPV_PARSER` : enable the SPIR-V input parser
+ * `TINT_BUILD_SPV_READER` : enable the SPIR-V input reader (off by default)
+ * `TINT_BUILD_WGSL_READER` : enable the WGSL input reader (on by default)
+ * `TINT_BUILD_SPV_WRITER` : enable the SPIR-V output writer (on by default)
+ * `TINT_BUILD_WGSL_WRITER` : enable the WGSL output writer (on by default)
 
 ## Building
 Tint uses Chromium dependency management so you need to [install depot_tools] and add it to your PATH.
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt
index 5fb3f61..b144b43 100644
--- a/samples/CMakeLists.txt
+++ b/samples/CMakeLists.txt
@@ -18,6 +18,11 @@
 
 ## Tint executable
 add_executable(tint ${TINT_SRCS})
-target_link_libraries(tint libtint SPIRV-Tools)
 tint_default_compile_options(tint)
+target_link_libraries(tint libtint)
+
+if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+  target_link_libraries(tint SPIRV-Tools)
+endif()
+
 
diff --git a/samples/main.cc b/samples/main.cc
index 708ab75..fc855d1 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -17,18 +17,27 @@
 #include <memory>
 #include <vector>
 
-#include "spirv-tools/libspirv.hpp"
 #include "src/reader/reader.h"
-#include "src/reader/wgsl/parser.h"
 #include "src/type_determiner.h"
 #include "src/validator.h"
-#include "src/writer/spirv/generator.h"
-#include "src/writer/wgsl/generator.h"
 #include "src/writer/writer.h"
 
-#if TINT_BUILD_SPV_PARSER
+#if TINT_BUILD_SPV_READER
 #include "src/reader/spirv/parser.h"
-#endif
+#endif  // TINT_BUILD_SPV_READER
+
+#if TINT_BUILD_WGSL_READER
+#include "src/reader/wgsl/parser.h"
+#endif  // TINT_BUILD_WGSL_READER
+
+#if TINT_BUILD_SPV_WRITER
+#include "spirv-tools/libspirv.hpp"
+#include "src/writer/spirv/generator.h"
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+#include "src/writer/wgsl/generator.h"
+#endif  // TINT_BUILD_WGSL_WRITER
 
 namespace {
 
@@ -61,13 +70,22 @@
   --dump-ast                -- Dump the generated AST to stdout
   -h                        -- This help text)";
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
 Format parse_format(const std::string& fmt) {
+#pragma clang diagnostic pop
+
+#if TINT_BUILD_SPV_WRITER
   if (fmt == "spirv")
     return Format::kSpirv;
   if (fmt == "spvasm")
     return Format::kSpvAsm;
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
   if (fmt == "wgsl")
     return Format::kWgsl;
+#endif  // TINT_BUILD_WGSL_WRITER
 
   return Format::kNone;
 }
@@ -119,8 +137,12 @@
 /// writes error messages to the standard error stream and returns false.
 /// Assumes the size of a |T| object is divisible by its required alignment.
 /// @returns true if we successfully read the file.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-template"
 template <typename T>
 bool ReadFile(const std::string& input_file, std::vector<T>* buffer) {
+#pragma Clang pop
+
   if (!buffer) {
     std::cerr << "The buffer pointer was null" << std::endl;
     return false;
@@ -168,6 +190,7 @@
   return true;
 }
 
+#if TINT_BUILD_SPV_WRITER
 std::string Disassemble(const std::vector<uint32_t>& data) {
   std::string spv_errors;
   spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
@@ -204,6 +227,7 @@
                         SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
   return result;
 }
+#endif  // TINT_BUILD_SPV_WRITER
 
 }  // namespace
 
@@ -227,6 +251,7 @@
   }
 
   std::unique_ptr<tint::reader::Reader> reader;
+#if TINT_BUILD_WGSL_READER
   if (options.input_filename.size() > 5 &&
       options.input_filename.substr(options.input_filename.size() - 5) ==
           ".wgsl") {
@@ -237,7 +262,9 @@
     reader = std::make_unique<tint::reader::wgsl::Parser>(
         std::string(data.begin(), data.end()));
   }
-#if TINT_BUILD_SPV_PARSER
+#endif  // TINT_BUILD_WGSL_READER
+
+#if TINT_BUILD_SPV_READER
   if (options.input_filename.size() > 4 &&
       options.input_filename.substr(options.input_filename.size() - 4) ==
           ".spv") {
@@ -247,7 +274,8 @@
     }
     reader = std::make_unique<tint::reader::spirv::Parser>(data);
   }
-#endif
+#endif  // TINT_BUILD_SPV_READER
+
   if (!reader) {
     std::cerr << "Failed to create reader for input file: "
               << options.input_filename << std::endl;
@@ -279,12 +307,21 @@
   }
 
   std::unique_ptr<tint::writer::Writer> writer;
+
+#if TINT_BUILD_SPV_WRITER
   if (options.format == Format::kSpirv || options.format == Format::kSpvAsm) {
     writer =
         std::make_unique<tint::writer::spirv::Generator>(std::move(module));
-  } else if (options.format == Format::kWgsl) {
+  }
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+  if (options.format == Format::kWgsl) {
     writer = std::make_unique<tint::writer::wgsl::Generator>(std::move(module));
-  } else {
+  }
+#endif  // TINT_BUILD_WGSL_WRITER
+
+  if (!writer) {
     std::cerr << "Unknown output format specified" << std::endl;
     return 1;
   }
@@ -294,18 +331,25 @@
     return 1;
   }
 
+#if TINT_BUILD_SPV_WRITER
   if (options.format == Format::kSpvAsm) {
     auto w = static_cast<tint::writer::spirv::Generator*>(writer.get());
     auto str = Disassemble(w->result());
     // TODO(dsinclair): Write to file if output_file given
     std::cout << str << std::endl;
-  } else if (options.format == Format::kSpirv) {
+  }
+  if (options.format == Format::kSpirv) {
     // auto w = static_cast<tint::writer::spirv::Generator*>(writer.get());
     // TODO(dsincliair): Write to to file
-  } else if (options.format == Format::kWgsl) {
+  }
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+  if (options.format == Format::kWgsl) {
     auto w = static_cast<tint::writer::wgsl::Generator*>(writer.get());
     std::cout << w->result() << std::endl;
   }
+#endif  // TINT_BUILD_WGSL_WRITER
 
   return 0;
 }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6501ac1..0cc9488 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,6 +12,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+function(tint_spvtools_compile_options TARGET)
+  # We'll use the optimizer for its nice SPIR-V in-memory representation
+  target_link_libraries(${TARGET} SPIRV-Tools-opt SPIRV-Tools)
+
+  # We'll be cheating: using internal interfaces to the SPIRV-Tools
+  # optimizer.
+  target_include_directories(${TARGET} PRIVATE
+    ${spirv-tools_SOURCE_DIR}
+    ${spirv-tools_BINARY_DIR}
+  )
+
+  if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+    # The SPIRV-Tools code is conditioned against C++ and an older version of Clang.
+    # Suppress warnings triggered in our current compilation environment.
+    # TODO(dneto): Fix the issues upstream.
+    target_compile_options(${TARGET} PRIVATE
+      -Wno-newline-eof
+      -Wno-sign-conversion
+      -Wno-old-style-cast
+      -Wno-weak-vtables
+    )
+  endif()
+endfunction()
+
 set(TINT_LIB_SRCS
   ast/array_accessor_expression.cc
   ast/array_accessor_expression.h
@@ -159,14 +183,6 @@
   ast/variable_statement.h
   reader/reader.cc
   reader/reader.h
-  reader/wgsl/lexer.cc
-  reader/wgsl/lexer.h
-  reader/wgsl/parser.cc
-  reader/wgsl/parser.h
-  reader/wgsl/parser_impl.cc
-  reader/wgsl/parser_impl.h
-  reader/wgsl/token.cc
-  reader/wgsl/token.h
   source.h
   type_determiner.cc
   type_determiner.h
@@ -176,26 +192,11 @@
   validator.h
   validator_impl.cc
   validator_impl.h
-  # TODO(dsinclair): The writers should all be optional
-  writer/spirv/binary_writer.cc
-  writer/spirv/binary_writer.h
-  writer/spirv/builder.cc
-  writer/spirv/builder.h
-  writer/spirv/generator.cc
-  writer/spirv/generator.h
-  writer/spirv/instruction.cc
-  writer/spirv/instruction.h
-  writer/spirv/operand.cc
-  writer/spirv/operand.h
-  writer/wgsl/generator.cc
-  writer/wgsl/generator.h
-  writer/wgsl/generator_impl.cc
-  writer/wgsl/generator_impl.h
   writer/writer.cc
   writer/writer.h
 )
 
-if(TINT_BUILD_SPV_PARSER)
+if(${TINT_BUILD_SPV_READER})
   list(APPEND TINT_LIB_SRCS
     reader/spirv/fail_stream.h
     reader/spirv/parser.cc
@@ -205,6 +206,43 @@
   )
 endif()
 
+if(${TINT_BUILD_WGSL_READER})
+  list(APPEND TINT_LIB_SRCS
+    reader/wgsl/lexer.cc
+    reader/wgsl/lexer.h
+    reader/wgsl/parser.cc
+    reader/wgsl/parser.h
+    reader/wgsl/parser_impl.cc
+    reader/wgsl/parser_impl.h
+    reader/wgsl/token.cc
+    reader/wgsl/token.h
+  )
+endif()
+
+if(${TINT_BUILD_SPV_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/spirv/binary_writer.cc
+    writer/spirv/binary_writer.h
+    writer/spirv/builder.cc
+    writer/spirv/builder.h
+    writer/spirv/generator.cc
+    writer/spirv/generator.h
+    writer/spirv/instruction.cc
+    writer/spirv/instruction.h
+    writer/spirv/operand.cc
+    writer/spirv/operand.h
+  )
+endif()
+
+if(${TINT_BUILD_WGSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/wgsl/generator.cc
+    writer/wgsl/generator.h
+    writer/wgsl/generator_impl.cc
+    writer/wgsl/generator_impl.h
+  )
+endif()
+
 set(TINT_TEST_SRCS
   ast/array_accessor_expression_test.cc
   ast/as_expression_test.cc
@@ -259,111 +297,21 @@
   ast/unless_statement_test.cc
   ast/variable_statement_test.cc
   ast/variable_test.cc
-  reader/wgsl/lexer_test.cc
-  reader/wgsl/parser_test.cc
-  reader/wgsl/parser_impl_additive_expression_test.cc
-  reader/wgsl/parser_impl_and_expression_test.cc
-  reader/wgsl/parser_impl_argument_expression_list_test.cc
-  reader/wgsl/parser_impl_assignment_stmt_test.cc
-  reader/wgsl/parser_impl_body_stmt_test.cc
-  reader/wgsl/parser_impl_break_stmt_test.cc
-  reader/wgsl/parser_impl_builtin_decoration_test.cc
-  reader/wgsl/parser_impl_case_body_test.cc
-  reader/wgsl/parser_impl_const_expr_test.cc
-  reader/wgsl/parser_impl_const_literal_test.cc
-  reader/wgsl/parser_impl_continue_stmt_test.cc
-  reader/wgsl/parser_impl_continuing_stmt_test.cc
-  reader/wgsl/parser_impl_derivative_modifier_test.cc
-  reader/wgsl/parser_impl_else_stmt_test.cc
-  reader/wgsl/parser_impl_elseif_stmt_test.cc
-  reader/wgsl/parser_impl_entry_point_decl_test.cc
-  reader/wgsl/parser_impl_equality_expression_test.cc
-  reader/wgsl/parser_impl_exclusive_or_expression_test.cc
-  reader/wgsl/parser_impl_function_decl_test.cc
-  reader/wgsl/parser_impl_function_header_test.cc
-  reader/wgsl/parser_impl_function_type_decl_test.cc
-  reader/wgsl/parser_impl_global_constant_decl_test.cc
-  reader/wgsl/parser_impl_global_decl_test.cc
-  reader/wgsl/parser_impl_global_variable_decl_test.cc
-  reader/wgsl/parser_impl_if_stmt_test.cc
-  reader/wgsl/parser_impl_import_decl_test.cc
-  reader/wgsl/parser_impl_inclusive_or_expression_test.cc
-  reader/wgsl/parser_impl_logical_and_expression_test.cc
-  reader/wgsl/parser_impl_logical_or_expression_test.cc
-  reader/wgsl/parser_impl_loop_stmt_test.cc
-  reader/wgsl/parser_impl_multiplicative_expression_test.cc
-  reader/wgsl/parser_impl_param_list_test.cc
-  reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
-  reader/wgsl/parser_impl_pipeline_stage_test.cc
-  reader/wgsl/parser_impl_postfix_expression_test.cc
-  reader/wgsl/parser_impl_premerge_stmt_test.cc
-  reader/wgsl/parser_impl_primary_expression_test.cc
-  reader/wgsl/parser_impl_regardless_stmt_test.cc
-  reader/wgsl/parser_impl_relational_expression_test.cc
-  reader/wgsl/parser_impl_shift_expression_test.cc
-  reader/wgsl/parser_impl_statement_test.cc
-  reader/wgsl/parser_impl_statements_test.cc
-  reader/wgsl/parser_impl_storage_class_test.cc
-  reader/wgsl/parser_impl_struct_body_decl_test.cc
-  reader/wgsl/parser_impl_struct_decl_test.cc
-  reader/wgsl/parser_impl_struct_decoration_decl_test.cc
-  reader/wgsl/parser_impl_struct_decoration_test.cc
-  reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc
-  reader/wgsl/parser_impl_struct_member_decoration_test.cc
-  reader/wgsl/parser_impl_struct_member_test.cc
-  reader/wgsl/parser_impl_switch_body_test.cc
-  reader/wgsl/parser_impl_switch_stmt_test.cc
-  reader/wgsl/parser_impl_test.cc
-  reader/wgsl/parser_impl_type_alias_test.cc
-  reader/wgsl/parser_impl_type_decl_test.cc
-  reader/wgsl/parser_impl_unary_expression_test.cc
-  reader/wgsl/parser_impl_unless_stmt_test.cc
-  reader/wgsl/parser_impl_variable_decl_test.cc
-  reader/wgsl/parser_impl_variable_decoration_list_test.cc
-  reader/wgsl/parser_impl_variable_decoration_test.cc
-  reader/wgsl/parser_impl_variable_ident_decl_test.cc
-  reader/wgsl/parser_impl_variable_stmt_test.cc
-  reader/wgsl/parser_impl_variable_storage_decoration_test.cc
-  reader/wgsl/token_test.cc
   type_manager_test.cc
   validator_impl_import_test.cc
-  writer/spirv/binary_writer_test.cc
-  writer/spirv/builder_test.cc
-  writer/spirv/instruction_test.cc
-  writer/spirv/operand_test.cc
-  writer/wgsl/generator_impl_test.cc
 )
 
-function(tint_spvtools_compile_options TARGET)
-  # We'll use the optimizer for its nice SPIR-V in-memory representation
-  target_link_libraries(libtint SPIRV-Tools-opt SPIRV-Tools)
-  # We'll be cheating: using internal interfaces to the SPIRV-Tools
-  # optimizer.
-  target_include_directories(libtint PRIVATE ${spirv-tools_SOURCE_DIR} ${spirv-tools_BINARY_DIR})
-  if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
-    # The SPIRV-Tools code is conditioned against C++ and an older version of Clang.
-    # Suppress warnings triggered in our current compilation environment.
-    # TODO(dneto): Fix the issues upstream.
-    target_compile_options(${TARGET} PRIVATE
-      -Wno-newline-eof
-      -Wno-sign-conversion
-      -Wno-old-style-cast
-      -Wno-weak-vtables
-    )
-  endif()
-endfunction()
-
-
 ## Tint library
 add_library(libtint ${TINT_LIB_SRCS})
 tint_default_compile_options(libtint)
 set_target_properties(libtint PROPERTIES OUTPUT_NAME "tint")
-if(${TINT_BUILD_SPV_PARSER})
+
+if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
   tint_spvtools_compile_options(libtint)
 endif()
 
-if(${TINT_BUILD_SPV_PARSER})
-  list (APPEND TINT_TEST_SRCS
+if(${TINT_BUILD_SPV_READER})
+  list(APPEND TINT_TEST_SRCS
     reader/spirv/fail_stream_test.cc
     reader/spirv/parser_impl_import_test.cc
     reader/spirv/parser_impl_test.cc
@@ -373,11 +321,94 @@
   )
 endif()
 
-add_executable(tint_unittests ${TINT_TEST_SRCS})
-if (${TINT_BUILD_SPV_PARSER})
-  tint_spvtools_compile_options(tint_unittests)
+if(${TINT_BUILD_WGSL_READER})
+  list(APPEND TINT_TEST_SRCS
+    reader/wgsl/lexer_test.cc
+    reader/wgsl/parser_test.cc
+    reader/wgsl/parser_impl_additive_expression_test.cc
+    reader/wgsl/parser_impl_and_expression_test.cc
+    reader/wgsl/parser_impl_argument_expression_list_test.cc
+    reader/wgsl/parser_impl_assignment_stmt_test.cc
+    reader/wgsl/parser_impl_body_stmt_test.cc
+    reader/wgsl/parser_impl_break_stmt_test.cc
+    reader/wgsl/parser_impl_builtin_decoration_test.cc
+    reader/wgsl/parser_impl_case_body_test.cc
+    reader/wgsl/parser_impl_const_expr_test.cc
+    reader/wgsl/parser_impl_const_literal_test.cc
+    reader/wgsl/parser_impl_continue_stmt_test.cc
+    reader/wgsl/parser_impl_continuing_stmt_test.cc
+    reader/wgsl/parser_impl_derivative_modifier_test.cc
+    reader/wgsl/parser_impl_else_stmt_test.cc
+    reader/wgsl/parser_impl_elseif_stmt_test.cc
+    reader/wgsl/parser_impl_entry_point_decl_test.cc
+    reader/wgsl/parser_impl_equality_expression_test.cc
+    reader/wgsl/parser_impl_exclusive_or_expression_test.cc
+    reader/wgsl/parser_impl_function_decl_test.cc
+    reader/wgsl/parser_impl_function_header_test.cc
+    reader/wgsl/parser_impl_function_type_decl_test.cc
+    reader/wgsl/parser_impl_global_constant_decl_test.cc
+    reader/wgsl/parser_impl_global_decl_test.cc
+    reader/wgsl/parser_impl_global_variable_decl_test.cc
+    reader/wgsl/parser_impl_if_stmt_test.cc
+    reader/wgsl/parser_impl_import_decl_test.cc
+    reader/wgsl/parser_impl_inclusive_or_expression_test.cc
+    reader/wgsl/parser_impl_logical_and_expression_test.cc
+    reader/wgsl/parser_impl_logical_or_expression_test.cc
+    reader/wgsl/parser_impl_loop_stmt_test.cc
+    reader/wgsl/parser_impl_multiplicative_expression_test.cc
+    reader/wgsl/parser_impl_param_list_test.cc
+    reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+    reader/wgsl/parser_impl_pipeline_stage_test.cc
+    reader/wgsl/parser_impl_postfix_expression_test.cc
+    reader/wgsl/parser_impl_premerge_stmt_test.cc
+    reader/wgsl/parser_impl_primary_expression_test.cc
+    reader/wgsl/parser_impl_regardless_stmt_test.cc
+    reader/wgsl/parser_impl_relational_expression_test.cc
+    reader/wgsl/parser_impl_shift_expression_test.cc
+    reader/wgsl/parser_impl_statement_test.cc
+    reader/wgsl/parser_impl_statements_test.cc
+    reader/wgsl/parser_impl_storage_class_test.cc
+    reader/wgsl/parser_impl_struct_body_decl_test.cc
+    reader/wgsl/parser_impl_struct_decl_test.cc
+    reader/wgsl/parser_impl_struct_decoration_decl_test.cc
+    reader/wgsl/parser_impl_struct_decoration_test.cc
+    reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc
+    reader/wgsl/parser_impl_struct_member_decoration_test.cc
+    reader/wgsl/parser_impl_struct_member_test.cc
+    reader/wgsl/parser_impl_switch_body_test.cc
+    reader/wgsl/parser_impl_switch_stmt_test.cc
+    reader/wgsl/parser_impl_test.cc
+    reader/wgsl/parser_impl_type_alias_test.cc
+    reader/wgsl/parser_impl_type_decl_test.cc
+    reader/wgsl/parser_impl_unary_expression_test.cc
+    reader/wgsl/parser_impl_unless_stmt_test.cc
+    reader/wgsl/parser_impl_variable_decl_test.cc
+    reader/wgsl/parser_impl_variable_decoration_list_test.cc
+    reader/wgsl/parser_impl_variable_decoration_test.cc
+    reader/wgsl/parser_impl_variable_ident_decl_test.cc
+    reader/wgsl/parser_impl_variable_stmt_test.cc
+    reader/wgsl/parser_impl_variable_storage_decoration_test.cc
+    reader/wgsl/token_test.cc
+  )
 endif()
-if (NOT MSVC)
+
+if(${TINT_BUILD_SPV_WRITER})
+  list(APPEND TINT_TEST_SRCS
+    writer/spirv/binary_writer_test.cc
+    writer/spirv/builder_test.cc
+    writer/spirv/instruction_test.cc
+    writer/spirv/operand_test.cc
+  )
+endif()
+
+if(${TINT_BUILD_WGSL_WRITER})
+  list(APPEND TINT_TEST_SRCS
+    writer/wgsl/generator_impl_test.cc
+  )
+endif()
+add_executable(tint_unittests ${TINT_TEST_SRCS})
+
+if(NOT MSVC)
   target_compile_options(tint_unittests PRIVATE
     -Wno-global-constructors
     -Wno-weak-vtables
@@ -387,11 +418,11 @@
 ## Test executable
 target_include_directories(
     tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
-if(${TINT_BUILD_SPV_PARSER})
-  target_include_directories(tint_unittests
-	  PRIVATE ${spirv-tools_SOURCE_DIR} ${spirv-tools_BINARY_DIR})
-endif()
 target_link_libraries(tint_unittests libtint gmock_main)
 tint_default_compile_options(tint_unittests)
 
+if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+  tint_spvtools_compile_options(tint_unittests)
+endif()
+
 add_test(NAME tint_unittests COMMAND tint_unittests)
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 2c9ae57..229e8ee 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/reader/spv/parser_impl.h"
+#include "src/reader/spirv/parser_impl.h"
 
 #include <cstring>
 
diff --git a/src/validator_impl_import_test.cc b/src/validator_impl_import_test.cc
index e396ea2..2a9d1cc 100644
--- a/src/validator_impl_import_test.cc
+++ b/src/validator_impl_import_test.cc
@@ -15,42 +15,40 @@
 #include <iostream>
 
 #include "gtest/gtest.h"
-#include "src/reader/wgsl/parser.h"
+#include "src/ast/import.h"
+#include "src/ast/module.h"
 #include "src/validator_impl.h"
 
 namespace tint {
 namespace {
 
-ast::Module build_module(std::string data) {
-  auto reader = std::make_unique<tint::reader::wgsl::Parser>(
-      std::string(data.begin(), data.end()));
-  EXPECT_TRUE(reader->Parse()) << reader->error();
-  return reader->module();
-}
-
 using ValidatorImplTest = testing::Test;
 
 TEST_F(ValidatorImplTest, Import) {
-  std::string input = "import \"GLSL.std.450\" as glsl;";
-  auto module = build_module(input);
+  ast::Module m;
+  m.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "glsl"));
+
   tint::ValidatorImpl v;
-  EXPECT_TRUE(v.CheckImports(module));
+  EXPECT_TRUE(v.CheckImports(m));
 }
 
 TEST_F(ValidatorImplTest, Import_Fail_NotGLSL) {
-  std::string input = "import \"not.GLSL\" as glsl;";
-  auto module = build_module(input);
+  ast::Module m;
+  m.AddImport(std::make_unique<ast::Import>(Source{1, 1}, "not.GLSL", "glsl"));
+
   tint::ValidatorImpl v;
-  EXPECT_FALSE(v.CheckImports(module));
+  EXPECT_FALSE(v.CheckImports(m));
   ASSERT_TRUE(v.has_error());
   EXPECT_EQ(v.error(), "1:1: v-0001: unknown import: not.GLSL");
 }
 
 TEST_F(ValidatorImplTest, Import_Fail_Typo) {
-  std::string input = "import \"GLSL.std.4501\" as glsl;";
-  auto module = build_module(input);
+  ast::Module m;
+  m.AddImport(
+      std::make_unique<ast::Import>(Source{1, 1}, "GLSL.std.4501", "glsl"));
+
   tint::ValidatorImpl v;
-  EXPECT_FALSE(v.CheckImports(module));
+  EXPECT_FALSE(v.CheckImports(m));
   ASSERT_TRUE(v.has_error());
   EXPECT_EQ(v.error(), "1:1: v-0001: unknown import: GLSL.std.4501");
 }
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 48e646c..5928c2d 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -14,8 +14,8 @@
 
 add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/googletest EXCLUDE_FROM_ALL)
 
-# SPIRV-Tools is only linked into tint if we're using the SPIR-V parser. We
-# always build it regardless so we can use the validator for testing purposes.
-set(SPIRV-Headers_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers CACHE STRING "")
-set(SPIRV_SKIP_TESTS ON CACHE BOOL ON)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools)
+if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+  set(SPIRV-Headers_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers CACHE STRING "")
+  set(SPIRV_SKIP_TESTS ON CACHE BOOL ON)
+  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools)
+endif()