[PATCH] D27140: Allow clang to write compilation database records

2016-12-06 Thread Joerg Sonnenberger via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL288821: Allow clang to write compilation database records. 
(authored by joerg).

Changed prior to commit:
  https://reviews.llvm.org/D27140?vs=79997=80429#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D27140

Files:
  cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
  cfe/trunk/include/clang/Driver/Options.td
  cfe/trunk/lib/Driver/Tools.cpp
  cfe/trunk/lib/Driver/Tools.h
  cfe/trunk/test/Driver/compilation_database.c

Index: cfe/trunk/include/clang/Driver/Options.td
===
--- cfe/trunk/include/clang/Driver/Options.td
+++ cfe/trunk/include/clang/Driver/Options.td
@@ -266,6 +266,8 @@
 MetaVarName<"">;
 def MG : Flag<["-"], "MG">, Group, Flags<[CC1Option]>,
 HelpText<"Add missing headers to depfile">;
+def MJ : JoinedOrSeparate<["-"], "MJ">, Group,
+HelpText<"Write a compilation database entry per input">;
 def MP : Flag<["-"], "MP">, Group, Flags<[CC1Option]>,
 HelpText<"Create phony target for each dependency (other than main file)">;
 def MQ : JoinedOrSeparate<["-"], "MQ">, Group, Flags<[CC1Option]>,
Index: cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
===
--- cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
+++ cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
@@ -87,6 +87,8 @@
   "the clang compiler does not support '%0' for C++ on Darwin/i386">;
 def err_drv_command_failed : Error<
   "%0 command failed with exit code %1 (use -v to see invocation)">;
+def err_drv_compilationdatabase : Error<
+  "compilation database '%0' could not be opened: %1">;
 def err_drv_command_signalled : Error<
   "%0 command failed due to signal (use -v to see invocation)">;
 def err_drv_force_crash : Error<
Index: cfe/trunk/test/Driver/compilation_database.c
===
--- cfe/trunk/test/Driver/compilation_database.c
+++ cfe/trunk/test/Driver/compilation_database.c
@@ -0,0 +1,10 @@
+// RUN: %clang -MD -MP -c -x c %s -xc++ %s -Wall -MJ - 2>&1 | FileCheck %s
+// RUN: not %clang -c -x c %s -MJ %s/non-existant 2>&1 | FileCheck --check-prefix=ERROR %s
+
+// CHECK: { "directory": "[[CWD:[^"]+]]",  "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc", "[[SRC]]", "-c", "-Wall", "--target={{[^"]+}}"]},
+// CHECK: { "directory": "[[CWD:[^"]+]]",  "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc++", "[[SRC]]", "-c", "-Wall", "--target={{[^"]+}}"]},
+// ERROR: error: compilation database '{{.*}}/non-existant' could not be opened:
+
+int main(void) {
+  return 0;
+}
Index: cfe/trunk/lib/Driver/Tools.h
===
--- cfe/trunk/lib/Driver/Tools.h
+++ cfe/trunk/lib/Driver/Tools.h
@@ -17,6 +17,7 @@
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Compiler.h"
 
 namespace clang {
@@ -101,6 +102,12 @@
 
   mutable std::unique_ptr CLFallback;
 
+  mutable std::unique_ptr CompilationDatabase = nullptr;
+  void DumpCompilationDatabase(Compilation , StringRef Filename,
+   StringRef Target,
+   const InputInfo , const InputInfo ,
+   const llvm::opt::ArgList ) const;
+
 public:
   // CAUTION! The first constructor argument ("clang") is not arbitrary,
   // as it is for other tools. Some operations on a Tool actually test
Index: cfe/trunk/lib/Driver/Tools.cpp
===
--- cfe/trunk/lib/Driver/Tools.cpp
+++ cfe/trunk/lib/Driver/Tools.cpp
@@ -40,9 +40,9 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TargetParser.h"
+#include "llvm/Support/YAMLParser.h"
 
 #ifdef LLVM_ON_UNIX
 #include  // For getuid().
@@ -4030,6 +4030,60 @@
 CmdArgs.push_back("-KPIC");
 }
 
+void Clang::DumpCompilationDatabase(Compilation , StringRef Filename,
+StringRef Target, const InputInfo ,
+const InputInfo , const ArgList ) const {
+  // If this is a dry run, do not create the compilation database file.
+  if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH))
+return;
+
+  using llvm::yaml::escape;
+  const Driver  = getToolChain().getDriver();
+
+  if (!CompilationDatabase) {
+std::error_code EC;
+auto File = llvm::make_unique(Filename, EC, llvm::sys::fs::F_Text);
+if (EC) {
+  D.Diag(clang::diag::err_drv_compilationdatabase) << 

[PATCH] D27140: Allow clang to write compilation database records

2016-12-06 Thread Manuel Klimek via Phabricator via cfe-commits
klimek accepted this revision.
klimek added a comment.
This revision is now accepted and ready to land.

LG. We can always add more intricate ways later on.


Repository:
  rL LLVM

https://reviews.llvm.org/D27140



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27140: Allow clang to write compilation database records

2016-12-05 Thread Alexander Richardson via Phabricator via cfe-commits
arichardson added a comment.

It would also be good to append --sysroot=DEFAULT_SYSROOT if DEFAULT_SYSROOT is 
not empty and there is no explicit --sysroot flag. That way the compilation 
database can also be consumed by a libclang based too that does not have the 
same DEFAULT_SYSROOT set.


Repository:
  rL LLVM

https://reviews.llvm.org/D27140



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27140: Allow clang to write compilation database records

2016-12-05 Thread Joerg Sonnenberger via Phabricator via cfe-commits
joerg added a comment.

Ping


Repository:
  rL LLVM

https://reviews.llvm.org/D27140



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27140: Allow clang to write compilation database records

2016-12-01 Thread Joerg Sonnenberger via Phabricator via cfe-commits
joerg updated the summary for this revision.
joerg set the repository for this revision to rL LLVM.
joerg updated this revision to Diff 79997.
joerg added a comment.

Add test case.


Repository:
  rL LLVM

https://reviews.llvm.org/D27140

Files:
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Driver/Options.td
  lib/Driver/Tools.cpp
  lib/Driver/Tools.h
  test/Driver/compilation_database.c

Index: test/Driver/compilation_database.c
===
--- test/Driver/compilation_database.c
+++ test/Driver/compilation_database.c
@@ -0,0 +1,10 @@
+// RUN: %clang -MD -MP -c -x c %s -xc++ %s -Wall -MJ - 2>&1 | FileCheck %s
+// RUN: not %clang -c -x c %s -MJ %s/non-existant 2>&1 | FileCheck --check-prefix=ERROR %s
+
+// CHECK: { "directory": "[[CWD:[^"]+]]",  "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc", "[[SRC]]", "-c", "-Wall", "--target={{[^"]+}}"]},
+// CHECK: { "directory": "[[CWD:[^"]+]]",  "file": "[[SRC:[^"]+[/|\\]compilation_database.c]]", "output": "compilation_database.o", "arguments": ["{{[^"]*}}clang{{[^"]*}}", "-xc++", "[[SRC]]", "-c", "-Wall", "--target={{[^"]+}}"]},
+// ERROR: error: compilation database '{{.*}}/non-existant' could not be opened:
+
+int main(void) {
+  return 0;
+}
Index: lib/Driver/Tools.h
===
--- lib/Driver/Tools.h
+++ lib/Driver/Tools.h
@@ -17,6 +17,7 @@
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Compiler.h"
 
 namespace clang {
@@ -101,6 +102,12 @@
 
   mutable std::unique_ptr CLFallback;
 
+  mutable std::unique_ptr CompilationDatabase = nullptr;
+  void DumpCompilationDatabase(Compilation , StringRef Filename,
+   StringRef Target,
+   const InputInfo , const InputInfo ,
+   const llvm::opt::ArgList ) const;
+
 public:
   // CAUTION! The first constructor argument ("clang") is not arbitrary,
   // as it is for other tools. Some operations on a Tool actually test
Index: lib/Driver/Tools.cpp
===
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -40,9 +40,9 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TargetParser.h"
+#include "llvm/Support/YAMLParser.h"
 
 #ifdef LLVM_ON_UNIX
 #include  // For getuid().
@@ -4005,6 +4005,60 @@
 CmdArgs.push_back("-KPIC");
 }
 
+void Clang::DumpCompilationDatabase(Compilation , StringRef Filename,
+StringRef Target, const InputInfo ,
+const InputInfo , const ArgList ) const {
+  // If this is a dry run, do not create the compilation database file.
+  if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH))
+return;
+
+  using llvm::yaml::escape;
+  const Driver  = getToolChain().getDriver();
+
+  if (!CompilationDatabase) {
+std::error_code EC;
+auto File = llvm::make_unique(Filename, EC, llvm::sys::fs::F_Text);
+if (EC) {
+  D.Diag(clang::diag::err_drv_compilationdatabase) << Filename
+   << EC.message();
+  return;
+}
+CompilationDatabase = std::move(File);
+  }
+  auto  = *CompilationDatabase;
+  SmallString<128> Buf;
+  if (llvm::sys::fs::current_path(Buf))
+Buf = ".";
+  CDB << "{ \"directory\": \"" << escape(Buf) << "\"";
+  CDB << ", \"file\": \"" << escape(Input.getFilename()) << "\"";
+  CDB << ", \"output\": \"" << escape(Output.getFilename()) << "\"";
+  CDB << ", \"arguments\": [\"" << escape(D.ClangExecutable) << "\"";
+  Buf = "-x";
+  Buf += types::getTypeName(Input.getType());
+  CDB << ", \"" << escape(Buf) << "\"";
+  CDB << ", \"" << escape(Input.getFilename()) << "\"";
+  for (auto : Args) {
+auto  = A->getOption();
+// Skip language selection, which is positional.
+if (O.getID() == options::OPT_x)
+  continue;
+// Skip writing dependency output and the compilation database itself.
+if (O.getGroup().isValid() && O.getGroup().getID() == options::OPT_M_Group)
+  continue;
+// Skip inputs.
+if (O.getKind() == Option::InputClass)
+  continue;
+// All other arguments are quoted and appended.
+ArgStringList ASL;
+A->render(Args, ASL);
+for (auto : ASL)
+  CDB << ", \"" << escape(it) << "\"";
+  }
+  Buf = "--target=";
+  Buf += Target;
+  CDB << ", \"" << escape(Buf) << "\"]},\n";
+}
+
 void Clang::ConstructJob(Compilation , const JobAction ,
  const InputInfo , const InputInfoList ,
  const ArgList , const char *LinkingOutput) 

[PATCH] D27140: Allow clang to write compilation database records

2016-11-29 Thread Manuel Klimek via Phabricator via cfe-commits
klimek added reviewers: bkramer, akyrtzi.
klimek added a comment.

Adding some folks. One question is whether we can use the additional output 
stuff doug added at some point for this.


https://reviews.llvm.org/D27140



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27140: Allow clang to write compilation database records

2016-11-28 Thread Joerg Sonnenberger via Phabricator via cfe-commits
joerg updated this revision to Diff 79482.
joerg added a comment.

Move implementation into the Clang class, keep track of the raw_fd_stream. This 
avoids reopening it on multiple inputs and removes the need for append mode. 
Short circuit the function when -### is present. Add proper diagnostics on open 
failure.

Still not completely happy with the place and the associated need for the 
mutable member.


https://reviews.llvm.org/D27140

Files:
  docs/JSONCompilationDatabase.rst
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Driver/Options.td
  lib/Driver/Tools.cpp
  lib/Driver/Tools.h

Index: lib/Driver/Tools.h
===
--- lib/Driver/Tools.h
+++ lib/Driver/Tools.h
@@ -17,6 +17,7 @@
 #include "clang/Driver/Util.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Compiler.h"
 
 namespace clang {
@@ -101,6 +102,11 @@
 
   mutable std::unique_ptr CLFallback;
 
+  mutable std::unique_ptr CompilationDatabase = nullptr;
+  void DumpCompilationDatabase(StringRef Filename, StringRef Target,
+   const InputInfo , const InputInfo ,
+   const llvm::opt::ArgList ) const;
+
 public:
   // CAUTION! The first constructor argument ("clang") is not arbitrary,
   // as it is for other tools. Some operations on a Tool actually test
Index: lib/Driver/Tools.cpp
===
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -40,9 +40,9 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TargetParser.h"
+#include "llvm/Support/YAMLParser.h"
 
 #ifdef LLVM_ON_UNIX
 #include  // For getuid().
@@ -4002,6 +4002,60 @@
 CmdArgs.push_back("-KPIC");
 }
 
+void Clang::DumpCompilationDatabase(StringRef Filename, StringRef Target, const InputInfo ,
+const InputInfo , const ArgList ) const {
+
+  // If this is a dry run, do not create the compilation database file.
+  if (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH))
+return;
+
+  using llvm::yaml::escape;
+  const Driver  = getToolChain().getDriver();
+
+  if (!CompilationDatabase) {
+std::error_code EC;
+auto File = llvm::make_unique(Filename, EC, llvm::sys::fs::F_Text);
+if (EC) {
+  D.Diag(clang::diag::err_drv_compilationdatabase) << Filename
+   << EC.message();
+  return;
+}
+CompilationDatabase = std::move(File);
+  }
+  auto  = *CompilationDatabase;
+  SmallString<128> Buf;
+  if (llvm::sys::fs::current_path(Buf))
+Buf = ".";
+  CDB << "{ \"directory\": \"" << escape(Buf) << "\"";
+  CDB << ", \"file\": \"" << escape(Input.getFilename()) << "\"";
+  CDB << ", \"output\": \"" << escape(Output.getFilename()) << "\"";
+  CDB << ", \"arguments\": [\"" << escape(D.ClangExecutable) << "\"";
+  Buf = "-x";
+  Buf += types::getTypeName(Input.getType());
+  CDB << ", \"" << escape(Buf) << "\"";
+  CDB << ", \"" << escape(Input.getFilename()) << "\"";
+  for (auto : Args) {
+auto  = A->getOption();
+// Skip language selection, which is positional.
+if (O.getID() == options::OPT_x)
+  continue;
+// Skip writing dependency output and the compilation database itself.
+if (O.getGroup().isValid() && O.getGroup().getID() == options::OPT_M_Group)
+  continue;
+// Skip inputs.
+if (O.getKind() == Option::InputClass)
+  continue;
+// All other arguments are quoted and appended.
+ArgStringList ASL;
+A->render(Args, ASL);
+for (auto : ASL)
+  CDB << ", \"" << escape(it) << "\"";
+  }
+  Buf = "--target=";
+  Buf += Target;
+  CDB << ", \"" << escape(Buf) << "\"]},\n";
+}
+
 void Clang::ConstructJob(Compilation , const JobAction ,
  const InputInfo , const InputInfoList ,
  const ArgList , const char *LinkingOutput) const {
@@ -4046,6 +4100,11 @@
   CmdArgs.push_back("-triple");
   CmdArgs.push_back(Args.MakeArgString(TripleStr));
 
+  if (const Arg *MJ = Args.getLastArg(options::OPT_MJ)) {
+DumpCompilationDatabase(MJ->getValue(), TripleStr, Output, Input, Args);
+Args.ClaimAllArgs(options::OPT_MJ);
+  }
+
   if (IsCuda) {
 // We have to pass the triple of the host if compiling for a CUDA device and
 // vice-versa.
Index: include/clang/Driver/Options.td
===
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -266,6 +266,8 @@
 MetaVarName<"">;
 def MG : Flag<["-"], "MG">, Group, Flags<[CC1Option]>,
 HelpText<"Add missing headers to depfile">;
+def MJ : JoinedOrSeparate<["-"], "MJ">, Group,
+HelpText<"Write a compilation database entry per 

[PATCH] D27140: Allow clang to write compilation database records

2016-11-28 Thread Joerg Sonnenberger via Phabricator via cfe-commits
joerg removed rL LLVM as the repository for this revision.
joerg updated this revision to Diff 79423.
joerg added a comment.

Use llvm::yaml::escape.


https://reviews.llvm.org/D27140

Files:
  include/clang/Driver/Options.td
  lib/Driver/Tools.cpp

Index: include/clang/Driver/Options.td
===
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -266,6 +266,8 @@
 MetaVarName<"">;
 def MG : Flag<["-"], "MG">, Group, Flags<[CC1Option]>,
 HelpText<"Add missing headers to depfile">;
+def MJ : JoinedOrSeparate<["-"], "MJ">, Group,
+HelpText<"Write a compilation database entry per input">;
 def MP : Flag<["-"], "MP">, Group, Flags<[CC1Option]>,
 HelpText<"Create phony target for each dependency (other than main file)">;
 def MQ : JoinedOrSeparate<["-"], "MQ">, Group, Flags<[CC1Option]>,
Index: lib/Driver/Tools.cpp
===
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -43,6 +43,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TargetParser.h"
+#include "llvm/Support/YAMLParser.h"
 
 #ifdef LLVM_ON_UNIX
 #include  // For getuid().
@@ -4002,6 +4003,64 @@
 CmdArgs.push_back("-KPIC");
 }
 
+static void QuoteJSONString(llvm::raw_fd_ostream , StringRef Str) {
+  Stream << "\"";
+  Stream << llvm::yaml::escape(Str);
+  Stream << "\"";
+}
+
+static void DumpCompilationDatabase(const Driver , StringRef Filename, StringRef Target, const InputInfo ,
+const InputInfo , const ArgList ) {
+  std::error_code EC;
+  llvm::raw_fd_ostream File(Filename, EC, llvm::sys::fs::F_Text | llvm::sys::fs::F_Append);
+  if (EC) {
+//errs() << "Failed to open " << Filename << ": " << EC.message() << "\n" ;
+return;
+  }
+  SmallString<128> Buf;
+  if (llvm::sys::fs::current_path(Buf))
+Buf = ".";
+  File << "{ \"directory\": ";
+  QuoteJSONString(File, Buf);
+  File << ", \"file\": ";
+  QuoteJSONString(File, Input.getFilename());
+  File << ", \"output\": ";
+  QuoteJSONString(File, Output.getFilename());
+
+  File << ", \"arguments\": [";
+  QuoteJSONString(File, D.ClangExecutable);
+  File << ", ";
+  Buf = "-x";
+  Buf += types::getTypeName(Input.getType());
+  QuoteJSONString(File, Buf);
+  File << ", ";
+  QuoteJSONString(File, Input.getFilename());
+  for (auto : Args) {
+auto  = A->getOption();
+// Skip language selection, which is positional.
+if (O.getID() == options::OPT_x)
+  continue;
+// Skip writing dependency output and the compiliation database itself.
+if (O.getGroup().isValid() && O.getGroup().getID() == options::OPT_M_Group)
+  continue;
+// Skip inputs.
+if (O.getKind() == Option::InputClass)
+  continue;
+// All other arguments are quoted and appended.
+ArgStringList ASL;
+A->render(Args, ASL);
+for (auto : ASL) {
+  File << ", ";
+  QuoteJSONString(File, it);
+}
+  }
+  File << ", ";
+  Buf = "--target=";
+  Buf += Target;
+  QuoteJSONString(File, Buf);
+  File << "]},\n";
+}
+
 void Clang::ConstructJob(Compilation , const JobAction ,
  const InputInfo , const InputInfoList ,
  const ArgList , const char *LinkingOutput) const {
@@ -4046,6 +4105,10 @@
   CmdArgs.push_back("-triple");
   CmdArgs.push_back(Args.MakeArgString(TripleStr));
 
+  if (const Arg *MJ = Args.getLastArg(options::OPT_MJ))
+DumpCompilationDatabase(C.getDriver(), MJ->getValue(), TripleStr, Output, Input, Args);
+  Args.ClaimAllArgs(options::OPT_MJ);
+
   if (IsCuda) {
 // We have to pass the triple of the host if compiling for a CUDA device and
 // vice-versa.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27140: Allow clang to write compilation database records

2016-11-25 Thread Joerg Sonnenberger via Phabricator via cfe-commits
joerg created this revision.
joerg added reviewers: klimek, rsmith.
joerg added a subscriber: cfe-commits.
joerg set the repository for this revision to rL LLVM.
joerg added a dependency: D27138: Extend CompilationDatabase by a field for the 
output filename.

When integrating compilation database output into existing build systems, two 
approaches dominate so far. Ad-hoc implementation of the JSON output rules or 
using compiler wrappers. This patch adds a new option "-MJ foo.json" which 
gives a slightly cleaned up compilation record. The output is a fragment, i.e. 
you still need to add the array markers, but it allows multiple files to be 
easy merged. This way the only change in a build system is adding the option 
with potentially a per-target output file and merging the files with something 
like `(echo '['; cat *.o.json; echo ']' > compilation_database.json`.

The current implementation has two issues:

1. It doesn't honor -###. This would be easily fixable though.
2. It opens the output file more than once. That's why it is currently using 
append mode. Fixing this requires either moving it to a different part in the 
processing chain or storing the stream in an appropiate place.

The output record currently depends on https://reviews.llvm.org/D27138, but 
would be easily adjustable if necessary. I'm aware of the missing test cases, 
will provide them once the architectural side is clear.


Repository:
  rL LLVM

https://reviews.llvm.org/D27140

Files:
  include/clang/Driver/Options.td
  lib/Driver/Tools.cpp

Index: include/clang/Driver/Options.td
===
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -266,6 +266,8 @@
 MetaVarName<"">;
 def MG : Flag<["-"], "MG">, Group, Flags<[CC1Option]>,
 HelpText<"Add missing headers to depfile">;
+def MJ : JoinedOrSeparate<["-"], "MJ">, Group,
+HelpText<"Write a compilation database entry per input">;
 def MP : Flag<["-"], "MP">, Group, Flags<[CC1Option]>,
 HelpText<"Create phony target for each dependency (other than main file)">;
 def MQ : JoinedOrSeparate<["-"], "MQ">, Group, Flags<[CC1Option]>,
Index: lib/Driver/Tools.cpp
===
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -4002,6 +4002,72 @@
 CmdArgs.push_back("-KPIC");
 }
 
+static void QuoteJSONString(llvm::raw_fd_ostream , StringRef Str) {
+  Stream << "\"";
+  if (Str.find_first_of("\\\"") == Str.npos) {
+Stream << Str;
+  } else {
+for (auto ch: Str) {
+  if (ch == '\\' || ch == '"')
+Stream << '\\';
+  Stream << ch;
+}
+  }
+  Stream << "\"";
+}
+
+static void DumpCompilationDatabase(const Driver , StringRef Filename, StringRef Target, const InputInfo ,
+const InputInfo , const ArgList ) {
+  std::error_code EC;
+  llvm::raw_fd_ostream File(Filename, EC, llvm::sys::fs::F_Text | llvm::sys::fs::F_Append);
+  if (EC) {
+//errs() << "Failed to open " << Filename << ": " << EC.message() << "\n" ;
+return;
+  }
+  SmallString<128> Buf;
+  if (llvm::sys::fs::current_path(Buf))
+Buf = ".";
+  File << "{ \"directory\": ";
+  QuoteJSONString(File, Buf);
+  File << ", \"file\": ";
+  QuoteJSONString(File, Input.getFilename());
+  File << ", \"output\": ";
+  QuoteJSONString(File, Output.getFilename());
+
+  File << ", \"arguments\": [";
+  QuoteJSONString(File, D.ClangExecutable);
+  File << ", ";
+  Buf = "-x";
+  Buf += types::getTypeName(Input.getType());
+  QuoteJSONString(File, Buf);
+  File << ", ";
+  QuoteJSONString(File, Input.getFilename());
+  for (auto : Args) {
+auto  = A->getOption();
+// Skip language selection, which is positional.
+if (O.getID() == options::OPT_x)
+  continue;
+// Skip writing dependency output and the compiliation database itself.
+if (O.getGroup().isValid() && O.getGroup().getID() == options::OPT_M_Group)
+  continue;
+// Skip inputs.
+if (O.getKind() == Option::InputClass)
+  continue;
+// All other arguments are quoted and appended.
+ArgStringList ASL;
+A->render(Args, ASL);
+for (auto : ASL) {
+  File << ", ";
+  QuoteJSONString(File, it);
+}
+  }
+  File << ", ";
+  Buf = "--target=";
+  Buf += Target;
+  QuoteJSONString(File, Buf);
+  File << "]},\n";
+}
+
 void Clang::ConstructJob(Compilation , const JobAction ,
  const InputInfo , const InputInfoList ,
  const ArgList , const char *LinkingOutput) const {
@@ -4046,6 +4112,10 @@
   CmdArgs.push_back("-triple");
   CmdArgs.push_back(Args.MakeArgString(TripleStr));
 
+  if (const Arg *MJ = Args.getLastArg(options::OPT_MJ))
+DumpCompilationDatabase(C.getDriver(), MJ->getValue(), TripleStr, Output, Input, Args);
+  Args.ClaimAllArgs(options::OPT_MJ);
+
   if (IsCuda) {
 // We have to pass the triple of the host if compiling for a CUDA device and