Skip to content

[clang][MBD] set up module build daemon infrastructure #67562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
aca58d3
[clang][MBD] set up module build daemon infrastructure
cpsughrue Jul 10, 2023
4257079
Set ModuleBuildDaemonServer member functions to return void
cpsughrue Feb 6, 2024
5efeff9
Modify ListeningServer::accept to take std::optional<std::chrono::mic…
cpsughrue Feb 6, 2024
cc21148
Make raw_socket_stream FD atomic
cpsughrue Feb 7, 2024
9a46ae1
Remove exit in signal handler
cpsughrue Feb 7, 2024
04ef24e
Create setupSignal functionality so I can set signals back to their d…
cpsughrue Feb 7, 2024
f17a145
Make blacker happy
cpsughrue Feb 7, 2024
15c5933
Fix bug where if daemon crashes and does not unlink socket file the n…
cpsughrue Feb 7, 2024
f73d5ca
Remove std::move(s) preventing copy elision -Wpessimizing-move
cpsughrue Feb 10, 2024
0e47580
Remove exit in shutdownDeamon because ListeningSocket destructor will…
cpsughrue Feb 11, 2024
601bb4d
Take advantage of ability to convert between std::seconds and std::mi…
cpsughrue Feb 11, 2024
1792d90
Switch to break statement to get rid of state whn calling createUnix
cpsughrue Feb 11, 2024
bf58d5d
Fix the style of a few variable names
cpsughrue Feb 11, 2024
6f408ae
After ModuleBuildDaemonServer deconstructs ignore signals
cpsughrue Feb 11, 2024
145f565
Switch over to using non-deprecated version of close and unlink for w…
cpsughrue Feb 11, 2024
87caa38
fix windows build and formatting issues
cpsughrue Feb 13, 2024
cfb4a7f
Remove check that mbd.sock was removed - fails on windows due to how …
cpsughrue Feb 14, 2024
21efa0d
Write test to crash situation where MBD crashes
cpsughrue Feb 14, 2024
e6f9329
Build fixes after rebase
cpsughrue Apr 10, 2024
5ce60ce
Use llvm support ExponentialBackoff function
cpsughrue Apr 10, 2024
4a5a708
Remove llvm Support changes from PR
cpsughrue Apr 17, 2024
f24a7e8
Remove OBE socket function that was just serving as a pass through
cpsughrue Apr 17, 2024
691d4d6
Move where signals begin to be ignored
cpsughrue Apr 17, 2024
860230c
Update error code handling with new and improved ListeningSocket::accept
cpsughrue Apr 17, 2024
fb86101
cppcheck fixes
cpsughrue Apr 17, 2024
96ad3fd
Change how we close stdin on linux
cpsughrue Apr 17, 2024
c8057b2
Remove blank lines after function declaration and remove instances of
cpsughrue Apr 20, 2024
873a4c5
Remove redundant return
cpsughrue Apr 20, 2024
c137f76
Move location of signal handler so that I don't have to wory about sy…
cpsughrue Apr 20, 2024
ed0b755
Remove keyword from header file
cpsughrue Apr 20, 2024
1352a40
Add test with multiple TUs in one command line
cpsughrue Apr 20, 2024
565fdc4
Changes to after rebase
cpsughrue Jul 24, 2024
5b42777
Merge branch 'main' into MBD_handshake
cpsughrue Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this deserves a new DiagnosticBuildDaemonKind.td

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's a bad idea. There are currently only 6 options in the ModuleBuildDaemon group so for now I'd vote to keep them a part of DiagnositcFrontendKinds.td but in subsequent PRs if the number increases, I can pull them out to there own .td file

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">,
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3110,6 +3110,19 @@ 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 the path to "
"where the module build daemon will write log and socket files">,
MarshallingInfoString<FrontendOpts<"ModuleBuildDaemonPath">>;

def fmodules_cache_path : Joined<["-"], "fmodules-cache-path=">, Group<i_Group>,
Flags<[]>, Visibility<[ClangOption, CC1Option]>,
MetaVarName<"<directory>">,
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned EmitPrettySymbolGraphs : 1;

/// Connect to module build daemon.
LLVM_PREFERRED_TYPE(bool)
unsigned ModuleBuildDaemon : 1;

/// Whether to generate reduced BMI for C++20 named modules.
LLVM_PREFERRED_TYPE(bool)
unsigned GenReducedBMI : 1;
Expand Down Expand Up @@ -504,6 +508,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;

Expand Down
36 changes: 36 additions & 0 deletions clang/include/clang/Tooling/ModuleBuildDaemon/Frontend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------- 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/CompilerInvocation.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_socket_stream.h"

namespace clang::tooling::cc1modbuildd {

llvm::Error attemptHandshake(llvm::raw_socket_stream &Client,
clang::DiagnosticsEngine &Diag);

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);

void spawnModuleBuildDaemonAndHandshake(const clang::CompilerInvocation &Clang,
const char *Argv0,
clang::DiagnosticsEngine &Diag);

} // namespace clang::tooling::cc1modbuildd

#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_CLIENT_H
131 changes: 131 additions & 0 deletions clang/include/clang/Tooling/ModuleBuildDaemon/SocketSupport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//===------------------------- 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 "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 {

enum class ActionType { 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 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 llvm {
namespace yaml {
template <>
struct ScalarEnumerationTraits<clang::tooling::cc1modbuildd::StatusType> {
static void enumeration(IO &Io, clang::tooling::cc1modbuildd::StatusType &S) {
Io.enumCase(S, "REQUEST",
clang::tooling::cc1modbuildd::StatusType::REQUEST);
Io.enumCase(S, "SUCCESS",
clang::tooling::cc1modbuildd::StatusType::SUCCESS);
Io.enumCase(S, "FAILURE",
clang::tooling::cc1modbuildd::StatusType::FAILURE);
}
};

template <>
struct ScalarEnumerationTraits<clang::tooling::cc1modbuildd::ActionType> {
static void enumeration(IO &Io, clang::tooling::cc1modbuildd::ActionType &A) {
Io.enumCase(A, "HANDSHAKE",
clang::tooling::cc1modbuildd::ActionType::HANDSHAKE);
}
};

template <> struct MappingTraits<clang::tooling::cc1modbuildd::HandshakeMsg> {
static void mapping(IO &Io, clang::tooling::cc1modbuildd::HandshakeMsg &H) {
Io.mapRequired("Action", H.MsgAction);
Io.mapRequired("Status", H.MsgStatus);
}
};
} // namespace yaml
} // namespace llvm
#endif // LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_SOCKETMSGSUPPORT_H
56 changes: 56 additions & 0 deletions clang/include/clang/Tooling/ModuleBuildDaemon/Utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===------------------------------ 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 frontend and the module build daemon
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H
#define LLVM_CLANG_TOOLING_MODULEBUILDDAEMON_UTILS_H

#include "llvm/Support/Error.h"

#include <chrono>
#include <string>

#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);

} // namespace clang::tooling::cc1modbuildd

#endif
13 changes: 13 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3982,6 +3982,19 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
bool HaveStdCXXModules = IsCXX && HaveStd20;
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.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Tooling/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_subdirectory(ASTDiff)
add_subdirectory(Syntax)
add_subdirectory(DependencyScanning)
add_subdirectory(Transformer)
add_subdirectory(ModuleBuildDaemon)

add_clang_library(clangTooling
AllTUsExecution.cpp
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Tooling/ModuleBuildDaemon/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangModuleBuildDaemon
Frontend.cpp
SocketSupport.cpp
Utils.cpp
)
Loading