[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 07:19:21 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/4] [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/4] 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/4] 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/4] 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) {
More information about the cfe-commits
mailing list