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