main: Replace --dawn-validation with --validate
Performs output validation with spirv-val for SPIR-V (as before), HLSL
validation with DXC, and MSL validation with the Metal Shader Compiler.
Disable HLSL tests that fail to validate
Bug: tint:812
Change-Id: If78c351b4e23c7fb50d333eacf9ee7cc81d18564
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51280
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index 6ed2d45..6253416 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -19,6 +19,7 @@
sources = [ "main.cc" ]
deps = [
"${tint_root_dir}/src:libtint",
+ "${tint_root_dir}/src:tint_val",
"${tint_spirv_tools_dir}/:spvtools",
"${tint_spirv_tools_dir}/:spvtools_opt",
"${tint_spirv_tools_dir}/:spvtools_val",
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt
index b144b43..b500040 100644
--- a/samples/CMakeLists.txt
+++ b/samples/CMakeLists.txt
@@ -19,7 +19,7 @@
## Tint executable
add_executable(tint ${TINT_SRCS})
tint_default_compile_options(tint)
-target_link_libraries(tint libtint)
+target_link_libraries(tint libtint tint_val)
if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
target_link_libraries(tint SPIRV-Tools)
diff --git a/samples/main.cc b/samples/main.cc
index 7bd5bac..5d65afb 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -24,6 +24,8 @@
#include "spirv-tools/libspirv.hpp"
#endif // TINT_BUILD_SPV_READER
+#include "src/utils/io/command.h"
+#include "src/val/val.h"
#include "tint/tint.h"
namespace {
@@ -52,7 +54,7 @@
bool parse_only = false;
bool dump_ast = false;
- bool dawn_validation = false;
+ bool validate = false;
bool demangle = false;
bool dump_inspector_bindings = false;
@@ -62,6 +64,9 @@
std::string ep_name;
std::vector<std::string> transforms;
+
+ std::string dxc_path;
+ std::string xcrun_path;
};
const char kUsage[] = R"(Usage: tint [options] <input-file>
@@ -86,12 +91,16 @@
renamer
--parse-only -- Stop after parsing the input
--dump-ast -- Dump the generated AST to stdout
- --dawn-validation -- SPIRV outputs are validated with the same flags
- as Dawn does. Has no effect on non-SPIRV outputs.
--demangle -- Preserve original source names. Demangle them.
Affects AST dumping, and text-based output languages.
--dump-inspector-bindings -- Dump reflection data about bindins to stdout.
- -h -- This help text)";
+ -h -- This help text
+ --validate -- Validates generated SPIR-V with spirv-val.
+ Has no effect on non-SPIRV outputs.
+ --dxc -- Path to DXC executable, used to validate HLSL output.
+ When specified, automatically enables --validate
+ --xcrun -- Path to xcrun executable, used to validate MSL output.
+ When specified, automatically enables --validate)";
#ifdef _MSC_VER
#pragma warning(disable : 4068; suppress : 4100)
@@ -389,12 +398,28 @@
opts->parse_only = true;
} else if (arg == "--dump-ast") {
opts->dump_ast = true;
- } else if (arg == "--dawn-validation") {
- opts->dawn_validation = true;
} else if (arg == "--demangle") {
opts->demangle = true;
} else if (arg == "--dump-inspector-bindings") {
opts->dump_inspector_bindings = true;
+ } else if (arg == "--validate") {
+ opts->validate = true;
+ } else if (arg == "--dxc") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for " << arg << std::endl;
+ return false;
+ }
+ opts->dxc_path = args[i];
+ opts->validate = true;
+ } else if (arg == "--xcrun") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for " << arg << std::endl;
+ return false;
+ }
+ opts->xcrun_path = args[i];
+ opts->validate = true;
} else if (!arg.empty()) {
if (arg[0] == '-') {
std::cerr << "Unrecognized option: " << arg << std::endl;
@@ -772,33 +797,47 @@
std::unique_ptr<tint::writer::Writer> writer;
+ switch (options.format) {
+ case Format::kSpirv:
+ case Format::kSpvAsm:
#if TINT_BUILD_SPV_WRITER
- if (options.format == Format::kSpirv || options.format == Format::kSpvAsm) {
- writer = std::make_unique<tint::writer::spirv::Generator>(program.get());
- }
+ writer = std::make_unique<tint::writer::spirv::Generator>(program.get());
+ break;
+#else
+ std::cerr << "SPIR-V writer not enabled in tint build" << std::endl;
+ return 1;
#endif // TINT_BUILD_SPV_WRITER
+ case Format::kWgsl:
#if TINT_BUILD_WGSL_WRITER
- if (options.format == Format::kWgsl) {
- writer = std::make_unique<tint::writer::wgsl::Generator>(program.get());
- }
+ writer = std::make_unique<tint::writer::wgsl::Generator>(program.get());
+ break;
+#else
+ std::cerr << "WGSL writer not enabled in tint build" << std::endl;
+ return 1;
#endif // TINT_BUILD_WGSL_WRITER
+ case Format::kMsl:
#if TINT_BUILD_MSL_WRITER
- if (options.format == Format::kMsl) {
- writer = std::make_unique<tint::writer::msl::Generator>(program.get());
- }
+ writer = std::make_unique<tint::writer::msl::Generator>(program.get());
+ break;
+#else
+ std::cerr << "MSL writer not enabled in tint build" << std::endl;
+ return 1;
#endif // TINT_BUILD_MSL_WRITER
+ case Format::kHlsl:
#if TINT_BUILD_HLSL_WRITER
- if (options.format == Format::kHlsl) {
- writer = std::make_unique<tint::writer::hlsl::Generator>(program.get());
- }
+ writer = std::make_unique<tint::writer::hlsl::Generator>(program.get());
+ break;
+#else
+ std::cerr << "HLSL writer not enabled in tint build" << std::endl;
+ return 1;
#endif // TINT_BUILD_HLSL_WRITER
- if (!writer) {
- std::cerr << "Unknown output format specified" << std::endl;
- return 1;
+ default:
+ std::cerr << "Unknown output format specified" << std::endl;
+ return 1;
}
if (!writer->Generate()) {
@@ -807,27 +846,82 @@
return 1;
}
-#if TINT_BUILD_SPV_WRITER
- bool dawn_validation_failed = false;
- std::ostringstream stream;
+ bool validation_failed = false;
+ std::ostringstream validation_msgs;
- if (options.dawn_validation &&
- (options.format == Format::kSpvAsm || options.format == Format::kSpirv)) {
- // Use Vulkan 1.1, since this is what Tint, internally, uses.
- spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
- tools.SetMessageConsumer([&stream](spv_message_level_t, const char*,
- const spv_position_t& pos,
- const char* msg) {
- stream << (pos.line + 1) << ":" << (pos.column + 1) << ": " << msg
- << std::endl;
- });
- auto* w = static_cast<tint::writer::spirv::Generator*>(writer.get());
- if (!tools.Validate(w->result().data(), w->result().size(),
- spvtools::ValidatorOptions())) {
- dawn_validation_failed = true;
+ if (options.validate) {
+ switch (options.format) {
+#if TINT_BUILD_SPV_WRITER
+ case Format::kSpirv:
+ case Format::kSpvAsm: {
+ // Use Vulkan 1.1, since this is what Tint, internally, uses.
+ spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
+ tools.SetMessageConsumer(
+ [&validation_msgs](spv_message_level_t, const char*,
+ const spv_position_t& pos, const char* msg) {
+ validation_msgs << (pos.line + 1) << ":" << (pos.column + 1)
+ << ": " << msg << std::endl;
+ });
+ auto* w = static_cast<tint::writer::spirv::Generator*>(writer.get());
+ if (!tools.Validate(w->result().data(), w->result().size(),
+ spvtools::ValidatorOptions())) {
+ validation_failed = true;
+ }
+ break;
+ }
+#endif
+#if TINT_BUILD_HLSL_WRITER
+ case Format::kHlsl: {
+ auto dxc = tint::utils::Command::LookPath(
+ options.dxc_path.empty() ? "dxc" : options.dxc_path);
+ if (dxc.Found()) {
+ auto* w = static_cast<tint::writer::Text*>(writer.get());
+ auto hlsl = w->result();
+ auto res = tint::val::Hlsl(dxc.Path(), hlsl, program.get());
+ if (res.failed) {
+ validation_failed = true;
+ validation_msgs << res.source << std::endl;
+ validation_msgs << res.output;
+ }
+ } else {
+ validation_failed = true;
+ validation_msgs << "DXC executable not found. Cannot validate";
+ }
+ break;
+ }
+#endif // TINT_BUILD_HLSL_WRITER
+#if TINT_BUILD_MSL_WRITER
+ case Format::kMsl: {
+#ifdef _WIN32
+ const char* default_xcrun_exe = "metal.exe";
+#else
+ const char* default_xcrun_exe = "xcrun";
+#endif
+ auto xcrun = tint::utils::Command::LookPath(options.xcrun_path.empty()
+ ? default_xcrun_exe
+ : options.xcrun_path);
+ if (xcrun.Found()) {
+ auto* w = static_cast<tint::writer::Text*>(writer.get());
+ auto msl = w->result();
+ auto res = tint::val::Msl(xcrun.Path(), msl);
+ if (res.failed) {
+ validation_failed = true;
+ validation_msgs << res.source << std::endl;
+ validation_msgs << res.output;
+ }
+ } else {
+ validation_failed = true;
+ validation_msgs << "xcrun executable not found. Cannot validate";
+ }
+ break;
+ }
+#endif // TINT_BUILD_MSL_WRITER
+ default:
+ break;
}
}
+#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());
@@ -841,12 +935,13 @@
return 1;
}
}
- if (dawn_validation_failed) {
+#endif // TINT_BUILD_SPV_WRITER
+
+ if (validation_failed) {
std::cerr << std::endl << std::endl << "Validation Failure:" << std::endl;
- std::cerr << stream.str();
+ std::cerr << validation_msgs.str();
return 1;
}
-#endif // TINT_BUILD_SPV_WRITER
if (options.format != Format::kSpvAsm && options.format != Format::kSpirv) {
auto* w = static_cast<tint::writer::Text*>(writer.get());
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 04c5cd7..6c91770 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -209,6 +209,43 @@
}
###############################################################################
+# Helper library for IO operations
+# Only to be used by tests and sample executable
+###############################################################################
+source_set("tint_utils_io") {
+ sources = [
+ "utils/io/command.h",
+ "utils/io/tmpfile.h",
+ ]
+
+ if (is_linux || is_mac) {
+ sources += [ "utils/io/command_posix.cc" ]
+ sources += [ "utils/io/tmpfile_posix.cc" ]
+ } else if (is_win) {
+ sources += [ "utils/io/command_windows.cc" ]
+ sources += [ "utils/io/tmpfile_windows.cc" ]
+ } else {
+ sources += [ "utils/io/command_other.cc" ]
+ sources += [ "utils/io/tmpfile_other.cc" ]
+ }
+
+ public_deps = [ ":libtint_core_all_src" ]
+}
+
+###############################################################################
+# Helper library for validating generated shaders
+# As this depends on tint_utils_io, this is only to be used by tests and sample
+# executable
+###############################################################################
+source_set("tint_val") {
+ sources = [
+ "val/val.cc",
+ "val/val.h",
+ ]
+ public_deps = [ ":tint_utils_io" ]
+}
+
+###############################################################################
# Library - Tint core and optional modules of libtint
###############################################################################
# libtint source sets are divided into a non-optional core in :libtint_core_src
@@ -464,9 +501,9 @@
"sem/i32_type.cc",
"sem/i32_type.h",
"sem/info.h",
+ "sem/intrinsic.h",
"sem/intrinsic_type.cc",
"sem/intrinsic_type.h",
- "sem/intrinsic.h",
"sem/matrix_type.cc",
"sem/matrix_type.h",
"sem/multisampled_texture_type.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a040999..9066b18 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -449,6 +449,23 @@
)
endif()
+## Tint IO utilities. Used by tint_val.
+add_library(tint_utils_io
+ utils/io/command_${TINT_OS_CC_SUFFIX}.cc
+ utils/io/command.h
+ utils/io/tmpfile_${TINT_OS_CC_SUFFIX}.cc
+ utils/io/tmpfile.h
+)
+tint_default_compile_options(tint_utils_io)
+
+## Tint validation utilities. Used by tests and the tint executable.
+add_library(tint_val
+ val/val.cc
+ val/val.h
+)
+tint_default_compile_options(tint_val)
+target_link_libraries(tint_val tint_utils_io)
+
## Tint library
add_library(libtint ${TINT_LIB_SRCS})
tint_default_compile_options(libtint)
@@ -603,16 +620,12 @@
sem/type_manager_test.cc
sem/u32_type_test.cc
sem/vector_type_test.cc
- utils/command_${TINT_OS_CC_SUFFIX}.cc
- utils/command_test.cc
- utils/command.h
utils/get_or_create_test.cc
utils/hash_test.cc
+ utils/io/command_test.cc
+ utils/io/tmpfile_test.cc
utils/math_test.cc
utils/scoped_assignment_test.cc
- utils/tmpfile_${TINT_OS_CC_SUFFIX}.cc
- utils/tmpfile_test.cc
- utils/tmpfile.h
utils/unique_vector_test.cc
writer/append_vector_test.cc
writer/float_to_string_test.cc
@@ -913,7 +926,7 @@
## Test executable
target_include_directories(
tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
- target_link_libraries(tint_unittests libtint gmock)
+ target_link_libraries(tint_unittests libtint gmock tint_val)
tint_default_compile_options(tint_unittests)
if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
diff --git a/src/test_main.cc b/src/test_main.cc
index d4425c7..5cff409 100644
--- a/src/test_main.cc
+++ b/src/test_main.cc
@@ -14,7 +14,7 @@
#include "gmock/gmock.h"
#include "src/reader/spirv/parser_impl_test_helper.h"
-#include "src/utils/command.h"
+#include "src/utils/io/command.h"
#include "src/writer/hlsl/test_helper.h"
#include "src/writer/msl/test_helper.h"
diff --git a/src/utils/command.h b/src/utils/io/command.h
similarity index 96%
rename from src/utils/command.h
rename to src/utils/io/command.h
index 213c7dc..acedc21 100644
--- a/src/utils/command.h
+++ b/src/utils/io/command.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SRC_UTILS_COMMAND_H_
-#define SRC_UTILS_COMMAND_H_
+#ifndef SRC_UTILS_IO_COMMAND_H_
+#define SRC_UTILS_IO_COMMAND_H_
#include <string>
#include <utility>
@@ -81,4 +81,4 @@
} // namespace utils
} // namespace tint
-#endif // SRC_UTILS_COMMAND_H_
+#endif // SRC_UTILS_IO_COMMAND_H_
diff --git a/src/utils/command_other.cc b/src/utils/io/command_other.cc
similarity index 96%
rename from src/utils/command_other.cc
rename to src/utils/io/command_other.cc
index 2f246a2..90d3414 100644
--- a/src/utils/command_other.cc
+++ b/src/utils/io/command_other.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/command.h"
+#include "src/utils/io/command.h"
namespace tint {
namespace utils {
diff --git a/src/utils/command_posix.cc b/src/utils/io/command_posix.cc
similarity index 99%
rename from src/utils/command_posix.cc
rename to src/utils/io/command_posix.cc
index 6ec2a98..b000ecb 100644
--- a/src/utils/command_posix.cc
+++ b/src/utils/io/command_posix.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/command.h"
+#include "src/utils/io/command.h"
#include <sys/poll.h>
#include <sys/stat.h>
diff --git a/src/utils/command_test.cc b/src/utils/io/command_test.cc
similarity index 98%
rename from src/utils/command_test.cc
rename to src/utils/io/command_test.cc
index c0da897..8127dc2 100644
--- a/src/utils/command_test.cc
+++ b/src/utils/io/command_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/command.h"
+#include "src/utils/io/command.h"
#include "gtest/gtest.h"
diff --git a/src/utils/command_windows.cc b/src/utils/io/command_windows.cc
similarity index 99%
rename from src/utils/command_windows.cc
rename to src/utils/io/command_windows.cc
index 65e3b38..36ae01a 100644
--- a/src/utils/command_windows.cc
+++ b/src/utils/io/command_windows.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/command.h"
+#include "src/utils/io/command.h"
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
diff --git a/src/utils/tmpfile.h b/src/utils/io/tmpfile.h
similarity index 95%
rename from src/utils/tmpfile.h
rename to src/utils/io/tmpfile.h
index b24fa51..3ff3fb1 100644
--- a/src/utils/tmpfile.h
+++ b/src/utils/io/tmpfile.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SRC_UTILS_TMPFILE_H_
-#define SRC_UTILS_TMPFILE_H_
+#ifndef SRC_UTILS_IO_TMPFILE_H_
+#define SRC_UTILS_IO_TMPFILE_H_
#include <sstream>
#include <string>
@@ -73,4 +73,4 @@
} // namespace utils
} // namespace tint
-#endif // SRC_UTILS_TMPFILE_H_
+#endif // SRC_UTILS_IO_TMPFILE_H_
diff --git a/src/utils/tmpfile_other.cc b/src/utils/io/tmpfile_other.cc
similarity index 95%
rename from src/utils/tmpfile_other.cc
rename to src/utils/io/tmpfile_other.cc
index 93325f5..dd3080f 100644
--- a/src/utils/tmpfile_other.cc
+++ b/src/utils/io/tmpfile_other.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/tmpfile.h"
+#include "src/utils/io/tmpfile.h"
namespace tint {
namespace utils {
diff --git a/src/utils/tmpfile_posix.cc b/src/utils/io/tmpfile_posix.cc
similarity index 97%
rename from src/utils/tmpfile_posix.cc
rename to src/utils/io/tmpfile_posix.cc
index fac1847..9d66331 100644
--- a/src/utils/tmpfile_posix.cc
+++ b/src/utils/io/tmpfile_posix.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/tmpfile.h"
+#include "src/utils/io/tmpfile.h"
#include <unistd.h>
#include <limits>
diff --git a/src/utils/tmpfile_test.cc b/src/utils/io/tmpfile_test.cc
similarity index 98%
rename from src/utils/tmpfile_test.cc
rename to src/utils/io/tmpfile_test.cc
index 5416da5..b8c04f2 100644
--- a/src/utils/tmpfile_test.cc
+++ b/src/utils/io/tmpfile_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/tmpfile.h"
+#include "src/utils/io/tmpfile.h"
#include <fstream>
diff --git a/src/utils/tmpfile_windows.cc b/src/utils/io/tmpfile_windows.cc
similarity index 97%
rename from src/utils/tmpfile_windows.cc
rename to src/utils/io/tmpfile_windows.cc
index 764f2ad..93634a8 100644
--- a/src/utils/tmpfile_windows.cc
+++ b/src/utils/io/tmpfile_windows.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/tmpfile.h"
+#include "src/utils/io/tmpfile.h"
#include <stdio.h>
#include <cstdio>
diff --git a/src/val/val.cc b/src/val/val.cc
new file mode 100644
index 0000000..f755e15
--- /dev/null
+++ b/src/val/val.cc
@@ -0,0 +1,133 @@
+// Copyright 2021 The Tint 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 "src/val/val.h"
+
+#include "src/ast/module.h"
+#include "src/program.h"
+#include "src/utils/io/command.h"
+#include "src/utils/io/tmpfile.h"
+
+namespace tint {
+namespace val {
+
+Result Hlsl(const std::string& dxc_path,
+ const std::string& source,
+ Program* program) {
+ Result result;
+
+ auto dxc = utils::Command(dxc_path);
+ if (!dxc.Found()) {
+ result.output = "DXC not found at '" + std::string(dxc_path) + "'";
+ result.failed = true;
+ return result;
+ }
+
+ result.source = source;
+
+ utils::TmpFile file;
+ file << source;
+
+ bool found_an_entrypoint = false;
+ for (auto* func : program->AST().Functions()) {
+ if (func->IsEntryPoint()) {
+ found_an_entrypoint = true;
+
+ const char* profile = "";
+
+ switch (func->pipeline_stage()) {
+ case ast::PipelineStage::kNone:
+ result.output = "Invalid PipelineStage";
+ result.failed = true;
+ return result;
+ case ast::PipelineStage::kVertex:
+ profile = "-T vs_6_0";
+ break;
+ case ast::PipelineStage::kFragment:
+ profile = "-T ps_6_0";
+ break;
+ case ast::PipelineStage::kCompute:
+ profile = "-T cs_6_0";
+ break;
+ }
+
+ auto name = program->Symbols().NameFor(func->symbol());
+ auto res = dxc(profile, "-E " + name, file.Path());
+ if (!res.out.empty()) {
+ if (!result.output.empty()) {
+ result.output += "\n";
+ }
+ result.output += res.out;
+ }
+ if (!res.err.empty()) {
+ if (!result.output.empty()) {
+ result.output += "\n";
+ }
+ result.output += res.err;
+ }
+ result.failed = (res.error_code != 0);
+ }
+ }
+
+ if (!found_an_entrypoint) {
+ result.output = "No entrypoint found";
+ result.failed = true;
+ return result;
+ }
+
+ return result;
+}
+
+Result Msl(const std::string& xcrun_path, const std::string& source) {
+ Result result;
+
+ auto xcrun = utils::Command(xcrun_path);
+ if (!xcrun.Found()) {
+ result.output = "xcrun not found at '" + std::string(xcrun_path) + "'";
+ result.failed = true;
+ return result;
+ }
+
+ result.source = source;
+
+ utils::TmpFile file(".metal");
+ file << result.source;
+
+#ifdef _WIN32
+ // On Windows, we should actually be running metal.exe from the Metal
+ // Developer Tools for Windows
+ auto res = xcrun("-x", "metal", "-c", "-o", "NUL", file.Path());
+#else
+ auto res =
+ xcrun("-sdk", "macosx", "metal", "-o", "/dev/null", "-c", file.Path());
+#endif
+ if (!res.out.empty()) {
+ if (!result.output.empty()) {
+ result.output += "\n";
+ }
+ result.output += res.out;
+ }
+ if (!res.err.empty()) {
+ if (!result.output.empty()) {
+ result.output += "\n";
+ }
+ result.output += res.err;
+ }
+ result.failed = (res.error_code != 0);
+
+ return result;
+}
+
+} // namespace val
+} // namespace tint
diff --git a/src/val/val.h b/src/val/val.h
new file mode 100644
index 0000000..489dcdc
--- /dev/null
+++ b/src/val/val.h
@@ -0,0 +1,58 @@
+// Copyright 2021 The Tint 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.
+
+#ifndef SRC_VAL_VAL_H_
+#define SRC_VAL_VAL_H_
+
+#include <string>
+
+// Forward declarations
+namespace tint {
+class Program;
+} // namespace tint
+
+namespace tint {
+namespace val {
+
+/// The return structure of Validate()
+struct Result {
+ /// True if validation passed
+ bool failed = false;
+ /// Output of DXC.
+ std::string output;
+ /// The generated source that was compiled
+ std::string source;
+};
+
+/// Hlsl attempts to compile the shader with DXC, verifying that the shader
+/// compiles successfully.
+/// @param dxc_path path to DXC
+/// @param source the generated HLSL source
+/// @param program the HLSL program
+/// @return the result of the compile
+Result Hlsl(const std::string& dxc_path,
+ const std::string& source,
+ Program* program);
+
+/// Msl attempts to compile the shader with the Metal Shader Compiler,
+/// verifying that the shader compiles successfully.
+/// @param xcrun_path path to xcrun
+/// @param source the generated MSL source
+/// @return the result of the compile
+Result Msl(const std::string& xcrun_path, const std::string& source);
+
+} // namespace val
+} // namespace tint
+
+#endif // SRC_VAL_VAL_H_
diff --git a/src/writer/hlsl/test_helper.cc b/src/writer/hlsl/test_helper.cc
index b600690..acadabd 100644
--- a/src/writer/hlsl/test_helper.cc
+++ b/src/writer/hlsl/test_helper.cc
@@ -14,9 +14,6 @@
#include "src/writer/hlsl/test_helper.h"
-#include "src/utils/command.h"
-#include "src/utils/tmpfile.h"
-
namespace tint {
namespace writer {
namespace hlsl {
@@ -31,81 +28,16 @@
dxc_path = dxc;
}
-CompileResult Compile(Program* program, GeneratorImpl* generator) {
- CompileResult result;
-
+val::Result Validate(Program* program, GeneratorImpl* generator) {
if (!dxc_path) {
- result.status = CompileResult::Status::kVerificationNotEnabled;
- return result;
- }
-
- auto dxc = utils::Command(dxc_path);
- if (!dxc.Found()) {
- result.output = "DXC not found at '" + std::string(dxc_path) + "'";
- result.status = CompileResult::Status::kFailed;
- return result;
+ return val::Result{};
}
std::ostringstream hlsl;
if (!generator->Generate(hlsl)) {
- result.output = generator->error();
- result.status = CompileResult::Status::kFailed;
- return result;
+ return {true, generator->error(), ""};
}
- result.hlsl = hlsl.str();
-
- utils::TmpFile file;
- file << result.hlsl;
-
- bool found_an_entrypoint = false;
- for (auto* func : program->AST().Functions()) {
- if (func->IsEntryPoint()) {
- found_an_entrypoint = true;
-
- const char* profile = "";
-
- switch (func->pipeline_stage()) {
- case ast::PipelineStage::kNone:
- result.output = "Invalid PipelineStage";
- result.status = CompileResult::Status::kFailed;
- return result;
- case ast::PipelineStage::kVertex:
- profile = "-T vs_6_0";
- break;
- case ast::PipelineStage::kFragment:
- profile = "-T ps_6_0";
- break;
- case ast::PipelineStage::kCompute:
- profile = "-T cs_6_0";
- break;
- }
-
- auto name = program->Symbols().NameFor(func->symbol());
- auto res = dxc(profile, "-E " + name, file.Path());
- if (!res.out.empty()) {
- if (!result.output.empty()) {
- result.output += "\n";
- }
- result.output += res.out;
- }
- if (!res.err.empty()) {
- if (!result.output.empty()) {
- result.output += "\n";
- }
- result.output += res.err;
- }
- result.status = (res.error_code == 0) ? CompileResult::Status::kSuccess
- : CompileResult::Status::kFailed;
- }
- }
-
- if (!found_an_entrypoint) {
- result.output = "No entrypoint found";
- result.status = CompileResult::Status::kFailed;
- return result;
- }
-
- return result;
+ return val::Hlsl(dxc_path, hlsl.str(), program);
}
} // namespace hlsl
diff --git a/src/writer/hlsl/test_helper.h b/src/writer/hlsl/test_helper.h
index 8b3604e..8a71505 100644
--- a/src/writer/hlsl/test_helper.h
+++ b/src/writer/hlsl/test_helper.h
@@ -23,6 +23,7 @@
#include "src/transform/hlsl.h"
#include "src/transform/manager.h"
#include "src/transform/renamer.h"
+#include "src/val/val.h"
#include "src/writer/hlsl/generator_impl.h"
namespace tint {
@@ -34,23 +35,11 @@
/// @param dxc_path the path to the DXC executable
void EnableHLSLValidation(const char* dxc_path);
-/// The return structure of Compile()
-struct CompileResult {
- /// Status is an enumerator of status codes from Compile()
- enum class Status { kSuccess, kFailed, kVerificationNotEnabled };
- /// The resulting status of the compile
- Status status;
- /// Output of DXC.
- std::string output;
- /// The HLSL source that was compiled
- std::string hlsl;
-};
-
-/// Compile attempts to compile the shader with DXC if found on PATH.
+/// Validate attempts to compile the shader with DXC if found on PATH.
/// @param program the HLSL program
/// @param generator the HLSL generator
/// @return the result of the compile
-CompileResult Compile(Program* program, GeneratorImpl* generator);
+val::Result Validate(Program* program, GeneratorImpl* generator);
/// Helper class for testing
template <typename BODY>
@@ -122,10 +111,10 @@
/// If DXC finds problems the test will fail.
/// If DXC is not on `PATH` then Validate() does nothing.
void Validate() const {
- auto res = Compile(program.get(), gen_.get());
- if (res.status == CompileResult::Status::kFailed) {
+ auto res = hlsl::Validate(program.get(), gen_.get());
+ if (res.failed) {
FAIL() << "HLSL Validation failed.\n\n"
- << res.hlsl << "\n\n"
+ << res.source << "\n\n"
<< res.output;
}
}
diff --git a/src/writer/msl/test_helper.cc b/src/writer/msl/test_helper.cc
index 87c10a2..6672507 100644
--- a/src/writer/msl/test_helper.cc
+++ b/src/writer/msl/test_helper.cc
@@ -14,8 +14,8 @@
#include "src/writer/msl/test_helper.h"
-#include "src/utils/command.h"
-#include "src/utils/tmpfile.h"
+#include "src/utils/io/command.h"
+#include "src/utils/io/tmpfile.h"
namespace tint {
namespace writer {
@@ -31,50 +31,16 @@
xcrun_path = xcrun;
}
-CompileResult Compile(Program* program) {
- CompileResult result;
-
+val::Result Validate(Program* program) {
if (!xcrun_path) {
- result.status = CompileResult::Status::kVerificationNotEnabled;
- return result;
- }
-
- auto xcrun = utils::Command(xcrun_path);
- if (!xcrun.Found()) {
- result.output = "xcrun not found at '" + std::string(xcrun_path) + "'";
- result.status = CompileResult::Status::kFailed;
- return result;
+ return val::Result{};
}
auto gen = std::make_unique<GeneratorImpl>(program);
if (!gen->Generate()) {
- result.output = gen->error();
- result.status = CompileResult::Status::kFailed;
- return result;
+ return {true, gen->error(), ""};
}
- result.msl = gen->result();
-
- utils::TmpFile file(".metal");
- file << result.msl;
-
- auto xcrun_res =
- xcrun("-sdk", "macosx", "metal", "-o", "/dev/null", "-c", file.Path());
- if (!xcrun_res.out.empty()) {
- if (!result.output.empty()) {
- result.output += "\n";
- }
- result.output += xcrun_res.out;
- }
- if (!xcrun_res.err.empty()) {
- if (!result.output.empty()) {
- result.output += "\n";
- }
- result.output += xcrun_res.err;
- }
- result.status = (xcrun_res.error_code == 0) ? CompileResult::Status::kSuccess
- : CompileResult::Status::kFailed;
-
- return result;
+ return val::Msl(xcrun_path, gen->result());
}
} // namespace msl
diff --git a/src/writer/msl/test_helper.h b/src/writer/msl/test_helper.h
index 0e85ae0..af75a21 100644
--- a/src/writer/msl/test_helper.h
+++ b/src/writer/msl/test_helper.h
@@ -22,6 +22,7 @@
#include "gtest/gtest.h"
#include "src/program_builder.h"
#include "src/transform/msl.h"
+#include "src/val/val.h"
#include "src/writer/msl/generator_impl.h"
namespace tint {
@@ -33,22 +34,10 @@
/// @param xcrun_path the path to the `xcrun` executable
void EnableMSLValidation(const char* xcrun_path);
-/// The return structure of Compile()
-struct CompileResult {
- /// Status is an enumerator of status codes from Compile()
- enum class Status { kSuccess, kFailed, kVerificationNotEnabled };
- /// The resulting status of the compilation
- Status status;
- /// Output of the Metal compiler
- std::string output;
- /// The MSL source that was compiled
- std::string msl;
-};
-
-/// Compile attempts to compile the shader with xcrun if found on PATH.
+/// Validate attempts to compile the shader with DXC if found on PATH.
/// @param program the MSL program
/// @return the result of the compile
-CompileResult Compile(Program* program);
+val::Result Validate(Program* program);
/// Helper class for testing
template <typename BASE>
@@ -115,9 +104,11 @@
/// This function does nothing, if the Metal compiler path has not been
/// configured by calling `EnableMSLValidation()`.
void Validate() {
- auto res = Compile(program.get());
- if (res.status == CompileResult::Status::kFailed) {
- FAIL() << "MSL Validation failed.\n\n" << res.msl << "\n\n" << res.output;
+ auto res = msl::Validate(program.get());
+ if (res.failed) {
+ FAIL() << "MSL Validation failed.\n\n"
+ << res.source << "\n\n"
+ << res.output;
}
}
diff --git a/test/BUILD.gn b/test/BUILD.gn
index b955a01..12a5fa5 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -90,11 +90,11 @@
sources = [ "../src/test_main.cc" ]
configs += [ ":tint_unittests_config" ]
deps += [
- ":tint_test_helpers",
":tint_unittests_hlsl_writer_src",
":tint_unittests_msl_writer_src",
":tint_unittests_spv_reader_src",
"${tint_root_dir}/src:libtint",
+ "${tint_root_dir}/src:tint_val",
]
}
}
@@ -114,36 +114,6 @@
]
}
-source_set("tint_test_helpers") {
- testonly = true
-
- sources = [
- "../src/ast/intrinsic_texture_helper_test.cc",
- "../src/ast/intrinsic_texture_helper_test.h",
- "../src/transform/test_helper.h",
- "../src/utils/command.h",
- "../src/utils/tmpfile.h",
- ]
-
- if (is_linux || is_mac) {
- sources += [ "../src/utils/command_posix.cc" ]
- sources += [ "../src/utils/tmpfile_posix.cc" ]
- } else if (is_win) {
- sources += [ "../src/utils/command_windows.cc" ]
- sources += [ "../src/utils/tmpfile_windows.cc" ]
- } else {
- sources += [ "../src/utils/command_other.cc" ]
- sources += [ "../src/utils/tmpfile_other.cc" ]
- }
-
- configs += [ ":tint_unittests_config" ]
-
- public_deps = [
- ":gmock_and_gtest",
- "${tint_root_dir}/src:libtint",
- ]
-}
-
template("tint_unittests_source_set") {
source_set(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
@@ -162,7 +132,11 @@
if (!defined(invoker.deps)) {
deps = []
}
- deps += [ ":tint_test_helpers" ]
+ deps += [
+ ":gmock_and_gtest",
+ "${tint_root_dir}/src:libtint",
+ "${tint_root_dir}/src:tint_val",
+ ]
}
}
@@ -203,6 +177,8 @@
"../src/ast/identifier_expression_test.cc",
"../src/ast/if_statement_test.cc",
"../src/ast/int_literal_test.cc",
+ "../src/ast/intrinsic_texture_helper_test.cc",
+ "../src/ast/intrinsic_texture_helper_test.h",
"../src/ast/location_decoration_test.cc",
"../src/ast/loop_statement_test.cc",
"../src/ast/matrix_test.cc",
@@ -307,15 +283,16 @@
"../src/transform/renamer_test.cc",
"../src/transform/simplify_test.cc",
"../src/transform/single_entry_point_test.cc",
+ "../src/transform/test_helper.h",
"../src/transform/transform_test.cc",
"../src/transform/var_for_dynamic_index_test.cc",
"../src/transform/vertex_pulling_test.cc",
- "../src/utils/command_test.cc",
"../src/utils/get_or_create_test.cc",
"../src/utils/hash_test.cc",
+ "../src/utils/io/command_test.cc",
+ "../src/utils/io/tmpfile_test.cc",
"../src/utils/math_test.cc",
"../src/utils/scoped_assignment_test.cc",
- "../src/utils/tmpfile_test.cc",
"../src/utils/unique_vector_test.cc",
"../src/writer/append_vector_test.cc",
"../src/writer/float_to_string_test.cc",
@@ -365,7 +342,10 @@
"../src/reader/spirv/usage_test.cc",
]
- deps = [ "${tint_root_dir}/src:libtint_spv_reader_src" ]
+ deps = [
+ ":tint_unittests_core_src",
+ "${tint_root_dir}/src:libtint_spv_reader_src",
+ ]
}
tint_unittests_source_set("tint_unittests_spv_writer_src") {
@@ -406,6 +386,7 @@
]
deps = [
+ ":tint_unittests_core_src",
"${tint_root_dir}/src:libtint_spv_writer_src",
"${tint_spirv_tools_dir}/:spvtools",
]
@@ -489,7 +470,10 @@
"../src/reader/wgsl/token_test.cc",
]
- deps = [ "${tint_root_dir}/src:libtint_wgsl_reader_src" ]
+ deps = [
+ ":tint_unittests_core_src",
+ "${tint_root_dir}/src:libtint_wgsl_reader_src",
+ ]
}
tint_unittests_source_set("tint_unittests_wgsl_writer_src") {
@@ -525,7 +509,10 @@
"../src/writer/wgsl/test_helper.h",
]
- deps = [ "${tint_root_dir}/src:libtint_wgsl_writer_src" ]
+ deps = [
+ ":tint_unittests_core_src",
+ "${tint_root_dir}/src:libtint_wgsl_writer_src",
+ ]
}
tint_unittests_source_set("tint_unittests_msl_writer_src") {
@@ -563,7 +550,10 @@
"../src/writer/msl/test_helper.h",
]
- deps = [ "${tint_root_dir}/src:libtint_msl_writer_src" ]
+ deps = [
+ ":tint_unittests_core_src",
+ "${tint_root_dir}/src:libtint_msl_writer_src",
+ ]
}
tint_unittests_source_set("tint_unittests_hlsl_writer_src") {
@@ -603,7 +593,10 @@
"../src/writer/hlsl/test_helper.h",
]
- deps = [ "${tint_root_dir}/src:libtint_hlsl_writer_src" ]
+ deps = [
+ ":tint_unittests_core_src",
+ "${tint_root_dir}/src:libtint_hlsl_writer_src",
+ ]
}
source_set("tint_unittests_src") {
diff --git a/test/bug/tint/221.wgsl b/test/bug/tint/221.wgsl
index 25fbff5..f764d71 100644
--- a/test/bug/tint/221.wgsl
+++ b/test/bug/tint/221.wgsl
@@ -6,7 +6,7 @@
data : Arr;
};
-[[group(0), binding (0)]] var<storage> b : [[access(read)]] Buf;
+[[group(0), binding (0)]] var<storage> b : [[access(read_write)]] Buf;
[[stage(compute)]]
fn main() {
diff --git a/test/bug/tint/221.wgsl.expected.hlsl b/test/bug/tint/221.wgsl.expected.hlsl
index cea6ddf..ea15e31 100644
--- a/test/bug/tint/221.wgsl.expected.hlsl
+++ b/test/bug/tint/221.wgsl.expected.hlsl
@@ -1,5 +1,5 @@
-ByteAddressBuffer b : register(t0, space0);
+RWByteAddressBuffer b : register(u0, space0);
[numthreads(1, 1, 1)]
void main() {
diff --git a/test/bug/tint/221.wgsl.expected.msl b/test/bug/tint/221.wgsl.expected.msl
index 1438663..1c7b74b 100644
--- a/test/bug/tint/221.wgsl.expected.msl
+++ b/test/bug/tint/221.wgsl.expected.msl
@@ -8,7 +8,7 @@
/* 0x0004 */ uint data[50];
};
-kernel void tint_symbol(const device Buf& b [[buffer(0)]]) {
+kernel void tint_symbol(device Buf& b [[buffer(0)]]) {
uint i = 0u;
{
bool tint_msl_is_first_1 = true;
diff --git a/test/bug/tint/221.wgsl.expected.spvasm b/test/bug/tint/221.wgsl.expected.spvasm
index bfa04c2..993823b 100644
--- a/test/bug/tint/221.wgsl.expected.spvasm
+++ b/test/bug/tint/221.wgsl.expected.spvasm
@@ -17,7 +17,6 @@
OpMemberDecorate %Buf 0 Offset 0
OpMemberDecorate %Buf 1 Offset 4
OpDecorate %_arr_uint_uint_50 ArrayStride 4
- OpDecorate %b NonWritable
OpDecorate %b DescriptorSet 0
OpDecorate %b Binding 0
%uint = OpTypeInt 32 0
diff --git a/test/bug/tint/221.wgsl.expected.wgsl b/test/bug/tint/221.wgsl.expected.wgsl
index a3af0d1..d7130c8 100644
--- a/test/bug/tint/221.wgsl.expected.wgsl
+++ b/test/bug/tint/221.wgsl.expected.wgsl
@@ -6,7 +6,7 @@
data : Arr;
};
-[[group(0), binding(0)]] var<storage> b : [[access(read)]] Buf;
+[[group(0), binding(0)]] var<storage> b : [[access(read_write)]] Buf;
[[stage(compute)]]
fn main() {
diff --git a/test/test-all.sh b/test/test-all.sh
index 48073bc..5609fd3 100755
--- a/test/test-all.sh
+++ b/test/test-all.sh
@@ -20,7 +20,7 @@
function usage() {
echo "test-all.sh is a simple wrapper around <tint>/tools/test-runner that"
- echo "injects the <tint>/tools directory as the second command line argument"
+ echo "injects the <tint>/test directory as the second command line argument"
echo
echo "Usage of <tint>/tools/test-runner:"
"${SCRIPT_DIR}/../tools/test-runner" --help
@@ -41,4 +41,4 @@
exit 1
fi
-"${SCRIPT_DIR}/../tools/test-runner" ${@:2} "${TINT}" "${SCRIPT_DIR}"
+"${SCRIPT_DIR}/../tools/test-runner" "${@:2}" "${TINT}" "${SCRIPT_DIR}"
diff --git a/tools/src/cmd/test-runner/main.go b/tools/src/cmd/test-runner/main.go
index c7afb39..160f465 100644
--- a/tools/src/cmd/test-runner/main.go
+++ b/tools/src/cmd/test-runner/main.go
@@ -68,11 +68,13 @@
}
func run() error {
- var formatList, filter string
+ var formatList, filter, dxcPath, xcrunPath string
numCPU := runtime.NumCPU()
generateExpected := false
flag.StringVar(&formatList, "format", "all", "comma separated list of formats to emit. Possible values are: all, wgsl, spvasm, msl, hlsl")
flag.StringVar(&filter, "filter", "**.wgsl, **.spvasm, **.spv", "comma separated list of glob patterns for test files")
+ flag.StringVar(&dxcPath, "dxc", "", "path to DXC executable for validating HLSL output")
+ flag.StringVar(&xcrunPath, "xcrun", "", "path to xcrun executable for validating MSL output")
flag.BoolVar(&generateExpected, "generate-expected", false, "create or update all expected outputs")
flag.IntVar(&numCPU, "j", numCPU, "maximum number of concurrent threads to run tests")
flag.Usage = showUsage
@@ -147,6 +149,46 @@
}
}
+ default_msl_exe := "xcrun"
+ if runtime.GOOS == "windows" {
+ default_msl_exe = "metal.exe"
+ }
+
+ // If explicit verification compilers have been specified, check they exist.
+ // Otherwise, look on PATH for them, but don't error if they cannot be found.
+ for _, tool := range []struct {
+ name string
+ lang string
+ path *string
+ }{
+ {"dxc", "hlsl", &dxcPath},
+ {default_msl_exe, "msl", &xcrunPath},
+ } {
+ if *tool.path == "" {
+ p, err := exec.LookPath(tool.name)
+ if err == nil && fileutils.IsExe(p) {
+ *tool.path = p
+ }
+ } else if !fileutils.IsExe(*tool.path) {
+ return fmt.Errorf("%v not found at '%v'", tool.name, *tool.path)
+ }
+
+ color.Set(color.FgCyan)
+ fmt.Printf("%-4s", tool.lang)
+ color.Unset()
+ fmt.Printf(" validation ")
+ if *tool.path == "" {
+ color.Set(color.FgRed)
+ fmt.Printf("DISABLED")
+ } else {
+ color.Set(color.FgGreen)
+ fmt.Printf("ENABLED")
+ }
+ color.Unset()
+ fmt.Println()
+ }
+ fmt.Println()
+
results := make([]map[outputFormat]*status, len(files))
jobs := make(chan job, 256)
@@ -157,7 +199,7 @@
go func() {
defer wg.Done()
for job := range jobs {
- job.run(exe, generateExpected)
+ job.run(exe, dxcPath, xcrunPath, generateExpected)
}
}()
}
@@ -313,7 +355,7 @@
result *status
}
-func (j job) run(exe string, generateExpected bool) {
+func (j job) run(exe, dxcPath, xcrunPath string, generateExpected bool) {
// Is there an expected output?
expected := loadExpectedFile(j.file, j.format)
if strings.HasPrefix(expected, "SKIP") { // Special SKIP token
@@ -321,9 +363,28 @@
return
}
+ expected = strings.ReplaceAll(expected, "\r\n", "\n")
+
+ args := []string{j.file, "--format", string(j.format)}
+
+ // Can we validate?
+ switch j.format {
+ case spvasm:
+ args = append(args, "--validate") // spirv-val is statically linked, always available
+ case hlsl:
+ if dxcPath != "" {
+ args = append(args, "--dxc", dxcPath)
+ }
+ case msl:
+ if xcrunPath != "" {
+ args = append(args, "--xcrun", xcrunPath)
+ }
+ }
+
// Invoke the compiler...
var err error
- if ok, out := invoke(exe, j.file, "--format", string(j.format), "--dawn-validation"); ok {
+ if ok, out := invoke(exe, args...); ok {
+ out = strings.ReplaceAll(out, "\r\n", "\n")
if generateExpected {
// If --generate-expected was passed, write out the output
err = saveExpectedFile(j.file, j.format, out)
diff --git a/tools/src/fileutils/fileutils.go b/tools/src/fileutils/fileutils_linux.go
similarity index 100%
rename from tools/src/fileutils/fileutils.go
rename to tools/src/fileutils/fileutils_linux.go
diff --git a/src/utils/tmpfile_other.cc b/tools/src/fileutils/fileutils_windows.go
similarity index 70%
copy from src/utils/tmpfile_other.cc
copy to tools/src/fileutils/fileutils_windows.go
index 93325f5..d6185a3 100644
--- a/src/utils/tmpfile_other.cc
+++ b/tools/src/fileutils/fileutils_windows.go
@@ -12,18 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/utils/tmpfile.h"
+// Package fileutils contains utility functions for files
+package fileutils
-namespace tint {
-namespace utils {
-
-TmpFile::TmpFile(std::string) {}
-
-TmpFile::~TmpFile() = default;
-
-bool TmpFile::Append(const void*, size_t) const {
- return false;
+// IsExe returns true if the file at path is an executable
+func IsExe(path string) bool {
+ return true
}
-
-} // namespace utils
-} // namespace tint