[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