diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt index 6804dc234555b..43ea252459c88 100644 --- a/lldb/tools/CMakeLists.txt +++ b/lldb/tools/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(lldb-fuzzer EXCLUDE_FROM_ALL) add_lldb_tool_subdirectory(lldb-instr) add_lldb_tool_subdirectory(lldb-dap) +add_lldb_tool_subdirectory(lldb-sb-gen) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_lldb_tool_subdirectory(darwin-debug) diff --git a/lldb/tools/lldb-sb-gen/CMakeLists.txt b/lldb/tools/lldb-sb-gen/CMakeLists.txt new file mode 100644 index 0000000000000..1609c964ffe3b --- /dev/null +++ b/lldb/tools/lldb-sb-gen/CMakeLists.txt @@ -0,0 +1,20 @@ +if(APPLE) + # add_compile_options(-isysroot ${SYSROOT}) +endif() + + +add_lldb_tool(lldb-sb-gen + SBGenerator.cpp + + LINK_COMPONENTS + Support + CLANG_LIBS + clangAST + clangBasic + clangCodeGen + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + ) diff --git a/lldb/tools/lldb-sb-gen/SBGenerator.cpp b/lldb/tools/lldb-sb-gen/SBGenerator.cpp new file mode 100644 index 0000000000000..db49f4b3a93b8 --- /dev/null +++ b/lldb/tools/lldb-sb-gen/SBGenerator.cpp @@ -0,0 +1,161 @@ +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/CodeGen/ObjectFilePCHContainerWriter.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Serialization/ObjectFilePCHContainerReader.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ToolOutputFile.h" + +#include +#include +#include +#include + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; + +static llvm::cl::OptionCategory SBGenCategory("LLDB Scripting Bridge Generator"); +static llvm::cl::opt + OutputDir("output-dir", + llvm::cl::desc("Directory to output generated files to"), + llvm::cl::init(""), llvm::cl::cat(SBGenCategory)); +static llvm::cl::opt GenerateOptionalClass( + "generate-optional-class", + llvm::cl::desc("Generate a header & source files for SBOptional class"), + llvm::cl::init(false), + llvm::cl::cat(SBGenCategory)); +static llvm::cl::opt Verbose( + "verbose", + llvm::cl::desc("Dumps all the visited SB classes."), + llvm::cl::init(false), + llvm::cl::cat(SBGenCategory)); + +static std::unique_ptr +CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { + llvm::SmallString<256> Path(OutputDir); + llvm::sys::path::append(Path, Filename); + + std::error_code EC; + auto OutputFile = + std::make_unique(Path, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::errs() << "Failed to create output file: " << Path << "!\n"; + return nullptr; + } + return OutputFile; +} + +class SBVisitor : public RecursiveASTVisitor { +public: + SBVisitor(std::set& ClassNames) : ClassNames(ClassNames) {} + + bool VisitCXXRecordDecl(CXXRecordDecl *Decl) { + // Skip implicit declarations and non-definitions + if (!Decl->isThisDeclarationADefinition() || Decl->isImplicit()) + return true; + + // Skip redeclarations (we only want the canonical definition) + if (Decl != Decl->getDefinition()) + return true; + + ClassNames.insert(Decl->getNameAsString()); + return true; + } + +private: + std::set& ClassNames; +}; + +class SBConsumer : public ASTConsumer { +public: + SBConsumer(Rewriter &R, ASTContext &Context, std::set& ClassNames) : Visitor(ClassNames) {} + + void HandleTranslationUnit(ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + SBVisitor Visitor; +}; + +class SBAction : public ASTFrontendAction { +public: + explicit SBAction(std::set& ClassNames) : ClassNames(ClassNames) {} + + bool BeginSourceFileAction(CompilerInstance &CI) override { return true; } + + void EndSourceFileAction() override { MyRewriter.overwriteChangedFiles(); } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef File) override { + MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique(MyRewriter, CI.getASTContext(), ClassNames); + } + +private: + std::set& ClassNames; + Rewriter MyRewriter; +}; + +class SBActionFactory : public clang::tooling::FrontendActionFactory { +public: + explicit SBActionFactory(std::set &ClassNames) + : ClassNames(ClassNames) {} + + std::unique_ptr create() override { + return std::make_unique(ClassNames); + } + +private: + std::set &ClassNames; +}; + +int main(int argc, const char **argv) { + auto ExpectedParser = CommonOptionsParser::create( + argc, argv, SBGenCategory, llvm::cl::OneOrMore, + "Utility for generating the scripting bridge dynamic APIs for LLDB's " + "framework."); + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + if (OutputDir.empty()) { + llvm::errs() << "Please specify an output directory for the generated " + "files with --output-dir!\n"; + 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()); + } + + CommonOptionsParser &OP = ExpectedParser.get(); + + auto PCHOpts = std::make_shared(); + PCHOpts->registerWriter(std::make_unique()); + PCHOpts->registerReader(std::make_unique()); + + ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); + + std::set ClassNames; + SBActionFactory Factory(ClassNames); + T.run(&Factory); + + if (Verbose) + for (auto& class_name: ClassNames) + std::cout << class_name << std::endl; + + return 0; +}