arphaman created this revision.
arphaman added reviewers: Bigcheese, jkorous, dexonsmith.
Herald added a project: clang.

This patch adds a new option called `-gen-cdb-fragment-path`  to the driver, 
which can be used to specify a directory path to which clang can emit a 
fragment of a CDB for each compilation it needs to invoke.

The CDB fragment is emitted into a unique file in the specified directory. It 
contains the `-cc1` clang invocation in its command. The file itself is 
actually a valid standalone CDB (if you disregard the not yet well supported 
`-cc1` innovation by the CDB, which I'll fix). To load the full CDB that can be 
emitted during a build, I'm going to add a new CDB reader from a directory that 
reads all fragments and aggregates them into one CDB in a follow-up patch.

This is useful to setup verification infrastructure for `clang-scan-deps` for 
some projects that don't have a way of constructing a CDB from their build.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D66555

Files:
  clang/include/clang/Driver/Driver.h
  clang/include/clang/Driver/Job.h
  clang/include/clang/Driver/Options.td
  clang/lib/Driver/Compilation.cpp
  clang/lib/Driver/Driver.cpp
  clang/test/Driver/gen-cdb-fragment.c

Index: clang/test/Driver/gen-cdb-fragment.c
===================================================================
--- /dev/null
+++ clang/test/Driver/gen-cdb-fragment.c
@@ -0,0 +1,18 @@
+// REQUIRES: x86-registered-target
+// RUN: rm -rf %t.cdb
+// RUN: %clang -target x86_64-apple-macos10.15 -c %s -o -  -gen-cdb-fragment-path %t.cdb
+// RUN: ls %t.cdb | FileCheck --check-prefix=CHECK-LS %s
+// CHECK-LS: gen-cdb-fragment.c.{{.*}}.json
+
+// RUN: cat %t.cdb/*.json | FileCheck --check-prefix=CHECK %s
+// CHECK: [{
+// CHECK-NEXT: "directory":"{{.*}}",
+// CHECK-NEXT: "command":"{{.*}} -cc1 -triple x86_64-apple-macosx10.15.0 {{.*}}",
+// CHECK-NEXT: "file":"{{.*}}gen-cdb-fragment.c"
+// CHECK: }]
+
+// RUN: rm -rf %t.cdb
+// RUN: mkdir %t.cdb
+// RUN: ls %t.cdb | not FileCheck --check-prefix=CHECK-LS %s
+// RUN: %clang -target x86_64-apple-macos10.15 -S %s -o -  -gen-cdb-fragment-path %t.cdb
+// RUN: ls %t.cdb | FileCheck --check-prefix=CHECK-LS %s
Index: clang/lib/Driver/Driver.cpp
===================================================================
--- clang/lib/Driver/Driver.cpp
+++ clang/lib/Driver/Driver.cpp
@@ -1040,6 +1040,8 @@
   GenReproducer = Args.hasFlag(options::OPT_gen_reproducer,
                                options::OPT_fno_crash_diagnostics,
                                !!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"));
+  if (const Arg *A = Args.getLastArg(options::OPT_gen_cdb_fragment_path))
+    PathToCDBFragmentDir = A->getValue();
   // FIXME: TargetTriple is used by the target-prefixed calls to as/ld
   // and getToolChain is const.
   if (IsCLMode()) {
@@ -4862,3 +4864,46 @@
 bool clang::driver::isOptimizationLevelFast(const ArgList &Args) {
   return Args.hasFlag(options::OPT_Ofast, options::OPT_O_Group, false);
 }
+
+void Driver::emitCompilationDatabaseFragment(StringRef DestDir,
+                                             StringRef Executable,
+                                             StringRef Filename,
+                                             const ArgStringList &Args) const {
+  auto CWD = getVFS().getCurrentWorkingDirectory();
+  if (!CWD)
+    return;
+
+  SmallString<256> Path = DestDir;
+  getVFS().makeAbsolute(Path);
+  auto Err = llvm::sys::fs::create_directory(Path, /*IgnoreExisting=*/true);
+  if (Err)
+    return;
+
+  llvm::sys::path::append(Path, Twine(llvm::sys::path::filename(Filename)) +
+                                    ".%%%%.json");
+  int FD;
+  SmallString<256> TempPath;
+  Err = llvm::sys::fs::createUniqueFile(Path, FD, TempPath);
+  if (Err)
+    return;
+
+  llvm::raw_fd_ostream OS(FD, /*ShouldClose=*/true);
+  OS << "[{\n";
+  OS << R"("directory":")" << *CWD << "\",\n";
+  OS << R"("command":")";
+  auto EmitCommandLineFragment = [&OS](StringRef Arg) {
+    for (char C : Arg) {
+      if (C == ' ' || C == '\'' || C == '"' || C == '\\')
+        OS << '\\';
+      OS << C;
+    }
+  };
+  EmitCommandLineFragment(Executable);
+  for (const auto &Arg : Args) {
+    OS << ' ';
+    EmitCommandLineFragment(Arg);
+  }
+  OS << "\",\n";
+  OS << R"("file":")" << Filename << "\"\n";
+  OS << "}]";
+}
Index: clang/lib/Driver/Compilation.cpp
===================================================================
--- clang/lib/Driver/Compilation.cpp
+++ clang/lib/Driver/Compilation.cpp
@@ -176,6 +176,14 @@
 
     C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions);
   }
+  if (auto CDBOutputDir = getDriver().getPathToCDBFragmentDir()) {
+    if (isa<AssembleJobAction>(C.getSource()) ||
+        isa<BackendJobAction>(C.getSource())) {
+      for (const auto &F : C.getInputFilenames())
+        getDriver().emitCompilationDatabaseFragment(
+            *CDBOutputDir, C.getExecutable(), F, C.getArguments());
+    }
+  }
 
   std::string Error;
   bool ExecutionFailed;
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -280,6 +280,8 @@
   Flags<[CC1Option]>;
 def gen_reproducer: Flag<["-"], "gen-reproducer">, InternalDebugOpt,
   HelpText<"Auto-generates preprocessed source files and a reproduction script">;
+def gen_cdb_fragment_path: Separate<["-"], "gen-cdb-fragment-path">, InternalDebugOpt,
+  HelpText<"Emit a compilation database fragment to the specified directory">;
 
 def _migrate : Flag<["--"], "migrate">, Flags<[DriverOption]>,
   HelpText<"Run the migrator">;
Index: clang/include/clang/Driver/Job.h
===================================================================
--- clang/include/clang/Driver/Job.h
+++ clang/include/clang/Driver/Job.h
@@ -125,6 +125,10 @@
 
   const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
 
+  const llvm::opt::ArgStringList &getInputFilenames() const {
+    return InputFilenames;
+  }
+
   /// Print a command argument, and optionally quote it.
   static void printArg(llvm::raw_ostream &OS, StringRef Arg, bool Quote);
 
Index: clang/include/clang/Driver/Driver.h
===================================================================
--- clang/include/clang/Driver/Driver.h
+++ clang/include/clang/Driver/Driver.h
@@ -226,7 +226,20 @@
   /// jobs.
   unsigned CheckInputsExist : 1;
 
+  /// The directory to which the driver should write out a compilation database
+  /// fragment for its invocation.
+  std::string PathToCDBFragmentDir;
+
 public:
+  /// Returns the directory to which a compilation database fragment should be
+  /// written out for its invocation, or \c None if the fragments should not be
+  /// saved.
+  Optional<StringRef> getPathToCDBFragmentDir() const {
+    if (PathToCDBFragmentDir.empty())
+      return None;
+    return StringRef(PathToCDBFragmentDir);
+  }
+
   /// Force clang to emit reproducer for driver invocation. This is enabled
   /// indirectly by setting FORCE_CLANG_DIAGNOSTICS_CRASH environment variable
   /// or when using the -gen-reproducer driver flag.
@@ -606,6 +619,13 @@
                                 MutableArrayRef<unsigned> Digits);
   /// Compute the default -fmodule-cache-path.
   static void getDefaultModuleCachePath(SmallVectorImpl<char> &Result);
+
+  /// Emits a compilation database fragment to the given directory, that
+  /// contains an entry for the specified filename and arguments.
+  void
+  emitCompilationDatabaseFragment(StringRef DestDir, StringRef Executable,
+                                  StringRef Filename,
+                                  const llvm::opt::ArgStringList &Args) const;
 };
 
 /// \return True if the last defined optimization level is -Ofast.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to