aganea created this revision. aganea added reviewers: hans, amccarth, thakis, mstorsjo, akhuang. Herald added subscribers: llvm-commits, cfe-commits, hiraditya. Herald added projects: clang, LLVM.
This patch adds some missing information to the `LF_BUILDINFO` which allows for rebuilding a .CPP without any external dependency but the .OBJ itself (other than the compiler). Some external tools that we are using (Recode <https://www.indefiant.com/>, Live++ <https://liveplusplus.tech/>) are extracting the information to reproduce a build without any knowledge of the build system. The `LF_BUILDINFO` therefore stores a full path to the compiler, the PWD, a relative or absolute path to the TU, and the full CC1 command line. The command line needs to be freestanding (not depend on any environment variables). In the same way, MSVC doesn't store the provided command-line, but an expanded version (somehow their equivalent of CC1) which is also freestanding. For more information see PR36198 and D43002 <https://reviews.llvm.org/D43002>. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D80833 Files: clang/include/clang/Basic/CodeGenOptions.h clang/include/clang/Frontend/CompilerInvocation.h clang/lib/CodeGen/BackendUtil.cpp clang/lib/Frontend/CompilerInvocation.cpp clang/lib/Frontend/CreateInvocationFromCommandLine.cpp clang/test/CodeGen/debug-info-codeview-buildinfo.c clang/tools/driver/cc1_main.cpp clang/tools/driver/driver.cpp llvm/include/llvm/MC/MCTargetOptions.h llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -831,6 +831,31 @@ return TypeTable.writeLeafType(SIR); } +static std::string renderCommandLine(ArrayRef<const char *> CommandLineArgs, + StringRef MainFile) { + std::string FlatCmdLine; + SmallString<128> TempArg; + auto maybeQuote = [&TempArg](const char *Arg) -> StringRef { + const bool Escape = + StringRef(Arg).find_first_of(" \"\\$") != StringRef::npos; + if (!Escape) + return Arg; + TempArg.clear(); + (Twine("\"") + Arg + "\"").toVector(TempArg); + return TempArg; + }; + for (auto &Arg : CommandLineArgs) { + if (Arg == nullptr) + break; + // The command-line shall not contain the file to compile. + if (Arg == MainFile) + continue; + FlatCmdLine += maybeQuote(Arg); + FlatCmdLine += " "; + } + return FlatCmdLine; +} + void CodeViewDebug::emitBuildInfo() { // First, make LF_BUILDINFO. It's a sequence of strings with various bits of // build info. The known prefix is: @@ -851,6 +876,13 @@ getStringIdTypeIdx(TypeTable, MainSourceFile->getDirectory()); BuildInfoArgs[BuildInfoRecord::SourceFile] = getStringIdTypeIdx(TypeTable, MainSourceFile->getFilename()); + if (!StringRef(Asm->TM.Options.MCOptions.BuildTool).empty()) { + BuildInfoArgs[BuildInfoRecord::BuildTool] = + getStringIdTypeIdx(TypeTable, Asm->TM.Options.MCOptions.BuildTool); + BuildInfoArgs[BuildInfoRecord::CommandLine] = getStringIdTypeIdx( + TypeTable, renderCommandLine(Asm->TM.Options.MCOptions.CommandLineArgs, + MainSourceFile->getFilename())); + } // FIXME: Path to compiler and command line. PDB is intentionally blank unless // we implement /Zi type servers. BuildInfoRecord BIR(BuildInfoArgs); Index: llvm/include/llvm/MC/MCTargetOptions.h =================================================================== --- llvm/include/llvm/MC/MCTargetOptions.h +++ llvm/include/llvm/MC/MCTargetOptions.h @@ -9,6 +9,7 @@ #ifndef LLVM_MC_MCTARGETOPTIONS_H #define LLVM_MC_MCTARGETOPTIONS_H +#include "llvm/ADT/ArrayRef.h" #include <string> #include <vector> @@ -59,6 +60,9 @@ std::string AssemblyLanguage; std::string SplitDwarfFile; + const char *BuildTool = nullptr; + ArrayRef<const char *> CommandLineArgs; + /// Additional paths to search for `.include` directives when using the /// integrated assembler. std::vector<std::string> IASSearchPaths; Index: clang/tools/driver/driver.cpp =================================================================== --- clang/tools/driver/driver.cpp +++ clang/tools/driver/driver.cpp @@ -203,8 +203,7 @@ } } -extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, - void *MainAddr); +extern int cc1_main(ArrayRef<const char *> Argv, void *MainAddr); extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv, @@ -327,7 +326,7 @@ StringRef Tool = ArgV[1]; void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; if (Tool == "-cc1") - return cc1_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); + return cc1_main(makeArrayRef(ArgV), GetExecutablePathVP); if (Tool == "-cc1as") return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); Index: clang/tools/driver/cc1_main.cpp =================================================================== --- clang/tools/driver/cc1_main.cpp +++ clang/tools/driver/cc1_main.cpp @@ -181,7 +181,7 @@ return 0; } -int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { +int cc1_main(ArrayRef<const char *> Argv, void *MainAddr) { ensureSufficientStack(); std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); @@ -203,12 +203,11 @@ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); - bool Success = - CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, Diags); - + bool Success = CompilerInvocation::CreateFromArgs( + Clang->getInvocation(), Argv.slice(1), Diags, Argv[0]); if (Clang->getFrontendOpts().TimeTrace) { llvm::timeTraceProfilerInitialize( - Clang->getFrontendOpts().TimeTraceGranularity, Argv0); + Clang->getFrontendOpts().TimeTraceGranularity, Argv[0]); } // --print-supported-cpus takes priority over the actual compilation. if (Clang->getFrontendOpts().PrintSupportedCPUs) @@ -218,7 +217,7 @@ if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && Clang->getHeaderSearchOpts().ResourceDir.empty()) Clang->getHeaderSearchOpts().ResourceDir = - CompilerInvocation::GetResourcesPath(Argv0, MainAddr); + CompilerInvocation::GetResourcesPath(Argv[0], MainAddr); // Create the actual diagnostics engine. Clang->createDiagnostics(); Index: clang/test/CodeGen/debug-info-codeview-buildinfo.c =================================================================== --- /dev/null +++ clang/test/CodeGen/debug-info-codeview-buildinfo.c @@ -0,0 +1,17 @@ +// RUN: %clang_cl /c /Z7 %s /Fo%t.obj +// RUN: llvm-pdbutil dump --types %t.obj | FileCheck %s + +int main() { return 42; } + +// CHECK: Types (.debug$T) +// CHECK: ============================================================ +// CHECK: 0x[[PWD:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: [[PWDVAL:.+]] +// CHECK: 0x[[FILEPATH:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: [[FILEPATHVAL:.+[\\/]debug-info-codeview-buildinfo.c]] +// CHECK: 0x[[TOOL:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: [[TOOLVAL:.+[\\/]clang.*]] +// CHECK: 0x[[CMDLINE:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: -cc1 +// CHECK: 0x{{.+}} | LF_BUILDINFO [size = {{.+}}] +// CHECK: 0x[[PWD]]: `[[PWDVAL]]` +// CHECK: 0x[[TOOL]]: `[[TOOLVAL]]` +// CHECK: 0x[[FILEPATH]]: `[[FILEPATHVAL]]` +// CHECK: <no type>: `` +// CHECK: 0x[[CMDLINE]]: `-cc1 Index: clang/lib/Frontend/CreateInvocationFromCommandLine.cpp =================================================================== --- clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -93,7 +93,7 @@ if (CC1Args) *CC1Args = {CCArgs.begin(), CCArgs.end()}; auto CI = std::make_unique<CompilerInvocation>(); - if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags) && + if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags, Args[0]) && !ShouldRecoverOnErorrs) return nullptr; return CI; Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -3608,7 +3608,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef<const char *> CommandLineArgs, - DiagnosticsEngine &Diags) { + DiagnosticsEngine &Diags, + const char *BuildTool) { bool Success = true; // Parse the arguments. @@ -3728,6 +3729,11 @@ Res.getCodeGenOpts().FineGrainedBitfieldAccesses = false; Diags.Report(diag::warn_drv_fine_grained_bitfield_accesses_ignored); } + + // Store the command-line for using in CodeView backend. + Res.getCodeGenOpts().BuildTool = BuildTool; + Res.getCodeGenOpts().CommandLineArgs = CommandLineArgs; + return Success; } Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -519,6 +519,8 @@ Entry.Group == frontend::IncludeDirGroup::System)) Options.MCOptions.IASSearchPaths.push_back( Entry.IgnoreSysRoot ? Entry.Path : HSOpts.Sysroot + Entry.Path); + Options.MCOptions.BuildTool = CodeGenOpts.BuildTool; + Options.MCOptions.CommandLineArgs = CodeGenOpts.CommandLineArgs; } static Optional<GCOVOptions> getGCOVOptions(const CodeGenOptions &CodeGenOpts) { if (CodeGenOpts.DisableGCov) Index: clang/include/clang/Frontend/CompilerInvocation.h =================================================================== --- clang/include/clang/Frontend/CompilerInvocation.h +++ clang/include/clang/Frontend/CompilerInvocation.h @@ -155,7 +155,8 @@ /// \param [out] Res - The resulting invocation. static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef<const char *> CommandLineArgs, - DiagnosticsEngine &Diags); + DiagnosticsEngine &Diags, + const char *BuildTool = nullptr); /// Get the directory where the compiler headers /// reside, relative to the compiler binary (found by the passed in Index: clang/include/clang/Basic/CodeGenOptions.h =================================================================== --- clang/include/clang/Basic/CodeGenOptions.h +++ clang/include/clang/Basic/CodeGenOptions.h @@ -316,6 +316,10 @@ /// coverage pass should actually not be instrumented. std::vector<std::string> SanitizeCoverageBlacklistFiles; + /// Executable and command-line used to create a given CompilerInvocation. + const char *BuildTool = nullptr; + ArrayRef<const char *> CommandLineArgs; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits