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 @@ -191,8 +191,7 @@ public: ModuleDepCollector(std::unique_ptr Opts, CompilerInstance &I, DependencyConsumer &C, - std::map> - OriginalPrebuiltModuleFiles); + CompilerInvocation &&OriginalCI); void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; @@ -215,9 +214,8 @@ std::unordered_map ModularDeps; /// Options that control the dependency output generation. std::unique_ptr Opts; - /// The mapping between prebuilt module names and module files that were - /// present in the original CompilerInvocation. - std::map> OriginalPrebuiltModuleFiles; + /// The original Clang invocation passed to dependency scanner. + CompilerInvocation OriginalInvocation; /// Checks whether the module is known as being prebuilt. bool isPrebuiltModule(const Module *M); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -47,9 +47,11 @@ /// A listener that collects the names and paths to imported modules. class ImportCollectingListener : public ASTReaderListener { + using PrebuiltModuleFilesT = + decltype(HeaderSearchOptions::PrebuiltModuleFiles); + public: - ImportCollectingListener( - std::map &PrebuiltModuleFiles) + ImportCollectingListener(PrebuiltModuleFilesT &PrebuiltModuleFiles) : PrebuiltModuleFiles(PrebuiltModuleFiles) {} bool needsImportVisitation() const override { return true; } @@ -59,7 +61,7 @@ } private: - std::map &PrebuiltModuleFiles; + PrebuiltModuleFilesT &PrebuiltModuleFiles; }; /// Transform arbitrary file name into an object-like file name. @@ -99,6 +101,9 @@ FileManager *FileMgr, std::shared_ptr PCHContainerOps, DiagnosticConsumer *DiagConsumer) override { + // Make a deep copy of the original Clang invocation. + CompilerInvocation OriginalInvocation(*Invocation); + // Create a compiler instance to handle the actual work. CompilerInstance Compiler(std::move(PCHContainerOps)); Compiler.setInvocation(std::move(Invocation)); @@ -144,24 +149,19 @@ Compiler.setFileManager(FileMgr); Compiler.createSourceManager(*FileMgr); - std::map PrebuiltModuleFiles; if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty()) { - /// Collect the modules that were prebuilt as part of the PCH. - ImportCollectingListener Listener(PrebuiltModuleFiles); + // Collect the modules that were prebuilt as part of the PCH and pass them + // to the compiler. This will prevent the implicit build to create + // duplicate modules and force reuse of existing prebuilt module files + // instead. + ImportCollectingListener Listener( + Compiler.getHeaderSearchOpts().PrebuiltModuleFiles); ASTReader::readASTFileControlBlock( Compiler.getPreprocessorOpts().ImplicitPCHInclude, Compiler.getFileManager(), Compiler.getPCHContainerReader(), /*FindModuleFileExtensions=*/false, Listener, /*ValidateDiagnosticOptions=*/false); } - /// Make a backup of the original prebuilt module file arguments. - std::map> OrigPrebuiltModuleFiles = - Compiler.getHeaderSearchOpts().PrebuiltModuleFiles; - /// Configure the compiler with discovered prebuilt modules. This will - /// prevent the implicit build of duplicate modules and force reuse of - /// existing prebuilt module files instead. - Compiler.getHeaderSearchOpts().PrebuiltModuleFiles.insert( - PrebuiltModuleFiles.begin(), PrebuiltModuleFiles.end()); // Create the dependency collector that will collect the produced // dependencies. @@ -187,8 +187,7 @@ break; case ScanningOutputFormat::Full: Compiler.addDependencyCollector(std::make_shared( - std::move(Opts), Compiler, Consumer, - std::move(OrigPrebuiltModuleFiles))); + std::move(Opts), Compiler, Consumer, std::move(OriginalInvocation))); break; } 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 @@ -20,8 +20,8 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( const ModuleDeps &Deps) const { - // Make a deep copy of the invocation. - CompilerInvocation CI(Instance.getInvocation()); + // Make a deep copy of the original Clang invocation. + CompilerInvocation CI(OriginalInvocation); // Remove options incompatible with explicit module build. CI.getFrontendOpts().Inputs.clear(); @@ -39,9 +39,6 @@ CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile); } - // Restore the original set of prebuilt module files. - CI.getHeaderSearchOpts().PrebuiltModuleFiles = OriginalPrebuiltModuleFiles; - CI.getPreprocessorOpts().ImplicitPCHInclude.clear(); return CI; @@ -269,10 +266,9 @@ ModuleDepCollector::ModuleDepCollector( std::unique_ptr Opts, CompilerInstance &I, - DependencyConsumer &C, - std::map> OriginalPrebuiltModuleFiles) + DependencyConsumer &C, CompilerInvocation &&OriginalCI) : Instance(I), Consumer(C), Opts(std::move(Opts)), - OriginalPrebuiltModuleFiles(std::move(OriginalPrebuiltModuleFiles)) {} + OriginalInvocation(std::move(OriginalCI)) {} void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { PP.addPPCallbacks(std::make_unique(Instance, *this)); diff --git a/clang/test/ClangScanDeps/Inputs/preserved-args/cdb.json.template b/clang/test/ClangScanDeps/Inputs/preserved-args/cdb.json.template new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/preserved-args/cdb.json.template @@ -0,0 +1,7 @@ +[ + { + "directory": "DIR", + "command": "clang -MD -MT my_target -serialize-diagnostics DIR/tu.dia -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -fmodule-file=Foo=DIR/foo.pcm -o DIR/tu.o", + "file": "DIR/tu.c" + } +] diff --git a/clang/test/ClangScanDeps/Inputs/preserved-args/mod.h b/clang/test/ClangScanDeps/Inputs/preserved-args/mod.h new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/preserved-args/mod.h @@ -0,0 +1 @@ +// mod.h diff --git a/clang/test/ClangScanDeps/Inputs/preserved-args/module.modulemap b/clang/test/ClangScanDeps/Inputs/preserved-args/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/preserved-args/module.modulemap @@ -0,0 +1,3 @@ +module Mod { + header "mod.h" +} diff --git a/clang/test/ClangScanDeps/Inputs/preserved-args/tu.c b/clang/test/ClangScanDeps/Inputs/preserved-args/tu.c new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/preserved-args/tu.c @@ -0,0 +1 @@ +#include "mod.h" diff --git a/clang/test/ClangScanDeps/preserved-args.c b/clang/test/ClangScanDeps/preserved-args.c new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/preserved-args.c @@ -0,0 +1,26 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: cp -r %S/Inputs/preserved-args/* %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: echo -%t > %t/result.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full >> %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s + +// CHECK: -[[PREFIX:.*]] +// CHECK-NEXT: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-serialize-diagnostic-file" +// CHECK-NEXT: "[[PREFIX]]/tu.dia" +// CHECK: "-fmodule-file=Foo=[[PREFIX]]/foo.pcm" +// CHECK: "-MT" +// CHECK-NEXT: "my_target" +// CHECK: "-dependency-file" +// CHECK-NEXT: "[[PREFIX]]/tu.d" +// CHECK: ], +// CHECK: "name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK: }