[clang] 64287d6 - Recommit [C++20] [Modules] [ClangScanDeps] Enable to print make-style dependency file within P1689 format (4/4)

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Sun Feb 12 19:30:04 PST 2023


Author: Chuanqi Xu
Date: 2023-02-13T11:26:34+08:00
New Revision: 64287d69827c1d05edf8142742f6beb293d031fc

URL: https://github.com/llvm/llvm-project/commit/64287d69827c1d05edf8142742f6beb293d031fc
DIFF: https://github.com/llvm/llvm-project/commit/64287d69827c1d05edf8142742f6beb293d031fc.diff

LOG: Recommit [C++20] [Modules] [ClangScanDeps] Enable to print make-style dependency file within P1689 format (4/4)

Required in https://reviews.llvm.org/D137534.

The build systems needs the information to know that "header X changed,
scanning may have changed, so please rerun scanning". Although it is
possible to get the information by running clang-scan-deps for the
second time with make format, it is not user friendly clearly.

Reviewed By: jansvoboda11

Differential Revision: https://reviews.llvm.org/D139168

Added: 
    

Modified: 
    clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
    clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
    clang/test/ClangScanDeps/P1689.cppm
    clang/tools/clang-scan-deps/ClangScanDeps.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
index 505137c539e6f..13d42867bdd58 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -92,9 +92,21 @@ class DependencyScanningTool {
   llvm::Expected<std::string>
   getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD);
 
+  /// Collect the module dependency in P1689 format for C++20 named modules.
+  ///
+  /// \param MakeformatOutput The output parameter for dependency information
+  /// in make format if the command line requires to generate make-format
+  /// dependency information by `-MD -MF <dep_file>`.
+  ///
+  /// \param MakeformatOutputPath The output parameter for the path to
+  /// \param MakeformatOutput.
+  ///
+  /// \returns A \c StringError with the diagnostic output if clang errors
+  /// occurred, P1689 dependency format rules otherwise.
   llvm::Expected<P1689Rule>
-  getP1689ModuleDependencyFile(const CompileCommand &Command,
-                               StringRef CWD);
+  getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
+                               StringRef CWD, std::string &MakeformatOutput,
+                               std::string &MakeformatOutputPath);
 
   /// Given a Clang driver command-line for a translation unit, gather the
   /// modular dependencies and return the information needed for explicit build.

diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index ded5684190221..03e996a7dbd9c 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -19,66 +19,68 @@ DependencyScanningTool::DependencyScanningTool(
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
     : Worker(Service, std::move(FS)) {}
 
-llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
-    const std::vector<std::string> &CommandLine, StringRef CWD) {
-  /// Prints out all of the gathered dependencies into a string.
-  class MakeDependencyPrinterConsumer : public DependencyConsumer {
-  public:
-    void handleBuildCommand(Command) override {}
-
-    void
-    handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
-      this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
-    }
+namespace {
+/// Prints out all of the gathered dependencies into a string.
+class MakeDependencyPrinterConsumer : public DependencyConsumer {
+public:
+  void handleBuildCommand(Command) override {}
+
+  void
+  handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
+    this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
+  }
 
-    void handleFileDependency(StringRef File) override {
-      Dependencies.push_back(std::string(File));
-    }
+  void handleFileDependency(StringRef File) override {
+    Dependencies.push_back(std::string(File));
+  }
 
-    void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
-      // Same as `handleModuleDependency`.
-    }
+  void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
+    // Same as `handleModuleDependency`.
+  }
 
-    void handleModuleDependency(ModuleDeps MD) override {
-      // These are ignored for the make format as it can't support the full
-      // set of deps, and handleFileDependency handles enough for implicitly
-      // built modules to work.
-    }
+  void handleModuleDependency(ModuleDeps MD) override {
+    // These are ignored for the make format as it can't support the full
+    // set of deps, and handleFileDependency handles enough for implicitly
+    // built modules to work.
+  }
 
-    void handleContextHash(std::string Hash) override {}
+  void handleContextHash(std::string Hash) override {}
 
-    std::string lookupModuleOutput(const ModuleID &ID,
-                                   ModuleOutputKind Kind) override {
-      llvm::report_fatal_error("unexpected call to lookupModuleOutput");
-    }
+  std::string lookupModuleOutput(const ModuleID &ID,
+                                 ModuleOutputKind Kind) override {
+    llvm::report_fatal_error("unexpected call to lookupModuleOutput");
+  }
 
-    void printDependencies(std::string &S) {
-      assert(Opts && "Handled dependency output options.");
-
-      class DependencyPrinter : public DependencyFileGenerator {
-      public:
-        DependencyPrinter(DependencyOutputOptions &Opts,
-                          ArrayRef<std::string> Dependencies)
-            : DependencyFileGenerator(Opts) {
-          for (const auto &Dep : Dependencies)
-            addDependency(Dep);
-        }
-
-        void printDependencies(std::string &S) {
-          llvm::raw_string_ostream OS(S);
-          outputDependencyFile(OS);
-        }
-      };
-
-      DependencyPrinter Generator(*Opts, Dependencies);
-      Generator.printDependencies(S);
-    }
+  void printDependencies(std::string &S) {
+    assert(Opts && "Handled dependency output options.");
+
+    class DependencyPrinter : public DependencyFileGenerator {
+    public:
+      DependencyPrinter(DependencyOutputOptions &Opts,
+                        ArrayRef<std::string> Dependencies)
+          : DependencyFileGenerator(Opts) {
+        for (const auto &Dep : Dependencies)
+          addDependency(Dep);
+      }
+
+      void printDependencies(std::string &S) {
+        llvm::raw_string_ostream OS(S);
+        outputDependencyFile(OS);
+      }
+    };
+
+    DependencyPrinter Generator(*Opts, Dependencies);
+    Generator.printDependencies(S);
+  }
 
-  private:
-    std::unique_ptr<DependencyOutputOptions> Opts;
-    std::vector<std::string> Dependencies;
-  };
+protected:
+  std::unique_ptr<DependencyOutputOptions> Opts;
+  std::vector<std::string> Dependencies;
+};
+} // anonymous namespace
 
+llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
+    const std::vector<std::string> &CommandLine, StringRef CWD) {
   MakeDependencyPrinterConsumer Consumer;
   auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer);
   if (Result)
@@ -89,8 +91,10 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
 }
 
 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
-    const CompileCommand &Command, StringRef CWD) {
-  class P1689ModuleDependencyPrinterConsumer : public DependencyConsumer {
+    const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
+    std::string &MakeformatOutputPath) {
+  class P1689ModuleDependencyPrinterConsumer
+      : public MakeDependencyPrinterConsumer {
   public:
     P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
                                          const CompileCommand &Command)
@@ -98,17 +102,6 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
       Rule.PrimaryOutput = Command.Output;
     }
 
-    void
-    handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
-    void handleFileDependency(StringRef File) override {}
-    void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
-    void handleModuleDependency(ModuleDeps MD) override {}
-    void handleContextHash(std::string Hash) override {}
-    std::string lookupModuleOutput(const ModuleID &ID,
-                                   ModuleOutputKind Kind) override {
-      llvm::report_fatal_error("unexpected call to lookupModuleOutput");
-    }
-
     void handleProvidedAndRequiredStdCXXModules(
         std::optional<P1689ModuleInfo> Provided,
         std::vector<P1689ModuleInfo> Requires) override {
@@ -118,6 +111,12 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
       Rule.Requires = Requires;
     }
 
+    StringRef getMakeFormatDependencyOutputPath() {
+      if (Opts->OutputFormat != DependencyOutputFormat::Make)
+        return {};
+      return Opts->OutputFile;
+    }
+
   private:
     StringRef Filename;
     P1689Rule &Rule;
@@ -128,6 +127,10 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
   auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
   if (Result)
     return std::move(Result);
+
+  MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
+  if (!MakeformatOutputPath.empty())
+    Consumer.printDependencies(MakeformatOutput);
   return Rule;
 }
 

diff  --git a/clang/test/ClangScanDeps/P1689.cppm b/clang/test/ClangScanDeps/P1689.cppm
index 0cdab71b002bf..c9ef8f4c83fa2 100644
--- a/clang/test/ClangScanDeps/P1689.cppm
+++ b/clang/test/ClangScanDeps/P1689.cppm
@@ -28,42 +28,50 @@
 // RUN: clang-scan-deps -format=p1689 \
 // RUN:   -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/User.cpp -o %t/User.o \
 // RUN:   | FileCheck %t/User.cpp -DPREFIX=%/t
+//
+// Check we can generate the make-style dependencies as expected.
+// RUN: clang-scan-deps -format=p1689 \
+// RUN:   -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/impl_part.cppm -o %t/impl_part.o \
+// RUN:      -MT %t/impl_part.o.ddi -MD -MF %t/impl_part.dep
+// RUN:   cat %t/impl_part.dep | FileCheck %t/impl_part.cppm -DPREFIX=%/t --check-prefix=CHECK-MAKE
+//
+// Check that we can generate multiple make-style dependency information with compilation database.
+// RUN: cat %t/P1689.dep | FileCheck %t/Checks.cpp -DPREFIX=%/t --check-prefix=CHECK-MAKE
 
 //--- P1689.json.in
 [
 {
   "directory": "DIR",
-  "command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o",
+  "command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o -MT DIR/M.o.ddi -MD -MF DIR/P1689.dep",
   "file": "DIR/M.cppm",
   "output": "DIR/M.o"
 },
 {
   "directory": "DIR",
-  "command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o",
+  "command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o -MT DIR/Impl.o.ddi -MD -MF DIR/P1689.dep",
   "file": "DIR/Impl.cpp",
   "output": "DIR/Impl.o"
 },
 {
   "directory": "DIR",
-  "command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o",
+  "command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o -MT DIR/impl_part.o.ddi -MD -MF DIR/P1689.dep",
   "file": "DIR/impl_part.cppm",
   "output": "DIR/impl_part.o"
 },
 {
   "directory": "DIR",
-  "command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o",
+  "command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o -MT DIR/interface_part.o.ddi -MD -MF DIR/P1689.dep",
   "file": "DIR/interface_part.cppm",
   "output": "DIR/interface_part.o"
 },
 {
   "directory": "DIR",
-  "command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o",
+  "command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o -MT DIR/User.o.ddi -MD -MF DIR/P1689.dep",
   "file": "DIR/User.cpp",
   "output": "DIR/User.o"
 }
 ]
 
-
 //--- M.cppm
 export module M;
 export import :interface_part;
@@ -278,4 +286,17 @@ int main() {
 // CHECK-NEXT:   "version": 1
 // CHECK-NEXT: }
 
+// CHECK-MAKE-DAG: [[PREFIX]]/impl_part.o.ddi: \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/impl_part.cppm \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/header.mock
+// CHECK-MAKE-DAG: [[PREFIX]]/interface_part.o.ddi: \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/interface_part.cppm
+// CHECK-MAKE-DAG: [[PREFIX]]/M.o.ddi: \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/M.cppm
+// CHECK-MAKE-DAG: [[PREFIX]]/User.o.ddi: \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/User.cpp
+// CHECK-MAKE-DAG: [[PREFIX]]/Impl.o.ddi: \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/Impl.cpp \
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/header.mock
+
 //--- header.mock

diff  --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 2092edc86d07f..2cca188e28bb6 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -829,10 +829,48 @@ int main(int argc, const char **argv) {
                                              Errs))
             HadErrors = true;
         } else if (Format == ScanningOutputFormat::P1689) {
-          auto MaybeRule =
-              WorkerTools[I]->getP1689ModuleDependencyFile(*Input, CWD);
+          // It is useful to generate the make-format dependency output during
+          // the scanning for P1689. Otherwise the users need to scan again for
+          // it. We will generate the make-format dependency output if we find
+          // `-MF` in the command lines.
+          std::string MakeformatOutputPath;
+          std::string MakeformatOutput;
+
+          auto MaybeRule = WorkerTools[I]->getP1689ModuleDependencyFile(
+              *Input, CWD, MakeformatOutput, MakeformatOutputPath);
+
           if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
             HadErrors = true;
+
+          if (!MakeformatOutputPath.empty() && !MakeformatOutput.empty() &&
+              !HadErrors) {
+            static std::mutex Lock;
+            // With compilation database, we may open 
diff erent files
+            // concurrently or we may write the same file concurrently. So we
+            // use a map here to allow multiple compile commands to write to the
+            // same file. Also we need a lock here to avoid data race.
+            static llvm::StringMap<llvm::raw_fd_ostream> OSs;
+            std::unique_lock<std::mutex> LockGuard(Lock);
+
+            auto OSIter = OSs.find(MakeformatOutputPath);
+            if (OSIter == OSs.end()) {
+              std::error_code EC;
+              OSIter = OSs.try_emplace(MakeformatOutputPath,
+                                       MakeformatOutputPath, EC)
+                           .first;
+              if (EC)
+                llvm::errs()
+                    << "Failed to open P1689 make format output file \""
+                    << MakeformatOutputPath << "\" for " << EC.message()
+                    << "\n";
+            }
+
+            SharedStream MakeformatOS(OSIter->second);
+            llvm::Expected<std::string> MaybeOutput(MakeformatOutput);
+            if (handleMakeDependencyToolResult(Filename, MaybeOutput,
+                                               MakeformatOS, Errs))
+              HadErrors = true;
+          }
         } else if (MaybeModuleName) {
           auto MaybeModuleDepsGraph = WorkerTools[I]->getModuleDependencies(
               *MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules,


        


More information about the cfe-commits mailing list