diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -225,9 +225,11 @@ bool hasInvocation() const { return Invocation != nullptr; } - CompilerInvocation &getInvocation() { + CompilerInvocation &getInvocation() { return *getInvocationPtr(); } + + std::shared_ptr getInvocationPtr() { assert(Invocation && "Compiler instance has no invocation!"); - return *Invocation; + return Invocation; } /// setInvocation - Replace the current invocation. diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -12,6 +12,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" @@ -47,6 +48,9 @@ /// The identifier of the module. ModuleID ID; + /// Whether this is a "system" module. + bool IsSystem; + /// The path to the modulemap file which defines this module. /// /// This can be used to explicitly build this module. This file will @@ -71,6 +75,10 @@ // the primary TU. bool ImportedByMainFile = false; + /// The compiler invocation associated with the translation unit that imports + /// this module. + std::shared_ptr Invocation; + /// Gets the full command line suitable for passing to clang. /// /// \param LookupPCMPath This function is called to fill in `-fmodule-file=` diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -12,32 +12,61 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "llvm/Support/StringSaver.h" using namespace clang; using namespace tooling; using namespace dependencies; +static CompilerInvocation +makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps) { + // Make a deep copy of the invocation. + CompilerInvocation CI(*Deps.Invocation); + + // Remove options incompatible with explicit module build. + CI.getFrontendOpts().Inputs.clear(); + CI.getFrontendOpts().OutputFile.clear(); + + CI.getFrontendOpts().ProgramAction = frontend::GenerateModule; + CI.getLangOpts()->ModuleName = Deps.ID.ModuleName; + CI.getFrontendOpts().IsSystemModule = Deps.IsSystem; + + CI.getLangOpts()->ImplicitModules = false; + CI.getHeaderSearchOpts().ImplicitModuleMaps = false; + + return CI; +} + +static std::vector +serializeCompilerInvocation(CompilerInvocation &CI) { + // Set up string allocator. + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Strings(Alloc); + auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); }; + SmallVector Args; + + // Synthesize full command line from the CompilerInvocation. + CI.generateCC1CommandLine(Args, SA); + + // Convert arguments to the return type. + std::vector Ret; + Ret.reserve(Args.size()); + for (const char *Arg : Args) + Ret.emplace_back(Arg); + + return Ret; +} + std::vector ModuleDeps::getFullCommandLine( std::function LookupPCMPath, std::function LookupModuleDeps) const { - // TODO: Build full command line. That also means capturing the original - // command line into NonPathCommandLine. - - std::vector Ret{ - "-fno-implicit-modules", - "-fno-implicit-module-maps", - }; + CompilerInvocation CI(makeInvocationForModuleBuildWithoutPaths(*this)); - std::vector PCMPaths; - std::vector ModMapPaths; dependencies::detail::collectPCMAndModuleMapPaths( - ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths); - for (const std::string &PCMPath : PCMPaths) - Ret.push_back("-fmodule-file=" + PCMPath); - for (const std::string &ModMapPath : ModMapPaths) - Ret.push_back("-fmodule-map-file=" + ModMapPath); + ClangModuleDeps, LookupPCMPath, LookupModuleDeps, + CI.getFrontendOpts().ModuleFiles, CI.getFrontendOpts().ModuleMapFiles); - return Ret; + return serializeCompilerInvocation(CI); } void dependencies::detail::collectPCMAndModuleMapPaths( @@ -149,10 +178,12 @@ .getModuleMap() .getContainingModuleMapFile(M); + MD.Invocation = Instance.getInvocationPtr(); MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : ""); MD.ID.ModuleName = M->getFullModuleName(); MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName()); MD.ID.ContextHash = MDC.ContextHash; + MD.IsSystem = M->IsSystem; serialization::ModuleFile *MF = MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile()); MDC.Instance.getASTReader()->visitInputFiles( diff --git a/clang/test/ClangScanDeps/modules-full.cpp b/clang/test/ClangScanDeps/modules-full.cpp --- a/clang/test/ClangScanDeps/modules-full.cpp +++ b/clang/test/ClangScanDeps/modules-full.cpp @@ -37,11 +37,11 @@ // CHECK-NEXT: ], // CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", // CHECK-NEXT: "command-line": [ -// CHECK-NEXT: "-fno-implicit-modules", -// CHECK-NEXT: "-fno-implicit-module-maps", -// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache{{(_clangcl)?}}/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm", -// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" -// CHECK-NEXT: ], +// CHECK: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap", +// CHECK: "-fmodule-file=[[PREFIX]]/module-cache{{(_clangcl)?}}/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm", +// CHECK-NOT: "-fimplicit-module-maps", +// CHECK: "-fno-implicit-modules", +// CHECK: ], // CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", // CHECK-NEXT: "file-deps": [ // CHECK-NEXT: "[[PREFIX]]/Inputs/header.h", @@ -53,9 +53,9 @@ // CHECK-NEXT: "clang-module-deps": [], // CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", // CHECK-NEXT: "command-line": [ -// CHECK-NEXT: "-fno-implicit-modules", -// CHECK-NEXT: "-fno-implicit-module-maps" -// CHECK-NEXT: ], +// CHECK-NOT: "-fimplicit-module-maps", +// CHECK: "-fno-implicit-modules", +// CHECK: ], // CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H2:[A-Z0-9]+]]", // CHECK-NEXT: "file-deps": [ // CHECK-NEXT: "[[PREFIX]]/Inputs/header.h", @@ -67,9 +67,9 @@ // CHECK-NEXT: "clang-module-deps": [], // CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", // CHECK-NEXT: "command-line": [ -// CHECK-NEXT: "-fno-implicit-modules", -// CHECK-NEXT: "-fno-implicit-module-maps" -// CHECK-NEXT: ], +// CHECK-NOT: "-fimplicit-module-maps", +// CHECK: "-fno-implicit-modules", +// CHECK: ], // CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", // CHECK-NEXT: "file-deps": [ // CHECK-NEXT: "[[PREFIX]]/Inputs/header2.h",