[clang] 591fdcd - [C++20] [Modules] [ClangScanDeps] Allow clang-scan-deps to without specified compilation database in P1689 (3/4)

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 9 18:43:04 PST 2023


Author: Chuanqi Xu
Date: 2023-02-10T10:42:04+08:00
New Revision: 591fdcde921334b2d502779006d7c168307a2106

URL: https://github.com/llvm/llvm-project/commit/591fdcde921334b2d502779006d7c168307a2106
DIFF: https://github.com/llvm/llvm-project/commit/591fdcde921334b2d502779006d7c168307a2106.diff

LOG: [C++20] [Modules] [ClangScanDeps] Allow clang-scan-deps to without specified compilation database in P1689 (3/4)

In a private chat with @ben.boeckel , we get in consensus it would be
great for cmake if the invocation of clang-scan-deps can get rid of
compilation database. Due to the compilation database can't do very well
for the files which are not existed yet. @ben.boeckel may have more
context to add here.

This patch should be innocent for others usages.

Reviewed By: jansvoboda11

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

Added: 
    

Modified: 
    clang/test/ClangScanDeps/P1689.cppm
    clang/tools/clang-scan-deps/ClangScanDeps.cpp

Removed: 
    


################################################################################
diff  --git a/clang/test/ClangScanDeps/P1689.cppm b/clang/test/ClangScanDeps/P1689.cppm
index c539f8ffb713..22d7721c2e9b 100644
--- a/clang/test/ClangScanDeps/P1689.cppm
+++ b/clang/test/ClangScanDeps/P1689.cppm
@@ -5,6 +5,26 @@
 // RUN: sed "s|DIR|%/t|g" %t/P1689.json.in > %t/P1689.json
 // RUN: clang-scan-deps -compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t
 // RUN: clang-scan-deps --mode=preprocess-dependency-directives -compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t
+//
+// Check the separated dependency format. This is required by CMake for the case
+// that we have non-exist files in a fresh build and potentially out-of-date after that.
+// So the build system need to wrtie a compilation database just for scanning purposes,
+// which is not so good. So here is the per file mode for P1689.
+// RUN: clang-scan-deps -format=p1689 \
+// RUN:   -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/M.cppm -o %t/M.o \
+// RUN:   | FileCheck %t/M.cppm -DPREFIX=%/t
+// RUN: clang-scan-deps -format=p1689 \
+// RUN:   -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/Impl.cpp -o %t/Impl.o \
+// RUN:   | FileCheck %t/Impl.cpp -DPREFIX=%/t
+// 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:   | FileCheck %t/impl_part.cppm -DPREFIX=%/t
+// RUN: clang-scan-deps -format=p1689 \
+// RUN:   -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/interface_part.cppm -o %t/interface_part.o \
+// RUN:   | FileCheck %t/interface_part.cppm -DPREFIX=%/t
+// 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
 
 //--- P1689.json.in
 [
@@ -47,6 +67,31 @@ export import :interface_part;
 import :impl_part;
 export void Hello();
 
+// CHECK: {
+// CHECK-NEXT:   "revision": 0,
+// CHECK-NEXT:   "rules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "primary-output": "[[PREFIX]]/M.o",
+// CHECK-NEXT:       "provides": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "is-interface": true,
+// CHECK-NEXT:           "logical-name": "M",
+// CHECK-NEXT:           "source-path": "[[PREFIX]]/M.cppm"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "requires": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "M:interface_part"
+// CHECK-NEXT:         },
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "M:impl_part"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "version": 1
+// CHECK-NEXT: }
+
 //--- Impl.cpp
 module;
 #include "header.mock"
@@ -55,6 +100,21 @@ void Hello() {
     std::cout << "Hello ";
 }
 
+// CHECK: {
+// CHECK-NEXT:   "revision": 0,
+// CHECK-NEXT:   "rules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "primary-output": "[[PREFIX]]/Impl.o",
+// CHECK-NEXT:       "requires": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "M"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "version": 1
+// CHECK-NEXT: }
+
 //--- impl_part.cppm
 module;
 #include "header.mock"
@@ -66,10 +126,53 @@ void World() {
     std::cout << W << std::endl;
 }
 
+// CHECK: {
+// CHECK-NEXT:   "revision": 0,
+// CHECK-NEXT:   "rules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "primary-output": "[[PREFIX]]/impl_part.o",
+// CHECK-NEXT:       "provides": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "is-interface": false,
+// CHECK-NEXT:           "logical-name": "M:impl_part",
+// CHECK-NEXT:           "source-path": "[[PREFIX]]/impl_part.cppm"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "requires": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "M:interface_part"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "version": 1
+// CHECK-NEXT: }
+
+// CHECK-MAKE: [[PREFIX]]/impl_part.o.ddi:
+// CHECK-MAKE:   [[PREFIX]]/impl_part.cppm
+// CHECK-MAKE:   [[PREFIX]]/header.mock
+
 //--- interface_part.cppm
 export module M:interface_part;
 export void World();
 
+// CHECK: {
+// CHECK-NEXT:   "revision": 0,
+// CHECK-NEXT:   "rules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "primary-output": "[[PREFIX]]/interface_part.o",
+// CHECK-NEXT:       "provides": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "is-interface": true,
+// CHECK-NEXT:           "logical-name": "M:interface_part",
+// CHECK-NEXT:           "source-path": "[[PREFIX]]/interface_part.cppm"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "version": 1
+// CHECK-NEXT: }
+
 //--- User.cpp
 import M;
 import third_party_module;
@@ -79,6 +182,24 @@ int main() {
     return 0;
 }
 
+// CHECK: {
+// CHECK-NEXT:   "revision": 0,
+// CHECK-NEXT:   "rules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "primary-output": "[[PREFIX]]/User.o",
+// CHECK-NEXT:       "requires": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "M"
+// CHECK-NEXT:         },
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "logical-name": "third_party_module"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "version": 1
+// CHECK-NEXT: }
+
 //--- Checks.cpp
 // CHECK: {
 // CHECK-NEXT:   "revision": 0,

diff  --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 49d9648f3c6a..b22046574d13 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -18,6 +19,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Host.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/Program.h"
@@ -169,9 +171,14 @@ llvm::cl::opt<unsigned>
 
 llvm::cl::opt<std::string>
     CompilationDB("compilation-database",
-                  llvm::cl::desc("Compilation database"), llvm::cl::Required,
+                  llvm::cl::desc("Compilation database"), llvm::cl::Optional,
                   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 "
+                   "the dependencies are to be computed."));
+
 llvm::cl::opt<std::string> ModuleName(
     "module-name", llvm::cl::Optional,
     llvm::cl::desc("the module of which the dependencies are to be computed"),
@@ -583,19 +590,109 @@ static std::string getModuleCachePath(ArrayRef<std::string> Args) {
   return std::string(Path);
 }
 
-int main(int argc, const char **argv) {
+// getCompilationDataBase - If -compilation-database is set, load the
+// compilation database from the specified file. Otherwise if the we're
+// generating P1689 format, trying to generate the compilation database
+// form specified command line after the positional parameter "--".
+static std::unique_ptr<tooling::CompilationDatabase>
+getCompilationDataBase(int argc, const char **argv, std::string &ErrorMessage) {
   llvm::InitLLVM X(argc, argv);
   llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
   if (!llvm::cl::ParseCommandLineOptions(argc, argv))
-    return 1;
+    return nullptr;
+
+  if (!CompilationDB.empty())
+    return tooling::JSONCompilationDatabase::loadFromFile(
+        CompilationDB, ErrorMessage,
+        tooling::JSONCommandLineSyntax::AutoDetect);
+
+  if (Format != ScanningOutputFormat::P1689) {
+    llvm::errs() << "the --compilation-database option: must be specified at "
+                    "least once!";
+    return nullptr;
+  }
+
+  // Trying to get the input file, the output file and the command line options
+  // from the positional parameter "--".
+  const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
+  if (DoubleDash == argv + argc) {
+    llvm::errs() << "The command line arguments is required after '--' in "
+                    "P1689 per file mode.";
+    return nullptr;
+  }
+  std::vector<const char *> CommandLine(DoubleDash + 1, argv + argc);
+
+  llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+      CompilerInstance::createDiagnostics(new DiagnosticOptions);
+  driver::Driver TheDriver(CommandLine[0], llvm::sys::getDefaultTargetTriple(),
+                           *Diags);
+  std::unique_ptr<driver::Compilation> C(
+      TheDriver.BuildCompilation(CommandLine));
+  if (!C)
+    return nullptr;
+
+  auto Cmd = C->getJobs().begin();
+  auto CI = std::make_unique<CompilerInvocation>();
+  CompilerInvocation::CreateFromArgs(*CI, Cmd->getArguments(), *Diags,
+                                     CommandLine[0]);
+  if (!CI)
+    return nullptr;
+
+  FrontendOptions &FEOpts = CI->getFrontendOpts();
+  if (FEOpts.Inputs.size() != 1) {
+    llvm::errs() << "Only one input file is allowed in P1689 per file mode.";
+    return nullptr;
+  }
+
+  // There might be multiple jobs for a compilation. Extract the specified
+  // output filename from the last job.
+  auto LastCmd = C->getJobs().end();
+  LastCmd--;
+  if (LastCmd->getOutputFilenames().size() != 1) {
+    llvm::errs() << "The command line should provide exactly one output file "
+                    "in P1689 per file mode.\n";
+  }
+  StringRef OutputFile = LastCmd->getOutputFilenames().front();
+
+  class InplaceCompilationDatabase : public tooling::CompilationDatabase {
+  public:
+    InplaceCompilationDatabase(StringRef InputFile, StringRef OutputFile,
+                               ArrayRef<const char *> CommandLine)
+        : Command(".", InputFile, {}, OutputFile) {
+      for (auto *C : CommandLine)
+        Command.CommandLine.push_back(C);
+    }
 
+    std::vector<tooling::CompileCommand>
+    getCompileCommands(StringRef FilePath) const override {
+      if (FilePath != Command.Filename)
+        return {};
+      return {Command};
+    }
+
+    std::vector<std::string> getAllFiles() const override {
+      return {Command.Filename};
+    }
+
+    std::vector<tooling::CompileCommand>
+    getAllCompileCommands() const override {
+      return {Command};
+    }
+
+  private:
+    tooling::CompileCommand Command;
+  };
+
+  return std::make_unique<InplaceCompilationDatabase>(
+      FEOpts.Inputs[0].getFile(), OutputFile, CommandLine);
+}
+
+int main(int argc, const char **argv) {
   std::string ErrorMessage;
-  std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
-      tooling::JSONCompilationDatabase::loadFromFile(
-          CompilationDB, ErrorMessage,
-          tooling::JSONCommandLineSyntax::AutoDetect);
+  std::unique_ptr<tooling::CompilationDatabase> Compilations =
+      getCompilationDataBase(argc, argv, ErrorMessage);
   if (!Compilations) {
-    llvm::errs() << "error: " << ErrorMessage << "\n";
+    llvm::errs() << ErrorMessage << "\n";
     return 1;
   }
 


        


More information about the cfe-commits mailing list