diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -6,6 +6,9 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/DiagnosticSerialization.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" @@ -16,6 +19,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/Threading.h" @@ -25,6 +29,7 @@ #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Core/ModuleList.h" +#include "lldb/Core/Progress.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/CompileUnit.h" @@ -61,6 +66,8 @@ void EndSourceFile() override; private: + bool HandleModuleRemark(const clang::Diagnostic &info); + typedef std::pair IDAndDiagnostic; std::vector m_diagnostics; @@ -72,6 +79,8 @@ /// Output string filled by m_os. Will be reused for different diagnostics. std::string m_output; Log *m_log; + std::unique_ptr m_current_progress_up; + std::vector m_module_build_stack; }; /// The private implementation of our ClangModulesDeclVendor. Contains all the @@ -140,6 +149,9 @@ void StoringDiagnosticConsumer::HandleDiagnostic( clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { + if (HandleModuleRemark(info)) + return; + // Print the diagnostic to m_output. m_output.clear(); m_diag_printer->HandleDiagnostic(DiagLevel, info); @@ -170,9 +182,42 @@ } void StoringDiagnosticConsumer::EndSourceFile() { + m_current_progress_up.reset(nullptr); m_diag_printer->EndSourceFile(); } +bool StoringDiagnosticConsumer::HandleModuleRemark( + const clang::Diagnostic &info) { + Log *log = GetLog(LLDBLog::Expressions); + switch (info.getID()) { + case clang::diag::remark_module_build: { + const auto &module_name = info.getArgStdStr(0); + const auto &module_file_name = info.getArgStdStr(1); + m_module_build_stack.push_back(module_name); + // End the previous event before starting a new event. + m_current_progress_up.reset(nullptr); + m_current_progress_up.reset(new Progress( + llvm::formatv("Currently building module {0}", module_name))); + LLDB_LOG(log, "Building Clang module {0} as {1}", module_name, + module_file_name); + return true; + } + case clang::diag::remark_module_build_done: { + const auto &module_name = info.getArgStdStr(0); + const auto &parent_module_name = m_module_build_stack.back(); + // End the previous event before starting a new event. + m_current_progress_up.reset(nullptr); + m_current_progress_up.reset(new Progress( + llvm::formatv("Currently building module {0}", parent_module_name))); + LLDB_LOG(log, "Finished building Clang module {0}", module_name); + m_module_build_stack.pop_back(); + return true; + } + default: + return false; + } +} + ClangModulesDeclVendor::ClangModulesDeclVendor() : ClangDeclVendor(eClangModuleDeclVendor) {} @@ -610,7 +655,8 @@ arch.GetTriple().str(), "-fmodules-validate-system-headers", "-Werror=non-modular-include-in-framework-module", - "-Xclang=-fincremental-extensions"}; + "-Xclang=-fincremental-extensions", + "-Rmodule-build"}; target.GetPlatform()->AddClangModuleCompilationOptions( &target, compiler_invocation_arguments); @@ -648,16 +694,18 @@ } } - llvm::IntrusiveRefCntPtr diagnostics_engine = - clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions, - new StoringDiagnosticConsumer); - std::vector compiler_invocation_argument_cstrs; compiler_invocation_argument_cstrs.reserve( compiler_invocation_arguments.size()); for (const std::string &arg : compiler_invocation_arguments) compiler_invocation_argument_cstrs.push_back(arg.c_str()); + auto diag_options_up = + clang::CreateAndPopulateDiagOpts(compiler_invocation_argument_cstrs); + llvm::IntrusiveRefCntPtr diagnostics_engine = + clang::CompilerInstance::createDiagnostics(diag_options_up.release(), + new StoringDiagnosticConsumer); + Log *log = GetLog(LLDBLog::Expressions); LLDB_LOG(log, "ClangModulesDeclVendor's compiler flags {0:$[ ]}", llvm::make_range(compiler_invocation_arguments.begin(), diff --git a/lldb/test/API/functionalities/progress_reporting/clang_modules/Makefile b/lldb/test/API/functionalities/progress_reporting/clang_modules/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/progress_reporting/clang_modules/Makefile @@ -0,0 +1,5 @@ +OBJC_SOURCES := main.m +CFLAGS_EXTRAS := -fmodules +LD_EXTRAS := -framework Foundation + +include Makefile.rules diff --git a/lldb/test/API/functionalities/progress_reporting/clang_modules/TestClangModuleBuildProgress.py b/lldb/test/API/functionalities/progress_reporting/clang_modules/TestClangModuleBuildProgress.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/progress_reporting/clang_modules/TestClangModuleBuildProgress.py @@ -0,0 +1,41 @@ +""" +Test clang module build progress events. +""" +import os +import shutil + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class TestCase(TestBase): + @skipUnlessDarwin + def test_clang_module_build_progress_report(self): + """Test receipt of progress events for clang module builds""" + self.build() + + # Ensure an empty module cache. + mod_cache = os.path.join(self.getBuildDir(), "new-modules") + if os.path.isdir(mod_cache): + shutil.rmtree(mod_cache) + self.runCmd(f"settings set symbols.clang-modules-cache-path {mod_cache}") + + lldbutil.run_to_name_breakpoint(self, "main") + + # Just before triggering module builds, start listening for progress + # events. Listening any earlier would result in a queue filled with + # other unrelated progress events. + broadcaster = self.dbg.GetBroadcaster() + listener = lldbutil.start_listening_from( + broadcaster, lldb.SBDebugger.eBroadcastBitProgress + ) + + # Trigger module builds. + self.expect("p @2") + + event = lldbutil.fetch_next_event(self, listener, broadcaster) + payload = lldb.SBDebugger.GetProgressFromEvent(event) + message = payload[0] + self.assertEqual(message, "Currently building module Foundation") diff --git a/lldb/test/API/functionalities/progress_reporting/clang_modules/main.m b/lldb/test/API/functionalities/progress_reporting/clang_modules/main.m new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/progress_reporting/clang_modules/main.m @@ -0,0 +1,3 @@ +@import Foundation; + +int main() {}