[clang] [WIP][clang][MBD] Initial implementation of module build daemon (PR #68498)
Connor Sughrue via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 15 12:57:29 PDT 2024
https://github.com/cpsughrue updated https://github.com/llvm/llvm-project/pull/68498
>From 9fe97509277fbce0333c454bb4e2619fed04b189 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Sun, 9 Jul 2023 23:19:58 -0400
Subject: [PATCH 1/6] [WIP][clang][MBD] module build daemon
---
clang/include/clang/Driver/Options.td | 12 +
.../include/clang/Frontend/FrontendOptions.h | 7 +
.../clang/Tooling/ModuleBuildDaemon/Client.h | 56 ++
.../ModuleBuildDaemon/SocketMsgSupport.h | 166 ++++++
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 31 +
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 28 +
clang/lib/Driver/ToolChains/Clang.cpp | 14 +-
clang/lib/Tooling/CMakeLists.txt | 1 +
.../Tooling/ModuleBuildDaemon/CMakeLists.txt | 9 +
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 224 +++++++
.../ModuleBuildDaemon/SocketSupport.cpp | 128 ++++
clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp | 32 +
clang/test/Driver/unknown-arg.c | 2 +-
clang/test/ModuleBuildDaemon/launch.c | 10 +
clang/test/ModuleBuildDaemon/parallel-scan.c | 31 +
clang/test/ModuleBuildDaemon/scan.c | 20 +
clang/tools/driver/CMakeLists.txt | 3 +
clang/tools/driver/cc1_main.cpp | 74 ++-
clang/tools/driver/cc1modbuildd_main.cpp | 553 ++++++++++++++++++
clang/tools/driver/driver.cpp | 17 +-
20 files changed, 1408 insertions(+), 10 deletions(-)
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
create mode 100644 clang/test/ModuleBuildDaemon/launch.c
create mode 100644 clang/test/ModuleBuildDaemon/parallel-scan.c
create mode 100644 clang/test/ModuleBuildDaemon/scan.c
create mode 100644 clang/tools/driver/cc1modbuildd_main.cpp
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 5415b18d3f406d..33c24e182fccca 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2883,6 +2883,18 @@ defm declspec : BoolOption<"f", "declspec",
NegFlag<SetFalse, [], [ClangOption], "Disallow">,
BothFlags<[], [ClangOption, CC1Option],
" __declspec as a keyword">>, Group<f_clang_Group>;
+
+def fmodule_build_daemon : Flag<["-"], "fmodule-build-daemon">, Group<f_Group>,
+ Flags<[NoXarchOption]>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Enables module build daemon functionality">,
+ MarshallingInfoFlag<FrontendOpts<"ModuleBuildDaemon">>;
+def fmodule_build_daemon_EQ : Joined<["-"], "fmodule-build-daemon=">, Group<f_Group>,
+ Flags<[NoXarchOption]>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Enables module build daemon functionality and defines location of output files">,
+ MarshallingInfoString<FrontendOpts<"ModuleBuildDaemonPath">>;
+
def fmodules_cache_path : Joined<["-"], "fmodules-cache-path=">, Group<i_Group>,
Flags<[NoXarchOption]>, Visibility<[ClangOption, CC1Option]>,
MetaVarName<"<directory>">,
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 117e35de6f76c4..8ce97a57d413c0 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -350,6 +350,9 @@ class FrontendOptions {
/// Whether to share the FileManager when building modules.
unsigned ModulesShareFileManager : 1;
+ /// Connect to module build daemon
+ unsigned ModuleBuildDaemon : 1;
+
CodeCompleteOptions CodeCompleteOpts;
/// Specifies the output format of the AST.
@@ -435,6 +438,10 @@ class FrontendOptions {
/// The output file, if any.
std::string OutputFile;
+ /// If given, the path to the module build daemon's output files and socket
+ /// address
+ std::string ModuleBuildDaemonPath;
+
/// If given, the new suffix for fix-it rewritten files.
std::string FixItSuffix;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
new file mode 100644
index 00000000000000..5d5df74743a6a6
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -0,0 +1,56 @@
+//===----------------------------- Protocol.h -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#define MAX_BUFFER 4096
+#define SOCKET_FILE_NAME "mbd.sock"
+#define STDOUT_FILE_NAME "mbd.out"
+#define STDERR_FILE_NAME "mbd.err"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+// Returns where to store log files and socket address. Of the format
+// /tmp/clang-<BLAKE3HashOfClagnFullVersion>/
+std::string getBasePath();
+
+llvm::Error attemptHandshake(int SocketFD);
+
+llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0);
+
+Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath);
+
+// Sends request to module build daemon
+llvm::Error registerTranslationUnit(ArrayRef<const char *> CC1Cmd,
+ StringRef Argv0, StringRef CWD,
+ int ServerFD);
+
+// Processes response from module build daemon
+Expected<std::vector<std::string>> getUpdatedCC1(int ServerFD);
+
+// Work in progress. Eventually function will modify CC1 command line to include
+// path to modules already built by the daemon
+Expected<std::vector<std::string>>
+updateCC1WithModuleBuildDaemon(const CompilerInvocation &Clang,
+ ArrayRef<const char *> CC1Cmd, const char *Argv0,
+ StringRef CWD);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_PROTOCAL_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
new file mode 100644
index 00000000000000..c02a426054f117
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -0,0 +1,166 @@
+//===------------------------- SocketMsgSupport.h -------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
+
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+enum class ActionType { REGISTER, HANDSHAKE };
+enum class StatusType { REQUEST, SUCCESS, FAILURE };
+
+struct BaseMsg {
+ ActionType MsgAction;
+ StatusType MsgStatus;
+
+ BaseMsg() = default;
+ BaseMsg(ActionType Action, StatusType Status)
+ : MsgAction(Action), MsgStatus(Status) {}
+};
+
+struct RegisterMsg : public BaseMsg {
+ std::optional<std::string> WorkingDirectory;
+ // The scanner requires the path to the clang executable
+ std::optional<std::string> ExecutablePath;
+ // Does not include executable
+ std::optional<std::vector<std::string>> CC1CommandLine;
+
+ RegisterMsg() = default;
+
+ RegisterMsg(ActionType Action, StatusType Status,
+ const std::optional<std::string> &CurrentWD,
+ const std::optional<std::string> &Argv0,
+ const std::optional<std::vector<std::string>> &Argv)
+ : BaseMsg(Action, Status), WorkingDirectory(CurrentWD),
+ ExecutablePath(Argv0), CC1CommandLine(Argv) {}
+
+ RegisterMsg(ActionType Action, StatusType Status)
+ : BaseMsg(Action, Status), WorkingDirectory(std::nullopt),
+ ExecutablePath(std::nullopt), CC1CommandLine(std::nullopt) {}
+};
+
+struct HandshakeMsg : public BaseMsg {
+ HandshakeMsg() = default;
+ HandshakeMsg(ActionType Action, StatusType Status)
+ : BaseMsg(Action, Status) {}
+};
+
+template <typename T> std::string getBufferFromSocketMsg(T Msg) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+ llvm::yaml::Output YamlOut(OS);
+
+ YamlOut << Msg;
+ return Buffer;
+}
+
+template <typename T> Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ T ClientRequest;
+ llvm::yaml::Input YamlIn(Buffer);
+ YamlIn >> ClientRequest;
+
+ if (YamlIn.error()) {
+ std::string Msg = "Syntax or semantic error during YAML parsing";
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+
+ return ClientRequest;
+}
+
+template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
+ if (!MaybeResponseBuffer)
+ return std::move(MaybeResponseBuffer.takeError());
+
+ // Wait for response from module build daemon
+ Expected<T> MaybeResponse =
+ getSocketMsgFromBuffer<T>(std::move(*MaybeResponseBuffer).c_str());
+ if (!MaybeResponse)
+ return std::move(MaybeResponse.takeError());
+ return std::move(*MaybeResponse);
+}
+
+template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ std::string Buffer = getBufferFromSocketMsg(Msg);
+ if (llvm::Error Err = writeToSocket(Buffer, FD))
+ return std::move(Err);
+
+ return llvm::Error::success();
+}
+
+template <typename T>
+Expected<int> connectAndWriteSocketMsgToSocket(T Msg, StringRef SocketPath) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
+ "T must inherit from cc1modbuildd::BaseMsg");
+
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (!MaybeFD)
+ return std::move(MaybeFD.takeError());
+ int FD = std::move(*MaybeFD);
+
+ if (llvm::Error Err = writeSocketMsgToSocket(Msg, FD))
+ return std::move(Err);
+
+ return FD;
+}
+
+} // namespace cc1modbuildd
+
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::StatusType> {
+ static void enumeration(IO &Io, cc1modbuildd::StatusType &Value) {
+ Io.enumCase(Value, "REQUEST", cc1modbuildd::StatusType::REQUEST);
+ Io.enumCase(Value, "SUCCESS", cc1modbuildd::StatusType::SUCCESS);
+ Io.enumCase(Value, "FAILURE", cc1modbuildd::StatusType::FAILURE);
+ }
+};
+
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::ActionType> {
+ static void enumeration(IO &Io, cc1modbuildd::ActionType &Value) {
+ Io.enumCase(Value, "REGISTER", cc1modbuildd::ActionType::REGISTER);
+ Io.enumCase(Value, "HANDSHAKE", cc1modbuildd::ActionType::HANDSHAKE);
+ }
+};
+
+template <> struct llvm::yaml::MappingTraits<cc1modbuildd::RegisterMsg> {
+ static void mapping(IO &Io, cc1modbuildd::RegisterMsg &Info) {
+ Io.mapRequired("Action", Info.MsgAction);
+ Io.mapRequired("Status", Info.MsgStatus);
+ Io.mapOptional("WorkingDirectory", Info.WorkingDirectory);
+ Io.mapOptional("ExecutablePath", Info.ExecutablePath);
+ Io.mapOptional("CC1CommandLine", Info.CC1CommandLine);
+ }
+};
+
+template <> struct llvm::yaml::MappingTraits<cc1modbuildd::HandshakeMsg> {
+ static void mapping(IO &Io, cc1modbuildd::HandshakeMsg &Info) {
+ Io.mapRequired("Action", Info.MsgAction);
+ Io.mapRequired("Status", Info.MsgStatus);
+ }
+};
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
new file mode 100644
index 00000000000000..bc21084faab396
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -0,0 +1,31 @@
+//===-------------------------- SocketSupport.h ---------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace cc1modbuildd {
+
+Expected<int> createSocket();
+Expected<int> connectToSocket(StringRef SocketPath);
+Expected<int> connectAndWriteToSocket(std::string Buffer, StringRef SocketPath);
+Expected<std::string> readFromSocket(int FD);
+llvm::Error writeToSocket(std::string Buffer, int WriteFD);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
new file mode 100644
index 00000000000000..79a2ffc3c1804d
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -0,0 +1,28 @@
+//===------------------------------ Utils.h -------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions required by both the module build daemon (server) and clang
+// invocation (client)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace cc1modbuildd {
+
+void writeError(llvm::Error Err, std::string Msg);
+std::string getFullErrorMsg(llvm::Error Err, std::string Msg);
+llvm::Error makeStringError(llvm::Error Err, std::string Msg);
+
+} // namespace cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
\ No newline at end of file
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index bfd6c5c2864abf..62fbcae0f87cf4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-
#include "Clang.h"
#include "AMDGPU.h"
#include "Arch/AArch64.h"
@@ -3740,6 +3739,19 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
Std->containsValue("c++latest") || Std->containsValue("gnu++latest"));
bool HaveModules = HaveStdCXXModules;
+ // -fmodule-build-daemon enables module build daemon functionality
+ if (Args.hasArg(options::OPT_fmodule_build_daemon))
+ Args.AddLastArg(CmdArgs, options::OPT_fmodule_build_daemon);
+
+ // by default module build daemon socket address and output files are saved
+ // under /tmp/ but that can be overridden by providing the
+ // -fmodule-build-daemon=<path> flag
+ if (Arg *A = Args.getLastArg(options::OPT_fmodule_build_daemon_EQ)) {
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-fmodule-build-daemon=") + A->getValue()));
+ CmdArgs.push_back("-fmodule-build-daemon");
+ }
+
// -fmodules enables the use of precompiled modules (off by default).
// Users can pass -fno-cxx-modules to turn off modules support for
// C++/Objective-C++ programs.
diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt
index aff39e4de13c0b..85752e57733265 100644
--- a/clang/lib/Tooling/CMakeLists.txt
+++ b/clang/lib/Tooling/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(DumpTool)
add_subdirectory(Syntax)
add_subdirectory(DependencyScanning)
add_subdirectory(Transformer)
+add_subdirectory(ModuleBuildDaemon)
# Replace the last lib component of the current binary directory with include
string(FIND ${CMAKE_CURRENT_BINARY_DIR} "/lib/" PATH_LIB_START REVERSE)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
new file mode 100644
index 00000000000000..9c1f5dc1aa2c0c
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangModuleBuildDaemon
+ Client.cpp
+ SocketSupport.cpp
+ Utils.cpp
+ )
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
new file mode 100644
index 00000000000000..08fe14476c901b
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -0,0 +1,224 @@
+//===----------------------------- Client.cpp -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/BLAKE3.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+using namespace clang;
+using namespace llvm;
+using namespace cc1modbuildd;
+
+std::string cc1modbuildd::getBasePath() {
+ llvm::BLAKE3 Hash;
+ Hash.update(getClangFullVersion());
+ auto HashResult = Hash.final<sizeof(uint64_t)>();
+ uint64_t HashValue =
+ llvm::support::endian::read<uint64_t, llvm::support::native>(
+ HashResult.data());
+ std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
+
+ // set paths
+ SmallString<128> BasePath;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
+ llvm::sys::path::append(BasePath, "clang-" + Key);
+ return BasePath.c_str();
+}
+
+llvm::Error cc1modbuildd::attemptHandshake(int SocketFD) {
+
+ cc1modbuildd::HandshakeMsg Request{ActionType::HANDSHAKE,
+ StatusType::REQUEST};
+ std::string Buffer = cc1modbuildd::getBufferFromSocketMsg(Request);
+
+ if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
+ return std::move(Err);
+
+ Expected<RegisterMsg> MaybeServerResponse =
+ readSocketMsgFromSocket<RegisterMsg>(SocketFD);
+ if (!MaybeServerResponse)
+ return std::move(MaybeServerResponse.takeError());
+ RegisterMsg ServerResponse = std::move(*MaybeServerResponse);
+
+ assert(ServerResponse.MsgAction == ActionType::HANDSHAKE &&
+ "At this point response ActionType should only ever be HANDSHAKE");
+
+ if (ServerResponse.MsgStatus == StatusType::SUCCESS)
+ return llvm::Error::success();
+
+ return llvm::make_error<StringError>("Handshake failed",
+ inconvertibleErrorCode());
+}
+
+llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
+ const char *Argv0) {
+ std::string BasePathStr = BasePath.str();
+ const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
+ pid_t pid;
+ int EC = posix_spawn(&pid, Args[0],
+ /*file_actions*/ nullptr,
+ /*spawnattr*/ nullptr, const_cast<char **>(Args),
+ /*envp*/ nullptr);
+ if (EC)
+ return createStringError(std::error_code(EC, std::generic_category()),
+ "failed to spawn module build daemon process");
+
+ return llvm::Error::success();
+}
+
+Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
+ StringRef BasePath) {
+
+ SmallString<128> SocketPath = BasePath;
+ llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (MaybeFD)
+ return std::move(*MaybeFD);
+ consumeError(MaybeFD.takeError());
+ }
+
+ if (llvm::Error Err = cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0))
+ return std::move(Err);
+
+ const unsigned int MICROSEC_IN_SEC = 1000000;
+ constexpr unsigned int MAX_TIME = 30 * MICROSEC_IN_SEC;
+ const unsigned short INTERVAL = 100;
+
+ unsigned int CumulativeTime = 0;
+ unsigned int WaitTime = 0;
+
+ while (CumulativeTime <= MAX_TIME) {
+ // Wait a bit then check to see if the module build daemon has initialized
+ usleep(WaitTime);
+
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<int> MaybeFD = connectToSocket(SocketPath);
+ if (MaybeFD)
+ return std::move(*MaybeFD);
+ consumeError(MaybeFD.takeError());
+ }
+
+ CumulativeTime += INTERVAL;
+ }
+
+ // After waiting 30 seconds give up
+ return llvm::make_error<StringError>(
+ "Module build daemon did not exist after spawn attempt",
+ inconvertibleErrorCode());
+}
+
+llvm::Error
+cc1modbuildd::registerTranslationUnit(ArrayRef<const char *> CC1Command,
+ StringRef Argv0, StringRef CWD,
+ int ServerFD) {
+
+ std::vector<std::string> StrCC1Command;
+ for (const char *Arg : CC1Command)
+ StrCC1Command.emplace_back(Arg);
+
+ cc1modbuildd::RegisterMsg Request{ActionType::REGISTER, StatusType::REQUEST,
+ CWD.str(), Argv0.str(), StrCC1Command};
+
+ llvm::Error WriteErr = writeSocketMsgToSocket(Request, ServerFD);
+ if (WriteErr)
+ return std::move(WriteErr);
+
+ return llvm::Error::success();
+}
+
+Expected<std::vector<std::string>> cc1modbuildd::getUpdatedCC1(int ServerFD) {
+
+ // Blocks cc1 invocation until module build daemon is done processing
+ // translation unit. Currently receives a SUCCESS message and returns
+ // llvm::Error::success() but will eventually recive updated cc1 command line
+ Expected<RegisterMsg> MaybeServerResponse =
+ readSocketMsgFromSocket<RegisterMsg>(ServerFD);
+ if (!MaybeServerResponse)
+ return std::move(MaybeServerResponse.takeError());
+ RegisterMsg ServerResponse = std::move(*MaybeServerResponse);
+
+ // Confirm response is REGISTER and MsgStatus is SUCCESS
+ assert(ServerResponse.MsgAction == ActionType::REGISTER &&
+ "At this point response ActionType should only ever be REGISTER");
+
+ if (ServerResponse.MsgStatus == StatusType::SUCCESS)
+ return ServerResponse.CC1CommandLine.value();
+
+ return llvm::make_error<StringError>(
+ "Daemon failed to processes registered translation unit",
+ inconvertibleErrorCode());
+}
+
+Expected<std::vector<std::string>>
+cc1modbuildd::updateCC1WithModuleBuildDaemon(const CompilerInvocation &Clang,
+ ArrayRef<const char *> CC1Cmd,
+ const char *Argv0, StringRef CWD) {
+
+ // The module build daemon stores all output files and its socket address
+ // under BasePath. Either set BasePath to a user provided option or create an
+ // appropriate BasePath based on the hash of the clang version
+ std::string BasePath;
+ if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
+ BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
+ else
+ BasePath = cc1modbuildd::getBasePath();
+
+ // If module build daemon does not exist spawn module build daemon
+ Expected<int> MaybeDaemonFD =
+ cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath);
+ if (!MaybeDaemonFD)
+ return makeStringError(MaybeDaemonFD.takeError(),
+ "Connect to daemon failed: ");
+ int DaemonFD = std::move(*MaybeDaemonFD);
+
+ if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD))
+ return makeStringError(std::move(HandshakeErr),
+ "Failed to hadshake with daemon: ");
+
+ // Send translation unit information to module build daemon for processing
+ if (llvm::Error RegisterErr =
+ registerTranslationUnit(CC1Cmd, Argv0, CWD, DaemonFD))
+ return makeStringError(std::move(RegisterErr),
+ "Register translation unti failed: ");
+
+ // Wait for response from module build daemon. Response will hopefully be an
+ // updated cc1 command line with additional -fmodule-file=<file> flags and
+ // implicit module flags removed
+ Expected<std::vector<std::string>> MaybeUpdatedCC1 = getUpdatedCC1(DaemonFD);
+ if (!MaybeUpdatedCC1)
+ return makeStringError(MaybeUpdatedCC1.takeError(),
+ "Failed to get updated CC1: ");
+ return std::move(*MaybeUpdatedCC1);
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
new file mode 100644
index 00000000000000..58526e4422f457
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -0,0 +1,128 @@
+//===------------------------- SocketSupport.cpp --------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/BLAKE3.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+Expected<int> cc1modbuildd::createSocket() {
+ int FD;
+ if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ std::string Msg = "socket create error: " + std::string(strerror(errno));
+ return createStringError(inconvertibleErrorCode(), Msg);
+ }
+ return FD;
+}
+
+Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
+
+ Expected<int> MaybeFD = cc1modbuildd::createSocket();
+ if (!MaybeFD)
+ return std::move(MaybeFD.takeError());
+
+ int FD = std::move(*MaybeFD);
+
+ struct sockaddr_un Addr;
+ memset(&Addr, 0, sizeof(Addr));
+ Addr.sun_family = AF_UNIX;
+ strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
+
+ if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
+ close(FD);
+ std::string msg = "socket connect error: " + std::string(strerror(errno));
+ return createStringError(inconvertibleErrorCode(), msg);
+ }
+ return FD;
+}
+
+Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
+ StringRef SocketPath) {
+
+ Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
+ if (!MaybeConnectedFD)
+ return std::move(MaybeConnectedFD.takeError());
+
+ int ConnectedFD = std::move(*MaybeConnectedFD);
+ llvm::Error Err = writeToSocket(Buffer, ConnectedFD);
+ if (Err)
+ return std::move(Err);
+
+ return ConnectedFD;
+}
+
+Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
+
+ const size_t BUFFER_SIZE = 4096;
+ std::vector<char> Buffer(BUFFER_SIZE);
+ size_t TotalBytesRead = 0;
+
+ ssize_t n;
+ while ((n = read(FD, Buffer.data() + TotalBytesRead,
+ Buffer.size() - TotalBytesRead)) > 0) {
+
+ TotalBytesRead += n;
+ // Read until ...\n encountered (last line of YAML document)
+ if (std::string(&Buffer[TotalBytesRead - 4], 4) == "...\n")
+ break;
+ if (Buffer.size() - TotalBytesRead < BUFFER_SIZE)
+ Buffer.resize(Buffer.size() + BUFFER_SIZE);
+ }
+
+ if (n < 0) {
+ std::string Msg = "socket read error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+ if (n == 0)
+ return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
+ return std::string(Buffer.begin(), Buffer.end());
+}
+
+llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
+
+ ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
+ const char *Bytes = Buffer.c_str();
+
+ while (BytesToWrite) {
+ ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
+ if (BytesWritten == -1) {
+ std::string Msg = "socket write error: " + std::string(strerror(errno));
+ return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ }
+
+ if (!BytesWritten || BytesWritten > BytesToWrite)
+ return llvm::errorCodeToError(
+ std::error_code(EIO, std::generic_category()));
+
+ BytesToWrite -= BytesWritten;
+ Bytes += BytesWritten;
+ }
+ return llvm::Error::success();
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
new file mode 100644
index 00000000000000..c03e8e3762114f
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
@@ -0,0 +1,32 @@
+//===------------------------------ Utils.cpp -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <clang/Tooling/ModuleBuildDaemon/Utils.h>
+#include <llvm/Support/Error.h>
+#include <string>
+
+using namespace llvm;
+
+void cc1modbuildd::writeError(llvm::Error Err, std::string Msg) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ errs() << Msg << EIB.message() << '\n';
+ });
+}
+
+std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
+ std::string ErrMessage;
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ ErrMessage = Msg + EIB.message();
+ });
+ return ErrMessage;
+}
+
+llvm::Error cc1modbuildd::makeStringError(llvm::Error Err, std::string Msg) {
+ std::string ErrMsg = getFullErrorMsg(std::move(Err), Msg);
+ return llvm::make_error<StringError>(ErrMsg, inconvertibleErrorCode());
+}
\ No newline at end of file
diff --git a/clang/test/Driver/unknown-arg.c b/clang/test/Driver/unknown-arg.c
index 52ea0f5ff3220f..3a22b824adc5c7 100644
--- a/clang/test/Driver/unknown-arg.c
+++ b/clang/test/Driver/unknown-arg.c
@@ -59,7 +59,7 @@
// SILENT-NOT: warning:
// CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'?
// CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'?
-// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1' and '-cc1as'.
+// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1', '-cc1as', '-cc1gen-reproducer', and '-cc1modbuildd'.
// RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
new file mode 100644
index 00000000000000..575aba0ac3ce60
--- /dev/null
+++ b/clang/test/ModuleBuildDaemon/launch.c
@@ -0,0 +1,10 @@
+// REQUIRES: !system-windows
+
+// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
+
+// RUN: %clang -cc1modbuildd mbd-launch -v
+// RUN: cat mbd-launch/mbd.out | FileCheck %s -DPREFIX=%t
+
+// CHECK: mbd created and binded to socket address at: mbd-launch/mbd.sock
+
+// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
\ No newline at end of file
diff --git a/clang/test/ModuleBuildDaemon/parallel-scan.c b/clang/test/ModuleBuildDaemon/parallel-scan.c
new file mode 100644
index 00000000000000..0dddaa0f161457
--- /dev/null
+++ b/clang/test/ModuleBuildDaemon/parallel-scan.c
@@ -0,0 +1,31 @@
+// Confirm module build daemon can handle two translation units simultaneously
+
+// REQUIRES: !system-windows
+
+// RUN: if pgrep -f "cc1modbuildd parallel-scan"; then pkill -f "cc1modbuildd parallel-scan"; fi
+// : rm -rf parallel-scan
+// RUN: split-file %s %t
+
+//--- main.c
+#include "app.h"
+int main() {return 0;}
+
+//--- app.c
+#include "util.h"
+
+//--- app.h
+
+//--- util.h
+
+// RUN: %clang -fmodule-build-daemon=parallel-scan %t/main.c %t/app.c
+// RUN: pwd && ls
+// RUN: cat parallel-scan/mbd.out
+// RUN: cat parallel-scan/mbd.out | FileCheck %s -DPREFIX=%t
+
+// CHECK: main.c
+// CHECK: app.h
+// CHECK: app.c
+// CHECK: util.h
+
+// RUN: if pgrep -f "cc1modbuildd parallel-scan"; then pkill -f "cc1modbuildd parallel-scan"; fi
+// : rm -rf parallel-scan
diff --git a/clang/test/ModuleBuildDaemon/scan.c b/clang/test/ModuleBuildDaemon/scan.c
new file mode 100644
index 00000000000000..f70210362ab644
--- /dev/null
+++ b/clang/test/ModuleBuildDaemon/scan.c
@@ -0,0 +1,20 @@
+// REQUIRES: !system-windows
+
+// RUN: if pgrep -f "cc1modbuildd scan"; then pkill -f "cc1modbuildd scan"; fi
+// RUN: rm -rf scan
+// RUN: split-file %s %t
+
+//--- main.c
+#include "header.h"
+int main() {return 0;}
+
+//--- header.h
+
+// RUN: %clang -fmodule-build-daemon=scan %t/main.c
+// RUN: cat scan/mbd.out | FileCheck %s -DPREFIX=%t
+
+// CHECK: main.c
+// CHECK: header.h
+
+// RUN: if pgrep -f "cc1modbuildd scan"; then pkill -f "cc1modbuildd scan"; fi
+// RUN: rm -rf scan
diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt
index 2182486f93a555..9f4c7a01f84893 100644
--- a/clang/tools/driver/CMakeLists.txt
+++ b/clang/tools/driver/CMakeLists.txt
@@ -28,6 +28,7 @@ add_clang_tool(clang
cc1_main.cpp
cc1as_main.cpp
cc1gen_reproducer_main.cpp
+ cc1modbuildd_main.cpp
DEPENDS
intrinsics_gen
@@ -39,9 +40,11 @@ clang_target_link_libraries(clang
PRIVATE
clangBasic
clangCodeGen
+ clangDependencyScanning
clangDriver
clangFrontend
clangFrontendTool
+ clangModuleBuildDaemon
clangSerialization
)
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index e9d2c6aad371db..b4b19c7e778226 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -25,6 +25,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/LinkAllPasses.h"
@@ -63,7 +64,7 @@ using namespace llvm::opt;
static void LLVMErrorHandler(void *UserData, const char *Message,
bool GenCrashDiag) {
- DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+ DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine *>(UserData);
Diags.Report(diag::err_fe_error_backend) << Message;
@@ -251,8 +252,69 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
Diags.setSeverity(diag::remark_cc1_round_trip_generated,
diag::Severity::Remark, {});
- bool Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(),
- Argv, Diags, Argv0);
+ std::shared_ptr<CompilerInvocation> Invocation =
+ std::make_shared<CompilerInvocation>();
+ bool Success =
+ CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags, Argv0);
+
+ // FIXME: does not actually flush any diagnostics
+ DiagsBuffer->FlushDiagnostics(Diags);
+ if (!Success) {
+ DiagsBuffer->finish();
+ return 1;
+ }
+
+ // The module build daemon may update the cc1 args and needs someplace to
+ // store a modified command line for the lifetime of the compilation
+ std::vector<std::string> UpdatedArgv;
+ std::vector<const char *> CharUpdatedArgv;
+
+#if LLVM_ON_UNIX
+ // Handle module build daemon functionality if enabled
+ if (Invocation->getFrontendOpts().ModuleBuildDaemon) {
+
+ // Scanner needs cc1 invocations working directory
+ IntrusiveRefCntPtr<vfs::FileSystem> System =
+ createVFSFromCompilerInvocation(*Invocation, Diags);
+ ErrorOr<std::string> MaybeCWD = System->getCurrentWorkingDirectory();
+
+ if (MaybeCWD.getError()) {
+ errs() << "Could not get working directory: "
+ << MaybeCWD.getError().message() << "\n";
+ return 1;
+ }
+
+ Expected<std::vector<std::string>> MaybeUpdatedArgv =
+ cc1modbuildd::updateCC1WithModuleBuildDaemon(*Invocation, Argv, Argv0,
+ *MaybeCWD);
+ if (!MaybeUpdatedArgv) {
+ errs() << toString(std::move(MaybeUpdatedArgv.takeError())) << '\n';
+ return 1;
+ }
+
+ // CompilerInvocation::CreateFromArgs expects an ArrayRef<const char *>
+ UpdatedArgv = std::move(*MaybeUpdatedArgv);
+ CharUpdatedArgv.reserve(UpdatedArgv.size());
+ for (const auto &Arg : UpdatedArgv) {
+ CharUpdatedArgv.push_back(Arg.c_str());
+ }
+ }
+#endif
+
+ outs() << "translation unit command line" << '\n';
+ for (const auto &Arg : Argv)
+ outs() << Arg << " ";
+ outs() << "\n";
+
+ // If Argv was modified by the module build daemon create new Invocation
+ if (!Argv.equals(ArrayRef<const char *>(CharUpdatedArgv)) &&
+ !CharUpdatedArgv.empty()) {
+ Argv = ArrayRef<const char *>(CharUpdatedArgv);
+ Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv,
+ Diags, Argv0);
+ } else {
+ Clang->setInvocation(Invocation);
+ }
if (!Clang->getFrontendOpts().TimeTracePath.empty()) {
llvm::timeTraceProfilerInitialize(
@@ -270,7 +332,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
Clang->getHeaderSearchOpts().ResourceDir =
- CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
+ CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
// Create the actual diagnostics engine.
Clang->createDiagnostics();
@@ -279,8 +341,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// Set an error handler, so that any LLVM backend diagnostics go through our
// error handler.
- llvm::install_fatal_error_handler(LLVMErrorHandler,
- static_cast<void*>(&Clang->getDiagnostics()));
+ llvm::install_fatal_error_handler(
+ LLVMErrorHandler, static_cast<void *>(&Clang->getDiagnostics()));
DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
if (!Success) {
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
new file mode 100644
index 00000000000000..96b95c1c8509bf
--- /dev/null
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -0,0 +1,553 @@
+//===------- cc1modbuildd_main.cpp - Clang CC1 Module Build Daemon --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/DiagnosticCategories.h"
+#include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+
+// TODO: Make portable
+#if LLVM_ON_UNIX
+
+#include <errno.h>
+#include <fstream>
+#include <mutex>
+#include <optional>
+#include <signal.h>
+#include <sstream>
+#include <stdbool.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <type_traits>
+#include <unistd.h>
+#include <unordered_map>
+
+using namespace llvm;
+using namespace clang;
+using namespace tooling::dependencies;
+using namespace cc1modbuildd;
+
+// Create unbuffered STDOUT stream so that any logging done by module build
+// daemon can be viewed without having to terminate the process
+static raw_fd_ostream &unbuff_outs() {
+ static raw_fd_ostream S(STDOUT_FILENO, false, true);
+ return S;
+}
+
+namespace {
+
+enum class BuildStatus { WAITING, BUILDING, BUILT };
+
+struct ModuleIDHash {
+ std::size_t
+ operator()(const clang::tooling::dependencies::ModuleID &ID) const {
+ return llvm::hash_value(ID);
+ }
+};
+
+struct ModuleBuildInfo {
+ const ModuleDeps Info;
+ BuildStatus ModuleBuildStatus;
+};
+
+// Thread safe hash map that stores dependency and build information
+class DependencyBuildData {
+public:
+ void insert(ModuleID Key, ModuleBuildInfo Value) {
+ std::lock_guard<std::mutex> lock(Mutex);
+ HashMap.insert({Key, Value});
+ }
+
+ std::optional<std::reference_wrapper<ModuleBuildInfo>> get(ModuleID Key) {
+ std::lock_guard<std::mutex> lock(Mutex);
+ if (auto search = HashMap.find(Key); search != HashMap.end())
+ return std::ref(search->second);
+ return std::nullopt;
+ }
+
+ bool updateBuildStatus(ModuleID Key, BuildStatus newStatus) {
+ std::lock_guard<std::mutex> lock(Mutex);
+ if (auto search = HashMap.find(Key); search != HashMap.end()) {
+ search->second.ModuleBuildStatus = newStatus;
+ return true;
+ }
+ return false;
+ }
+
+ void print() {
+ unbuff_outs() << "printing hash table keys" << '\n';
+ for (const auto &i : HashMap) {
+ unbuff_outs() << "Module: " << i.first.ModuleName << '\n';
+ unbuff_outs() << "Dependencies: ";
+ for (const auto &Dep : i.second.Info.ClangModuleDeps)
+ unbuff_outs() << Dep.ModuleName << ", ";
+ unbuff_outs() << '\n';
+ }
+ }
+
+private:
+ std::unordered_map<ModuleID, ModuleBuildInfo, ModuleIDHash> HashMap;
+ std::mutex Mutex;
+};
+
+class ModuleBuildDaemonServer {
+public:
+ SmallString<128> BasePath;
+ SmallString<128> SocketPath;
+ SmallString<128> PidPath;
+
+ ModuleBuildDaemonServer(SmallString<128> Path, ArrayRef<const char *> Argv)
+ : BasePath(Path), SocketPath(Path) {
+ llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+ }
+
+ ~ModuleBuildDaemonServer() { shutdownDaemon(SIGTERM); }
+
+ int forkDaemon();
+ int launchDaemon();
+ int listenForClients();
+
+ static void handleClient(int Client);
+ static void handleRegister(int Client, RegisterMsg ClientRequest);
+
+ void shutdownDaemon(int signal) {
+ unlink(SocketPath.c_str());
+ shutdown(ListenSocketFD, SHUT_RD);
+ close(ListenSocketFD);
+ exit(EXIT_SUCCESS);
+ }
+
+private:
+ // Initializes and returns DiagnosticsEngine
+ pid_t Pid = -1;
+ int ListenSocketFD = -1;
+};
+
+// Required to handle SIGTERM by calling Shutdown
+ModuleBuildDaemonServer *DaemonPtr = nullptr;
+void handleSignal(int Signal) {
+ if (DaemonPtr != nullptr) {
+ DaemonPtr->shutdownDaemon(Signal);
+ }
+}
+} // namespace
+
+static bool verbose = false;
+static void verbose_print(const llvm::Twine &message) {
+ if (verbose) {
+ unbuff_outs() << message << '\n';
+ }
+}
+
+static DependencyBuildData DaemonBuildData;
+
+static Expected<TranslationUnitDeps>
+scanTranslationUnit(cc1modbuildd::RegisterMsg Request) {
+
+ DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
+ ScanningOutputFormat::Full,
+ /*OptimizeArgs*/ false,
+ /*EagerLoadModules*/ false);
+
+ DependencyScanningTool Tool(Service);
+ llvm::DenseSet<ModuleID> AlreadySeenModules;
+ auto LookupOutput = [&](const ModuleID &MID, ModuleOutputKind MOK) {
+ return "/tmp/" + MID.ContextHash;
+ };
+
+ // Add executable path to cc1 command line for dependency scanner
+ std::vector<std::string> ScannerCommandLine;
+ ScannerCommandLine.push_back(Request.ExecutablePath.value());
+ ScannerCommandLine.insert(ScannerCommandLine.end(),
+ Request.CC1CommandLine.value().begin(),
+ Request.CC1CommandLine.value().end());
+
+ Expected<TranslationUnitDeps> MaybeTUDeps =
+ Tool.getTranslationUnitDependencies(ScannerCommandLine,
+ Request.WorkingDirectory.value(),
+ AlreadySeenModules, LookupOutput);
+
+ if (!MaybeTUDeps)
+ return std::move(MaybeTUDeps.takeError());
+
+ return std::move(*MaybeTUDeps);
+}
+
+static void storeScanResults(const TranslationUnitDeps &Results) {
+
+ if (Results.ModuleGraph.empty())
+ return;
+
+ // Insert children
+ for (const ModuleDeps &MD : Results.ModuleGraph)
+ DaemonBuildData.insert(MD.ID, {MD, BuildStatus::WAITING});
+}
+
+// Remove -fmodule-build-daemon and add -fno-implicit-modules to command line.
+// Return value can either be a std::vector of std::string or StringRef
+template <typename T>
+static std::vector<T> modifyCC1(const std::vector<std::string> &CommandLine) {
+ static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, StringRef>);
+
+ std::vector<T> ReturnArgs;
+ ReturnArgs.reserve(CommandLine.size());
+
+ for (const auto &Arg : CommandLine) {
+ if (Arg != "-fmodule-build-daemon")
+ ReturnArgs.emplace_back(Arg);
+ }
+ ReturnArgs.emplace_back("-fno-implicit-modules");
+ return ReturnArgs;
+}
+
+// TODO: Return llvm::Error
+static void precompileModuleID(const StringRef Executable, const ModuleID ID) {
+ unbuff_outs() << "module " << ID.ModuleName << " will be built" << '\n';
+
+ std::optional<std::reference_wrapper<ModuleBuildInfo>> MaybeDeps =
+ DaemonBuildData.get(ID);
+ if (!MaybeDeps)
+ return;
+ ModuleBuildInfo &Deps = MaybeDeps->get();
+
+ // TODO: look into making getBuildArguments a const method
+ ModuleDeps &NonConstDepsInfo = const_cast<ModuleDeps &>(Deps.Info);
+ const std::vector<std::string> &Args = NonConstDepsInfo.getBuildArguments();
+ std::vector<std::string> NonConstArgs =
+ const_cast<std::vector<std::string> &>(Args);
+
+ unbuff_outs() << "original command line" << '\n';
+ for (const auto &Arg : Args)
+ unbuff_outs() << Arg << " ";
+ unbuff_outs() << "\n\n";
+
+ const std::vector<StringRef> ProcessedArgs =
+ modifyCC1<StringRef>(NonConstArgs);
+
+ std::vector<StringRef> ExecuteCommandLine;
+ ExecuteCommandLine.push_back(Executable);
+ ExecuteCommandLine.insert(ExecuteCommandLine.end(), ProcessedArgs.begin(),
+ ProcessedArgs.end());
+
+ unbuff_outs() << "new command line" << '\n';
+ for (const auto &Arg : NonConstArgs)
+ unbuff_outs() << Arg << " ";
+ unbuff_outs() << "\n";
+
+ // TODO: Handle error code returned from ExecuteAndWait
+ llvm::sys::ExecuteAndWait(Executable,
+ ArrayRef<StringRef>(ExecuteCommandLine));
+ DaemonBuildData.updateBuildStatus(ID, BuildStatus::BUILT);
+
+ unbuff_outs() << "module " << ID.ModuleName << " finished building" << '\n';
+ unbuff_outs() << "\n\n";
+
+ return;
+}
+
+// TODO: implement concurrent approach
+// can only handle one translation unit at a time
+static void buildModuleID(const StringRef Executable, const ModuleID ID) {
+
+ std::optional<std::reference_wrapper<ModuleBuildInfo>> MaybeDeps =
+ DaemonBuildData.get(ID);
+ if (!MaybeDeps)
+ return;
+ ModuleBuildInfo &Deps = MaybeDeps->get();
+
+ if (Deps.ModuleBuildStatus == BuildStatus::BUILT)
+ return;
+
+ for (const ModuleID &Dep : Deps.Info.ClangModuleDeps)
+ buildModuleID(Executable, Dep);
+
+ // Do not build the root ID aka the registered translation unit
+ if (ID.ModuleName.empty())
+ return;
+
+ precompileModuleID(Executable, ID);
+ return;
+}
+
+// Takes a client request in the form of a cc1modbuildd::SocketMsg and
+// returns an updated cc1 command line for the registered cc1 invocation
+// after building all modular dependencies
+static Expected<std::vector<std::string>>
+processRegisterRequest(cc1modbuildd::RegisterMsg Request) {
+
+ Expected<TranslationUnitDeps> MaybeTUDeps = scanTranslationUnit(Request);
+ if (!MaybeTUDeps)
+ return std::move(MaybeTUDeps.takeError());
+ const TranslationUnitDeps TUDeps = std::move(*MaybeTUDeps);
+
+ // For now write dependencies to log file
+ for (const auto &Dep : TUDeps.FileDeps)
+ unbuff_outs() << Dep << '\n';
+
+ // If TU does not depend on modules then return command line as is
+ if (TUDeps.ModuleGraph.empty())
+ return Request.CC1CommandLine.value();
+
+ unbuff_outs() << "modules detected" << '\n';
+ storeScanResults(TUDeps);
+ DaemonBuildData.print();
+
+ // Build all direct and transitive dependencies by iterating over direct
+ // dependencies
+ for (const ModuleID &Dep : TUDeps.ClangModuleDeps)
+ buildModuleID(Request.ExecutablePath.value(), Dep);
+
+ return modifyCC1<std::string>(TUDeps.Commands[0].Arguments);
+}
+
+// Forks and detaches process, creating module build daemon
+int ModuleBuildDaemonServer::forkDaemon() {
+
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ Pid = getpid();
+
+ // close(STDIN_FILENO);
+ // close(STDOUT_FILENO);
+ // close(STDERR_FILENO);
+
+ // SmallString<128> STDOUT = BasePath;
+ // llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
+ // freopen(STDOUT.c_str(), "a", stdout);
+
+ // SmallString<128> STDERR = BasePath;
+ // llvm::sys::path::append(STDERR, STDERR_FILE_NAME);
+ // freopen(STDERR.c_str(), "a", stderr);
+
+ if (signal(SIGTERM, handleSignal) == SIG_ERR) {
+ errs() << "failed to handle SIGTERM" << '\n';
+ exit(EXIT_FAILURE);
+ }
+ if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
+ errs() << "failed to ignore SIGHUP" << '\n';
+ exit(EXIT_FAILURE);
+ }
+ if (setsid() == -1) {
+ errs() << "setsid failed" << '\n';
+ exit(EXIT_FAILURE);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+// Creates unix socket for IPC with module build daemon
+int ModuleBuildDaemonServer::launchDaemon() {
+
+ // new socket
+ if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ std::perror("Socket create error: ");
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1);
+
+ // bind to local address
+ if (bind(ListenSocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+
+ // If the socket address is already in use, exit because another module
+ // build daemon has successfully launched. When translation units are
+ // compiled in parallel, until the socket file is created, all clang
+ // invocations will spawn a module build daemon.
+ if (errno == EADDRINUSE) {
+ close(ListenSocketFD);
+ exit(EXIT_SUCCESS);
+ }
+ std::perror("Socket bind error: ");
+ exit(EXIT_FAILURE);
+ }
+ verbose_print("mbd created and binded to socket address at: " + SocketPath);
+
+ // set socket to accept incoming connection request
+ unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
+ if (listen(ListenSocketFD, MaxBacklog) == -1) {
+ std::perror("Socket listen error: ");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+void ModuleBuildDaemonServer::handleRegister(int Client,
+ RegisterMsg ClientRequest) {
+
+ Expected<std::vector<std::string>> MaybeExplicitCC1 =
+ processRegisterRequest(ClientRequest);
+
+ // if getUpdatedCC1 fails emit error
+ if (!MaybeExplicitCC1) {
+
+ RegisterMsg Msg(ActionType::REGISTER, StatusType::FAILURE);
+ llvm::Error RegisterFailureWriteErr = writeSocketMsgToSocket(Msg, Client);
+
+ if (RegisterFailureWriteErr) {
+ writeError(llvm::joinErrors(std::move(RegisterFailureWriteErr),
+ std::move(MaybeExplicitCC1.takeError())),
+ "Failed to process register request and was unable to notify "
+ "clang infocation: ");
+ return;
+ }
+ writeError(MaybeExplicitCC1.takeError(),
+ "Failed to process register request: ");
+ return;
+ }
+
+ // getUpdateCC1 success
+ std::vector<std::string> ExplicitCC1 = std::move(*MaybeExplicitCC1);
+
+ unbuff_outs() << "modified command line for translation unit" << '\n';
+ for (const auto &Arg : ExplicitCC1)
+ unbuff_outs() << Arg << " ";
+ unbuff_outs() << "\n";
+
+ // Send new CC1 command line to waiting clang invocation
+ RegisterMsg Msg(ActionType::REGISTER, StatusType::SUCCESS,
+ ClientRequest.WorkingDirectory, ClientRequest.ExecutablePath,
+ ExplicitCC1);
+
+ llvm::Error RegisterSuccessWriteErr = writeSocketMsgToSocket(Msg, Client);
+
+ if (RegisterSuccessWriteErr) {
+ writeError(std::move(RegisterSuccessWriteErr),
+ "Failed to notify clang invocation that register request was a "
+ "success: ");
+ return;
+ }
+ return;
+}
+
+// Function submitted to thread pool with each client connection. Not
+// responsible for closing client connections
+void ModuleBuildDaemonServer::handleClient(int Client) {
+
+ // Read handshake from client
+ Expected<HandshakeMsg> MaybeHandshake =
+ readSocketMsgFromSocket<HandshakeMsg>(Client);
+
+ if (!MaybeHandshake) {
+ writeError(MaybeHandshake.takeError(),
+ "Failed to read handshake message from socket: ");
+ return;
+ }
+
+ // Handle HANDSHAKE
+ RegisterMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
+ llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Client);
+
+ if (WriteErr) {
+ writeError(std::move(WriteErr),
+ "Failed to notify client that handshake was received");
+ return;
+ }
+
+ // Read register request from client
+ Expected<RegisterMsg> MaybeRegister =
+ readSocketMsgFromSocket<RegisterMsg>(Client);
+
+ if (!MaybeRegister) {
+ writeError(MaybeRegister.takeError(),
+ "Failed to read registration message from socket: ");
+ return;
+ }
+
+ RegisterMsg Register = std::move(*MaybeRegister);
+ handleRegister(Client, Register);
+ return;
+}
+
+int ModuleBuildDaemonServer::listenForClients() {
+
+ llvm::ThreadPool Pool;
+ int Client;
+
+ while (true) {
+
+ if ((Client = accept(ListenSocketFD, NULL, NULL)) == -1) {
+ std::perror("Socket accept error: ");
+ continue;
+ }
+
+ Pool.async(handleClient, Client);
+ }
+ return 0;
+}
+
+// Module build daemon is spawned with the following command line:
+//
+// clang -cc1modbuildd <path> -v
+//
+// <path> defines the location of all files created by the module build daemon
+// and should follow the format /path/to/dir. For example, `clang
+// -cc1modbuildd /tmp/` creates a socket file at `/tmp/mbd.sock`. /tmp is also
+// valid.
+//
+// When module build daemons are spawned by cc1 invocations, <path> follows
+// the format /tmp/clang-<BLAKE3HashOfClangFullVersion>
+//
+// -v is optional and provides debug information
+//
+int cc1modbuildd_main(ArrayRef<const char *> Argv) {
+
+ if (Argv.size() < 1) {
+ outs() << "spawning a module build daemon requies a command line format of "
+ "`clang -cc1modbuildd <path>`. <path> defines where the module "
+ "build daemon will create files"
+ << '\n';
+ return 1;
+ }
+
+ // Where to store log files and socket address
+ // TODO: Add check to confirm BasePath is directory
+ SmallString<128> BasePath(Argv[0]);
+ llvm::sys::fs::create_directories(BasePath);
+ ModuleBuildDaemonServer Daemon(BasePath, Argv);
+
+ // Used to handle signals
+ DaemonPtr = &Daemon;
+
+ if (find(Argv, StringRef("-v")) != Argv.end())
+ verbose = true;
+
+ Daemon.forkDaemon();
+ Daemon.launchDaemon();
+ Daemon.listenForClients();
+
+ return 0;
+}
+
+#endif // LLVM_ON_UNIX
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 531b5b4a61c180..9697bc6d457776 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -213,6 +213,9 @@ extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
const char *Argv0, void *MainAddr,
const llvm::ToolContext &);
+#if LLVM_ON_UNIX
+extern int cc1modbuildd_main(ArrayRef<const char *> Argv);
+#endif
static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
@@ -369,9 +372,19 @@ static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV,
if (Tool == "-cc1gen-reproducer")
return cc1gen_reproducer_main(ArrayRef(ArgV).slice(2), ArgV[0],
GetExecutablePathVP, ToolContext);
- // Reject unknown tools.
+ if (Tool == "-cc1modbuildd") {
+#if LLVM_ON_UNIX
+ return cc1modbuildd_main(ArrayRef(ArgV).slice(2));
+#else
+ llvm::errs() << "-cc1modbuildd not supported by current platform" << '\n';
+ return 1;
+#endif
+ }
+
+ // Reject unknown tools
llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
- << "Valid tools include '-cc1' and '-cc1as'.\n";
+ << "Valid tools include '-cc1', '-cc1as', '-cc1gen-reproducer', "
+ "and '-cc1modbuildd'.\n";
return 1;
}
>From 749d0489b403686b225dbaa39af5ba4f2626ff50 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Sat, 7 Oct 2023 16:54:25 -0400
Subject: [PATCH 2/6] temporarily remove tests. if a test fails daemon will
never terminate
---
clang/test/ModuleBuildDaemon/launch.c | 10 -------
clang/test/ModuleBuildDaemon/parallel-scan.c | 31 --------------------
clang/test/ModuleBuildDaemon/scan.c | 20 -------------
3 files changed, 61 deletions(-)
delete mode 100644 clang/test/ModuleBuildDaemon/launch.c
delete mode 100644 clang/test/ModuleBuildDaemon/parallel-scan.c
delete mode 100644 clang/test/ModuleBuildDaemon/scan.c
diff --git a/clang/test/ModuleBuildDaemon/launch.c b/clang/test/ModuleBuildDaemon/launch.c
deleted file mode 100644
index 575aba0ac3ce60..00000000000000
--- a/clang/test/ModuleBuildDaemon/launch.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// REQUIRES: !system-windows
-
-// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
-
-// RUN: %clang -cc1modbuildd mbd-launch -v
-// RUN: cat mbd-launch/mbd.out | FileCheck %s -DPREFIX=%t
-
-// CHECK: mbd created and binded to socket address at: mbd-launch/mbd.sock
-
-// RUN: if pgrep -f "cc1modbuildd mbd-launch"; then pkill -f "cc1modbuildd mbd-launch"; fi
\ No newline at end of file
diff --git a/clang/test/ModuleBuildDaemon/parallel-scan.c b/clang/test/ModuleBuildDaemon/parallel-scan.c
deleted file mode 100644
index 0dddaa0f161457..00000000000000
--- a/clang/test/ModuleBuildDaemon/parallel-scan.c
+++ /dev/null
@@ -1,31 +0,0 @@
-// Confirm module build daemon can handle two translation units simultaneously
-
-// REQUIRES: !system-windows
-
-// RUN: if pgrep -f "cc1modbuildd parallel-scan"; then pkill -f "cc1modbuildd parallel-scan"; fi
-// : rm -rf parallel-scan
-// RUN: split-file %s %t
-
-//--- main.c
-#include "app.h"
-int main() {return 0;}
-
-//--- app.c
-#include "util.h"
-
-//--- app.h
-
-//--- util.h
-
-// RUN: %clang -fmodule-build-daemon=parallel-scan %t/main.c %t/app.c
-// RUN: pwd && ls
-// RUN: cat parallel-scan/mbd.out
-// RUN: cat parallel-scan/mbd.out | FileCheck %s -DPREFIX=%t
-
-// CHECK: main.c
-// CHECK: app.h
-// CHECK: app.c
-// CHECK: util.h
-
-// RUN: if pgrep -f "cc1modbuildd parallel-scan"; then pkill -f "cc1modbuildd parallel-scan"; fi
-// : rm -rf parallel-scan
diff --git a/clang/test/ModuleBuildDaemon/scan.c b/clang/test/ModuleBuildDaemon/scan.c
deleted file mode 100644
index f70210362ab644..00000000000000
--- a/clang/test/ModuleBuildDaemon/scan.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// REQUIRES: !system-windows
-
-// RUN: if pgrep -f "cc1modbuildd scan"; then pkill -f "cc1modbuildd scan"; fi
-// RUN: rm -rf scan
-// RUN: split-file %s %t
-
-//--- main.c
-#include "header.h"
-int main() {return 0;}
-
-//--- header.h
-
-// RUN: %clang -fmodule-build-daemon=scan %t/main.c
-// RUN: cat scan/mbd.out | FileCheck %s -DPREFIX=%t
-
-// CHECK: main.c
-// CHECK: header.h
-
-// RUN: if pgrep -f "cc1modbuildd scan"; then pkill -f "cc1modbuildd scan"; fi
-// RUN: rm -rf scan
>From e5c4868c5c4647ed330aed1234dea4f4abdc55c5 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 15 Aug 2024 08:49:18 -0400
Subject: [PATCH 3/6] fix formatting
---
clang/include/clang/Frontend/FrontendOptions.h | 2 +-
clang/tools/driver/cc1_main.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 5821e121bfd76c..d928e11a973347 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -397,7 +397,7 @@ class FrontendOptions {
/// Connect to module build daemon
unsigned ModuleBuildDaemon : 1;
-
+
/// Whether to emit symbol graph files as a side effect of compilation.
LLVM_PREFERRED_TYPE(bool)
unsigned EmitSymbolGraph : 1;
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index f08958f93e74ee..6547d74a31fcd3 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -25,8 +25,8 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
+#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/llvm-config.h"
>From 579a69c5e4faa92d5fdd7e31547095026bce90f6 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 15 Aug 2024 10:19:09 -0400
Subject: [PATCH 4/6] Changes to get clang to compile
---
.../clang/Tooling/ModuleBuildDaemon/Client.h | 22 ++++++++---------
.../ModuleBuildDaemon/SocketMsgSupport.h | 17 ++++++-------
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 11 ++++-----
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 2 +-
.../ModuleBuildDaemon/SocketSupport.cpp | 24 +++++++++----------
clang/tools/driver/cc1_main.cpp | 14 +++++------
clang/tools/driver/cc1modbuildd_main.cpp | 6 ++---
7 files changed, 43 insertions(+), 53 deletions(-)
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index 5d5df74743a6a6..f8bd93267c9126 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -15,15 +15,13 @@
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/Error.h"
#define MAX_BUFFER 4096
#define SOCKET_FILE_NAME "mbd.sock"
#define STDOUT_FILE_NAME "mbd.out"
#define STDERR_FILE_NAME "mbd.err"
-using namespace clang;
-using namespace llvm;
-
namespace cc1modbuildd {
// Returns where to store log files and socket address. Of the format
@@ -32,24 +30,24 @@ std::string getBasePath();
llvm::Error attemptHandshake(int SocketFD);
-llvm::Error spawnModuleBuildDaemon(StringRef BasePath, const char *Argv0);
+llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0);
-Expected<int> getModuleBuildDaemon(const char *Argv0, StringRef BasePath);
+llvm::Expected<int> getModuleBuildDaemon(const char *Argv0, llvm::StringRef BasePath);
// Sends request to module build daemon
-llvm::Error registerTranslationUnit(ArrayRef<const char *> CC1Cmd,
- StringRef Argv0, StringRef CWD,
+llvm::Error registerTranslationUnit(llvm::ArrayRef<const char *> CC1Cmd,
+ llvm::StringRef Argv0, llvm::StringRef CWD,
int ServerFD);
// Processes response from module build daemon
-Expected<std::vector<std::string>> getUpdatedCC1(int ServerFD);
+llvm::Expected<std::vector<std::string>> getUpdatedCC1(int ServerFD);
// Work in progress. Eventually function will modify CC1 command line to include
// path to modules already built by the daemon
-Expected<std::vector<std::string>>
-updateCC1WithModuleBuildDaemon(const CompilerInvocation &Clang,
- ArrayRef<const char *> CC1Cmd, const char *Argv0,
- StringRef CWD);
+llvm::Expected<std::vector<std::string>>
+updateCC1WithModuleBuildDaemon(const clang::CompilerInvocation &Clang,
+ llvm::ArrayRef<const char *> CC1Cmd, const char *Argv0,
+ llvm::StringRef CWD);
} // namespace cc1modbuildd
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index c02a426054f117..482fed3830a1ee 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -12,9 +12,6 @@
#include "clang/Tooling/ModuleBuildDaemon/Client.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-using namespace clang;
-using namespace llvm;
-
namespace cc1modbuildd {
enum class ActionType { REGISTER, HANDSHAKE };
@@ -68,7 +65,7 @@ template <typename T> std::string getBufferFromSocketMsg(T Msg) {
return Buffer;
}
-template <typename T> Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
+template <typename T> llvm::Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -78,22 +75,22 @@ template <typename T> Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
if (YamlIn.error()) {
std::string Msg = "Syntax or semantic error during YAML parsing";
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
}
return ClientRequest;
}
-template <typename T> Expected<T> readSocketMsgFromSocket(int FD) {
+template <typename T> llvm::Expected<T> readSocketMsgFromSocket(int FD) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
- Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
+ llvm::Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
if (!MaybeResponseBuffer)
return std::move(MaybeResponseBuffer.takeError());
// Wait for response from module build daemon
- Expected<T> MaybeResponse =
+ llvm::Expected<T> MaybeResponse =
getSocketMsgFromBuffer<T>(std::move(*MaybeResponseBuffer).c_str());
if (!MaybeResponse)
return std::move(MaybeResponse.takeError());
@@ -112,11 +109,11 @@ template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
}
template <typename T>
-Expected<int> connectAndWriteSocketMsgToSocket(T Msg, StringRef SocketPath) {
+llvm::Expected<int> connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
- Expected<int> MaybeFD = connectToSocket(SocketPath);
+ llvm::Expected<int> MaybeFD = connectToSocket(SocketPath);
if (!MaybeFD)
return std::move(MaybeFD.takeError());
int FD = std::move(*MaybeFD);
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index bc21084faab396..645ea1d8db11e2 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -15,15 +15,12 @@
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-using namespace clang;
-using namespace llvm;
-
namespace cc1modbuildd {
-Expected<int> createSocket();
-Expected<int> connectToSocket(StringRef SocketPath);
-Expected<int> connectAndWriteToSocket(std::string Buffer, StringRef SocketPath);
-Expected<std::string> readFromSocket(int FD);
+llvm::Expected<int> createSocket();
+llvm::Expected<int> connectToSocket(llvm::StringRef SocketPath);
+llvm::Expected<int> connectAndWriteToSocket(std::string Buffer, llvm::StringRef SocketPath);
+llvm::Expected<std::string> readFromSocket(int FD);
llvm::Error writeToSocket(std::string Buffer, int WriteFD);
} // namespace cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
index 08fe14476c901b..4018d70a30ea5e 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
@@ -42,7 +42,7 @@ std::string cc1modbuildd::getBasePath() {
Hash.update(getClangFullVersion());
auto HashResult = Hash.final<sizeof(uint64_t)>();
uint64_t HashValue =
- llvm::support::endian::read<uint64_t, llvm::support::native>(
+ llvm::support::endian::read<uint64_t, llvm::endianness::native>(
HashResult.data());
std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index 58526e4422f457..bfecf18d9e86ea 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -31,18 +31,18 @@
#include <sys/un.h>
#include <unistd.h>
-Expected<int> cc1modbuildd::createSocket() {
+llvm::Expected<int> cc1modbuildd::createSocket() {
int FD;
if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
std::string Msg = "socket create error: " + std::string(strerror(errno));
- return createStringError(inconvertibleErrorCode(), Msg);
+ return llvm::createStringError(llvm::inconvertibleErrorCode(), Msg);
}
return FD;
}
-Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
+llvm::Expected<int> cc1modbuildd::connectToSocket(llvm::StringRef SocketPath) {
- Expected<int> MaybeFD = cc1modbuildd::createSocket();
+ llvm::Expected<int> MaybeFD = cc1modbuildd::createSocket();
if (!MaybeFD)
return std::move(MaybeFD.takeError());
@@ -56,15 +56,15 @@ Expected<int> cc1modbuildd::connectToSocket(StringRef SocketPath) {
if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
close(FD);
std::string msg = "socket connect error: " + std::string(strerror(errno));
- return createStringError(inconvertibleErrorCode(), msg);
+ return llvm::createStringError(llvm::inconvertibleErrorCode(), msg);
}
return FD;
}
-Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
- StringRef SocketPath) {
+llvm::Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
+ llvm::StringRef SocketPath) {
- Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
+ llvm::Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
if (!MaybeConnectedFD)
return std::move(MaybeConnectedFD.takeError());
@@ -76,7 +76,7 @@ Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
return ConnectedFD;
}
-Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
+llvm::Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
const size_t BUFFER_SIZE = 4096;
std::vector<char> Buffer(BUFFER_SIZE);
@@ -96,10 +96,10 @@ Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
if (n < 0) {
std::string Msg = "socket read error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
}
if (n == 0)
- return llvm::make_error<StringError>("EOF", inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>("EOF", llvm::inconvertibleErrorCode());
return std::string(Buffer.begin(), Buffer.end());
}
@@ -112,7 +112,7 @@ llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
if (BytesWritten == -1) {
std::string Msg = "socket write error: " + std::string(strerror(errno));
- return llvm::make_error<StringError>(Msg, inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
}
if (!BytesWritten || BytesWritten > BytesToWrite)
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 6547d74a31fcd3..b8d3ebf239cbef 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -260,12 +260,12 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Invocation->getFrontendOpts().ModuleBuildDaemon) {
// Scanner needs cc1 invocations working directory
- IntrusiveRefCntPtr<vfs::FileSystem> System =
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> System =
createVFSFromCompilerInvocation(*Invocation, Diags);
- ErrorOr<std::string> MaybeCWD = System->getCurrentWorkingDirectory();
+ llvm::ErrorOr<std::string> MaybeCWD = System->getCurrentWorkingDirectory();
if (MaybeCWD.getError()) {
- errs() << "Could not get working directory: "
+ llvm::errs() << "Could not get working directory: "
<< MaybeCWD.getError().message() << "\n";
return 1;
}
@@ -274,7 +274,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
cc1modbuildd::updateCC1WithModuleBuildDaemon(*Invocation, Argv, Argv0,
*MaybeCWD);
if (!MaybeUpdatedArgv) {
- errs() << toString(std::move(MaybeUpdatedArgv.takeError())) << '\n';
+ llvm::errs() << toString(std::move(MaybeUpdatedArgv.takeError())) << '\n';
return 1;
}
@@ -287,10 +287,10 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
#endif
- outs() << "translation unit command line" << '\n';
+ llvm::outs() << "translation unit command line" << '\n';
for (const auto &Arg : Argv)
- outs() << Arg << " ";
- outs() << "\n";
+ llvm::outs() << Arg << " ";
+ llvm::outs() << "\n";
// If Argv was modified by the module build daemon create new Invocation
if (!Argv.equals(ArrayRef<const char *>(CharUpdatedArgv)) &&
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index 96b95c1c8509bf..df30a3b8787f68 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -166,9 +166,7 @@ static Expected<TranslationUnitDeps>
scanTranslationUnit(cc1modbuildd::RegisterMsg Request) {
DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
- ScanningOutputFormat::Full,
- /*OptimizeArgs*/ false,
- /*EagerLoadModules*/ false);
+ ScanningOutputFormat::Full);
DependencyScanningTool Tool(Service);
llvm::DenseSet<ModuleID> AlreadySeenModules;
@@ -492,7 +490,7 @@ void ModuleBuildDaemonServer::handleClient(int Client) {
int ModuleBuildDaemonServer::listenForClients() {
- llvm::ThreadPool Pool;
+ llvm::DefaultThreadPool Pool;
int Client;
while (true) {
>From b0f4d9cf8bfa19bb981d52e85ce960768ff1e839 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 15 Aug 2024 10:29:32 -0400
Subject: [PATCH 5/6] formatting fixes
---
.../clang/Tooling/ModuleBuildDaemon/Client.h | 9 +++++----
.../Tooling/ModuleBuildDaemon/SocketMsgSupport.h | 9 ++++++---
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 3 ++-
.../Tooling/ModuleBuildDaemon/SocketSupport.cpp | 14 +++++++++-----
clang/tools/driver/cc1_main.cpp | 2 +-
5 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
index f8bd93267c9126..c17f46cfe25104 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
@@ -13,9 +13,9 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
-#include "llvm/Support/Error.h"
#define MAX_BUFFER 4096
#define SOCKET_FILE_NAME "mbd.sock"
@@ -32,7 +32,8 @@ llvm::Error attemptHandshake(int SocketFD);
llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0);
-llvm::Expected<int> getModuleBuildDaemon(const char *Argv0, llvm::StringRef BasePath);
+llvm::Expected<int> getModuleBuildDaemon(const char *Argv0,
+ llvm::StringRef BasePath);
// Sends request to module build daemon
llvm::Error registerTranslationUnit(llvm::ArrayRef<const char *> CC1Cmd,
@@ -46,8 +47,8 @@ llvm::Expected<std::vector<std::string>> getUpdatedCC1(int ServerFD);
// path to modules already built by the daemon
llvm::Expected<std::vector<std::string>>
updateCC1WithModuleBuildDaemon(const clang::CompilerInvocation &Clang,
- llvm::ArrayRef<const char *> CC1Cmd, const char *Argv0,
- llvm::StringRef CWD);
+ llvm::ArrayRef<const char *> CC1Cmd,
+ const char *Argv0, llvm::StringRef CWD);
} // namespace cc1modbuildd
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
index 482fed3830a1ee..2408a852e995fe 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
@@ -65,7 +65,8 @@ template <typename T> std::string getBufferFromSocketMsg(T Msg) {
return Buffer;
}
-template <typename T> llvm::Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
+template <typename T>
+llvm::Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
@@ -75,7 +76,8 @@ template <typename T> llvm::Expected<T> getSocketMsgFromBuffer(const char *Buffe
if (YamlIn.error()) {
std::string Msg = "Syntax or semantic error during YAML parsing";
- return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg,
+ llvm::inconvertibleErrorCode());
}
return ClientRequest;
@@ -109,7 +111,8 @@ template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
}
template <typename T>
-llvm::Expected<int> connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
+llvm::Expected<int>
+connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
"T must inherit from cc1modbuildd::BaseMsg");
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index 645ea1d8db11e2..3dae1a00779da0 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -19,7 +19,8 @@ namespace cc1modbuildd {
llvm::Expected<int> createSocket();
llvm::Expected<int> connectToSocket(llvm::StringRef SocketPath);
-llvm::Expected<int> connectAndWriteToSocket(std::string Buffer, llvm::StringRef SocketPath);
+llvm::Expected<int> connectAndWriteToSocket(std::string Buffer,
+ llvm::StringRef SocketPath);
llvm::Expected<std::string> readFromSocket(int FD);
llvm::Error writeToSocket(std::string Buffer, int WriteFD);
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index bfecf18d9e86ea..8a25b8f0bfd9df 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -61,8 +61,9 @@ llvm::Expected<int> cc1modbuildd::connectToSocket(llvm::StringRef SocketPath) {
return FD;
}
-llvm::Expected<int> cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
- llvm::StringRef SocketPath) {
+llvm::Expected<int>
+cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
+ llvm::StringRef SocketPath) {
llvm::Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
if (!MaybeConnectedFD)
@@ -96,10 +97,12 @@ llvm::Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
if (n < 0) {
std::string Msg = "socket read error: " + std::string(strerror(errno));
- return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(Msg,
+ llvm::inconvertibleErrorCode());
}
if (n == 0)
- return llvm::make_error<llvm::StringError>("EOF", llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>("EOF",
+ llvm::inconvertibleErrorCode());
return std::string(Buffer.begin(), Buffer.end());
}
@@ -112,7 +115,8 @@ llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
if (BytesWritten == -1) {
std::string Msg = "socket write error: " + std::string(strerror(errno));
- return llvm::make_error<llvm::StringError>(Msg, llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(
+ Msg, llvm::inconvertibleErrorCode());
}
if (!BytesWritten || BytesWritten > BytesToWrite)
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index b8d3ebf239cbef..3573d6f7e43b27 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -266,7 +266,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (MaybeCWD.getError()) {
llvm::errs() << "Could not get working directory: "
- << MaybeCWD.getError().message() << "\n";
+ << MaybeCWD.getError().message() << "\n";
return 1;
}
>From 9f1a11ad20d82983488252891e4288ebf4dd35b2 Mon Sep 17 00:00:00 2001
From: cpsughrue <cpsughrue at gmail.com>
Date: Thu, 15 Aug 2024 14:37:27 -0400
Subject: [PATCH 6/6] Incorporate raw_socket_stream work
---
.../clang/Basic/DiagnosticFrontendKinds.td | 21 +
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Tooling/ModuleBuildDaemon/Client.h | 55 ---
.../Tooling/ModuleBuildDaemon/Frontend.h | 50 +++
.../ModuleBuildDaemon/SocketMsgSupport.h | 166 --------
.../Tooling/ModuleBuildDaemon/SocketSupport.h | 155 ++++++-
.../clang/Tooling/ModuleBuildDaemon/Utils.h | 39 +-
.../Tooling/ModuleBuildDaemon/CMakeLists.txt | 2 +-
.../lib/Tooling/ModuleBuildDaemon/Client.cpp | 224 ----------
.../Tooling/ModuleBuildDaemon/Frontend.cpp | 222 ++++++++++
.../ModuleBuildDaemon/SocketSupport.cpp | 138 ++-----
clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp | 48 ++-
clang/tools/driver/cc1_main.cpp | 8 +-
clang/tools/driver/cc1modbuildd_main.cpp | 381 ++++++++++--------
clang/tools/driver/driver.cpp | 2 -
15 files changed, 755 insertions(+), 757 deletions(-)
delete mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
create mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
delete mode 100644 clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
delete mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
create mode 100644 clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 8a1462c670d68f..293b19227a521e 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -267,6 +267,27 @@ def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
+// Module Build Daemon
+def err_path_length :
+ Error<"path '%0' is longer then the max length of %1">,
+ DefaultFatal;
+def err_mbd_handshake :
+ Error<"attempt to handshake with module build daemon has failed: %0">,
+ DefaultFatal;
+def err_mbd_connect :
+ Error<"attempt to connect to module build daemon has failed: %0">,
+ DefaultFatal;
+def remark_mbd_spawn :
+ Remark<"successfully spawned module build daemon">,
+ InGroup<ModuleBuildDaemon>;
+def remark_mbd_connection :
+ Remark<"successfully connected to module build daemon at %0">,
+ InGroup<ModuleBuildDaemon>;
+def remark_mbd_handshake :
+ Remark<"clang invocation responsible for %0 successfully completed handshake "
+ "with module build daemon">,
+ InGroup<ModuleBuildDaemon>;
+
def warn_eagerly_load_for_standard_cplusplus_modules : Warning<
"the form '-fmodule-file=<BMI-path>' is deprecated for standard C++ named modules;"
"consider to use '-fmodule-file=<module-name>=<BMI-path>' instead">,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 19c3f1e0433496..427540fe12443c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -552,6 +552,7 @@ def MissingFieldInitializers : DiagGroup<"missing-field-initializers",
[MissingDesignatedFieldInitializers]>;
def ModuleLock : DiagGroup<"module-lock">;
def ModuleBuild : DiagGroup<"module-build">;
+def ModuleBuildDaemon : DiagGroup<"module-build-daemon">;
def ModuleImport : DiagGroup<"module-import">;
def ModuleConflict : DiagGroup<"module-conflict">;
def ModuleFileExtension : DiagGroup<"module-file-extension">;
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
deleted file mode 100644
index c17f46cfe25104..00000000000000
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Client.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------- Protocol.h -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
-#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
-
-#include "clang/Frontend/CompilerInstance.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/YAMLParser.h"
-#include "llvm/Support/YAMLTraits.h"
-
-#define MAX_BUFFER 4096
-#define SOCKET_FILE_NAME "mbd.sock"
-#define STDOUT_FILE_NAME "mbd.out"
-#define STDERR_FILE_NAME "mbd.err"
-
-namespace cc1modbuildd {
-
-// Returns where to store log files and socket address. Of the format
-// /tmp/clang-<BLAKE3HashOfClagnFullVersion>/
-std::string getBasePath();
-
-llvm::Error attemptHandshake(int SocketFD);
-
-llvm::Error spawnModuleBuildDaemon(llvm::StringRef BasePath, const char *Argv0);
-
-llvm::Expected<int> getModuleBuildDaemon(const char *Argv0,
- llvm::StringRef BasePath);
-
-// Sends request to module build daemon
-llvm::Error registerTranslationUnit(llvm::ArrayRef<const char *> CC1Cmd,
- llvm::StringRef Argv0, llvm::StringRef CWD,
- int ServerFD);
-
-// Processes response from module build daemon
-llvm::Expected<std::vector<std::string>> getUpdatedCC1(int ServerFD);
-
-// Work in progress. Eventually function will modify CC1 command line to include
-// path to modules already built by the daemon
-llvm::Expected<std::vector<std::string>>
-updateCC1WithModuleBuildDaemon(const clang::CompilerInvocation &Clang,
- llvm::ArrayRef<const char *> CC1Cmd,
- const char *Argv0, llvm::StringRef CWD);
-
-} // namespace cc1modbuildd
-
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_PROTOCAL_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
new file mode 100644
index 00000000000000..c885473a51dfc9
--- /dev/null
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
@@ -0,0 +1,50 @@
+//===----------------------------- Frontend.h -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_FRONTEND_H
+#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_FRONTEND_H
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_socket_stream.h"
+
+namespace clang::tooling::cc1modbuildd {
+
+llvm::Error attemptHandshake(llvm::raw_socket_stream &Client);
+
+llvm::Error spawnModuleBuildDaemon(const clang::CompilerInvocation &Clang,
+ const char *Argv0,
+ clang::DiagnosticsEngine &Diag,
+ std::string BasePath);
+
+llvm::Expected<std::unique_ptr<llvm::raw_socket_stream>>
+getModuleBuildDaemon(const clang::CompilerInvocation &Clang, const char *Argv0,
+ clang::DiagnosticsEngine &Diag, llvm::StringRef BasePath);
+
+// Sends request to module build daemon
+llvm::Error registerTranslationUnit(llvm::ArrayRef<const char *> CC1Cmd,
+ llvm::StringRef Argv0, llvm::StringRef CWD,
+ llvm::raw_socket_stream &Client);
+
+// Processes response from module build daemon
+llvm::Expected<std::vector<std::string>>
+getUpdatedCC1(llvm::raw_socket_stream &Client);
+
+// Work in progress. Eventually function will modify CC1 command line to include
+// path to modules already built by the daemon
+llvm::Expected<std::vector<std::string>> updateCC1WithModuleBuildDaemon(
+ const clang::CompilerInvocation &Clang, llvm::ArrayRef<const char *> CC1Cmd,
+ const char *Argv0, llvm::StringRef CWD, clang::DiagnosticsEngine &Diag);
+
+} // namespace clang::tooling::cc1modbuildd
+
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_FRONTEND_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
deleted file mode 100644
index 2408a852e995fe..00000000000000
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h
+++ /dev/null
@@ -1,166 +0,0 @@
-//===------------------------- SocketMsgSupport.h -------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
-#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
-
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-
-namespace cc1modbuildd {
-
-enum class ActionType { REGISTER, HANDSHAKE };
-enum class StatusType { REQUEST, SUCCESS, FAILURE };
-
-struct BaseMsg {
- ActionType MsgAction;
- StatusType MsgStatus;
-
- BaseMsg() = default;
- BaseMsg(ActionType Action, StatusType Status)
- : MsgAction(Action), MsgStatus(Status) {}
-};
-
-struct RegisterMsg : public BaseMsg {
- std::optional<std::string> WorkingDirectory;
- // The scanner requires the path to the clang executable
- std::optional<std::string> ExecutablePath;
- // Does not include executable
- std::optional<std::vector<std::string>> CC1CommandLine;
-
- RegisterMsg() = default;
-
- RegisterMsg(ActionType Action, StatusType Status,
- const std::optional<std::string> &CurrentWD,
- const std::optional<std::string> &Argv0,
- const std::optional<std::vector<std::string>> &Argv)
- : BaseMsg(Action, Status), WorkingDirectory(CurrentWD),
- ExecutablePath(Argv0), CC1CommandLine(Argv) {}
-
- RegisterMsg(ActionType Action, StatusType Status)
- : BaseMsg(Action, Status), WorkingDirectory(std::nullopt),
- ExecutablePath(std::nullopt), CC1CommandLine(std::nullopt) {}
-};
-
-struct HandshakeMsg : public BaseMsg {
- HandshakeMsg() = default;
- HandshakeMsg(ActionType Action, StatusType Status)
- : BaseMsg(Action, Status) {}
-};
-
-template <typename T> std::string getBufferFromSocketMsg(T Msg) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- std::string Buffer;
- llvm::raw_string_ostream OS(Buffer);
- llvm::yaml::Output YamlOut(OS);
-
- YamlOut << Msg;
- return Buffer;
-}
-
-template <typename T>
-llvm::Expected<T> getSocketMsgFromBuffer(const char *Buffer) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- T ClientRequest;
- llvm::yaml::Input YamlIn(Buffer);
- YamlIn >> ClientRequest;
-
- if (YamlIn.error()) {
- std::string Msg = "Syntax or semantic error during YAML parsing";
- return llvm::make_error<llvm::StringError>(Msg,
- llvm::inconvertibleErrorCode());
- }
-
- return ClientRequest;
-}
-
-template <typename T> llvm::Expected<T> readSocketMsgFromSocket(int FD) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- llvm::Expected<std::string> MaybeResponseBuffer = readFromSocket(FD);
- if (!MaybeResponseBuffer)
- return std::move(MaybeResponseBuffer.takeError());
-
- // Wait for response from module build daemon
- llvm::Expected<T> MaybeResponse =
- getSocketMsgFromBuffer<T>(std::move(*MaybeResponseBuffer).c_str());
- if (!MaybeResponse)
- return std::move(MaybeResponse.takeError());
- return std::move(*MaybeResponse);
-}
-
-template <typename T> llvm::Error writeSocketMsgToSocket(T Msg, int FD) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- std::string Buffer = getBufferFromSocketMsg(Msg);
- if (llvm::Error Err = writeToSocket(Buffer, FD))
- return std::move(Err);
-
- return llvm::Error::success();
-}
-
-template <typename T>
-llvm::Expected<int>
-connectAndWriteSocketMsgToSocket(T Msg, llvm::StringRef SocketPath) {
- static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value,
- "T must inherit from cc1modbuildd::BaseMsg");
-
- llvm::Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (!MaybeFD)
- return std::move(MaybeFD.takeError());
- int FD = std::move(*MaybeFD);
-
- if (llvm::Error Err = writeSocketMsgToSocket(Msg, FD))
- return std::move(Err);
-
- return FD;
-}
-
-} // namespace cc1modbuildd
-
-template <>
-struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::StatusType> {
- static void enumeration(IO &Io, cc1modbuildd::StatusType &Value) {
- Io.enumCase(Value, "REQUEST", cc1modbuildd::StatusType::REQUEST);
- Io.enumCase(Value, "SUCCESS", cc1modbuildd::StatusType::SUCCESS);
- Io.enumCase(Value, "FAILURE", cc1modbuildd::StatusType::FAILURE);
- }
-};
-
-template <>
-struct llvm::yaml::ScalarEnumerationTraits<cc1modbuildd::ActionType> {
- static void enumeration(IO &Io, cc1modbuildd::ActionType &Value) {
- Io.enumCase(Value, "REGISTER", cc1modbuildd::ActionType::REGISTER);
- Io.enumCase(Value, "HANDSHAKE", cc1modbuildd::ActionType::HANDSHAKE);
- }
-};
-
-template <> struct llvm::yaml::MappingTraits<cc1modbuildd::RegisterMsg> {
- static void mapping(IO &Io, cc1modbuildd::RegisterMsg &Info) {
- Io.mapRequired("Action", Info.MsgAction);
- Io.mapRequired("Status", Info.MsgStatus);
- Io.mapOptional("WorkingDirectory", Info.WorkingDirectory);
- Io.mapOptional("ExecutablePath", Info.ExecutablePath);
- Io.mapOptional("CC1CommandLine", Info.CC1CommandLine);
- }
-};
-
-template <> struct llvm::yaml::MappingTraits<cc1modbuildd::HandshakeMsg> {
- static void mapping(IO &Io, cc1modbuildd::HandshakeMsg &Info) {
- Io.mapRequired("Action", Info.MsgAction);
- Io.mapRequired("Status", Info.MsgStatus);
- }
-};
-
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
index 3dae1a00779da0..255b2bf0c7916d 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-
#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
@@ -14,16 +13,154 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_socket_stream.h"
+
+namespace clang::tooling::cc1modbuildd {
+
+enum class ActionType { REGISTER, HANDSHAKE };
+enum class StatusType { REQUEST, SUCCESS, FAILURE };
+
+struct BaseMsg {
+ ActionType MsgAction;
+ StatusType MsgStatus;
+
+ BaseMsg() = default;
+ BaseMsg(ActionType Action, StatusType Status)
+ : MsgAction(Action), MsgStatus(Status) {}
+};
+
+struct RegisterMsg : public BaseMsg {
+ std::optional<std::string> WorkingDirectory;
+ // The scanner requires the path to the clang executable
+ std::optional<std::string> ExecutablePath;
+ // Does not include executable
+ std::optional<std::vector<std::string>> CC1CommandLine;
+
+ RegisterMsg() = default;
+
+ RegisterMsg(ActionType Action, StatusType Status,
+ const std::optional<std::string> &CurrentWD,
+ const std::optional<std::string> &Argv0,
+ const std::optional<std::vector<std::string>> &Argv)
+ : BaseMsg(Action, Status), WorkingDirectory(CurrentWD),
+ ExecutablePath(Argv0), CC1CommandLine(Argv) {}
+
+ RegisterMsg(ActionType Action, StatusType Status)
+ : BaseMsg(Action, Status), WorkingDirectory(std::nullopt),
+ ExecutablePath(std::nullopt), CC1CommandLine(std::nullopt) {}
+};
+
+struct HandshakeMsg : public BaseMsg {
+ HandshakeMsg() = default;
+ HandshakeMsg(ActionType Action, StatusType Status)
+ : BaseMsg(Action, Status) {}
+};
+
+llvm::Expected<std::string>
+readBufferFromSocket(llvm::raw_socket_stream &Socket);
+llvm::Error writeBufferToSocket(llvm::raw_socket_stream &Socket,
+ llvm::StringRef Buffer);
+
+template <typename T> std::string convertMsgStructToBuffer(T MsgStruct) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
+
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+ llvm::yaml::Output YamlOut(OS);
+
+ // TODO confirm yaml::Output does not have any error messages
+ YamlOut << MsgStruct;
+
+ return Buffer;
+}
+
+template <typename T>
+llvm::Expected<T> convertBufferToMsgStruct(llvm::StringRef Buffer) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
+
+ T MsgStruct;
+ llvm::yaml::Input YamlIn(Buffer);
+ YamlIn >> MsgStruct;
+
+ // YamlIn.error() dumps an error message if there is one
+ if (YamlIn.error())
+ return llvm::make_error<llvm::StringError>(
+ "Syntax or semantic error during YAML parsing",
+ llvm::inconvertibleErrorCode());
+
+ return MsgStruct;
+}
+
+template <typename T>
+llvm::Expected<T> readMsgStructFromSocket(llvm::raw_socket_stream &Socket) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
+
+ llvm::Expected<std::string> MaybeBuffer = readBufferFromSocket(Socket);
+ if (!MaybeBuffer)
+ return std::move(MaybeBuffer.takeError());
+ std::string Buffer = std::move(*MaybeBuffer);
+
+ llvm::Expected<T> MaybeMsgStruct = convertBufferToMsgStruct<T>(Buffer);
+ if (!MaybeMsgStruct)
+ return std::move(MaybeMsgStruct.takeError());
+ return std::move(*MaybeMsgStruct);
+}
+
+template <typename T>
+llvm::Error writeMsgStructToSocket(llvm::raw_socket_stream &Socket,
+ T MsgStruct) {
+ static_assert(std::is_base_of<cc1modbuildd::BaseMsg, T>::value);
+
+ std::string Buffer = convertMsgStructToBuffer(MsgStruct);
+ if (llvm::Error Err = writeBufferToSocket(Socket, Buffer))
+ return Err;
+ return llvm::Error::success();
+}
+} // namespace clang::tooling::cc1modbuildd
-namespace cc1modbuildd {
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<
+ clang::tooling::cc1modbuildd::StatusType> {
+ static void enumeration(IO &Io,
+ clang::tooling::cc1modbuildd::StatusType &Value) {
+ Io.enumCase(Value, "REQUEST",
+ clang::tooling::cc1modbuildd::StatusType::REQUEST);
+ Io.enumCase(Value, "SUCCESS",
+ clang::tooling::cc1modbuildd::StatusType::SUCCESS);
+ Io.enumCase(Value, "FAILURE",
+ clang::tooling::cc1modbuildd::StatusType::FAILURE);
+ }
+};
-llvm::Expected<int> createSocket();
-llvm::Expected<int> connectToSocket(llvm::StringRef SocketPath);
-llvm::Expected<int> connectAndWriteToSocket(std::string Buffer,
- llvm::StringRef SocketPath);
-llvm::Expected<std::string> readFromSocket(int FD);
-llvm::Error writeToSocket(std::string Buffer, int WriteFD);
+template <>
+struct llvm::yaml::ScalarEnumerationTraits<
+ clang::tooling::cc1modbuildd::ActionType> {
+ static void enumeration(IO &Io,
+ clang::tooling::cc1modbuildd::ActionType &Value) {
+ Io.enumCase(Value, "REGISTER",
+ clang::tooling::cc1modbuildd::ActionType::REGISTER);
+ Io.enumCase(Value, "HANDSHAKE",
+ clang::tooling::cc1modbuildd::ActionType::HANDSHAKE);
+ }
+};
-} // namespace cc1modbuildd
+template <>
+struct llvm::yaml::MappingTraits<clang::tooling::cc1modbuildd::RegisterMsg> {
+ static void mapping(IO &Io, clang::tooling::cc1modbuildd::RegisterMsg &Info) {
+ Io.mapRequired("Action", Info.MsgAction);
+ Io.mapRequired("Status", Info.MsgStatus);
+ Io.mapOptional("WorkingDirectory", Info.WorkingDirectory);
+ Io.mapOptional("ExecutablePath", Info.ExecutablePath);
+ Io.mapOptional("CC1CommandLine", Info.CC1CommandLine);
+ }
+};
+template <>
+struct llvm::yaml::MappingTraits<clang::tooling::cc1modbuildd::HandshakeMsg> {
+ static void mapping(IO &Io,
+ clang::tooling::cc1modbuildd::HandshakeMsg &Info) {
+ Io.mapRequired("Action", Info.MsgAction);
+ Io.mapRequired("Status", Info.MsgStatus);
+ }
+};
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETSUPPORT_H
diff --git a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
index 79a2ffc3c1804d..6d4acc3a348b9e 100644
--- a/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
+++ b/clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
@@ -15,14 +15,47 @@
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
#include "llvm/Support/Error.h"
+
+#include <chrono>
#include <string>
-namespace cc1modbuildd {
+#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+// winsock2.h must be included before afunix.h
+// clang-format off
+#include <winsock2.h>
+#include <afunix.h>
+// clang-format on
+#else
+#include <sys/un.h>
+#endif
+
+namespace clang::tooling::cc1modbuildd {
+
+constexpr std::string_view SocketFileName = "mbd.sock";
+constexpr std::string_view StdoutFileName = "mbd.out";
+constexpr std::string_view StderrFileName = "mbd.err";
+constexpr std::string_view ModuleBuildDaemonFlag = "-cc1modbuildd";
+
+// A llvm::raw_socket_stream uses sockaddr_un
+constexpr size_t SocketAddrMaxLength = sizeof(sockaddr_un::sun_path);
+
+constexpr size_t BasePathMaxLength =
+ SocketAddrMaxLength - SocketFileName.length();
+
+// Get a temprary location where the daemon can store log files and a socket
+// address. Of the format /tmp/clang-<BLAKE3HashOfClangFullVersion>/
+std::string getBasePath();
+
+// Check if the user provided BasePath is short enough
+bool validBasePathLength(llvm::StringRef);
void writeError(llvm::Error Err, std::string Msg);
std::string getFullErrorMsg(llvm::Error Err, std::string Msg);
llvm::Error makeStringError(llvm::Error Err, std::string Msg);
-} // namespace cc1modbuildd
+} // namespace clang::tooling::cc1modbuildd
-#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
\ No newline at end of file
+#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
index 9c1f5dc1aa2c0c..c043557206ef9e 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
+++ b/clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangModuleBuildDaemon
- Client.cpp
+ Frontend.cpp
SocketSupport.cpp
Utils.cpp
)
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
deleted file mode 100644
index 4018d70a30ea5e..00000000000000
--- a/clang/lib/Tooling/ModuleBuildDaemon/Client.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-//===----------------------------- Client.cpp -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "clang/Basic/Version.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/BLAKE3.h"
-
-// TODO: Make portable
-#if LLVM_ON_UNIX
-
-#include <cerrno>
-#include <filesystem>
-#include <fstream>
-#include <signal.h>
-#include <spawn.h>
-#include <string>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-using namespace clang;
-using namespace llvm;
-using namespace cc1modbuildd;
-
-std::string cc1modbuildd::getBasePath() {
- llvm::BLAKE3 Hash;
- Hash.update(getClangFullVersion());
- auto HashResult = Hash.final<sizeof(uint64_t)>();
- uint64_t HashValue =
- llvm::support::endian::read<uint64_t, llvm::endianness::native>(
- HashResult.data());
- std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
-
- // set paths
- SmallString<128> BasePath;
- llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
- llvm::sys::path::append(BasePath, "clang-" + Key);
- return BasePath.c_str();
-}
-
-llvm::Error cc1modbuildd::attemptHandshake(int SocketFD) {
-
- cc1modbuildd::HandshakeMsg Request{ActionType::HANDSHAKE,
- StatusType::REQUEST};
- std::string Buffer = cc1modbuildd::getBufferFromSocketMsg(Request);
-
- if (llvm::Error Err = writeToSocket(Buffer, SocketFD))
- return std::move(Err);
-
- Expected<RegisterMsg> MaybeServerResponse =
- readSocketMsgFromSocket<RegisterMsg>(SocketFD);
- if (!MaybeServerResponse)
- return std::move(MaybeServerResponse.takeError());
- RegisterMsg ServerResponse = std::move(*MaybeServerResponse);
-
- assert(ServerResponse.MsgAction == ActionType::HANDSHAKE &&
- "At this point response ActionType should only ever be HANDSHAKE");
-
- if (ServerResponse.MsgStatus == StatusType::SUCCESS)
- return llvm::Error::success();
-
- return llvm::make_error<StringError>("Handshake failed",
- inconvertibleErrorCode());
-}
-
-llvm::Error cc1modbuildd::spawnModuleBuildDaemon(StringRef BasePath,
- const char *Argv0) {
- std::string BasePathStr = BasePath.str();
- const char *Args[] = {Argv0, "-cc1modbuildd", BasePathStr.c_str(), nullptr};
- pid_t pid;
- int EC = posix_spawn(&pid, Args[0],
- /*file_actions*/ nullptr,
- /*spawnattr*/ nullptr, const_cast<char **>(Args),
- /*envp*/ nullptr);
- if (EC)
- return createStringError(std::error_code(EC, std::generic_category()),
- "failed to spawn module build daemon process");
-
- return llvm::Error::success();
-}
-
-Expected<int> cc1modbuildd::getModuleBuildDaemon(const char *Argv0,
- StringRef BasePath) {
-
- SmallString<128> SocketPath = BasePath;
- llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
-
- if (llvm::sys::fs::exists(SocketPath)) {
- Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD)
- return std::move(*MaybeFD);
- consumeError(MaybeFD.takeError());
- }
-
- if (llvm::Error Err = cc1modbuildd::spawnModuleBuildDaemon(BasePath, Argv0))
- return std::move(Err);
-
- const unsigned int MICROSEC_IN_SEC = 1000000;
- constexpr unsigned int MAX_TIME = 30 * MICROSEC_IN_SEC;
- const unsigned short INTERVAL = 100;
-
- unsigned int CumulativeTime = 0;
- unsigned int WaitTime = 0;
-
- while (CumulativeTime <= MAX_TIME) {
- // Wait a bit then check to see if the module build daemon has initialized
- usleep(WaitTime);
-
- if (llvm::sys::fs::exists(SocketPath)) {
- Expected<int> MaybeFD = connectToSocket(SocketPath);
- if (MaybeFD)
- return std::move(*MaybeFD);
- consumeError(MaybeFD.takeError());
- }
-
- CumulativeTime += INTERVAL;
- }
-
- // After waiting 30 seconds give up
- return llvm::make_error<StringError>(
- "Module build daemon did not exist after spawn attempt",
- inconvertibleErrorCode());
-}
-
-llvm::Error
-cc1modbuildd::registerTranslationUnit(ArrayRef<const char *> CC1Command,
- StringRef Argv0, StringRef CWD,
- int ServerFD) {
-
- std::vector<std::string> StrCC1Command;
- for (const char *Arg : CC1Command)
- StrCC1Command.emplace_back(Arg);
-
- cc1modbuildd::RegisterMsg Request{ActionType::REGISTER, StatusType::REQUEST,
- CWD.str(), Argv0.str(), StrCC1Command};
-
- llvm::Error WriteErr = writeSocketMsgToSocket(Request, ServerFD);
- if (WriteErr)
- return std::move(WriteErr);
-
- return llvm::Error::success();
-}
-
-Expected<std::vector<std::string>> cc1modbuildd::getUpdatedCC1(int ServerFD) {
-
- // Blocks cc1 invocation until module build daemon is done processing
- // translation unit. Currently receives a SUCCESS message and returns
- // llvm::Error::success() but will eventually recive updated cc1 command line
- Expected<RegisterMsg> MaybeServerResponse =
- readSocketMsgFromSocket<RegisterMsg>(ServerFD);
- if (!MaybeServerResponse)
- return std::move(MaybeServerResponse.takeError());
- RegisterMsg ServerResponse = std::move(*MaybeServerResponse);
-
- // Confirm response is REGISTER and MsgStatus is SUCCESS
- assert(ServerResponse.MsgAction == ActionType::REGISTER &&
- "At this point response ActionType should only ever be REGISTER");
-
- if (ServerResponse.MsgStatus == StatusType::SUCCESS)
- return ServerResponse.CC1CommandLine.value();
-
- return llvm::make_error<StringError>(
- "Daemon failed to processes registered translation unit",
- inconvertibleErrorCode());
-}
-
-Expected<std::vector<std::string>>
-cc1modbuildd::updateCC1WithModuleBuildDaemon(const CompilerInvocation &Clang,
- ArrayRef<const char *> CC1Cmd,
- const char *Argv0, StringRef CWD) {
-
- // The module build daemon stores all output files and its socket address
- // under BasePath. Either set BasePath to a user provided option or create an
- // appropriate BasePath based on the hash of the clang version
- std::string BasePath;
- if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
- BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
- else
- BasePath = cc1modbuildd::getBasePath();
-
- // If module build daemon does not exist spawn module build daemon
- Expected<int> MaybeDaemonFD =
- cc1modbuildd::getModuleBuildDaemon(Argv0, BasePath);
- if (!MaybeDaemonFD)
- return makeStringError(MaybeDaemonFD.takeError(),
- "Connect to daemon failed: ");
- int DaemonFD = std::move(*MaybeDaemonFD);
-
- if (llvm::Error HandshakeErr = attemptHandshake(DaemonFD))
- return makeStringError(std::move(HandshakeErr),
- "Failed to hadshake with daemon: ");
-
- // Send translation unit information to module build daemon for processing
- if (llvm::Error RegisterErr =
- registerTranslationUnit(CC1Cmd, Argv0, CWD, DaemonFD))
- return makeStringError(std::move(RegisterErr),
- "Register translation unti failed: ");
-
- // Wait for response from module build daemon. Response will hopefully be an
- // updated cc1 command line with additional -fmodule-file=<file> flags and
- // implicit module flags removed
- Expected<std::vector<std::string>> MaybeUpdatedCC1 = getUpdatedCC1(DaemonFD);
- if (!MaybeUpdatedCC1)
- return makeStringError(MaybeUpdatedCC1.takeError(),
- "Failed to get updated CC1: ");
- return std::move(*MaybeUpdatedCC1);
-}
-
-#endif // LLVM_ON_UNIX
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
new file mode 100644
index 00000000000000..ee5cd05606f67d
--- /dev/null
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Frontend.cpp
@@ -0,0 +1,222 @@
+//===---------------------------- Frontend.cpp ----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ModuleBuildDaemon/Frontend.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/ExponentialBackoff.h"
+#include "llvm/Support/Program.h"
+
+#include <cerrno>
+#include <filesystem>
+#include <fstream>
+#include <signal.h>
+#include <spawn.h>
+#include <string>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+using namespace clang;
+using namespace llvm;
+
+namespace clang::tooling::cc1modbuildd {
+
+llvm::Error attemptHandshake(llvm::raw_socket_stream &Client) {
+ // Send HandshakeMsg to module build daemon
+ HandshakeMsg Request{ActionType::HANDSHAKE, StatusType::REQUEST};
+ if (llvm::Error Err = writeMsgStructToSocket(Client, Request))
+ return Err;
+
+ // Read response from module build daemon
+ Expected<HandshakeMsg> MaybeResponse =
+ readMsgStructFromSocket<HandshakeMsg>(Client);
+ if (!MaybeResponse) {
+ return MaybeResponse.takeError();
+ }
+ HandshakeMsg Response = std::move(*MaybeResponse);
+
+ assert(Response.MsgAction == ActionType::HANDSHAKE &&
+ "The response ActionType should only ever be HANDSHAKE");
+
+ if (Response.MsgStatus == StatusType::SUCCESS) {
+ return llvm::Error::success();
+ }
+
+ return llvm::make_error<llvm::StringError>(
+ "Received handshake response 'FAILURE' from module build daemon",
+ std::make_error_code(std::errc::operation_not_permitted));
+}
+
+llvm::Error spawnModuleBuildDaemon(const CompilerInvocation &Clang,
+ const char *Argv0, DiagnosticsEngine &Diag,
+ std::string BasePath) {
+ std::vector<StringRef> Args = {Argv0, ModuleBuildDaemonFlag};
+ if (!Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
+ Args.push_back(BasePath.c_str());
+
+ std::string ErrorBuffer;
+ llvm::sys::ExecuteNoWait(Argv0, Args, std::nullopt, {}, 0, &ErrorBuffer,
+ nullptr, nullptr, /*DetachProcess*/ true);
+
+ // llvm::sys::ExecuteNoWait can fail for a variety of reasons which can't be
+ // generalized to one error code
+ if (!ErrorBuffer.empty())
+ return llvm::make_error<llvm::StringError>(ErrorBuffer,
+ llvm::inconvertibleErrorCode());
+
+ Diag.Report(diag::remark_mbd_spawn);
+ return llvm::Error::success();
+}
+
+Expected<std::unique_ptr<llvm::raw_socket_stream>>
+getModuleBuildDaemon(const CompilerInvocation &Clang, const char *Argv0,
+ DiagnosticsEngine &Diag, StringRef BasePath) {
+ SmallString<128> SocketPath = BasePath;
+ llvm::sys::path::append(SocketPath, SocketFileName);
+
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<std::unique_ptr<llvm::raw_socket_stream>> MaybeClient =
+ llvm::raw_socket_stream::createConnectedUnix(SocketPath);
+ if (MaybeClient)
+ return std::move(*MaybeClient);
+ consumeError(MaybeClient.takeError());
+ }
+
+ if (llvm::Error Err =
+ spawnModuleBuildDaemon(Clang, Argv0, Diag, BasePath.str()))
+ return std::move(Err);
+
+ std::chrono::seconds MaxWaitTime(30);
+ llvm::ExponentialBackoff Backoff(MaxWaitTime);
+ do {
+ if (llvm::sys::fs::exists(SocketPath)) {
+ Expected<std::unique_ptr<llvm::raw_socket_stream>> MaybeClient =
+ llvm::raw_socket_stream::createConnectedUnix(SocketPath);
+ if (MaybeClient) {
+ Diag.Report(diag::remark_mbd_connection) << SocketPath;
+ return std::move(*MaybeClient);
+ }
+ consumeError(MaybeClient.takeError());
+ }
+ } while (Backoff.waitForNextAttempt());
+
+ // After waiting around 30 seconds give up and return an error
+ return llvm::make_error<llvm::StringError>(
+ "Max wait time exceeded",
+ std::make_error_code(std::errc::no_such_process));
+}
+
+llvm::Error registerTranslationUnit(ArrayRef<const char *> CC1Command,
+ StringRef Argv0, StringRef CWD,
+ llvm::raw_socket_stream &Client) {
+
+ std::vector<std::string> StrCC1Command;
+ for (const char *Arg : CC1Command)
+ StrCC1Command.emplace_back(Arg);
+
+ cc1modbuildd::RegisterMsg Request{ActionType::REGISTER, StatusType::REQUEST,
+ CWD.str(), Argv0.str(), StrCC1Command};
+
+ llvm::Error WriteErr = writeMsgStructToSocket(Client, Request);
+ if (WriteErr)
+ return std::move(WriteErr);
+
+ return llvm::Error::success();
+}
+
+Expected<std::vector<std::string>>
+getUpdatedCC1(llvm::raw_socket_stream &Server) {
+
+ // Blocks cc1 invocation until module build daemon is done processing
+ // translation unit. Currently receives a SUCCESS message and returns
+ // llvm::Error::success() but will eventually recive updated cc1 command line
+ Expected<RegisterMsg> MaybeServerResponse =
+ readMsgStructFromSocket<RegisterMsg>(Server);
+ if (!MaybeServerResponse)
+ return std::move(MaybeServerResponse.takeError());
+ RegisterMsg ServerResponse = std::move(*MaybeServerResponse);
+
+ // Confirm response is REGISTER and MsgStatus is SUCCESS
+ assert(ServerResponse.MsgAction == ActionType::REGISTER &&
+ "At this point response ActionType should only ever be REGISTER");
+
+ if (ServerResponse.MsgStatus == StatusType::SUCCESS)
+ return ServerResponse.CC1CommandLine.value();
+
+ return llvm::make_error<StringError>(
+ "Daemon failed to processes registered translation unit",
+ inconvertibleErrorCode());
+}
+
+Expected<std::vector<std::string>>
+updateCC1WithModuleBuildDaemon(const CompilerInvocation &Clang,
+ ArrayRef<const char *> CC1Cmd, const char *Argv0,
+ StringRef CWD, DiagnosticsEngine &Diag) {
+ // The module build daemon stores all output files and its socket address
+ // under BasePath. Either set BasePath to a user provided option or create an
+ // appropriate BasePath based on the BLAKE3 hash of the full clang version
+ std::string BasePath;
+ if (Clang.getFrontendOpts().ModuleBuildDaemonPath.empty())
+ BasePath = getBasePath();
+ else {
+ // Get user provided BasePath and confirm it is short enough
+ BasePath = Clang.getFrontendOpts().ModuleBuildDaemonPath;
+ if (!validBasePathLength(BasePath)) {
+ Diag.Report(diag::err_path_length) << BasePath << BasePathMaxLength;
+ return make_error<StringError>(inconvertibleErrorCode(),
+ "BasePath is too long");
+ }
+ }
+
+ // If module build daemon does not exist spawn module build daemon
+ Expected<std::unique_ptr<llvm::raw_socket_stream>> MaybeClient =
+ getModuleBuildDaemon(Clang, Argv0, Diag, BasePath);
+ if (!MaybeClient) {
+ Diag.Report(diag::err_mbd_connect) << MaybeClient.takeError();
+ return make_error<StringError>(inconvertibleErrorCode(),
+ "Could not connect to ModuleBuildDaemon");
+ }
+ llvm::raw_socket_stream &Client = **MaybeClient;
+
+ if (llvm::Error HandshakeErr = attemptHandshake(Client)) {
+ Diag.Report(diag::err_mbd_handshake) << std::move(HandshakeErr);
+ return makeStringError(std::move(HandshakeErr),
+ "Failed to hadshake with daemon");
+ }
+
+ Diag.Report(diag::remark_mbd_handshake)
+ << Clang.getFrontendOpts().Inputs[0].getFile();
+
+ // Send translation unit information to module build daemon for processing
+ if (llvm::Error RegisterErr =
+ registerTranslationUnit(CC1Cmd, Argv0, CWD, Client))
+ return makeStringError(std::move(RegisterErr),
+ "Register translation unti failed");
+
+ // Wait for response from module build daemon. Response will hopefully be an
+ // updated cc1 command line with additional -fmodule-file=<file> flags and
+ // implicit module flags removed
+ Expected<std::vector<std::string>> MaybeUpdatedCC1 = getUpdatedCC1(Client);
+ if (!MaybeUpdatedCC1)
+ return makeStringError(MaybeUpdatedCC1.takeError(),
+ "Failed to get updated CC1");
+ return std::move(*MaybeUpdatedCC1);
+}
+
+} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
index 8a25b8f0bfd9df..8797fb2f4cb499 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/SocketSupport.cpp
@@ -6,127 +6,49 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
-#include "clang/Basic/Version.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_socket_stream.h"
-// TODO: Make portable
-#if LLVM_ON_UNIX
-
-#include <cerrno>
-#include <filesystem>
-#include <fstream>
-#include <signal.h>
-#include <spawn.h>
+#include <memory>
#include <string>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-llvm::Expected<int> cc1modbuildd::createSocket() {
- int FD;
- if ((FD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
- std::string Msg = "socket create error: " + std::string(strerror(errno));
- return llvm::createStringError(llvm::inconvertibleErrorCode(), Msg);
- }
- return FD;
-}
-
-llvm::Expected<int> cc1modbuildd::connectToSocket(llvm::StringRef SocketPath) {
-
- llvm::Expected<int> MaybeFD = cc1modbuildd::createSocket();
- if (!MaybeFD)
- return std::move(MaybeFD.takeError());
-
- int FD = std::move(*MaybeFD);
-
- struct sockaddr_un Addr;
- memset(&Addr, 0, sizeof(Addr));
- Addr.sun_family = AF_UNIX;
- strncpy(Addr.sun_path, SocketPath.str().c_str(), sizeof(Addr.sun_path) - 1);
-
- if (connect(FD, (struct sockaddr *)&Addr, sizeof(Addr)) == -1) {
- close(FD);
- std::string msg = "socket connect error: " + std::string(strerror(errno));
- return llvm::createStringError(llvm::inconvertibleErrorCode(), msg);
- }
- return FD;
-}
-llvm::Expected<int>
-cc1modbuildd::connectAndWriteToSocket(std::string Buffer,
- llvm::StringRef SocketPath) {
+namespace clang::tooling::cc1modbuildd {
- llvm::Expected<int> MaybeConnectedFD = connectToSocket(SocketPath);
- if (!MaybeConnectedFD)
- return std::move(MaybeConnectedFD.takeError());
+llvm::Expected<std::string>
+readBufferFromSocket(llvm::raw_socket_stream &Socket) {
+ constexpr unsigned short MAX_BUFFER = 4096;
+ constexpr std::chrono::milliseconds TIMEOUT(5000);
+ char Buffer[MAX_BUFFER];
+ std::string ReturnBuffer;
- int ConnectedFD = std::move(*MaybeConnectedFD);
- llvm::Error Err = writeToSocket(Buffer, ConnectedFD);
- if (Err)
- return std::move(Err);
-
- return ConnectedFD;
-}
-
-llvm::Expected<std::string> cc1modbuildd::readFromSocket(int FD) {
-
- const size_t BUFFER_SIZE = 4096;
- std::vector<char> Buffer(BUFFER_SIZE);
- size_t TotalBytesRead = 0;
-
- ssize_t n;
- while ((n = read(FD, Buffer.data() + TotalBytesRead,
- Buffer.size() - TotalBytesRead)) > 0) {
-
- TotalBytesRead += n;
- // Read until ...\n encountered (last line of YAML document)
- if (std::string(&Buffer[TotalBytesRead - 4], 4) == "...\n")
+ ssize_t n = 0;
+ while ((n = Socket.read(Buffer, MAX_BUFFER, TIMEOUT)) > 0) {
+ ReturnBuffer.append(Buffer, n);
+ // Read until \n... encountered which is the last line of a YAML document
+ if (ReturnBuffer.find("\n...") != std::string::npos)
break;
- if (Buffer.size() - TotalBytesRead < BUFFER_SIZE)
- Buffer.resize(Buffer.size() + BUFFER_SIZE);
}
- if (n < 0) {
- std::string Msg = "socket read error: " + std::string(strerror(errno));
- return llvm::make_error<llvm::StringError>(Msg,
- llvm::inconvertibleErrorCode());
+ if (Socket.has_error()) {
+ std::error_code EC = Socket.error();
+ Socket.clear_error();
+ return llvm::make_error<llvm::StringError>("Failed socket read", EC);
}
- if (n == 0)
- return llvm::make_error<llvm::StringError>("EOF",
- llvm::inconvertibleErrorCode());
- return std::string(Buffer.begin(), Buffer.end());
+ return ReturnBuffer;
}
-llvm::Error cc1modbuildd::writeToSocket(std::string Buffer, int WriteFD) {
-
- ssize_t BytesToWrite = static_cast<ssize_t>(Buffer.size());
- const char *Bytes = Buffer.c_str();
-
- while (BytesToWrite) {
- ssize_t BytesWritten = write(WriteFD, Bytes, BytesToWrite);
- if (BytesWritten == -1) {
- std::string Msg = "socket write error: " + std::string(strerror(errno));
- return llvm::make_error<llvm::StringError>(
- Msg, llvm::inconvertibleErrorCode());
- }
-
- if (!BytesWritten || BytesWritten > BytesToWrite)
- return llvm::errorCodeToError(
- std::error_code(EIO, std::generic_category()));
-
- BytesToWrite -= BytesWritten;
- Bytes += BytesWritten;
+llvm::Error writeBufferToSocket(llvm::raw_socket_stream &Socket,
+ llvm::StringRef Buffer) {
+ Socket << Buffer;
+ if (Socket.has_error()) {
+ std::error_code EC = Socket.error();
+ Socket.clear_error();
+ return llvm::make_error<llvm::StringError>("Failed socket write", EC);
}
+
+ Socket.flush();
return llvm::Error::success();
}
-#endif // LLVM_ON_UNIX
+} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
index c03e8e3762114f..d4dcfa7392d5a5 100644
--- a/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
+++ b/clang/lib/Tooling/ModuleBuildDaemon/Utils.cpp
@@ -6,19 +6,28 @@
//
//===----------------------------------------------------------------------===//
-#include <clang/Tooling/ModuleBuildDaemon/Utils.h>
-#include <llvm/Support/Error.h>
+#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
+#include "clang/Basic/Version.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/BLAKE3.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+
#include <string>
using namespace llvm;
-void cc1modbuildd::writeError(llvm::Error Err, std::string Msg) {
+namespace clang::tooling::cc1modbuildd {
+
+void writeError(llvm::Error Err, std::string Msg) {
handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
errs() << Msg << EIB.message() << '\n';
});
}
-std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
+std::string getFullErrorMsg(llvm::Error Err, std::string Msg) {
std::string ErrMessage;
handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
ErrMessage = Msg + EIB.message();
@@ -26,7 +35,34 @@ std::string cc1modbuildd::getFullErrorMsg(llvm::Error Err, std::string Msg) {
return ErrMessage;
}
-llvm::Error cc1modbuildd::makeStringError(llvm::Error Err, std::string Msg) {
+llvm::Error makeStringError(llvm::Error Err, std::string Msg) {
std::string ErrMsg = getFullErrorMsg(std::move(Err), Msg);
return llvm::make_error<StringError>(ErrMsg, inconvertibleErrorCode());
-}
\ No newline at end of file
+}
+
+std::string getBasePath() {
+ llvm::BLAKE3 Hash;
+ Hash.update(clang::getClangFullVersion());
+ auto HashResult = Hash.final<sizeof(uint64_t)>();
+ uint64_t HashValue =
+ llvm::support::endian::read<uint64_t, llvm::endianness::native>(
+ HashResult.data());
+ std::string Key = toString(llvm::APInt(64, HashValue), 36, /*Signed*/ false);
+
+ // Set paths
+ llvm::SmallString<128> BasePath;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot*/ true, BasePath);
+ llvm::sys::path::append(BasePath, "clang-" + Key);
+ return BasePath.c_str();
+}
+
+bool validBasePathLength(llvm::StringRef Address) {
+ // Confirm that the user provided BasePath is short enough to allow the socket
+ // address to fit within the space alloted to sockaddr_un::sun_path
+ if (Address.str().length() > BasePathMaxLength) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace clang::tooling::cc1modbuildd
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 3573d6f7e43b27..11829076142e70 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -26,7 +26,7 @@
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
-#include "clang/Tooling/ModuleBuildDaemon/Client.h"
+#include "clang/Tooling/ModuleBuildDaemon/Frontend.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/llvm-config.h"
@@ -255,7 +255,6 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
std::vector<std::string> UpdatedArgv;
std::vector<const char *> CharUpdatedArgv;
-#if LLVM_ON_UNIX
// Handle module build daemon functionality if enabled
if (Invocation->getFrontendOpts().ModuleBuildDaemon) {
@@ -271,8 +270,8 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
Expected<std::vector<std::string>> MaybeUpdatedArgv =
- cc1modbuildd::updateCC1WithModuleBuildDaemon(*Invocation, Argv, Argv0,
- *MaybeCWD);
+ clang::tooling::cc1modbuildd::updateCC1WithModuleBuildDaemon(
+ *Invocation, Argv, Argv0, *MaybeCWD, Diags);
if (!MaybeUpdatedArgv) {
llvm::errs() << toString(std::move(MaybeUpdatedArgv.takeError())) << '\n';
return 1;
@@ -285,7 +284,6 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
CharUpdatedArgv.push_back(Arg.c_str());
}
}
-#endif
llvm::outs() << "translation unit command line" << '\n';
for (const auto &Arg : Argv)
diff --git a/clang/tools/driver/cc1modbuildd_main.cpp b/clang/tools/driver/cc1modbuildd_main.cpp
index df30a3b8787f68..6098d9910563a1 100644
--- a/clang/tools/driver/cc1modbuildd_main.cpp
+++ b/clang/tools/driver/cc1modbuildd_main.cpp
@@ -13,7 +13,6 @@
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
-#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h"
#include "clang/Tooling/ModuleBuildDaemon/Utils.h"
#include "llvm/ADT/ArrayRef.h"
@@ -24,10 +23,9 @@
#include "llvm/Support/Threading.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_socket_stream.h"
-// TODO: Make portable
-#if LLVM_ON_UNIX
-
+#include <csignal>
#include <errno.h>
#include <fstream>
#include <mutex>
@@ -44,17 +42,40 @@
#include <unordered_map>
using namespace llvm;
-using namespace clang;
-using namespace tooling::dependencies;
-using namespace cc1modbuildd;
+using namespace clang::tooling::dependencies;
+using namespace clang::tooling::cc1modbuildd;
-// Create unbuffered STDOUT stream so that any logging done by module build
+// Create unbuffered STDOUT stream so that any logging done by the module build
// daemon can be viewed without having to terminate the process
-static raw_fd_ostream &unbuff_outs() {
- static raw_fd_ostream S(STDOUT_FILENO, false, true);
+static llvm::raw_fd_ostream &unbuff_outs() {
+ static llvm::raw_fd_ostream S(fileno(stdout), false, true);
return S;
}
+static bool LogVerbose = false;
+static void logVerbose(const llvm::Twine &message) {
+ if (LogVerbose) {
+ unbuff_outs() << message << '\n';
+ }
+}
+
+static void modifySignals(decltype(SIG_DFL) handler) {
+ if (std::signal(SIGTERM, handler) == SIG_ERR) {
+ llvm::errs() << "failed to handle SIGTERM" << '\n';
+ exit(EXIT_FAILURE);
+ }
+ if (std::signal(SIGINT, handler) == SIG_ERR) {
+ llvm::errs() << "failed to handle SIGINT" << '\n';
+ exit(EXIT_FAILURE);
+ }
+#ifdef SIGHUP
+ if (::signal(SIGHUP, SIG_IGN) == SIG_ERR) {
+ llvm::errs() << "failed to handle SIGHUP" << '\n';
+ exit(EXIT_FAILURE);
+ }
+#endif
+}
+
namespace {
enum class BuildStatus { WAITING, BUILDING, BUILT };
@@ -113,57 +134,56 @@ class DependencyBuildData {
class ModuleBuildDaemonServer {
public:
- SmallString<128> BasePath;
- SmallString<128> SocketPath;
- SmallString<128> PidPath;
-
- ModuleBuildDaemonServer(SmallString<128> Path, ArrayRef<const char *> Argv)
- : BasePath(Path), SocketPath(Path) {
- llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME);
+ llvm::SmallString<256> SocketPath;
+ llvm::SmallString<256> Stderr; // path to stderr
+ llvm::SmallString<256> Stdout; // path to stdout
+
+ explicit ModuleBuildDaemonServer(llvm::StringRef Path)
+ : SocketPath(Path), Stderr(Path), Stdout(Path) {
+ llvm::sys::path::append(SocketPath, SocketFileName);
+ llvm::sys::path::append(Stdout, StdoutFileName);
+ llvm::sys::path::append(Stderr, StderrFileName);
}
- ~ModuleBuildDaemonServer() { shutdownDaemon(SIGTERM); }
-
- int forkDaemon();
- int launchDaemon();
- int listenForClients();
-
- static void handleClient(int Client);
- static void handleRegister(int Client, RegisterMsg ClientRequest);
-
- void shutdownDaemon(int signal) {
- unlink(SocketPath.c_str());
- shutdown(ListenSocketFD, SHUT_RD);
- close(ListenSocketFD);
- exit(EXIT_SUCCESS);
+ ~ModuleBuildDaemonServer() { shutdownDaemon(); }
+
+ void setupDaemonEnv();
+ void createDaemonSocket();
+ void listenForClients();
+
+ static void handleRegister(llvm::raw_socket_stream &Client,
+ RegisterMsg ClientRequest);
+ static void
+ handleConnection(std::shared_ptr<llvm::raw_socket_stream> Connection);
+
+ // TODO: modify so when shutdownDaemon is called the daemon stops accepting
+ // new client connections and waits for all existing client connections to
+ // terminate before closing the file descriptor and exiting
+ // Meant to be called by signal handler to clean up resources
+ void shutdownDaemon() {
+ RunServiceLoop = false;
+ // Signal handler is installed after ServerListener is created and emplaced
+ // into the std::optional<llvm::ListeningSocket>
+ ServerListener.value().shutdown();
}
private:
- // Initializes and returns DiagnosticsEngine
- pid_t Pid = -1;
- int ListenSocketFD = -1;
+ std::atomic<bool> RunServiceLoop = true;
+ // llvm::ListeningSocket does not have a default constructor so use
+ // std::optional as storage
+ std::optional<llvm::ListeningSocket> ServerListener;
};
-// Required to handle SIGTERM by calling Shutdown
+// Used to handle signals
ModuleBuildDaemonServer *DaemonPtr = nullptr;
-void handleSignal(int Signal) {
- if (DaemonPtr != nullptr) {
- DaemonPtr->shutdownDaemon(Signal);
- }
-}
+// DaemonPtr is set to a valid ModuleBuildDaemonServer before the signal handler
+// is installed so there is no need to check if DaemonPtr equals nullptr
+void handleSignal(int) { DaemonPtr->shutdownDaemon(); }
} // namespace
-static bool verbose = false;
-static void verbose_print(const llvm::Twine &message) {
- if (verbose) {
- unbuff_outs() << message << '\n';
- }
-}
-
static DependencyBuildData DaemonBuildData;
-static Expected<TranslationUnitDeps>
-scanTranslationUnit(cc1modbuildd::RegisterMsg Request) {
+static Expected<TranslationUnitDeps> scanTranslationUnit(RegisterMsg Request) {
DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
ScanningOutputFormat::Full);
@@ -292,7 +312,7 @@ static void buildModuleID(const StringRef Executable, const ModuleID ID) {
// returns an updated cc1 command line for the registered cc1 invocation
// after building all modular dependencies
static Expected<std::vector<std::string>>
-processRegisterRequest(cc1modbuildd::RegisterMsg Request) {
+processRegisterRequest(RegisterMsg Request) {
Expected<TranslationUnitDeps> MaybeTUDeps = scanTranslationUnit(Request);
if (!MaybeTUDeps)
@@ -319,89 +339,69 @@ processRegisterRequest(cc1modbuildd::RegisterMsg Request) {
return modifyCC1<std::string>(TUDeps.Commands[0].Arguments);
}
-// Forks and detaches process, creating module build daemon
-int ModuleBuildDaemonServer::forkDaemon() {
-
- pid_t pid = fork();
-
- if (pid < 0) {
+// Sets up file descriptors and signals for module build daemon
+void ModuleBuildDaemonServer::setupDaemonEnv() {
+#ifdef _WIN32
+ if (std::freopen("NUL", "r", stdin) == NULL) {
+#else
+ if (std::freopen("/dev/null", "r", stdin) == NULL) {
+#endif
+ llvm::errs() << "Failed to close stdin" << '\n';
exit(EXIT_FAILURE);
}
- if (pid > 0) {
- exit(EXIT_SUCCESS);
- }
-
- Pid = getpid();
-
- // close(STDIN_FILENO);
- // close(STDOUT_FILENO);
- // close(STDERR_FILENO);
-
- // SmallString<128> STDOUT = BasePath;
- // llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME);
- // freopen(STDOUT.c_str(), "a", stdout);
-
- // SmallString<128> STDERR = BasePath;
- // llvm::sys::path::append(STDERR, STDERR_FILE_NAME);
- // freopen(STDERR.c_str(), "a", stderr);
-
- if (signal(SIGTERM, handleSignal) == SIG_ERR) {
- errs() << "failed to handle SIGTERM" << '\n';
- exit(EXIT_FAILURE);
- }
- if (signal(SIGHUP, SIG_IGN) == SIG_ERR) {
- errs() << "failed to ignore SIGHUP" << '\n';
+ if (std::freopen(Stdout.c_str(), "a", stdout) == NULL) {
+ llvm::errs() << "Failed to redirect stdout to " << Stdout << '\n';
exit(EXIT_FAILURE);
}
- if (setsid() == -1) {
- errs() << "setsid failed" << '\n';
+ if (std::freopen(Stderr.c_str(), "a", stderr) == NULL) {
+ llvm::errs() << "Failed to redirect stderr to " << Stderr << '\n';
exit(EXIT_FAILURE);
}
-
- return EXIT_SUCCESS;
}
-// Creates unix socket for IPC with module build daemon
-int ModuleBuildDaemonServer::launchDaemon() {
-
- // new socket
- if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
- std::perror("Socket create error: ");
- exit(EXIT_FAILURE);
- }
-
- struct sockaddr_un addr;
- memset(&addr, 0, sizeof(struct sockaddr_un));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1);
-
- // bind to local address
- if (bind(ListenSocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-
- // If the socket address is already in use, exit because another module
- // build daemon has successfully launched. When translation units are
- // compiled in parallel, until the socket file is created, all clang
- // invocations will spawn a module build daemon.
- if (errno == EADDRINUSE) {
- close(ListenSocketFD);
- exit(EXIT_SUCCESS);
+// Creates unix socket for IPC with frontends
+void ModuleBuildDaemonServer::createDaemonSocket() {
+ while (true) {
+ llvm::Expected<llvm::ListeningSocket> MaybeServerListener =
+ llvm::ListeningSocket::createUnix(SocketPath);
+
+ if (llvm::Error Err = MaybeServerListener.takeError()) {
+ llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
+ std::error_code EC = SE.convertToErrorCode();
+
+ // Exit successfully if the socket address is already in use. When
+ // TUs are compiled in parallel, until the socket file is created, all
+ // clang invocations will try to spawn a module build daemon.
+#ifdef _WIN32
+ if (EC.value() == WSAEADDRINUSE) {
+#else
+ if (EC == std::errc::address_in_use) {
+#endif
+ exit(EXIT_SUCCESS);
+ } else if (EC == std::errc::file_exists) {
+ if (std::remove(SocketPath.c_str()) != 0) {
+ llvm::errs() << "Failed to remove " << SocketPath << ": "
+ << strerror(errno) << '\n';
+ exit(EXIT_FAILURE);
+ }
+ // If a previous module build daemon invocation crashes, the socket
+ // file will need to be removed before the address can be bound to
+ logVerbose("Removing ineligible file: " + SocketPath);
+ } else {
+ llvm::errs() << "MBD failed to create unix socket: "
+ << SE.getMessage() << ": " << EC.message() << '\n';
+ exit(EXIT_FAILURE);
+ }
+ });
+ } else {
+ logVerbose("MBD created and bound to socket at: " + SocketPath);
+ ServerListener.emplace(std::move(*MaybeServerListener));
+ break;
}
- std::perror("Socket bind error: ");
- exit(EXIT_FAILURE);
}
- verbose_print("mbd created and binded to socket address at: " + SocketPath);
-
- // set socket to accept incoming connection request
- unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count();
- if (listen(ListenSocketFD, MaxBacklog) == -1) {
- std::perror("Socket listen error: ");
- exit(EXIT_FAILURE);
- }
-
- return 0;
}
-void ModuleBuildDaemonServer::handleRegister(int Client,
+void ModuleBuildDaemonServer::handleRegister(llvm::raw_socket_stream &Client,
RegisterMsg ClientRequest) {
Expected<std::vector<std::string>> MaybeExplicitCC1 =
@@ -411,7 +411,7 @@ void ModuleBuildDaemonServer::handleRegister(int Client,
if (!MaybeExplicitCC1) {
RegisterMsg Msg(ActionType::REGISTER, StatusType::FAILURE);
- llvm::Error RegisterFailureWriteErr = writeSocketMsgToSocket(Msg, Client);
+ llvm::Error RegisterFailureWriteErr = writeMsgStructToSocket(Client, Msg);
if (RegisterFailureWriteErr) {
writeError(llvm::joinErrors(std::move(RegisterFailureWriteErr),
@@ -438,7 +438,7 @@ void ModuleBuildDaemonServer::handleRegister(int Client,
ClientRequest.WorkingDirectory, ClientRequest.ExecutablePath,
ExplicitCC1);
- llvm::Error RegisterSuccessWriteErr = writeSocketMsgToSocket(Msg, Client);
+ llvm::Error RegisterSuccessWriteErr = writeMsgStructToSocket(Client, Msg);
if (RegisterSuccessWriteErr) {
writeError(std::move(RegisterSuccessWriteErr),
@@ -449,60 +449,76 @@ void ModuleBuildDaemonServer::handleRegister(int Client,
return;
}
-// Function submitted to thread pool with each client connection. Not
-// responsible for closing client connections
-void ModuleBuildDaemonServer::handleClient(int Client) {
-
- // Read handshake from client
- Expected<HandshakeMsg> MaybeHandshake =
- readSocketMsgFromSocket<HandshakeMsg>(Client);
-
- if (!MaybeHandshake) {
- writeError(MaybeHandshake.takeError(),
- "Failed to read handshake message from socket: ");
+// Function submitted to thread pool with each frontend connection. Not
+// responsible for closing frontend socket connections
+void ModuleBuildDaemonServer::handleConnection(
+ std::shared_ptr<llvm::raw_socket_stream> MovableConnection) {
+ llvm::raw_socket_stream &Connection = *MovableConnection;
+
+ // Read request from frontend
+ llvm::Expected<HandshakeMsg> MaybeHandshakeMsg =
+ readMsgStructFromSocket<HandshakeMsg>(Connection);
+ if (!MaybeHandshakeMsg) {
+ llvm::errs() << "MBD failed to read frontend request: "
+ << llvm::toString(MaybeHandshakeMsg.takeError()) << '\n';
return;
}
- // Handle HANDSHAKE
- RegisterMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
- llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Client);
-
- if (WriteErr) {
- writeError(std::move(WriteErr),
- "Failed to notify client that handshake was received");
+ // Send response to frontend
+ HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS);
+ if (llvm::Error WriteErr = writeMsgStructToSocket(Connection, Msg)) {
+ llvm::errs() << "MBD failed to respond to frontend request: "
+ << llvm::toString(std::move(WriteErr)) << '\n';
return;
}
- // Read register request from client
+ // Read request from frontend
Expected<RegisterMsg> MaybeRegister =
- readSocketMsgFromSocket<RegisterMsg>(Client);
-
+ readMsgStructFromSocket<RegisterMsg>(Connection);
if (!MaybeRegister) {
- writeError(MaybeRegister.takeError(),
- "Failed to read registration message from socket: ");
+ llvm::errs() << "Failed to read registration message from socket: "
+ << llvm::toString(std::move(MaybeRegister.takeError()))
+ << '\n';
return;
}
RegisterMsg Register = std::move(*MaybeRegister);
- handleRegister(Client, Register);
+ handleRegister(Connection, Register);
+
return;
}
-int ModuleBuildDaemonServer::listenForClients() {
-
+void ModuleBuildDaemonServer::listenForClients() {
llvm::DefaultThreadPool Pool;
- int Client;
-
- while (true) {
-
- if ((Client = accept(ListenSocketFD, NULL, NULL)) == -1) {
- std::perror("Socket accept error: ");
+ std::chrono::seconds DaemonTimeout(15);
+ modifySignals(handleSignal);
+
+ while (RunServiceLoop) {
+ llvm::Expected<std::unique_ptr<llvm::raw_socket_stream>> MaybeConnection =
+ ServerListener.value().accept(DaemonTimeout);
+
+ if (llvm::Error Err = MaybeConnection.takeError()) {
+ llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &SE) {
+ std::error_code EC = SE.convertToErrorCode();
+
+ if (EC == std::errc::timed_out) {
+ RunServiceLoop = false;
+ logVerbose("ListeningServer::accept timed out, shutting down");
+ } else if (EC == std::errc::operation_canceled &&
+ RunServiceLoop == false) {
+ logVerbose("Signal received, shutting down");
+ } else
+ llvm::errs() << "MBD failed to accept incoming connection: "
+ << SE.getMessage() << ": " << EC.message() << '\n';
+ });
continue;
}
- Pool.async(handleClient, Client);
+ // Connection must be copy constructable to be passed to Pool.async
+ std::shared_ptr<llvm::raw_socket_stream> Connection(
+ std::move(*MaybeConnection));
+ Pool.async(handleConnection, Connection);
}
- return 0;
}
// Module build daemon is spawned with the following command line:
@@ -520,32 +536,41 @@ int ModuleBuildDaemonServer::listenForClients() {
// -v is optional and provides debug information
//
int cc1modbuildd_main(ArrayRef<const char *> Argv) {
-
- if (Argv.size() < 1) {
- outs() << "spawning a module build daemon requies a command line format of "
- "`clang -cc1modbuildd <path>`. <path> defines where the module "
- "build daemon will create files"
- << '\n';
- return 1;
+ // -cc1modbuildd is sliced away when Argv is pased to cc1modbuildd_main
+ if (find(Argv, llvm::StringRef("-v")) != Argv.end())
+ LogVerbose = true;
+
+ std::string BasePath;
+ // If an argument exists and it is not -v then it must be a BasePath
+ if (!Argv.empty() && strcmp(Argv[0], "-v") != 0)
+ BasePath = Argv[0];
+ else
+ BasePath = getBasePath();
+
+ if (!validBasePathLength(BasePath)) {
+ llvm::errs() << "BasePath '" << BasePath
+ << "' is longer then the max length of "
+ << std::to_string(BasePathMaxLength) << '\n';
+ return EXIT_FAILURE;
}
- // Where to store log files and socket address
- // TODO: Add check to confirm BasePath is directory
- SmallString<128> BasePath(Argv[0]);
llvm::sys::fs::create_directories(BasePath);
- ModuleBuildDaemonServer Daemon(BasePath, Argv);
- // Used to handle signals
- DaemonPtr = &Daemon;
+ {
+ ModuleBuildDaemonServer Daemon(BasePath);
- if (find(Argv, StringRef("-v")) != Argv.end())
- verbose = true;
+ // Used to handle signals
+ DaemonPtr = &Daemon;
- Daemon.forkDaemon();
- Daemon.launchDaemon();
- Daemon.listenForClients();
+ Daemon.setupDaemonEnv();
+ Daemon.createDaemonSocket();
+ Daemon.listenForClients();
- return 0;
-}
+ // Prevents the signal handler from being called after the
+ // ModuleBuildDaemonServer is destructed. The daemon is shutting down and
+ // the program is about to return so signals can be ignored
+ modifySignals(SIG_IGN);
+ }
-#endif // LLVM_ON_UNIX
+ return EXIT_SUCCESS;
+}
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index e7de591c79340d..0cd7db725328f0 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -84,9 +84,7 @@ extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,
const char *Argv0, void *MainAddr,
const llvm::ToolContext &);
-#if LLVM_ON_UNIX
extern int cc1modbuildd_main(ArrayRef<const char *> Argv);
-#endif
static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
More information about the cfe-commits
mailing list