[tint] Add server-mode to Tint executable
When launched with `--server`, the Tint executable will read arguments
from stdin. Each line will invoke Tint with the supplied
arguments. Output on stdout and stderr will be delimited with \0
characters.
Bug: 408499924
Change-Id: I402e3dc25ab6d804741030c96dda0ddbe2ffb50c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/235294
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 39272a8..868632e 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -117,6 +117,11 @@
kIr,
};
+enum class ExeMode : uint8_t {
+ kStandalone,
+ kServer,
+};
+
#if TINT_BUILD_HLSL_WRITER
constexpr uint32_t kMinShaderModelForDXC = 60u;
constexpr uint32_t kMaxSupportedShaderModelForDXC = 66u;
@@ -221,7 +226,7 @@
// The actual warning occurs on `std::from_chars(hash.data(), hash.data() + hash.size(), value,
// base);`, but disabling/enabling warnings cannot be done within function scope
TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-bool ParseArgs(tint::VectorRef<std::string_view> arguments, Options* opts) {
+bool ParseArgs(tint::VectorRef<std::string_view> arguments, Options* opts, ExeMode exe_mode) {
using namespace tint::cli; // NOLINT(build/namespaces)
tint::Vector<EnumName<Format>, 8> format_enum_names{
@@ -266,15 +271,19 @@
format_enum_names, ShortName{"f"});
TINT_DEFER(opts->format = fmt.value.value_or(Format::kUnknown));
- auto& col = options.Add<EnumOption<tint::ColorMode>>(
- "color", "Use colored output",
- tint::Vector{
- EnumName{tint::ColorMode::kPlain, "off"},
- EnumName{tint::ColorMode::kDark, "dark"},
- EnumName{tint::ColorMode::kLight, "light"},
- },
- ShortName{"col"}, Default{tint::ColorModeDefault()});
- TINT_DEFER(opts->printer = CreatePrinter(*col.value));
+ if (exe_mode == ExeMode::kServer) {
+ opts->printer = CreatePrinter(tint::ColorMode::kPlain);
+ } else {
+ auto& col = options.Add<EnumOption<tint::ColorMode>>(
+ "color", "Use colored output",
+ tint::Vector{
+ EnumName{tint::ColorMode::kPlain, "off"},
+ EnumName{tint::ColorMode::kDark, "dark"},
+ EnumName{tint::ColorMode::kLight, "light"},
+ },
+ ShortName{"col"}, Default{tint::ColorModeDefault()});
+ TINT_DEFER(opts->printer = CreatePrinter(*col.value));
+ }
auto& ep = options.Add<StringOption>("entry-point", "Output single entry point",
ShortName{"ep"}, Parameter{"name"});
@@ -1426,15 +1435,15 @@
#endif
}
-} // namespace
-
-int main(int argc, const char** argv) {
- tint::Vector<std::string_view, 8> arguments = tint::args::Vectorize(argc, argv);
+int Run(tint::VectorRef<std::string_view> arguments, ExeMode exe_mode) {
Options options;
- tint::Initialize();
+ if (!ParseArgs(arguments, &options, exe_mode)) {
+ return 1;
+ }
- if (!ParseArgs(arguments, &options)) {
+ if (exe_mode == ExeMode::kServer && options.format == Format::kSpirv) {
+ std::cerr << "Cannot emit binary SPIR-V to stdout in server mode\n";
return 1;
}
@@ -1571,3 +1580,61 @@
}
return success ? 0 : 1;
}
+
+/// Run a server that accepts arguments on stdin.
+/// @returns 0 on success, non-zero on failure
+int RunServer() {
+ // Each line read from stdin will invoke Tint with the supplied arguments.
+ // Output on stdout and stderr will be delimited with \0 characters.
+ // The server will exit on failure or if stdin is closed.
+ while (!std::cin.eof()) {
+ // Read the next set of arguments.
+ std::string arg_line;
+ std::getline(std::cin, arg_line);
+
+ // Split the arguments by whitespace, taking double-quotes into account.
+ std::istringstream arg_in(arg_line);
+ tint::Vector<std::string, 8> arg_tokens;
+ while (!arg_in.eof()) {
+ std::string arg;
+ arg_in >> std::quoted(arg, '"', '\0');
+ if (!arg.empty()) {
+ arg_tokens.Push(arg);
+ }
+ }
+
+ // Run Tint with the provided arguments.
+ auto arguments =
+ tint::Transform(arg_tokens, [](const std::string& arg) -> std::string_view {
+ return std::string_view(arg); //
+ });
+ auto ret = Run(arguments, ExeMode::kServer);
+ if (ret != 0) {
+ // The Tint invocation failed, so exit the server.
+ return ret;
+ }
+
+ // Delimit stdout and stderr with \0 and flush them.
+ std::cout << '\0' << std::flush;
+ std::cerr << '\0' << std::flush;
+ }
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, const char** argv) {
+ tint::Vector<std::string_view, 8> arguments = tint::args::Vectorize(argc, argv);
+
+ tint::Initialize();
+
+ if (arguments.Length() > 0 && arguments[0] == "--server") {
+ if (arguments.Length() > 1) {
+ std::cerr << "--server must not be used with any other arguments\n";
+ return 1;
+ }
+ return RunServer();
+ }
+
+ return Run(arguments, ExeMode::kServer);
+}