[llvm] [Support] Introduce driver for running tools as daemons (PR #193706)

Henrik G. Olsson via llvm-commits llvm-commits at lists.llvm.org
Tue May 5 14:49:10 PDT 2026


================
@@ -0,0 +1,530 @@
+//===- llvm/Support/DaemonDriver.cpp - Daemon driver interface ---- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the interface for tools to be run in "daemon mode",
+// following the IPC protocol as described in docs/DaemonMode.rst.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/DaemonDriver.h"
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <filesystem>
+#include <optional>
+#include <system_error>
+
+#if defined _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+using namespace llvm;
+
+// Windows emulated POSIX functions are prefixed by `_`.
+#ifdef _WIN32
+#define CLOSE_FN ::_close
+#define DUP_FN ::_dup
+#define DUP2_FN ::_dup2
+#else
+#define CLOSE_FN ::close
+#define DUP_FN ::dup
+#define DUP2_FN ::dup2
+#endif
+
+// Standard stream fileno macros are not defined on Windows.
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+namespace {
+namespace util {
+/// RAII mechanism to redirect a file descriptor to the file pointed to by a
+/// different file descriptor. The destructor will reset the file descriptor to
+/// its original file.
+class ScopedFileRedirect {
+public:
+  /// Redirect `FromFd` to the same file as `ToFd` for the lifetime of this
+  /// object.
+  ScopedFileRedirect(int FromFd, int ToFd) : FromFd(FromFd) {
+    // Store a copy of the original file so that we can restore the original fd
+    // to this copy.
+    CopyFd = DUP_FN(FromFd);
+
+    // Close the source fd and reopen it to the target fd.
+    DUP2_FN(ToFd, FromFd);
+  }
+
+  ~ScopedFileRedirect() {
+    if (Moved)
+      return;
+
+    // Close the source fd and reopen it to the original file.
+    DUP2_FN(CopyFd, FromFd);
+
+    // Close the copied file descriptor, as it's no longer needed.
+    CLOSE_FN(CopyFd);
+  }
+
+  ScopedFileRedirect(ScopedFileRedirect &&Other) {
+    *this = Other;
+    Other.Moved = true;
+  }
+  ScopedFileRedirect &operator=(ScopedFileRedirect &&Other) {
+    *this = Other;
+    Other.Moved = true;
+    return *this;
+  }
+
+private:
+  ScopedFileRedirect(const ScopedFileRedirect &Other) = default;
+  ScopedFileRedirect &operator=(const ScopedFileRedirect &Other) = default;
+
+  int FromFd;
+  int CopyFd;
+  bool Moved = false;
+};
+
+static ErrorOr<std::string> readNextLine(FILE *File) {
+  std::string Result;
+  raw_string_ostream ResultOS(Result);
+
+  constexpr size_t BufSize = 512;
+  char Buf[BufSize];
+
+  // Read from the file, appending to the result, until a new line character
+  // is found.
+  while (std::fgets(Buf, BufSize, File)) {
+    ResultOS << Buf;
+
+    if (std::strchr(Buf, '\n'))
+      break;
+  }
+
+  if (std::ferror(File))
+    return std::make_error_code(static_cast<std::errc>(errno));
+
+  return Result;
+}
+
+} // namespace util
+
+/// Status code returned if the daemon fails to initialize, for example due to
+/// incorrect command line arguments.
+constexpr int StatusInitError = 2;
+/// Status code returned if the daemon receives a malformed command.
+constexpr int StatusCommandError = 3;
+
+/// This should only be used before the status pipe is set up - after,
+/// errors are reported to the user via the status pipe.
+[[noreturn]] static void reportInitError(const Twine &Err) {
+  errs() << "[daemon] Error: " << Err << "\n";
+  std::exit(StatusInitError);
+}
+
+/// Returns true if ``--daemon`` is passed as the first command line argument.
+static bool detectDaemonArg(int Argc, char **Argv) {
+  // `--daemon` must be the first argument.
+  return Argc >= 2 && Argv[1] == StringRef("--daemon");
+}
+
+struct DaemonCommandLineOptions {
+  bool DaemonModeEnabled;
+  std::string StatusPipe;
+};
+
+// Creates the command line options for configuring the daemon. The returned
+// reference points to a static struct where the option values will be stored.
+static const DaemonCommandLineOptions &initializeDaemonCommandLineOptions() {
+  // This is declared as a static local variables in this function rather than
+  // a static global variable as is common in LLVM to avoid adding global
+  // constructors and destructors to the program, which is forbidden in the
+  // Support library.
+  static DaemonCommandLineOptions Options;
+
+  static cl::opt<bool, true> DaemonModeEnabledOpt(
+      "daemon", cl::location(Options.DaemonModeEnabled), cl::init(false));
+
+  static cl::opt<std::string, true> StatusPipeOpt(
+      "daemon-status-pipe",
+      cl::desc("File to which the daemon tool will send status messages. May "
+               "be 'path:{filepath}', 'fd:{file descriptor}' or "
+               "'handle:{Windows file handle}'"),
+      cl::location(Options.StatusPipe), cl::init(""));
+
+  return Options;
+}
+
+/// Command sent to invoke the tool.
+struct RunCommand {
+  static constexpr StringRef Prefix = "run";
+
+  static Expected<RunCommand> parse(StringRef Remaining) {
+    Remaining = Remaining.trim();
+
+    if (!Remaining.trim().empty())
----------------
hnrklssn wrote:

Makes sense, thanks for explaining.

https://github.com/llvm/llvm-project/pull/193706


More information about the llvm-commits mailing list