[Lldb-commits] [lldb] 68c8c8c - Reland "[lldb][RPC] Upstream lldb-rpc-gen tool" (#146969)" Attempt 2 (#148996)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 23 18:10:19 PDT 2025
Author: Chelsea Cassanova
Date: 2025-07-23T18:10:16-07:00
New Revision: 68c8c8ceeba6da96189335236e3ec80a082e4d7b
URL: https://github.com/llvm/llvm-project/commit/68c8c8ceeba6da96189335236e3ec80a082e4d7b
DIFF: https://github.com/llvm/llvm-project/commit/68c8c8ceeba6da96189335236e3ec80a082e4d7b.diff
LOG: Reland "[lldb][RPC] Upstream lldb-rpc-gen tool" (#146969)" Attempt 2 (#148996)
Second attempt at relanding the lldb-rpc-gen tool. This should fix 2
issues:
- An assert that was hitting when building on Linux. The assert would
hit in the server source emitter, specifically when attemping to
determine the storage size for a return type is that is a pointer, but
isn't a const char *, const char ** or void pointer.
The assert would hit when attempting to generate
SBAttachInfo::GetProcessPluginName, which returns a const char *
(meaning it shouldn't have been in the code block for the assert at
all). The reason that it was hitting the assert when generating this
function is that lldb_rpc_gen::TypeIsConstCharPtr was returning false
for this function even though it did return a const char *. This was
happening because when checking the return type for a const char *,
TypeIsConstCharPtr would only check that the underlying type was a
signed char. This failed on Linux (but was fine on Darwin), as the
underlying type also needs to be checked for being an unsigned char.
- Cross compiling support
The build for lldb-rpc-gen had no support for cross compiling and as
such, the sources generated for lldb-rpc-gen would get compiled too
early in phase 2 when cross compiling, before the Clang toolchain was
built and this led to an error when trying to include stdlib files. This
reland splits this build into 2 by building the tool first and then
compiling the sources in the second stage of the cross-compiled build.
Original PR Description:
This commit upstreams the lldb-rpc-gen tool, a ClangTool that generates
the LLDB RPC client and server interfaces. This tool, as well as LLDB
RPC itself is built by default. If it needs to be disabled, put
-DLLDB_BUILD_LLDBRPC=OFF in your CMake invocation.
https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804
Original PR Link:
github.com/llvm/llvm-project/pull/138031
Added:
lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h
lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test
lldb/test/Shell/RPC/Generator/lit.local.cfg
lldb/tools/lldb-rpc-gen/CMakeLists.txt
lldb/tools/lldb-rpc-gen/RPCCommon.cpp
lldb/tools/lldb-rpc-gen/RPCCommon.h
lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.h
lldb/tools/lldb-rpc/CMakeLists.txt
lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake
lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake
Modified:
lldb/cmake/modules/LLDBConfig.cmake
lldb/test/CMakeLists.txt
lldb/test/Shell/helper/toolchain.py
lldb/test/Shell/lit.site.cfg.py.in
lldb/tools/CMakeLists.txt
Removed:
lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h
################################################################################
diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index 8c30b6e09d2c7..1bc494a48cb03 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -323,4 +323,23 @@ else()
set(LLDB_CAN_USE_DEBUGSERVER OFF)
endif()
+# In a cross-compile build, we need to skip building the generated
+# lldb-rpc sources in the first phase of host build so that they can
+# get built using the just-built Clang toolchain in the second phase.
+if (NOT DEFINED LLDB_CAN_USE_LLDB_RPC_SERVER)
+ if ((CMAKE_CROSSCOMPILING OR LLVM_HOST_TRIPLE MATCHES "${LLVM_DEFAULT_TARGET_TRIPLE}") AND
+ CMAKE_SYSTEM_NAME MATCHES "AIX|Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows")
+ set(LLDB_CAN_USE_LLDB_RPC_SERVER ON)
+ else()
+ set(LLDB_CAN_USE_LLDB_RPC_SERVER OFF)
+ endif()
+endif()
+
+if (CMAKE_CROSSCOMPILING)
+ set(LLDB_BUILD_LLDBRPC OFF CACHE BOOL "")
+ get_host_tool_path(lldb-rpc-gen LLDB_RPC_GEN_EXE lldb_rpc_gen_exe lldb_rpc_gen_target)
+else()
+ set(LLDB_BUILD_LLDBRPC ON CACHE BOOL "")
+endif()
+
include(LLDBGenerateConfig)
diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt
index 6449ac5a9247f..b786edc46cd2f 100644
--- a/lldb/test/CMakeLists.txt
+++ b/lldb/test/CMakeLists.txt
@@ -132,6 +132,10 @@ if(TARGET lldb-framework)
add_lldb_test_dependency(lldb-framework)
endif()
+if (LLDB_CAN_USE_LLDB_RPC_SERVER)
+ add_lldb_test_dependency(lldb-rpc-generate-sources)
+endif()
+
# Add dependencies that are not exported targets when building standalone.
if(NOT LLDB_BUILT_STANDALONE)
add_lldb_test_dependency(
@@ -249,7 +253,8 @@ llvm_canonicalize_cmake_booleans(
LLDB_TEST_SHELL_DISABLE_REMOTE
LLDB_TOOL_LLDB_SERVER_BUILD
LLDB_USE_SYSTEM_DEBUGSERVER
- LLDB_IS_64_BITS)
+ LLDB_IS_64_BITS
+ LLDB_BUILD_LLDBRPC)
# Configure the individual test suites.
add_subdirectory(API)
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h b/lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test b/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test
new file mode 100644
index 0000000000000..15fcf8fb39c7d
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test
@@ -0,0 +1,9 @@
+RUN: %lldb-rpc-gen --output-dir=%t %S/../Inputs/SBDummy.h
+
+RUN: ls %t | FileCheck %s
+
+# We're just making sure that the tool emits the class names,
+# methods and skipped methods file in the output directory.
+CHECK: SBAPI.def
+CHECK: SBClasses.def
+CHECK: SkippedMethods.txt
diff --git a/lldb/test/Shell/RPC/Generator/lit.local.cfg b/lldb/test/Shell/RPC/Generator/lit.local.cfg
new file mode 100644
index 0000000000000..db9494781c00c
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/lit.local.cfg
@@ -0,0 +1,3 @@
+# All tests for the tool need lldb-rpc-gen to be built.
+if not config.lldb_has_lldbrpc:
+ config.unsupported = True
diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py
index 42968128f2702..728f6347242f1 100644
--- a/lldb/test/Shell/helper/toolchain.py
+++ b/lldb/test/Shell/helper/toolchain.py
@@ -156,6 +156,16 @@ def use_lldb_substitutions(config):
extra_args=["platform"],
unresolved="ignore",
),
+ ToolSubst(
+ "%lldb-rpc-gen",
+ command=FindTool("lldb-rpc-gen"),
+ # We need the LLDB build directory root to pass into the tool, not the test build root.
+ extra_args=[
+ "-p " + config.lldb_build_directory + "/..",
+ '--extra-arg="-resource-dir=' + config.clang_resource_dir + '"',
+ ],
+ unresolved="ignore",
+ ),
"lldb-test",
"lldb-dap",
ToolSubst(
diff --git a/lldb/test/Shell/lit.site.cfg.py.in b/lldb/test/Shell/lit.site.cfg.py.in
index 5be5359217769..beaa41e6fd379 100644
--- a/lldb/test/Shell/lit.site.cfg.py.in
+++ b/lldb/test/Shell/lit.site.cfg.py.in
@@ -33,6 +33,7 @@ config.lldb_build_directory = "@LLDB_TEST_BUILD_DIRECTORY@"
config.have_lldb_server = @LLDB_TOOL_LLDB_SERVER_BUILD@
config.lldb_system_debugserver = @LLDB_USE_SYSTEM_DEBUGSERVER@
config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@"
+config.lldb_has_lldbrpc = @LLDB_BUILD_LLDBRPC@
# The shell tests use their own module caches.
config.lldb_module_cache = os.path.join("@LLDB_TEST_MODULE_CACHE_LLDB@", "lldb-shell")
config.clang_module_cache = os.path.join("@LLDB_TEST_MODULE_CACHE_CLANG@", "lldb-shell")
diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt
index 6804dc234555b..e2f039527ad75 100644
--- a/lldb/tools/CMakeLists.txt
+++ b/lldb/tools/CMakeLists.txt
@@ -10,6 +10,12 @@ add_subdirectory(lldb-fuzzer EXCLUDE_FROM_ALL)
add_lldb_tool_subdirectory(lldb-instr)
add_lldb_tool_subdirectory(lldb-dap)
+if (LLDB_BUILD_LLDBRPC)
+ add_lldb_tool_subdirectory(lldb-rpc-gen)
+endif()
+if (LLDB_CAN_USE_LLDB_RPC_SERVER)
+ add_subdirectory(lldb-rpc)
+endif()
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_lldb_tool_subdirectory(darwin-debug)
diff --git a/lldb/tools/lldb-rpc-gen/CMakeLists.txt b/lldb/tools/lldb-rpc-gen/CMakeLists.txt
new file mode 100644
index 0000000000000..65b76431d1bea
--- /dev/null
+++ b/lldb/tools/lldb-rpc-gen/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_lldb_tool(lldb-rpc-gen
+ RPCCommon.cpp
+ server/RPCServerHeaderEmitter.cpp
+ server/RPCServerSourceEmitter.cpp
+ lldb-rpc-gen.cpp
+
+ CLANG_LIBS
+ clangAST
+ clangBasic
+ clangCodeGen
+ clangFrontend
+ clangLex
+ clangRewrite
+ clangSerialization
+ clangTooling
+
+ LINK_COMPONENTS
+ Support
+ )
+
+if (NOT DEFINED LLDB_RPC_GEN_EXE)
+ set(LLDB_RPC_GEN_EXE $<TARGET_FILE:lldb-rpc-gen> CACHE STRING "Executable that generates lldb-rpc-server")
+endif()
diff --git a/lldb/tools/lldb-rpc-gen/RPCCommon.cpp b/lldb/tools/lldb-rpc-gen/RPCCommon.cpp
new file mode 100644
index 0000000000000..34791fa8ef231
--- /dev/null
+++ b/lldb/tools/lldb-rpc-gen/RPCCommon.cpp
@@ -0,0 +1,508 @@
+//===-- RPCCommon.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 "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Lex/Lexer.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cstring>
+
+using namespace clang;
+
+// We intentionally do not generate some classes because they are currently
+// inconvenient, they aren't really used by most consumers, or we're not sure
+// why they exist.
+static constexpr llvm::StringRef DisallowedClasses[] = {
+ "SBCommunication", // This class is pretty much unused by consumers, so we
+ // skip it.
+ "SBInputReader", // This class is pretty much unused by consumers, so we
+ // skip it.
+ "SBCommandPluginInterface", // This class uses virtual functions, and the SB
+ // API should not have those, so we skip this
+ // class.
+ "SBCommand", // There's nothing too
diff icult about this one, but many of
+ // its methods take a SBCommandPluginInterface pointer so
+ // there's no reason to support this.
+};
+
+// NOTE: In lldb-rpc-gen, we use mangled names when we need to work with
+// functions. We do this because we support many functions that have overloads,
+// and mangled names have no ambiguity which makes it easier to keep track of.
+// This is also possible since the LLDB SB API is stable.
+
+// We intentionally avoid generating certain methods either because they are
+//
diff icult to support correctly or they aren't really used much from C++.
+// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED.
+// Normally this macro defines to the deprecated annotation, but this
+// functionality is removed in SBDefines.h when generating SWIG bindings which
+// we use for testing. Because of this, there is no annotation for the tool to
+// pick up on so this list will be used while we have this restriction in
+// SBDefines.h.
+static constexpr llvm::StringRef DisallowedMethods[] = {
+ // The threading functionality in SBHostOS is deprecated and thus we do not
+ // generate them. It would be ideal to add the annotations to the methods
+ // and then support not generating deprecated methods. However, without
+ // annotations the generator generates most things correctly. This one is
+ // problematic because it returns a pointer to an "opaque" structure
+ // (thread_t) that is not `void *`, so special casing it is more effort than
+ // it's worth.
+ "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE",
+ "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE",
+ "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE",
+ "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE",
+ "_ZN4lldb8SBHostOS13ThreadCreatedEPKc",
+};
+
+static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = {
+ "SBHostOS",
+ "SBReproducer",
+};
+
+static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = {
+ "SBHostOS",
+ "SBReproducer",
+ "SBStream",
+ "SBProgress",
+};
+
+static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = {
+ "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm",
+ "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh",
+ "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh",
+ "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym",
+ "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm",
+ "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm",
+ "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim",
+ "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm",
+ "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym",
+ "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm",
+ "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm",
+ "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim",
+ "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm",
+ "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm",
+ "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm",
+ "_ZN4lldb10SBDebugger13DispatchInputEPKvm",
+ "_ZN4lldb6SBFile4ReadEPhmPm",
+ "_ZN4lldb6SBFile5WriteEPKhmPm",
+ "_ZNK4lldb10SBFileSpec7GetPathEPcm",
+ "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm",
+ "_ZN4lldb8SBModule10GetVersionEPjj",
+ "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm",
+ "_ZNK4lldb9SBProcess9GetSTDOUTEPcm",
+ "_ZNK4lldb9SBProcess9GetSTDERREPcm",
+ "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm",
+ "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE",
+ "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE",
+ "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE",
+ "_ZNK4lldb16SBStructuredData14GetStringValueEPcm",
+ "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_"
+ "14SBFileSpecListES6_",
+ "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE",
+ "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm",
+ "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm",
+ "_ZN4lldb8SBTarget15GetInstructionsEyPKvm",
+ "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm",
+ "_ZN4lldb8SBThread18GetStopDescriptionEPcm",
+ // The below mangled names are used for dummy methods in shell tests
+ // that test the emitters' output. If you're adding any new mangled names
+ // from the actual SB API to this list please add them above.
+ "_ZN4lldb33SBRPC_"
+ "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm",
+ "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm",
+ "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm",
+};
+
+// These classes inherit from rpc::ObjectRef directly (as opposed to
+// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI
+// breaking, so we preserve that compatibility here.
+//
+// lldb-rpc-gen emits classes as LocalObjectRefs by default.
+//
+// FIXME: Does it matter which one it emits by default?
+static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = {
+ "SBAddress",
+ "SBBreakpointName",
+ "SBCommandInterpreter",
+ "SBCommandReturnObject",
+ "SBError",
+ "SBExecutionContext",
+ "SBExpressionOptions",
+ "SBFileSpec",
+ "SBFileSpecList",
+ "SBFormat",
+ "SBFunction",
+ "SBHistoricalFrame",
+ "SBHistoricalLineEntry",
+ "SBHistoricalLineEntryList",
+ "SBLineEntry",
+ "SBStream",
+ "SBStringList",
+ "SBStructuredData",
+ "SBSymbolContext",
+ "SBSymbolContextList",
+ "SBTypeMember",
+ "SBTypeSummaryOptions",
+ "SBValueList",
+};
+
+QualType lldb_rpc_gen::GetUnderlyingType(QualType T) {
+ QualType UnderlyingType;
+ if (T->isPointerType())
+ UnderlyingType = T->getPointeeType();
+ else if (T->isReferenceType())
+ UnderlyingType = T.getNonReferenceType();
+ else
+ UnderlyingType = T;
+
+ return UnderlyingType;
+}
+
+QualType lldb_rpc_gen::GetUnqualifiedUnderlyingType(QualType T) {
+ return GetUnderlyingType(T).getUnqualifiedType();
+}
+
+std::string lldb_rpc_gen::GetMangledName(ASTContext &Context,
+ CXXMethodDecl *MDecl) {
+ std::string Mangled;
+ llvm::raw_string_ostream MangledStream(Mangled);
+
+ GlobalDecl GDecl;
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl))
+ GDecl = GlobalDecl(CtorDecl, Ctor_Complete);
+ else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(MDecl))
+ GDecl = GlobalDecl(DtorDecl, Dtor_Deleting);
+ else
+ GDecl = GlobalDecl(MDecl);
+
+ MangleContext *MC = Context.createMangleContext();
+ MC->mangleName(GDecl, MangledStream);
+ return Mangled;
+}
+
+static auto CheckTypeForLLDBPrivate = [](const Type *Ty) {};
+bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) {
+ auto CheckTypeForLLDBPrivate = [](const Type *Ty) {
+ if (!Ty)
+ return false;
+ const auto *CXXRDecl = Ty->getAsCXXRecordDecl();
+ if (!CXXRDecl)
+ return false;
+ const auto *NSDecl =
+ llvm::dyn_cast<NamespaceDecl>(CXXRDecl->getDeclContext());
+ if (!NSDecl)
+ return false;
+ return NSDecl->getName() == "lldb_private";
+ };
+
+ // First, get the underlying type (remove qualifications and strip off any
+ // pointers/references). Then we'll need to desugar this type. This will
+ // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll
+ // actually see something like "std::shared_ptr<lldb_private::Debugger>".
+ QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+ const Type *DesugaredType =
+ UnqualifiedUnderlyingType->getUnqualifiedDesugaredType();
+ assert(DesugaredType && "DesugaredType from a valid Type is nullptr!");
+
+ // Check the type itself.
+ if (CheckTypeForLLDBPrivate(DesugaredType))
+ return true;
+
+ // If that didn't work, it's possible that the type has a template argument
+ // that is an lldb_private type.
+ if (const auto *TemplateSDecl =
+ llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+ DesugaredType->getAsCXXRecordDecl())) {
+ for (const TemplateArgument &TA :
+ TemplateSDecl->getTemplateArgs().asArray()) {
+ if (TA.getKind() != TemplateArgument::Type)
+ continue;
+ if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr()))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool lldb_rpc_gen::TypeIsSBClass(QualType T) {
+ QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+ const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();
+ if (!CXXRDecl)
+ return false; // SB Classes are always C++ classes
+
+ return CXXRDecl->getName().starts_with("SB");
+}
+
+bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) {
+ if (!T->isPointerType())
+ return false;
+
+ QualType UnderlyingType = T->getPointeeType();
+ if (!UnderlyingType.isConstQualified())
+ return false;
+
+ // NOTE: We should be able to do `UnderlyingType->isCharType` but that will
+ // return true for `const uint8_t *` since that is effectively an unsigned
+ // char pointer. We currently do not support pointers other than `const char
+ // *` and `const char **`.
+
+ // NOTE: Checking that the underlying type is a signed integer works on Darwin
+ // platforms, but Linux platforms expect that the underlying type is an
+ // unsigned integer.
+ return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) ||
+ UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar) ||
+ UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_U) ||
+ UnderlyingType->isSpecificBuiltinType(BuiltinType::UChar);
+}
+
+bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) {
+ if (!T->isPointerType())
+ return false;
+
+ return TypeIsConstCharPtr(T->getPointeeType());
+}
+
+bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) {
+ QualType UUT = GetUnqualifiedUnderlyingType(T);
+ const auto *CXXRDecl = UUT->getAsCXXRecordDecl();
+ if (!CXXRDecl)
+ return false;
+
+ llvm::StringRef DeclName = CXXRDecl->getName();
+ for (const llvm::StringRef DisallowedClass : DisallowedClasses)
+ if (DeclName == DisallowedClass)
+ return true;
+ return false;
+}
+
+bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) {
+ return T->isFunctionPointerType();
+}
+
+bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context,
+ CXXMethodDecl *MDecl) {
+ bool isDisallowed = false;
+ std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl);
+ if (llvm::is_contained(DisallowedMethods, MangledName))
+ isDisallowed = true;
+
+ if (MDecl->hasAttrs()) {
+ for (auto *attr : MDecl->getAttrs()) {
+ if (strcmp(attr->getAttrName()->getNameStart(), "deprecated") == 0)
+ isDisallowed = true;
+ }
+ }
+ return isDisallowed;
+}
+
+bool lldb_rpc_gen::HasCallbackParameter(CXXMethodDecl *MDecl) {
+ bool HasCallbackParameter = false;
+ bool HasBatonParameter = false;
+ auto End = MDecl->parameters().end();
+ for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) {
+ if ((*Iter)->getType()->isFunctionPointerType())
+ HasCallbackParameter = true;
+ else if ((*Iter)->getType()->isVoidPointerType())
+ HasBatonParameter = true;
+ }
+
+ return HasCallbackParameter && HasBatonParameter;
+}
+
+// NOTE: There's possibly a more clever way to do this, but we're keeping
+// the string replacement way here. Here is why it is written this way:
+// By the time we have already created a `Method` object, we have extracted the
+// `QualifiedName` and the relevant QualTypes for parameters/return types, many
+// of which contains "lldb::" in them. To change it in a way that would be
+// friendly to liblldbrpc, we would need to have a way of replacing that
+// namespace at the time of creating a Method, and only for liblldbrpc methods.
+// IMO this would complicate Method more than what I'm doing here, and not
+// necessarily for any more benefit.
+// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do
+// something similar to this. It also operates primarily on string replacement,
+// but uses more sophisticated clang tooling to do so.
+// For now, this will do what we need it to do.
+std::string
+lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) {
+ const char *lldb_namespace = "lldb::";
+ auto Pos = Name.find(lldb_namespace);
+ while (Pos != std::string::npos) {
+ constexpr size_t SizeOfLLDBNamespace = 6;
+ Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc::");
+ Pos = Name.find(lldb_namespace);
+ }
+ return Name;
+}
+
+std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) {
+ const char *lldb_namespace = "lldb::";
+ auto Pos = Name.find(lldb_namespace);
+ if (Pos != std::string::npos) {
+ constexpr size_t SizeOfLLDBNamespace = 6;
+ Name = Name.substr(Pos + SizeOfLLDBNamespace);
+ }
+ return Name;
+}
+
+bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) {
+ return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName);
+}
+
+bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) {
+ return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName);
+}
+
+bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) {
+ return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName);
+}
+
+std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) {
+ assert(lldb_rpc_gen::TypeIsSBClass(T) &&
+ "Cannot get SBClass name from non-SB class type!");
+
+ QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T);
+ const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl();
+ assert(CXXRDecl && "SB class was not CXXRecordDecl!");
+ if (!CXXRDecl)
+ return std::string();
+
+ return CXXRDecl->getName().str();
+}
+lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy,
+ ASTContext &Context)
+ : Policy(Policy), Context(Context),
+ QualifiedName(MDecl->getQualifiedNameAsString()),
+ BaseName(MDecl->getNameAsString()),
+ MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)),
+ ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()),
+ IsInstance(MDecl->isInstance()), IsCtor(isa<CXXConstructorDecl>(MDecl)),
+ IsCopyAssign(MDecl->isCopyAssignmentOperator()),
+ IsMoveAssign(MDecl->isMoveAssignmentOperator()),
+ IsDtor(isa<CXXDestructorDecl>(MDecl)),
+ IsConversionMethod(isa<CXXConversionDecl>(MDecl)) {
+ uint8_t UnnamedArgIdx = 0;
+ bool PrevParamWasPointer = false;
+ for (const auto *ParamDecl : MDecl->parameters()) {
+ Param param;
+ if (ParamDecl->hasDefaultArg())
+ param.DefaultValueText =
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ ParamDecl->getDefaultArg()->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts())
+ .str();
+
+ param.IsFollowedByLen = false;
+ param.Name = ParamDecl->getNameAsString();
+ // If the parameter has no name, we'll generate one
+ if (param.Name.empty()) {
+ param.Name = "arg" + std::to_string(UnnamedArgIdx);
+ UnnamedArgIdx++;
+ }
+ param.Type = ParamDecl->getType();
+
+ // FIXME: Instead of using this heuristic, the ideal thing would be to add
+ // annotations to the SBAPI methods themselves. For now, we have a list of
+ // methods that we know will need this.
+ if (PrevParamWasPointer) {
+ PrevParamWasPointer = false;
+ const bool IsIntegerType = param.Type->isIntegerType() &&
+ !param.Type->isBooleanType() &&
+ !param.Type->isEnumeralType();
+ if (IsIntegerType && llvm::is_contained(MethodsWithPointerPlusLen,
+ llvm::StringRef(MangledName)))
+ Params.back().IsFollowedByLen = true;
+ }
+
+ if (param.Type->isPointerType() &&
+ !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) &&
+ !param.Type->isFunctionPointerType())
+ PrevParamWasPointer = true;
+
+ if (param.Type->isFunctionPointerType())
+ ContainsFunctionPointerParameter = true;
+
+ Params.push_back(param);
+ }
+
+ if (IsInstance)
+ ThisType = MDecl->getThisType();
+
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(MDecl)) {
+ IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit();
+ IsCopyCtor = CtorDecl->isCopyConstructor();
+ IsMoveCtor = CtorDecl->isMoveConstructor();
+ } else if (const auto *ConversionDecl = dyn_cast<CXXConversionDecl>(MDecl))
+ IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit();
+}
+
+// Adding a '<' allows us to use Methods in ordered containers.
+// The ordering is on memory addresses.
+bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const {
+ return this < &rhs;
+}
+
+std::string
+lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation,
+ bool IncludeDefaultValue) const {
+ assert((!IncludeDefaultValue || Generation == eLibrary) &&
+ "Default values should only be emitted on the library side!");
+
+ std::vector<std::string> ParamList;
+
+ if (Generation == eLibrary && RequiresConnectionParameter())
+ ParamList.push_back("const rpc::Connection &connection");
+
+ for (const auto &Param : Params) {
+ std::string ParamString;
+ llvm::raw_string_ostream ParamStringStream(ParamString);
+
+ if (Generation == eLibrary)
+ ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ Param.Type.getAsString(Policy));
+ else
+ ParamStringStream << Param.Type.getAsString(Policy);
+
+ ParamStringStream << " " << Param.Name;
+ if (IncludeDefaultValue && Generation == eLibrary &&
+ !Param.DefaultValueText.empty())
+ ParamStringStream << " = "
+ << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+ Param.DefaultValueText);
+
+ ParamList.push_back(ParamString);
+ }
+
+ return llvm::join(ParamList, ", ");
+}
+
+bool lldb_rpc_gen::Method::RequiresConnectionParameter() const {
+ if (!IsCtor && IsInstance)
+ return false;
+ if (IsCopyCtor || IsMoveCtor)
+ return false;
+ for (const auto &Param : Params) {
+ // We can re-use the connection from our parameter if possible.
+ // Const-qualified parameters are input parameters and already
+ // have a valid connection to provide to the current method.
+ if (TypeIsSBClass(Param.Type) &&
+ GetUnderlyingType(Param.Type).isConstQualified())
+ return false;
+ }
+
+ return true;
+}
diff --git a/lldb/tools/lldb-rpc-gen/RPCCommon.h b/lldb/tools/lldb-rpc-gen/RPCCommon.h
new file mode 100644
index 0000000000000..edc03b4f81a3d
--- /dev/null
+++ b/lldb/tools/lldb-rpc-gen/RPCCommon.h
@@ -0,0 +1,108 @@
+//===-- RPCCommon.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 LLDB_RPC_GEN_RPCCOMMON_H
+#define LLDB_RPC_GEN_RPCCOMMON_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <string>
+
+using namespace clang;
+
+namespace lldb_rpc_gen {
+QualType GetUnderlyingType(QualType T);
+QualType GetUnqualifiedUnderlyingType(QualType T);
+std::string GetMangledName(ASTContext &Context, CXXMethodDecl *MDecl);
+
+bool TypeIsFromLLDBPrivate(QualType T);
+bool TypeIsSBClass(QualType T);
+bool TypeIsConstCharPtr(QualType T);
+bool TypeIsConstCharPtrPtr(QualType T);
+bool TypeIsDisallowedClass(QualType T);
+bool TypeIsCallbackFunctionPointer(QualType T);
+
+bool MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl);
+bool HasCallbackParameter(CXXMethodDecl *MDecl);
+
+std::string ReplaceLLDBNamespaceWithRPCNamespace(std::string Name);
+std::string StripLLDBNamespace(std::string Name);
+bool SBClassRequiresDefaultCtor(const std::string &ClassName);
+bool SBClassRequiresCopyCtorAssign(const std::string &ClassName);
+bool SBClassInheritsFromObjectRef(const std::string &ClassName);
+std::string GetSBClassNameFromType(QualType T);
+struct Param {
+ std::string Name;
+ QualType Type;
+ std::string DefaultValueText;
+ bool IsFollowedByLen;
+};
+
+enum GenerationKind : bool { eServer, eLibrary };
+
+struct Method {
+ enum Type { eOther, eConstructor, eDestructor };
+
+ Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy,
+ ASTContext &Context);
+
+ // Adding a '<' allows us to use Methods in ordered containers.
+ // The ordering is on memory addresses.
+ bool operator<(const lldb_rpc_gen::Method &rhs) const;
+ const PrintingPolicy &Policy;
+ const ASTContext &Context;
+ std::string QualifiedName;
+ std::string BaseName;
+ std::string MangledName;
+ QualType ReturnType;
+ QualType ThisType;
+ std::vector<Param> Params;
+ bool IsConst = false;
+ bool IsInstance = false;
+ bool IsCtor = false;
+ bool IsCopyCtor = false;
+ bool IsCopyAssign = false;
+ bool IsMoveCtor = false;
+ bool IsMoveAssign = false;
+ bool IsDtor = false;
+ bool IsConversionMethod = false;
+ bool IsExplicitCtorOrConversionMethod = false;
+ bool ContainsFunctionPointerParameter = false;
+
+ std::string CreateParamListAsString(GenerationKind Generation,
+ bool IncludeDefaultValue = false) const;
+
+ bool RequiresConnectionParameter() const;
+};
+
+std::string
+GetDefaultArgumentsForConstructor(std::string ClassName,
+ const lldb_rpc_gen::Method &method);
+
+class FileEmitter {
+protected:
+ FileEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile)
+ : OutputFile(std::move(OutputFile)), IndentLevel(0) {}
+ void EmitLine(const std::string &line) {
+ for (auto i = 0; i < IndentLevel; i++)
+ OutputFile->os() << " ";
+
+ OutputFile->os() << line << "\n";
+ }
+
+ void EmitNewLine() { OutputFile->os() << "\n"; }
+
+ std::unique_ptr<llvm::ToolOutputFile> OutputFile;
+ uint8_t IndentLevel;
+};
+} // namespace lldb_rpc_gen
+#endif // LLDB_RPC_GEN_RPCCOMMON_H
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
similarity index 93%
rename from lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
rename to lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
index e6b601ea13012..fdcfee96a387e 100644
--- a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
+++ b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
#include "RPCCommon.h"
-#include "RPCServerHeaderEmitter.h"
-#include "RPCServerSourceEmitter.h"
+#include "server/RPCServerHeaderEmitter.h"
+#include "server/RPCServerSourceEmitter.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
@@ -40,15 +40,15 @@ static llvm::cl::opt<std::string>
llvm::cl::desc("Directory to output generated files to"),
llvm::cl::init(""), llvm::cl::cat(RPCGenCategory));
-static std::string GetLibraryOutputDirectory() {
+static std::string GetServerOutputDirectory() {
llvm::SmallString<128> Path(OutputDir.getValue());
- llvm::sys::path::append(Path, "lib");
+ llvm::sys::path::append(Path, "server");
return std::string(Path);
}
static std::unique_ptr<llvm::ToolOutputFile>
CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) {
- llvm::SmallString<128> Path(OutputDir);
+ llvm::SmallString<256> Path(OutputDir);
llvm::sys::path::append(Path, Filename);
std::error_code EC;
@@ -100,7 +100,8 @@ class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
for (CXXMethodDecl *MDecl : RDecl->methods()) {
const std::string MangledName =
lldb_rpc_gen::GetMangledName(Context, MDecl);
- const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(MangledName);
+ const bool IsDisallowed =
+ lldb_rpc_gen::MethodIsDisallowed(Context, MDecl);
const bool HasCallbackParameter =
lldb_rpc_gen::HasCallbackParameter(MDecl);
SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl);
@@ -314,9 +315,9 @@ bool EmitClassNamesFile(std::set<std::string> &ClassNames) {
if (!ClassNamesFile)
return false;
- ClassNamesFile->os() << "#ifndef SBCLASS\n";
- ClassNamesFile->os() << "#error \"SBClass must be defined\"\n";
- ClassNamesFile->os() << "#endif\n";
+ ClassNamesFile->os() << "#ifndef SBCLASS\n"
+ << "#error \"SBClass must be defined\"\n"
+ << "#endif\n";
for (const auto &ClassName : ClassNames) {
if (ClassName == "SBStream" || ClassName == "SBProgress")
@@ -340,9 +341,9 @@ bool EmitMethodNamesFile(std::set<std::string> &MangledMethodNames) {
if (!MethodNamesFile)
return false;
- MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n";
- MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n";
- MethodNamesFile->os() << "#endif\n";
+ MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n"
+ << "#error \"GENERATE_SBAPI must be defined\"\n"
+ << "#endif\n";
for (const auto &MangledName : MangledMethodNames) {
MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n";
@@ -358,9 +359,8 @@ bool EmitSkippedMethodsFile(std::set<std::string> &SkippedMethodNames) {
if (!File)
return false;
- for (const auto &Skipped : SkippedMethodNames) {
+ for (const auto &Skipped : SkippedMethodNames)
File->os() << Skipped << "\n";
- }
File->keep();
return true;
}
@@ -381,6 +381,14 @@ int main(int argc, const char *argv[]) {
return 1;
}
+ // Create the output directory if the user specified one does not exist.
+ if (!llvm::sys::fs::exists(OutputDir.getValue())) {
+ llvm::sys::fs::create_directory(OutputDir.getValue());
+ }
+
+ if (!llvm::sys::fs::exists(GetServerOutputDirectory())) {
+ llvm::sys::fs::create_directory(GetServerOutputDirectory());
+ }
CommonOptionsParser &OP = ExpectedParser.get();
auto PCHOpts = std::make_shared<PCHContainerOperations>();
PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp b/lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
similarity index 100%
rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
rename to lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h b/lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
similarity index 100%
rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
rename to lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.h
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp b/lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
similarity index 100%
rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
rename to lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h b/lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.h
similarity index 100%
rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h
rename to lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.h
diff --git a/lldb/tools/lldb-rpc/CMakeLists.txt b/lldb/tools/lldb-rpc/CMakeLists.txt
new file mode 100644
index 0000000000000..fdd6cf9163e96
--- /dev/null
+++ b/lldb/tools/lldb-rpc/CMakeLists.txt
@@ -0,0 +1,22 @@
+include(CheckCXXCompilerFlag)
+# Umbrella target for the entire framework is a default target.
+add_custom_target(lldb-rpc ALL)
+
+if(LLDB_CODESIGN_IDENTITY)
+ # Use explicit LLDB identity
+ set(LLVM_CODESIGNING_IDENTITY ${LLDB_CODESIGN_IDENTITY})
+else()
+ # Use explicit LLVM identity or default to ad-hoc signing if empty
+ if(NOT LLVM_CODESIGNING_IDENTITY)
+ set(LLVM_CODESIGNING_IDENTITY -)
+ endif()
+endif()
+
+# LLDBRPCGeneration.cmake needs the LLDB_RPC_GEN_EXE variable
+# which gets defined in the lldb-rpc-gen folder, so we're adding
+# this folder before we add that file.
+add_lldb_tool_subdirectory(lldb-rpc-gen)
+include(${CMAKE_CURRENT_SOURCE_DIR}/LLDBRPCGeneration.cmake)
+include(${CMAKE_CURRENT_SOURCE_DIR}/LLDBRPCHeaders.cmake)
+
+add_dependencies(lldb-rpc lldb-rpc-generate-sources liblldbrpc-headers)
diff --git a/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake b/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake
new file mode 100644
index 0000000000000..a4cacf8692a85
--- /dev/null
+++ b/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake
@@ -0,0 +1,80 @@
+if (NOT DEFINED LLDB_RPC_GEN_EXE)
+ message(FATAL_ERROR
+ "Unable to generate lldb-rpc sources because LLDB_RPC_GEN_EXE is not
+ defined. If you are cross-compiling, please build lldb-rpc-gen for your host
+ platform.")
+endif()
+set(lldb_rpc_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(lldb_rpc_server_generated_source_dir "${lldb_rpc_generated_dir}/server")
+
+file(GLOB api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SB*.h)
+# We don't generate SBCommunication
+list(REMOVE_ITEM api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SBCommunication.h)
+# SBDefines.h is mostly definitions and forward declarations, nothing to
+# generate.
+list(REMOVE_ITEM api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SBDefines.h)
+
+# Generate the list of byproducts. Note that we cannot just glob the files in
+# the directory with the generated sources because BYPRODUCTS needs to be known
+# at configure time but the files are generated at build time.
+set(lldb_rpc_gen_byproducts
+ ${lldb_rpc_generated_dir}/SBClasses.def
+ ${lldb_rpc_generated_dir}/SBAPI.def
+ ${lldb_rpc_generated_dir}/lldb.py
+ ${lldb_rpc_server_generated_source_dir}/SBAPI.h
+)
+
+set(lldb_rpc_gen_server_impl_files)
+foreach(path ${api_headers})
+ get_filename_component(filename_no_ext ${path} NAME_WLE)
+
+ set(server_header_file "Server_${filename_no_ext}.h")
+ list(APPEND lldb_rpc_gen_byproducts "${lldb_rpc_server_generated_source_dir}/${server_header_file}")
+
+ set(server_impl_file "Server_${filename_no_ext}.cpp")
+ list(APPEND lldb_rpc_gen_byproducts "${lldb_rpc_server_generated_source_dir}/${server_impl_file}")
+ list(APPEND lldb_rpc_gen_server_impl_files "${lldb_rpc_server_generated_source_dir}/${server_impl_file}")
+
+endforeach()
+
+# Make sure that the clang-resource-dir is set correctly or else the tool will
+# fail to run. This is only needed when we do a standalone build.
+set(clang_resource_dir_arg)
+if (TARGET clang-resource-headers)
+ set(clang_resource_headers_dir
+ $<TARGET_PROPERTY:clang-resource-headers,INTERFACE_INCLUDE_DIRECTORIES>)
+ set(clang_resource_dir_arg --extra-arg="-resource-dir=${clang_resource_headers_dir}/..")
+else()
+ set(clang_resource_dir_arg --extra-arg="-resource-dir=${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}")
+endif()
+
+set(sysroot_arg)
+if (DEFINED TOOLCHAIN_TARGET_SYSROOTFS)
+ set(sysroot_arg --extra-arg="-resource-dir=${TOOLCHAIN_TARGET_SYSROOTFS}")
+endif()
+
+add_custom_command(OUTPUT ${lldb_rpc_gen_byproducts}
+ COMMAND ${CMAKE_COMMAND} -E make_directory
+ ${lldb_rpc_generated_dir}
+
+ COMMAND ${CMAKE_COMMAND} -E make_directory
+ ${lldb_rpc_server_generated_source_dir}
+
+ COMMAND ${LLDB_RPC_GEN_EXE}
+ -p ${CMAKE_BINARY_DIR}
+ --output-dir=${lldb_rpc_generated_dir}
+ ${sysroot_arg}
+ --extra-arg="-USWIG"
+ ${api_headers}
+
+ DEPENDS ${LLDB_RPC_GEN_EXE} ${api_headers}
+ COMMENT "Generating sources for lldb-rpc-server..."
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
+
+add_custom_target(lldb-rpc-generate-sources
+ DEPENDS
+ ${lldb_rpc_gen_byproducts}
+ lldb-sbapi-dwarf-enums)
+
+add_dependencies(lldb-rpc-generate-sources clang-resource-headers)
diff --git a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake
new file mode 100644
index 0000000000000..97ad481140248
--- /dev/null
+++ b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake
@@ -0,0 +1,101 @@
+set(derived_headers_location "${CMAKE_CURRENT_BINARY_DIR}/DerivedHeaders")
+
+# Obtain the original headers from their staged location in the build directory.
+set(original_headers_location "${CMAKE_BINARY_DIR}/include/lldb")
+set(headers_to_process
+ SBDefines.h
+ lldb-defines.h
+ lldb-enumerations.h
+ lldb-types.h
+)
+
+file(MAKE_DIRECTORY ${derived_headers_location})
+
+# Take the original headers and convert them RPC as necessary using the conversion script.
+set(original_headers)
+set(derived_headers)
+foreach(header ${headers_to_process})
+ set(original_header "${original_headers_location}/${header}")
+
+ get_filename_component(header_filename ${header} NAME)
+ string(REPLACE "lldb-" "lldb-rpc-" rpc_header_filename "${header_filename}")
+ set(derived_header "${derived_headers_location}/${rpc_header_filename}")
+
+ list(APPEND original_headers "${original_header}")
+ list(APPEND derived_headers "${derived_header}")
+ add_custom_command(OUTPUT ${derived_header}
+ COMMAND ${Python3_EXECUTABLE} ${LLDB_SOURCE_DIR}/scripts/convert-lldb-header-to-rpc-header.py
+ ${original_header} ${derived_header}
+ DEPENDS ${original_header}
+
+ COMMENT "Creating ${derived_header}"
+ )
+endforeach()
+
+# Do the same thing for any header files that were autogenerated.
+set(generated_headers_to_process
+ API/SBLanguages.h
+)
+foreach(header ${generated_headers_to_process})
+ set(original_header "${LLDB_OBJ_DIR}/include/lldb/${header}")
+
+ get_filename_component(header_filename ${header} NAME)
+ string(REPLACE "lldb-" "lldb-rpc-" rpc_header_filename "${header_filename}")
+ set(derived_header "${derived_headers_location}/${rpc_header_filename}")
+
+ list(APPEND original_headers "${original_header}")
+ list(APPEND derived_headers "${derived_header}")
+ add_custom_command(OUTPUT ${derived_header}
+ COMMAND ${CMAKE_COMMAND} -E copy ${original_header} ${derived_header}
+ COMMAND ${Python3_EXECUTABLE} ${LLDB_SOURCE_DIR}/scripts/convert-lldb-header-to-rpc-header.py
+ ${original_header} ${derived_header}
+ DEPENDS lldb-sbapi-dwarf-enums
+
+ COMMENT "Creating ${derived_header}"
+ )
+endforeach()
+
+add_custom_target(copy-aux-rpc-headers DEPENDS ${derived_headers})
+add_dependencies(copy-aux-rpc-headers liblldb-header-staging)
+
+list(APPEND public_headers
+ ${derived_headers_location}/SBDefines.h
+ ${derived_headers_location}/SBLanguages.h
+ ${derived_headers_location}/lldb-rpc-enumerations.h
+ ${derived_headers_location}/lldb-rpc-types.h
+ ${derived_headers_location}/lldb-rpc-defines.h
+)
+
+# Collect and preprocess headers for the framework bundle
+set(version_header
+ ${derived_headers_location}/lldb-rpc-defines.h
+)
+
+function(FixIncludePaths in subfolder out)
+ get_filename_component(base_name ${in} NAME)
+ set(parked_header ${CMAKE_CURRENT_BINARY_DIR}/ParkedHeaders/${subfolder}/${base_name})
+ set(${out} ${parked_header} PARENT_SCOPE)
+ find_program(unifdef_EXECUTABLE unifdef)
+
+ add_custom_command(OUTPUT ${parked_header}
+ COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py
+ -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} USWIG
+ DEPENDS ${in}
+ COMMENT "Fixing includes in ${in}"
+ )
+endfunction()
+
+set(preprocessed_headers)
+
+# Apply include-paths fix and any version fix on all headers and park them.
+foreach(source_header ${public_headers})
+ FixIncludePaths(${source_header} Headers parked_header)
+ list(APPEND preprocessed_headers ${parked_header})
+endforeach()
+
+# Wrap header preprocessing in a target, so liblldbrpc can depend on.
+add_custom_target(liblldbrpc-headers DEPENDS ${preprocessed_headers})
+add_dependencies(liblldbrpc-headers copy-aux-rpc-headers liblldb-header-staging)
+set_target_properties(liblldbrpc-headers PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ParkedHeaders
+)
More information about the lldb-commits
mailing list