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/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/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 &C, StringRef Filename, + StringRef Target, + const InputInfo &Output, const InputInfo &Input, + const llvm::opt::ArgList &Args) 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 &C, StringRef Filename, + StringRef Target, const InputInfo &Output, + const InputInfo &Input, const ArgList &Args) 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 &D = 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 &CDB = *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 &A: Args) { + auto &O = 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 &it: ASL) + CDB << ", \"" << escape(it) << "\""; + } + Buf = "--target="; + Buf += Target; + CDB << ", \"" << escape(Buf) << "\"]},\n"; +} + void Clang::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { @@ -4074,6 +4128,11 @@ CmdArgs.push_back("-triple"); CmdArgs.push_back(Args.MakeArgString(TripleStr)); + if (const Arg *MJ = Args.getLastArg(options::OPT_MJ)) { + DumpCompilationDatabase(C, 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: 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; +}