ChuanqiXu created this revision.
ChuanqiXu added reviewers: ben.boeckel, Bigcheese, jansvoboda11.
Herald added a project: All.
ChuanqiXu requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D139168

Files:
  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

Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp
===================================================================
--- clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -187,6 +187,13 @@
                    "dependencies are to be computed."),
     llvm::cl::cat(DependencyScannerCategory));
 
+llvm::cl::opt<std::string> P1689MakeformatOutputpath(
+    "p1689-makeformat-output", llvm::cl::Optional,
+    llvm::cl::desc("Only supported for P1689. Print the make-style dependency "
+                   "output to the specified output. This is a helper for build "
+                   "systems to do duplicate scanning."),
+    llvm::cl::cat(DependencyScannerCategory));
+
 llvm::cl::opt<std::string> P1689TargettedCommand(
     llvm::cl::Positional, llvm::cl::ZeroOrMore,
     llvm::cl::desc("The command line flags for the target of which "
@@ -584,6 +591,19 @@
   return std::move(FixedCompilationDatabase);
 }
 
+static raw_ostream &getDependencyOS() {
+  if (!P1689MakeformatOutputpath.empty()) {
+    std::error_code EC;
+    static llvm::raw_fd_ostream OS(P1689MakeformatOutputpath, EC);
+    if (EC)
+      llvm::errs() << "Failed to open P1689 make format output file \""
+                   << P1689MakeformatOutputpath << "\" for " << EC.message()
+                   << "\n";
+    return OS;
+  }
+  return llvm::outs();
+}
+
 int main(int argc, const char **argv) {
   std::string ErrorMessage;
   std::unique_ptr<tooling::CompilationDatabase> Compilations =
@@ -662,7 +682,7 @@
 
   SharedStream Errs(llvm::errs());
   // Print out the dependency results to STDOUT by default.
-  SharedStream DependencyOS(llvm::outs());
+  SharedStream DependencyOS(getDependencyOS());
 
   DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
                                     EagerLoadModules);
@@ -722,10 +742,20 @@
                                              Errs))
             HadErrors = true;
         } else if (Format == ScanningOutputFormat::P1689) {
+          llvm::Optional<std::string> MakeformatOutput;
+          if (!P1689MakeformatOutputpath.empty())
+            MakeformatOutput.emplace();
+
           auto MaybeRule = WorkerTools[I]->getP1689ModuleDependencyFile(
-              *Input, CWD, MaybeModuleName);
-          if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
-            HadErrors = true;
+              *Input, CWD, MakeformatOutput, MaybeModuleName);
+          HadErrors =
+              handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs);
+
+          if (MakeformatOutput && !MakeformatOutput->empty() && !HadErrors) {
+            llvm::Expected<std::string> MaybeOutput(*MakeformatOutput);
+            HadErrors = handleMakeDependencyToolResult(Filename, MaybeOutput,
+                                                       DependencyOS, Errs);
+          }
         } else if (DeprecatedDriverCommand) {
           auto MaybeFullDeps =
               WorkerTools[I]->getFullDependenciesLegacyDriverCommand(
Index: clang/test/ClangScanDeps/P1689.cppm
===================================================================
--- clang/test/ClangScanDeps/P1689.cppm
+++ clang/test/ClangScanDeps/P1689.cppm
@@ -22,6 +22,14 @@
 // RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/User.cpp --p1689-targeted-output=%t/User.o \
 // RUN:   -- -std=c++20 -c \
 // RUN:   | FileCheck %t/User.cpp -DPREFIX=%/t
+//
+// Check we can generate the make-style dependencies as expected.
+// RUN: clang-scan-deps --compilation-database %t/P1689.json -format=p1689 --p1689-makeformat-output=%t/P1689.dep
+// RUN: cat %t/P1689.dep | FileCheck %t/Checks.cpp -DPREFIX=%/t --check-prefix=CHECK-MAKE
+//
+// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/impl_part.cppm --p1689-targeted-output=%t/impl_part.o \
+// RUN:   --p1689-makeformat-output=%t/impl_part.dep -- -std=c++20 -c
+// RUN: cat %t/impl_part.dep | FileCheck %t/impl_part.cppm -DPREFIX=%/t --check-prefix=CHECK-MAKE
 
 //--- P1689.json.in
 [
@@ -57,7 +65,6 @@
 }
 ]
 
-
 //--- M.cppm
 export module M;
 export import :interface_part;
@@ -145,6 +152,10 @@
 // CHECK-NEXT:   "version": 1
 // CHECK-NEXT: }
 
+// CHECK-MAKE: [[PREFIX]]/impl_part.o:
+// CHECK-MAKE:   [[PREFIX]]/impl_part.cppm
+// CHECK-MAKE:   [[PREFIX]]/header.mock
+
 //--- interface_part.cppm
 export module M:interface_part;
 export void World();
@@ -268,4 +279,17 @@
 // CHECK-NEXT:   "version": 1
 // CHECK-NEXT: }
 
+// CHECK-MAKE-DAG: [[PREFIX]]/User.o:
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/User.cpp
+// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/Impl.o:
+// CHECK-MAKE-DAG:   [[PREFIX]]/Impl.cpp
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/header.mock
+// CHECK-MAKE-DAG: [[PREFIX]]/M.o:
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/M.cppm
+// CHECK-MAKE-DAG: [[PREFIX]]/interface_part.o:
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/interface_part.cppm
+// CHECK-MAKE-DAG: [[PREFIX]]/impl_part.o:
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/impl_part.cppm
+// CHECK-MAKE-DAG-NEXT:   [[PREFIX]]/header.mock
+
 //--- header.mock
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -39,67 +39,69 @@
     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,
-    llvm::Optional<StringRef> ModuleName) {
-  /// Prints out all of the gathered dependencies into a string.
-  class MakeDependencyPrinterConsumer : public DependencyConsumer {
-  public:
-    void handleBuildCommand(Command) override {}
+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
-    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.");
 
-    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);
-    }
+    class DependencyPrinter : public DependencyFileGenerator {
+    public:
+      DependencyPrinter(DependencyOutputOptions &Opts,
+                        ArrayRef<std::string> Dependencies)
+          : DependencyFileGenerator(Opts) {
+        for (const auto &Dep : Dependencies)
+          addDependency(Dep);
+      }
 
-  private:
-    std::unique_ptr<DependencyOutputOptions> Opts;
-    std::vector<std::string> Dependencies;
-  };
+      void printDependencies(std::string &S) {
+        llvm::raw_string_ostream OS(S);
+        outputDependencyFile(OS);
+      }
+    };
 
+    DependencyPrinter Generator(*Opts, Dependencies);
+    Generator.printDependencies(S);
+  }
+
+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,
+    llvm::Optional<StringRef> ModuleName) {
   MakeDependencyPrinterConsumer Consumer;
   auto Result =
       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
@@ -112,8 +114,10 @@
 
 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
     const clang::tooling::CompileCommand &Command, StringRef CWD,
+    llvm::Optional<std::string> &MakeformatOutput,
     llvm::Optional<StringRef> ModuleName) {
-  class P1689ModuleDependencyPrinterConsumer : public DependencyConsumer {
+  class P1689ModuleDependencyPrinterConsumer
+      : public MakeDependencyPrinterConsumer {
   public:
     P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
                                          const CompileCommand &Command)
@@ -122,14 +126,13 @@
     }
 
     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");
+    handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
+      MakeDependencyPrinterConsumer::handleDependencyOutputOpts(Opts);
+      /// NOTE: Currently, the compiler will generate module unit (.cppm) files
+      /// into module file (.pcm) first. Then it compiles the module file (.pcm)
+      /// into object files. It results that the Targets[0] here will be .pcm
+      /// file instead of the expected output file.
+      this->Opts->Targets[0] = Rule.PrimaryOutput;
     }
 
     void handleProvidedAndRequiredStdCXXModules(
@@ -152,6 +155,8 @@
                                            ModuleName);
   if (Result)
     return std::move(Result);
+  if (MakeformatOutput)
+    Consumer.printDependencies(*MakeformatOutput);
   return Rule;
 }
 
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -92,9 +92,17 @@
   getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD,
                     llvm::Optional<StringRef> ModuleName = None);
 
+  /// Collect the module dependency in P1689 format for C++20 named modules.
+  ///
+  /// \param MakeformatOutput The output parameter for dependency information
+  /// in make format if \p MakeformatOutput isn't none.
+  ///
+  /// \returns A \c StringError with the diagnostic output if clang errors
+  /// occurred, P1689 dependency format rules otherwise.
   llvm::Expected<clang::tooling::dependencies::P1689Rule>
   getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command,
                                StringRef CWD,
+                               llvm::Optional<std::string> &MakeformatOutput,
                                llvm::Optional<StringRef> ModuleName = None);
 
   /// Collect the full module dependency graph for the input, ignoring any
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to