diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp index 1d55f615de8a9..46b2cc1ac99c1 100644 --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -97,7 +97,7 @@ static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) { OffloadCompilation = true; if (Jobs.size() > 1) { - for (auto *A : Actions){ + for (auto *A : Actions) { // On MacOSX real actions may end up being wrapped in BindArchAction if (isa(A)) A = *A->input_begin(); @@ -414,8 +414,8 @@ bool ToolInvocation::run() { Driver->BuildCompilation(llvm::ArrayRef(Argv))); if (!Compilation) return false; - const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments( - &*Diagnostics, Compilation.get()); + const llvm::opt::ArgStringList *const CC1Args = + getCC1Arguments(&*Diagnostics, Compilation.get()); if (!CC1Args) return false; std::unique_ptr Invocation( @@ -498,9 +498,7 @@ void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) { ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster)); } -void ClangTool::clearArgumentsAdjusters() { - ArgsAdjuster = nullptr; -} +void ClangTool::clearArgumentsAdjusters() { ArgsAdjuster = nullptr; } static void injectResourceDir(CommandLineArguments &Args, const char *Argv0, void *MainAddr) { @@ -555,8 +553,9 @@ int ClangTool::run(ToolAction *Action) { } size_t NumOfTotalFiles = AbsolutePaths.size(); - unsigned ProcessedFileCounter = 0; + unsigned CurrentFileIndex = 0; for (llvm::StringRef File : AbsolutePaths) { + ++CurrentFileIndex; // Currently implementations of CompilationDatabase::getCompileCommands can // change the state of the file system (e.g. prepare generated headers), so // this method needs to run right before we invoke the tool, as the next @@ -571,6 +570,7 @@ int ClangTool::run(ToolAction *Action) { FileSkipped = true; continue; } + unsigned CurrentCommandIndexForFile = 0; for (CompileCommand &CompileCommand : CompileCommandsForFile) { // If the 'directory' field of the compilation database is empty, display // an error and use the working directory instead. @@ -617,13 +617,20 @@ int ClangTool::run(ToolAction *Action) { // pass in made-up names here. Make sure this works on other platforms. injectResourceDir(CommandLine, "clang_tool", &StaticSymbol); + ++CurrentCommandIndexForFile; + // FIXME: We need a callback mechanism for the tool writer to output a // customized message for each file. - if (NumOfTotalFiles > 1) - llvm::errs() << "[" + std::to_string(++ProcessedFileCounter) + "/" + - std::to_string(NumOfTotalFiles) + - "] Processing file " + File - << ".\n"; + if (NumOfTotalFiles > 1 || CompileCommandsForFile.size() > 1) { + llvm::errs() << "[" << std::to_string(CurrentFileIndex) << "/" + << std::to_string(NumOfTotalFiles) << "]"; + if (CompileCommandsForFile.size() > 1) { + llvm::errs() << " (" << std::to_string(CurrentCommandIndexForFile) + << "/" << std::to_string(CompileCommandsForFile.size()) + << ")"; + } + llvm::errs() << " Processing file " << File << ".\n"; + } ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(), PCHContainerOps); Invocation.setDiagnosticConsumer(DiagConsumer); diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp index 25e1d67eb2294..9a7559405c43c 100644 --- a/clang/unittests/Tooling/ToolingTest.cpp +++ b/clang/unittests/Tooling/ToolingTest.cpp @@ -20,8 +20,10 @@ #include "clang/Testing/CommandLineArgs.h" #include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/JSONCompilationDatabase.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetSelect.h" #include "llvm/TargetParser/Host.h" @@ -1034,5 +1036,136 @@ TEST(runToolOnCode, TestResetDiagnostics) { "void func() { long x; Foo f(x); }")); } +namespace { +struct TestCommand { + llvm::StringRef File; + llvm::StringRef Command; +}; + +std::string runToolWithProgress(llvm::ArrayRef Commands, + llvm::StringRef BaseDir) { + std::string ErrorMessage; + + llvm::json::Array Entries; + for (const auto &Cmd : Commands) { + Entries.push_back(llvm::json::Object{ + {"directory", BaseDir}, {"command", Cmd.Command}, {"file", Cmd.File}}); + } + std::string DatabaseContent; + llvm::raw_string_ostream OS(DatabaseContent); + OS << llvm::json::Value(std::move(Entries)); + + std::unique_ptr Database( + JSONCompilationDatabase::loadFromBuffer(DatabaseContent, ErrorMessage, + JSONCommandLineSyntax::Gnu)); + if (!Database) { + ADD_FAILURE() << "Failed to load compilation database: " << ErrorMessage; + return ""; + } + + std::vector AbsoluteFiles; + for (const auto &Cmd : Commands) { + SmallString<32> NativeFile(BaseDir); + llvm::sys::path::append(NativeFile, Cmd.File); + llvm::sys::path::native(NativeFile); + std::string AbsPath = std::string(NativeFile); + if (AbsoluteFiles.empty() || AbsoluteFiles.back() != AbsPath) { + AbsoluteFiles.push_back(AbsPath); + } + } + + ClangTool Tool(*Database, AbsoluteFiles); + for (const auto &F : AbsoluteFiles) { + Tool.mapVirtualFile(F, "int x;"); + } + + testing::internal::CaptureStderr(); + Tool.run(newFrontendActionFactory().get()); + return testing::internal::GetCapturedStderr(); +} +} // namespace + +TEST(ClangToolTest, ProgressReportSingleFile) { + SmallString<32> BaseDir; + llvm::sys::path::system_temp_directory(false, BaseDir); + llvm::sys::path::native(BaseDir, llvm::sys::path::Style::posix); + + EXPECT_TRUE( + runToolWithProgress({{"test.cpp", "clang++ -c test.cpp"}}, BaseDir) + .empty()); +} + +TEST(ClangToolTest, ProgressReportMultipleFiles) { + SmallString<32> BaseDir; + llvm::sys::path::system_temp_directory(false, BaseDir); + llvm::sys::path::native(BaseDir, llvm::sys::path::Style::posix); + + std::string Output = + runToolWithProgress({{"test1.cpp", "clang++ -c test1.cpp"}, + {"test2.cpp", "clang++ -c test2.cpp"}}, + BaseDir); + + SmallString<32> NativeFile1(BaseDir); + llvm::sys::path::append(NativeFile1, "test1.cpp"); + llvm::sys::path::native(NativeFile1); + SmallString<32> NativeFile2(BaseDir); + llvm::sys::path::append(NativeFile2, "test2.cpp"); + llvm::sys::path::native(NativeFile2); + + std::string Expected = "[1/2] Processing file " + std::string(NativeFile1) + + ".\n" + "[2/2] Processing file " + + std::string(NativeFile2) + ".\n"; + EXPECT_EQ(Output, Expected); +} + +TEST(ClangToolTest, ProgressReportMultipleCommands) { + SmallString<32> BaseDir; + llvm::sys::path::system_temp_directory(false, BaseDir); + llvm::sys::path::native(BaseDir, llvm::sys::path::Style::posix); + + std::string Output = + runToolWithProgress({{"test.cpp", "clang++ -c test.cpp -DCMD1"}, + {"test.cpp", "clang++ -c test.cpp -DCMD2"}}, + BaseDir); + + SmallString<32> NativeFile(BaseDir); + llvm::sys::path::append(NativeFile, "test.cpp"); + llvm::sys::path::native(NativeFile); + std::string Expected = + "[1/1] (1/2) Processing file " + std::string(NativeFile) + ".\n" + + "[1/1] (2/2) Processing file " + std::string(NativeFile) + ".\n"; + EXPECT_EQ(Output, Expected); +} + +TEST(ClangToolTest, ProgressReportMixed) { + SmallString<32> BaseDir; + llvm::sys::path::system_temp_directory(false, BaseDir); + llvm::sys::path::native(BaseDir, llvm::sys::path::Style::posix); + + std::string Output = + runToolWithProgress({{"test1.cpp", "clang++ -c test1.cpp"}, + {"test2.cpp", "clang++ -c test2.cpp -DCMD1"}, + {"test2.cpp", "clang++ -c test2.cpp -DCMD2"}, + {"test3.cpp", "clang++ -c test3.cpp"}}, + BaseDir); + + SmallString<32> NativeFile1(BaseDir); + llvm::sys::path::append(NativeFile1, "test1.cpp"); + llvm::sys::path::native(NativeFile1); + SmallString<32> NativeFile2(BaseDir); + llvm::sys::path::append(NativeFile2, "test2.cpp"); + llvm::sys::path::native(NativeFile2); + SmallString<32> NativeFile3(BaseDir); + llvm::sys::path::append(NativeFile3, "test3.cpp"); + llvm::sys::path::native(NativeFile3); + + std::string Expected = + "[1/3] Processing file " + std::string(NativeFile1) + ".\n" + + "[2/3] (1/2) Processing file " + std::string(NativeFile2) + ".\n" + + "[2/3] (2/2) Processing file " + std::string(NativeFile2) + ".\n" + + "[3/3] Processing file " + std::string(NativeFile3) + ".\n"; + EXPECT_EQ(Output, Expected); +} + } // end namespace tooling } // end namespace clang