benlangmuir updated this revision to Diff 455315.
benlangmuir added a comment.
- Remove CompilerInvocation from Command and ModuleDeps. Only the arg strings 
are exposed now.
- Simplify Command, which is now just a simple struct.
- Move the logic for mutating the CompilerInvocation for the TU into the 
scanner.
- Minor refactoring to reduce nesting in DependencyScanningWorker.
- Rebase


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132405/new/

https://reviews.llvm.org/D132405

Files:
  clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
  clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
  clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
  clang/include/clang/Tooling/Tooling.h
  clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
  clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
  clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
  clang/lib/Tooling/Tooling.cpp
  clang/test/ClangScanDeps/deprecated-driver-api.c
  clang/test/ClangScanDeps/diagnostics.c
  clang/test/ClangScanDeps/header-search-pruning-transitive.c
  clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
  clang/test/ClangScanDeps/modules-context-hash-outputs.c
  clang/test/ClangScanDeps/modules-context-hash-warnings.c
  clang/test/ClangScanDeps/modules-context-hash.c
  clang/test/ClangScanDeps/modules-dep-args.c
  clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
  clang/test/ClangScanDeps/modules-full.cpp
  clang/test/ClangScanDeps/modules-inferred.m
  clang/test/ClangScanDeps/modules-no-undeclared-includes.c
  clang/test/ClangScanDeps/modules-pch-common-submodule.c
  clang/test/ClangScanDeps/modules-pch-common-via-submodule.c
  clang/test/ClangScanDeps/modules-pch.c
  clang/test/ClangScanDeps/multiple-commands.c
  clang/test/ClangScanDeps/removed-args.c
  clang/tools/clang-scan-deps/ClangScanDeps.cpp
  clang/unittests/Tooling/ToolingTest.cpp
  clang/utils/module-deps-to-rsp.py

Index: clang/utils/module-deps-to-rsp.py
===================================================================
--- clang/utils/module-deps-to-rsp.py
+++ clang/utils/module-deps-to-rsp.py
@@ -48,6 +48,9 @@
                       type=str)
   action.add_argument("--tu-index", help="The index of the translation unit to get arguments for",
                       type=int)
+  parser.add_argument("--tu-cmd-index",
+                      help="The index of the command within the translation unit (default=0)",
+                      type=int, default=0)
   args = parser.parse_args()
 
   full_deps = parseFullDeps(json.load(open(args.full_deps_file, 'r')))
@@ -58,7 +61,8 @@
     if args.module_name:
       cmd = findModule(args.module_name, full_deps)['command-line']
     elif args.tu_index != None:
-      cmd = full_deps.translation_units[args.tu_index]['command-line']
+      tu = full_deps.translation_units[args.tu_index]
+      cmd = tu['commands'][args.tu_cmd_index]['command-line']
 
     print(" ".join(map(quote, cmd)))
   except:
Index: clang/unittests/Tooling/ToolingTest.cpp
===================================================================
--- clang/unittests/Tooling/ToolingTest.cpp
+++ clang/unittests/Tooling/ToolingTest.cpp
@@ -326,6 +326,46 @@
   EXPECT_TRUE(Consumer.SawSourceManager);
 }
 
+TEST(ToolInvocation, CC1Args) {
+  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
+      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new llvm::vfs::InMemoryFileSystem);
+  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
+  llvm::IntrusiveRefCntPtr<FileManager> Files(
+      new FileManager(FileSystemOptions(), OverlayFileSystem));
+  std::vector<std::string> Args;
+  Args.push_back("tool-executable");
+  Args.push_back("-cc1");
+  Args.push_back("-fsyntax-only");
+  Args.push_back("test.cpp");
+  clang::tooling::ToolInvocation Invocation(
+      Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
+  InMemoryFileSystem->addFile(
+      "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
+  EXPECT_TRUE(Invocation.run());
+}
+
+TEST(ToolInvocation, CC1ArgsInvalid) {
+  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
+      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new llvm::vfs::InMemoryFileSystem);
+  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
+  llvm::IntrusiveRefCntPtr<FileManager> Files(
+      new FileManager(FileSystemOptions(), OverlayFileSystem));
+  std::vector<std::string> Args;
+  Args.push_back("tool-executable");
+  Args.push_back("-cc1");
+  Args.push_back("-invalid-arg");
+  Args.push_back("test.cpp");
+  clang::tooling::ToolInvocation Invocation(
+      Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
+  InMemoryFileSystem->addFile(
+      "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
+  EXPECT_FALSE(Invocation.run());
+}
+
 namespace {
 /// Overlays the real filesystem with the given VFS and returns the result.
 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp
===================================================================
--- clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -182,6 +182,11 @@
     llvm::cl::desc("The names of dependency targets for the dependency file"),
     llvm::cl::cat(DependencyScannerCategory));
 
+llvm::cl::opt<bool> DeprecatedDriverCommand(
+    "deprecated-driver-command", llvm::cl::Optional,
+    llvm::cl::desc("use a single driver command to build the tu (deprecated)"),
+    llvm::cl::cat(DependencyScannerCategory));
+
 enum ResourceDirRecipeKind {
   RDRK_ModifyCompilerPath,
   RDRK_InvokeCompiler,
@@ -256,7 +261,7 @@
 public:
   void mergeDeps(StringRef Input, FullDependenciesResult FDR,
                  size_t InputIndex) {
-    const FullDependencies &FD = FDR.FullDeps;
+    FullDependencies &FD = FDR.FullDeps;
 
     InputDeps ID;
     ID.FileName = std::string(Input);
@@ -274,7 +279,8 @@
       Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
     }
 
-    ID.CommandLine = FD.CommandLine;
+    ID.DriverCommandLine = std::move(FD.DriverCommandLine);
+    ID.Commands = std::move(FD.Commands);
     Inputs.push_back(std::move(ID));
   }
 
@@ -304,21 +310,40 @@
           {"file-deps", toJSONSorted(MD.FileDeps)},
           {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
           {"clang-modulemap-file", MD.ClangModuleMapFile},
-          {"command-line", MD.getCanonicalCommandLine()},
+          {"command-line", MD.BuildArguments},
       };
       OutModules.push_back(std::move(O));
     }
 
     Array TUs;
     for (auto &&I : Inputs) {
-      Object O{
-          {"input-file", I.FileName},
-          {"clang-context-hash", I.ContextHash},
-          {"file-deps", I.FileDeps},
-          {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
-          {"command-line", I.CommandLine},
-      };
-      TUs.push_back(std::move(O));
+      Array Commands;
+      if (I.DriverCommandLine.empty()) {
+        for (const auto &Cmd : I.Commands) {
+          Object O{
+              {"input-file", I.FileName},
+              {"clang-context-hash", I.ContextHash},
+              {"file-deps", I.FileDeps},
+              {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
+              {"executable", Cmd.Executable},
+              {"command-line", Cmd.Arguments},
+          };
+          Commands.push_back(std::move(O));
+        }
+      } else {
+        Object O{
+            {"input-file", I.FileName},
+            {"clang-context-hash", I.ContextHash},
+            {"file-deps", I.FileDeps},
+            {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
+            {"executable", "clang"},
+            {"command-line", I.DriverCommandLine},
+        };
+        Commands.push_back(std::move(O));
+      }
+      TUs.push_back(Object{
+          {"commands", std::move(Commands)},
+      });
     }
 
     Object Output{
@@ -353,7 +378,8 @@
     std::string ContextHash;
     std::vector<std::string> FileDeps;
     std::vector<ModuleID> ModuleDeps;
-    std::vector<std::string> CommandLine;
+    std::vector<std::string> DriverCommandLine;
+    std::vector<Command> Commands;
   };
 
   std::mutex Lock;
@@ -559,6 +585,14 @@
           if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
                                              Errs))
             HadErrors = true;
+        } else if (DeprecatedDriverCommand) {
+          auto MaybeFullDeps =
+              WorkerTools[I]->getFullDependenciesLegacyDriverCommand(
+                  Input->CommandLine, CWD, AlreadySeenModules, LookupOutput,
+                  MaybeModuleName);
+          if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
+                                             LocalIndex, DependencyOS, Errs))
+            HadErrors = true;
         } else {
           auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
               Input->CommandLine, CWD, AlreadySeenModules, LookupOutput,
Index: clang/test/ClangScanDeps/removed-args.c
===================================================================
--- clang/test/ClangScanDeps/removed-args.c
+++ clang/test/ClangScanDeps/removed-args.c
@@ -61,7 +61,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:.*]]",
+// CHECK:            "clang-context-hash": "[[HASH_TU:.*]]",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_MOD_HEADER]]",
@@ -73,13 +73,11 @@
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
-// CHECK-NEXT:         "-fsyntax-only",
+// CHECK-NEXT:         "-cc1",
 // CHECK-NOT:          "-fmodules-cache-path=
 // CHECK-NOT:          "-fmodules-validate-once-per-build-session"
+// CHECK-NOT:          "-fbuild-session-timestamp=
 // CHECK-NOT:          "-fbuild-session-file=
 // CHECK-NOT:          "-fmodules-prune-interval=
 // CHECK-NOT:          "-fmodules-prune-after=
 // CHECK:            ],
-// CHECK:          }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/multiple-commands.c
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/multiple-commands.c
@@ -0,0 +1,165 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
+
+// RUN: clang-scan-deps -compilation-database %t/cdb.json -module-files-dir %t/modules \
+// RUN:   -j 1 -format experimental-full -mode preprocess-dependency-directives \
+// RUN:   > %t/deps.json
+
+// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t
+
+// Build the -save-temps + -fmodules case
+// RUN: %deps-to-rsp %t/deps.json --module-name=Mod > %t/Mod.rsp
+// RUN: %deps-to-rsp %t/deps.json --tu-index 1 --tu-cmd-index 0 > %t/tu-cpp.rsp
+// RUN: %deps-to-rsp %t/deps.json --tu-index 1 --tu-cmd-index 1 > %t/tu-emit-ir.rsp
+// RUN: %deps-to-rsp %t/deps.json --tu-index 1 --tu-cmd-index 2 > %t/tu-emit-asm.rsp
+// RUN: %deps-to-rsp %t/deps.json --tu-index 1 --tu-cmd-index 3 > %t/tu-cc1as.rsp
+// RUN: %clang @%t/Mod.rsp
+// RUN: %clang @%t/tu-cpp.rsp
+// RUN: ls %t/tu_save_temps_module.i
+// RUN: %clang @%t/tu-emit-ir.rsp
+// RUN: ls %t/tu_save_temps_module.bc
+// RUN: %clang @%t/tu-emit-asm.rsp
+// RUN: ls %t/tu_save_temps_module.s
+// RUN: %clang @%t/tu-cc1as.rsp
+// RUN: ls %t/tu_save_temps_module.o
+
+
+// CHECK:      "modules": [
+// CHECK-NEXT:   {
+// CHECK:          "clang-modulemap-file": "[[PREFIX]]{{.}}module.modulemap"
+// CHECK:          "name": "Mod"
+// CHECK:        }
+// CHECK-NEXT: ]
+// CHECK-NEXT: "translation-units": [
+// CHECK-NEXT:   {
+// CHECK:          "commands": [
+// CHECK-NEXT:       {
+// CHECK-NEXT:         "clang-context-hash":
+// CHECK-NEXT:         "clang-module-deps": []
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_no_integrated_cpp{{.*}}.i"
+// CHECK:                "-E"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_no_integrated_cpp.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:       {
+// CHECK-NEXT:         "clang-context-hash":
+// CHECK-NEXT:         "clang-module-deps": []
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_no_integrated_cpp.o"
+// CHECK:                "-emit-obj"
+// CHECK:                "{{.*}}tu_no_integrated_cpp{{.*}}.i"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_no_integrated_cpp.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK-NEXT:   {
+// CHECK-NEXT:     "commands": [
+// CHECK-NEXT:       {
+// CHECK:              "clang-module-deps": [
+// CHECK-NEXT:           {
+// CHECK:                  "module-name": "Mod"
+// CHECK-NEXT:           }
+// CHECK-NEXT:         ]
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_save_temps_module.i"
+// CHECK:                "-E"
+// CHECK:                "-fmodule-file={{.*}}[[PREFIX]]{{.}}modules{{.*}}Mod-{{.*}}.pcm"
+// CHECK:                "{{.*}}tu_save_temps_module.c"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_save_temps_module.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:       {
+// CHECK:              "clang-module-deps": [
+// CHECK-NEXT:           {
+// CHECK:                  "module-name": "Mod"
+// CHECK-NEXT:           }
+// CHECK-NEXT:         ]
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_save_temps_module.bc"
+// CHECK:                "-emit-llvm-bc"
+// CHECK:                "{{.*}}tu_save_temps_module.i"
+// CHECK:                "-fmodule-file={{.*}}[[PREFIX]]{{.}}modules{{.*}}Mod-{{.*}}.pcm"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_save_temps_module.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:       {
+// CHECK:              "clang-module-deps": [
+// CHECK-NEXT:           {
+// CHECK:                  "module-name": "Mod"
+// CHECK-NEXT:           }
+// CHECK-NEXT:         ]
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_save_temps_module.s"
+// CHECK:                "-S"
+// CHECK:                "{{.*}}tu_save_temps_module.bc"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_save_temps_module.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:       {
+// CHECK:              "clang-module-deps": [
+// CHECK-NEXT:           {
+// CHECK:                  "module-name": "Mod"
+// CHECK-NEXT:           }
+// CHECK-NEXT:         ]
+// CHECK-NEXT:         "command-line": [
+// CHECK-NEXT:           "-cc1as"
+// CHECK:                "-o"
+// CHECK-NEXT:           "{{.*}}tu_save_temps_module.o"
+// CHECK:                "{{.*}}tu_save_temps_module.s"
+// CHECK:              ]
+// CHECK-NEXT:         "executable": "clang_tool"
+// CHECK:              "input-file": "[[PREFIX]]{{.}}tu_save_temps_module.c"
+// CHECK-NEXT:       }
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+
+//--- cdb.json.in
+[
+  {
+    "directory": "DIR"
+    "command": "clang_tool -c DIR/tu_no_integrated_cpp.c -no-integrated-cpp -o DIR/tu_no_integrated_cpp.o"
+    "file": "DIR/tu_no_integrated_cpp.c"
+  },
+  {
+    "directory": "DIR"
+    "command": "clang_tool -c DIR/tu_save_temps_module.c -save-temps=obj -o DIR/tu_save_temps_module.o -fmodules -fimplicit-modules -fimplicit-module-maps"
+    "file": "DIR/tu_save_temps_module.c"
+  }
+]
+
+//--- plain_header.h
+void foo(void);
+
+//--- module_header.h
+void bar(void);
+
+//--- module.modulemap
+module Mod { header "module_header.h" }
+
+//--- tu_no_integrated_cpp.c
+#include "plain_header.h"
+void tu_no_integrated_cpp(void) { foo(); }
+
+//--- tu_save_temps_module.c
+#include "module_header.h"
+void tu_save_temps(void) { bar(); }
Index: clang/test/ClangScanDeps/modules-pch.c
===================================================================
--- clang/test/ClangScanDeps/modules-pch.c
+++ clang/test/ClangScanDeps/modules-pch.c
@@ -61,7 +61,7 @@
 // CHECK-PCH-NEXT:   ],
 // CHECK-PCH-NEXT:   "translation-units": [
 // CHECK-PCH-NEXT:     {
-// CHECK-PCH-NEXT:       "clang-context-hash": "[[HASH_PCH:.*]]",
+// CHECK-PCH:            "clang-context-hash": "[[HASH_PCH:.*]]",
 // CHECK-PCH-NEXT:       "clang-module-deps": [
 // CHECK-PCH-NEXT:         {
 // CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_COMMON_1]]",
@@ -74,13 +74,11 @@
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "command-line": [
 // CHECK-PCH:            ],
-// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH:            "file-deps": [
 // CHECK-PCH-NEXT:         "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "input-file": "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:     }
-// CHECK-PCH-NEXT:   ]
-// CHECK-PCH-NEXT: }
 
 // Explicitly build the PCH:
 //
@@ -118,7 +116,7 @@
 // CHECK-TU-NEXT:   ],
 // CHECK-TU-NEXT:   "translation-units": [
 // CHECK-TU-NEXT:     {
-// CHECK-TU-NEXT:       "clang-context-hash": "[[HASH_TU:.*]]",
+// CHECK-TU:            "clang-context-hash": "[[HASH_TU:.*]]",
 // CHECK-TU-NEXT:       "clang-module-deps": [
 // CHECK-TU-NEXT:         {
 // CHECK-TU-NEXT:           "context-hash": "[[HASH_MOD_TU]]",
@@ -127,14 +125,12 @@
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "command-line": [
 // CHECK-TU:            ],
-// CHECK-TU-NEXT:       "file-deps": [
+// CHECK-TU:            "file-deps": [
 // CHECK-TU-NEXT:         "[[PREFIX]]/tu.c",
 // CHECK-TU-NEXT:         "[[PREFIX]]/pch.h.gch"
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-TU-NEXT:     }
-// CHECK-TU-NEXT:   ]
-// CHECK-TU-NEXT: }
 
 // Explicitly build the TU:
 //
@@ -168,7 +164,7 @@
 // CHECK-TU-WITH-COMMON-NEXT:   ],
 // CHECK-TU-WITH-COMMON-NEXT:   "translation-units": [
 // CHECK-TU-WITH-COMMON-NEXT:     {
-// CHECK-TU-WITH-COMMON-NEXT:       "clang-context-hash": "[[HASH_TU_WITH_COMMON:.*]]",
+// CHECK-TU-WITH-COMMON:            "clang-context-hash": "[[HASH_TU_WITH_COMMON:.*]]",
 // CHECK-TU-WITH-COMMON-NEXT:       "clang-module-deps": [
 // CHECK-TU-WITH-COMMON-NEXT:         {
 // CHECK-TU-WITH-COMMON-NEXT:           "context-hash": "[[HASH_MOD_TU_WITH_COMMON]]",
@@ -178,14 +174,12 @@
 // CHECK-TU-WITH-COMMON-NEXT:       "command-line": [
 // CHECK-TU-WITH-COMMON:              "-fmodule-file=[[PREFIX]]/build/{{.*}}/ModCommon2-{{.*}}.pcm"
 // CHECK-TU-WITH-COMMON:            ],
-// CHECK-TU-WITH-COMMON-NEXT:       "file-deps": [
+// CHECK-TU-WITH-COMMON:            "file-deps": [
 // CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/tu_with_common.c",
 // CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/pch.h.gch"
 // CHECK-TU-WITH-COMMON-NEXT:       ],
 // CHECK-TU-WITH-COMMON-NEXT:       "input-file": "[[PREFIX]]/tu_with_common.c"
 // CHECK-TU-WITH-COMMON-NEXT:     }
-// CHECK-TU-WITH-COMMON-NEXT:   ]
-// CHECK-TU-WITH-COMMON-NEXT: }
 
 // Explicitly build the TU that has common modules with the PCH:
 //
Index: clang/test/ClangScanDeps/modules-pch-common-via-submodule.c
===================================================================
--- clang/test/ClangScanDeps/modules-pch-common-via-submodule.c
+++ clang/test/ClangScanDeps/modules-pch-common-via-submodule.c
@@ -32,7 +32,7 @@
 // CHECK-PCH-NEXT:   ],
 // CHECK-PCH-NEXT:   "translation-units": [
 // CHECK-PCH-NEXT:     {
-// CHECK-PCH-NEXT:       "clang-context-hash": "[[HASH_PCH:.*]]",
+// CHECK-PCH:            "clang-context-hash": "[[HASH_PCH:.*]]",
 // CHECK-PCH-NEXT:       "clang-module-deps": [
 // CHECK-PCH-NEXT:         {
 // CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_COMMON]]",
@@ -41,13 +41,11 @@
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "command-line": [
 // CHECK-PCH:            ],
-// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH:            "file-deps": [
 // CHECK-PCH-NEXT:         "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "input-file": "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:     }
-// CHECK-PCH-NEXT:   ]
-// CHECK-PCH-NEXT: }
 
 // Explicitly build the PCH:
 //
@@ -82,7 +80,7 @@
 // CHECK-TU-NEXT:   ],
 // CHECK-TU-NEXT:   "translation-units": [
 // CHECK-TU-NEXT:     {
-// CHECK-TU-NEXT:       "clang-context-hash": "[[HASH_TU:.*]]",
+// CHECK-TU:            "clang-context-hash": "[[HASH_TU:.*]]",
 // CHECK-TU-NEXT:       "clang-module-deps": [
 // CHECK-TU-NEXT:         {
 // CHECK-TU-NEXT:           "context-hash": "[[HASH_MOD_TU]]"
@@ -91,14 +89,12 @@
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "command-line": [
 // CHECK-TU:            ],
-// CHECK-TU-NEXT:       "file-deps": [
+// CHECK-TU:            "file-deps": [
 // CHECK-TU-NEXT:         "[[PREFIX]]/tu.c",
 // CHECK-TU-NEXT:         "[[PREFIX]]/pch.h.gch"
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-TU-NEXT:     }
-// CHECK-TU-NEXT:   ]
-// CHECK-TU-NEXT: }
 
 // Explicitly build the TU:
 //
Index: clang/test/ClangScanDeps/modules-pch-common-submodule.c
===================================================================
--- clang/test/ClangScanDeps/modules-pch-common-submodule.c
+++ clang/test/ClangScanDeps/modules-pch-common-submodule.c
@@ -36,7 +36,7 @@
 // CHECK-PCH-NEXT:   ],
 // CHECK-PCH-NEXT:   "translation-units": [
 // CHECK-PCH-NEXT:     {
-// CHECK-PCH-NEXT:       "clang-context-hash": "[[HASH_PCH:.*]]",
+// CHECK-PCH:            "clang-context-hash": "[[HASH_PCH:.*]]",
 // CHECK-PCH-NEXT:       "clang-module-deps": [
 // CHECK-PCH-NEXT:         {
 // CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_COMMON]]",
@@ -45,13 +45,11 @@
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "command-line": [
 // CHECK-PCH:            ],
-// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH:            "file-deps": [
 // CHECK-PCH-NEXT:         "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:       ],
 // CHECK-PCH-NEXT:       "input-file": "[[PREFIX]]/pch.h"
 // CHECK-PCH-NEXT:     }
-// CHECK-PCH-NEXT:   ]
-// CHECK-PCH-NEXT: }
 
 // Explicitly build the PCH:
 //
@@ -85,7 +83,7 @@
 // CHECK-TU-NEXT:   ],
 // CHECK-TU-NEXT:   "translation-units": [
 // CHECK-TU-NEXT:     {
-// CHECK-TU-NEXT:       "clang-context-hash": "[[HASH_TU:.*]]",
+// CHECK-TU:            "clang-context-hash": "[[HASH_TU:.*]]",
 // CHECK-TU-NEXT:       "clang-module-deps": [
 // CHECK-TU-NEXT:         {
 // CHECK-TU-NEXT:           "context-hash": "[[HASH_MOD_TU]]"
@@ -94,14 +92,12 @@
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "command-line": [
 // CHECK-TU:            ],
-// CHECK-TU-NEXT:       "file-deps": [
+// CHECK-TU:            "file-deps": [
 // CHECK-TU-NEXT:         "[[PREFIX]]/tu.c",
 // CHECK-TU-NEXT:         "[[PREFIX]]/pch.h.gch"
 // CHECK-TU-NEXT:       ],
 // CHECK-TU-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-TU-NEXT:     }
-// CHECK-TU-NEXT:   ]
-// CHECK-TU-NEXT: }
 
 // Explicitly build the TU:
 //
Index: clang/test/ClangScanDeps/modules-no-undeclared-includes.c
===================================================================
--- clang/test/ClangScanDeps/modules-no-undeclared-includes.c
+++ clang/test/ClangScanDeps/modules-no-undeclared-includes.c
@@ -52,7 +52,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}"
+// CHECK:            "clang-context-hash": "{{.*}}"
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "{{.*}}"
@@ -61,13 +61,11 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/test.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/test.c"
 // CHECK-NEXT:     }
-// CHECK:        ]
-// CHECK-NEXT: }
 
 // RUN: %deps-to-rsp %t/result.json --module-name=User > %t/User.cc1.rsp
 // RUN: %deps-to-rsp %t/result.json --tu-index=0 > %t/tu.rsp
Index: clang/test/ClangScanDeps/modules-inferred.m
===================================================================
--- clang/test/ClangScanDeps/modules-inferred.m
+++ clang/test/ClangScanDeps/modules-inferred.m
@@ -31,7 +31,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
+// CHECK:            "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_INFERRED]]",
@@ -40,10 +40,8 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/modules_cdb_input.cpp"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/modules-full.cpp
===================================================================
--- clang/test/ClangScanDeps/modules-full.cpp
+++ clang/test/ClangScanDeps/modules-full.cpp
@@ -82,76 +82,96 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
-// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:       "commands": [
 // CHECK-NEXT:         {
-// CHECK-NEXT:           "context-hash": "[[HASH_H1]]",
-// CHECK-NEXT:           "module-name": "header1"
+// CHECK-NEXT:           "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
+// CHECK-NEXT:           "clang-module-deps": [
+// CHECK-NEXT:             {
+// CHECK-NEXT:               "context-hash": "[[HASH_H1]]",
+// CHECK-NEXT:               "module-name": "header1"
+// CHECK-NEXT:             }
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "command-line": [
+// CHECK-NOT:              "-fimplicit-modules"
+// CHECK-NOT:              "-fimplicit-module-maps"
+// CHECK:                  "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
+// CHECK:                ],
+// CHECK-NEXT:           "executable": "{{.*}}clang{{.*}}"
+// CHECK-NEXT:           "file-deps": [
+// CHECK-NEXT:             "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
 // CHECK-NEXT:         }
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "command-line": [
-// CHECK:              "-fno-implicit-modules"
-// CHECK:              "-fno-implicit-module-maps"
-// CHECK:              "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
-// CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
-// CHECK-NEXT:         "[[PREFIX]]/modules_cdb_input.cpp"
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:       ]
 // CHECK-NEXT:     },
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
-// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:       "commands": [
 // CHECK-NEXT:         {
-// CHECK-NEXT:           "context-hash": "[[HASH_H1]]",
-// CHECK-NEXT:           "module-name": "header1"
+// CHECK-NEXT:           "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
+// CHECK-NEXT:           "clang-module-deps": [
+// CHECK-NEXT:             {
+// CHECK-NEXT:               "context-hash": "[[HASH_H1]]",
+// CHECK-NEXT:               "module-name": "header1"
+// CHECK-NEXT:             }
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "command-line": [
+// CHECK-NOT:              "-fimplicit-modules"
+// CHECK-NOT:              "-fimplicit-module-maps"
+// CHECK:                  "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
+// CHECK:                ],
+// CHECK-NEXT:           "executable": "{{.*}}clang{{.*}}"
+// CHECK-NEXT:           "file-deps": [
+// CHECK-NEXT:             "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
 // CHECK-NEXT:         }
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "command-line": [
-// CHECK:              "-fno-implicit-modules"
-// CHECK:              "-fno-implicit-module-maps"
-// CHECK:              "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
-// CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
-// CHECK-NEXT:         "[[PREFIX]]/modules_cdb_input.cpp"
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:       ]
 // CHECK-NEXT:     },
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
-// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:       "commands": [
 // CHECK-NEXT:         {
-// CHECK-NEXT:           "context-hash": "[[HASH_H1]]",
-// CHECK-NEXT:           "module-name": "header1"
+// CHECK-NEXT:           "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
+// CHECK-NEXT:           "clang-module-deps": [
+// CHECK-NEXT:             {
+// CHECK-NEXT:               "context-hash": "[[HASH_H1]]",
+// CHECK-NEXT:               "module-name": "header1"
+// CHECK-NEXT:             }
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "command-line": [
+// CHECK-NOT:              "-fimplicit-modules"
+// CHECK-NOT:              "-fimplicit-module-maps"
+// CHECK:                  "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
+// CHECK:                ],
+// CHECK-NEXT:           "executable": "{{.*}}clang{{.*}}"
+// CHECK-NEXT:           "file-deps": [
+// CHECK-NEXT:             "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
 // CHECK-NEXT:         }
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "command-line": [
-// CHECK:              "-fno-implicit-modules"
-// CHECK:              "-fno-implicit-module-maps"
-// CHECK:              "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm"
-// CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
-// CHECK-NEXT:         "[[PREFIX]]/modules_cdb_input.cpp"
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
+// CHECK-NEXT:       ]
 // CHECK-NEXT:     },
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU_DINCLUDE:[A-Z0-9]+]]",
-// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:       "commands": [
 // CHECK-NEXT:         {
-// CHECK-NEXT:           "context-hash": "[[HASH_H1_DINCLUDE]]",
-// CHECK-NEXT:           "module-name": "header1"
+// CHECK-NEXT:           "clang-context-hash": "[[HASH_TU_DINCLUDE:[A-Z0-9]+]]",
+// CHECK-NEXT:           "clang-module-deps": [
+// CHECK-NEXT:             {
+// CHECK-NEXT:               "context-hash": "[[HASH_H1_DINCLUDE]]",
+// CHECK-NEXT:               "module-name": "header1"
+// CHECK-NEXT:             }
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "command-line": [
+// CHECK-NOT:              "-fimplicit-modules"
+// CHECK-NOT:              "-fimplicit-module-maps"
+// CHECK:                  "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1_DINCLUDE]]/header1-{{[A-Z0-9]+}}.pcm"
+// CHECK:                ],
+// CHECK-NEXT:           "executable": "{{.*}}clang{{.*}}"
+// CHECK-NEXT:           "file-deps": [
+// CHECK-NEXT:             "[[PREFIX]]/modules_cdb_input2.cpp"
+// CHECK-NEXT:           ],
+// CHECK-NEXT:           "input-file": "[[PREFIX]]/modules_cdb_input2.cpp"
 // CHECK-NEXT:         }
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "command-line": [
-// CHECK:              "-fno-implicit-modules"
-// CHECK:              "-fno-implicit-module-maps"
-// CHECK:              "-fmodule-file={{.*}}[[PREFIX]]/module-cache{{(_clangcl)?}}/[[HASH_H1_DINCLUDE]]/header1-{{[A-Z0-9]+}}.pcm"
-// CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
-// CHECK-NEXT:         "[[PREFIX]]/modules_cdb_input2.cpp"
-// CHECK-NEXT:       ],
-// CHECK-NEXT:       "input-file": "[[PREFIX]]/modules_cdb_input2.cpp"
+// CHECK-NEXT:       ]
 // CHECK-NEXT:     }
 // CHECK-NEXT:   ]
 // CHECK-NEXT: }
Index: clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
===================================================================
--- clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
+++ clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
@@ -33,7 +33,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
+// CHECK:            "clang-context-hash": "[[HASH_TU:[A-Z0-9]+]]",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_H2]]",
@@ -42,12 +42,10 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/modules-fmodule-name-no-module-built.m"
 // CHECK-NEXT:         "[[PREFIX]]/Inputs/header3.h"
 // CHECK-NEXT:         "[[PREFIX]]/Inputs/header.h"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/modules-fmodule-name-no-module-built.m"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/modules-dep-args.c
===================================================================
--- clang/test/ClangScanDeps/modules-dep-args.c
+++ clang/test/ClangScanDeps/modules-dep-args.c
@@ -78,7 +78,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK:            "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "{{.*}}",
@@ -93,10 +93,8 @@
 // CHECK_EAGER-NOT:    "-fmodule-map-file={{.*}}"
 // CHECK_EAGER:        "-fmodule-file=[[PREFIX]]/{{.*}}/Direct-{{.*}}.pcm"
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/tu.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
\ No newline at end of file
Index: clang/test/ClangScanDeps/modules-context-hash.c
===================================================================
--- clang/test/ClangScanDeps/modules-context-hash.c
+++ clang/test/ClangScanDeps/modules-context-hash.c
@@ -40,7 +40,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK:            "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_MOD_A]]",
@@ -49,15 +49,13 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/tu.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
-// CHECK-NEXT: {
-// CHECK-NEXT:   "modules": [
+
+// CHECK:       "modules": [
 // CHECK-NEXT:     {
 // CHECK-NEXT:       "clang-module-deps": [],
 // CHECK-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
@@ -79,7 +77,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK:            "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NOT:            "context-hash": "[[HASH_MOD_A]]",
@@ -88,10 +86,8 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/tu.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/modules-context-hash-warnings.c
===================================================================
--- clang/test/ClangScanDeps/modules-context-hash-warnings.c
+++ clang/test/ClangScanDeps/modules-context-hash-warnings.c
@@ -39,7 +39,7 @@
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu1.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:     {
+// CHECK:          {
 // CHECK:            "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH2]]"
Index: clang/test/ClangScanDeps/modules-context-hash-outputs.c
===================================================================
--- clang/test/ClangScanDeps/modules-context-hash-outputs.c
+++ clang/test/ClangScanDeps/modules-context-hash-outputs.c
@@ -35,11 +35,11 @@
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
-// CHECK:              "-MF"
+// CHECK:              "-dependency-file"
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu1.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:     {
+// CHECK:          {
 // CHECK:            "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH2]]"
@@ -48,6 +48,7 @@
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-MF"
+// CHECK-NOT:          "-dependency-file"
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu2.c"
 
Index: clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
===================================================================
--- clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
+++ clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
@@ -38,10 +38,11 @@
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-DFOO"
+// CHECK-NOT:          "FOO"
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu1.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:     {
+// CHECK:          {
 // CHECK:            "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_FOO]]"
@@ -49,11 +50,12 @@
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
-// CHECK:              "-DFOO"
+// CHECK:              "-D"
+// CHECK-NEXT:         "FOO"
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu2.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:     {
+// CHECK:          {
 // CHECK:            "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_NO_FOO]]"
@@ -61,8 +63,9 @@
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       "command-line": [
-// CHECK:              "-DFOO"
 // CHECK:              "-fmodules-ignore-macro=FOO"
+// CHECK:              "-D"
+// CHECK-NEXT:         "FOO"
 // CHECK:            ]
 // CHECK:            "input-file": "{{.*}}tu3.c"
 
Index: clang/test/ClangScanDeps/header-search-pruning-transitive.c
===================================================================
--- clang/test/ClangScanDeps/header-search-pruning-transitive.c
+++ clang/test/ClangScanDeps/header-search-pruning-transitive.c
@@ -95,7 +95,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK:            "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_X]]",
@@ -104,14 +104,13 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/test.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/test.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
-// CHECK-NEXT: {
+
+// CHECK:      {
 // CHECK-NEXT:   "modules": [
 // CHECK-NEXT:     {
 // CHECK-NEXT:       "clang-module-deps": [
@@ -149,7 +148,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK:            "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "{{.*}}",
@@ -158,10 +157,8 @@
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/test.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/test.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/diagnostics.c
===================================================================
--- clang/test/ClangScanDeps/diagnostics.c
+++ clang/test/ClangScanDeps/diagnostics.c
@@ -28,7 +28,7 @@
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "translation-units": [
 // CHECK-NEXT:     {
-// CHECK-NEXT:       "clang-context-hash": "[[HASH_TU:.*]],
+// CHECK:            "clang-context-hash": "[[HASH_TU:.*]],
 // CHECK-NEXT:       "clang-module-deps": [
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "context-hash": "[[HASH_MOD]]",
@@ -36,13 +36,11 @@
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
-// CHECK:              "-fno-implicit-modules"
-// CHECK:              "-fno-implicit-module-maps"
+// CHECK-NOT:          "-fimplicit-modules"
+// CHECK-NOT:          "-fimplicit-module-maps"
 // CHECK:            ],
-// CHECK-NEXT:       "file-deps": [
+// CHECK:            "file-deps": [
 // CHECK-NEXT:         "[[PREFIX]]/tu.c"
 // CHECK-NEXT:       ],
 // CHECK-NEXT:       "input-file": "[[PREFIX]]/tu.c"
 // CHECK-NEXT:     }
-// CHECK-NEXT:   ]
-// CHECK-NEXT: }
Index: clang/test/ClangScanDeps/deprecated-driver-api.c
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/deprecated-driver-api.c
@@ -0,0 +1,35 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
+
+// RUN: clang-scan-deps -compilation-database=%t/cdb.json -format experimental-full \
+// RUN:   -deprecated-driver-command | sed 's:\\\\\?:/:g' | FileCheck %s
+
+// CHECK: "command-line": [
+// CHECK:   "-c"
+// CHECK:   "{{.*}}tu.c"
+// CHECK:   "-save-temps"
+// CHECK:   "-fno-implicit-modules"
+// CHECK:   "-fno-implicit-module-maps"
+// CHECK: ]
+// CHECK: "file-deps": [
+// CHECK:   "{{.*}}tu.c",
+// CHECK:   "{{.*}}header.h"
+// CHECK: ]
+
+//--- cdb.json.in
+[{
+  "directory": "DIR",
+  "command": "clang -c DIR/tu.c -save-temps",
+  "file": "DIR/tu.c"
+}]
+
+//--- header.h
+void bar(void);
+
+//--- tu.c
+#include "header.h"
+
+void foo(void) {
+  bar();
+}
Index: clang/lib/Tooling/Tooling.cpp
===================================================================
--- clang/lib/Tooling/Tooling.cpp
+++ clang/lib/Tooling/Tooling.cpp
@@ -161,7 +161,7 @@
 
 /// Returns a clang build invocation initialized from the CC1 flags.
 CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
-                                  const llvm::opt::ArgStringList &CC1Args,
+                                  ArrayRef<const char *> CC1Args,
                                   const char *const BinaryName) {
   assert(!CC1Args.empty() && "Must at least contain the program name!");
   CompilerInvocation *Invocation = new CompilerInvocation;
@@ -339,7 +339,7 @@
 }
 
 bool ToolInvocation::run() {
-  std::vector<const char*> Argv;
+  llvm::opt::ArgStringList Argv;
   for (const std::string &Str : CommandLine)
     Argv.push_back(Str.c_str());
   const char *const BinaryName = Argv[0];
@@ -362,6 +362,17 @@
   SourceManager SrcMgr(*Diagnostics, *Files);
   Diagnostics->setSourceManager(&SrcMgr);
 
+  // We already have a cc1, just create an invocation.
+  if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
+    ArrayRef<const char *> CC1Args = makeArrayRef(Argv).drop_front();
+    std::unique_ptr<CompilerInvocation> Invocation(
+        newInvocation(&*Diagnostics, CC1Args, BinaryName));
+    if (Diagnostics->hasErrorOccurred())
+      return false;
+    return Action->runInvocation(std::move(Invocation), Files,
+                                 std::move(PCHContainerOps), DiagConsumer);
+  }
+
   const std::unique_ptr<driver::Driver> Driver(
       newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
   // The "input file not found" diagnostics from the driver are useful.
Index: clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -52,21 +52,12 @@
   return Result;
 }
 
-void ModuleDepCollector::addOutputPaths(ModuleDeps &Deps) {
-  CompilerInvocation &CI = Deps.BuildInvocation;
-
+void ModuleDepCollector::addOutputPaths(CompilerInvocation &CI,
+                                        const ModuleDeps &Deps) {
   // These are technically *inputs* to the compilation, but we populate them
   // here in order to make \c getModuleContextHash() independent of
   // \c lookupModuleOutput().
-  for (ModuleID MID : Deps.ClangModuleDeps) {
-    auto PCMPath =
-        Consumer.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
-    if (EagerLoadModules)
-      CI.getFrontendOpts().ModuleFiles.push_back(PCMPath);
-    else
-      CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
-          {MID.ModuleName, PCMPath});
-  }
+  addModuleFiles(CI, Deps.ClangModuleDeps);
 
   CI.getFrontendOpts().OutputFile =
       Consumer.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile);
@@ -126,24 +117,12 @@
   CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile,
                                            ModuleMapInputKind);
   CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps;
+  addModuleMapFiles(CI, Deps.ClangModuleDeps);
 
   // Report the prebuilt modules this module uses.
   for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
     CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
 
-  if (!EagerLoadModules) {
-    ModuleMap &ModMap =
-        ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
-    for (ModuleID MID : Deps.ClangModuleDeps) {
-      const Module *M = ModMap.findModule(MID.ModuleName);
-      assert(M && "Modular dependency not found");
-      auto MDeps = ModularDeps.find(M);
-      assert(MDeps != ModularDeps.end() && "Inconsistent dependency info");
-      CI.getFrontendOpts().ModuleMapFiles.push_back(
-          MDeps->second->ClangModuleMapFile);
-    }
-  }
-
   // Remove any macro definitions that are explicitly ignored.
   if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {
     llvm::erase_if(
@@ -170,11 +149,85 @@
   return CI;
 }
 
-std::vector<std::string> ModuleDeps::getCanonicalCommandLine() const {
-  return BuildInvocation.getCC1CommandLine();
+void ModuleDepCollector::addModuleMapFiles(
+    CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
+  if (EagerLoadModules)
+    return; // Only pcm is needed for eager load.
+
+  for (const ModuleID &MID : ClangModuleDeps) {
+    ModuleDeps *MD = getModuleDepsForID(MID);
+    assert(MD && "Inconsistent dependency info");
+    CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile);
+  }
+}
+
+void ModuleDepCollector::addModuleFiles(
+    CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
+  for (const ModuleID &MID : ClangModuleDeps) {
+    std::string PCMPath =
+        Consumer.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
+    if (EagerLoadModules)
+      CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
+    else
+      CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
+          {MID.ModuleName, std::move(PCMPath)});
+  }
+}
+
+static bool needsModules(FrontendInputFile FIF) {
+  switch (FIF.getKind().getLanguage()) {
+  case Language::Unknown:
+  case Language::Asm:
+  case Language::LLVM_IR:
+    return false;
+  default:
+    return true;
+  }
+}
+
+void ModuleDepCollector::handleInvocation(CompilerInvocation CI) {
+  CI.clearImplicitModuleBuildOptions();
+
+  if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) {
+    SmallVector<ModuleID> DirectDeps;
+    for (const auto &KV : ModularDeps)
+      if (KV.second->ImportedByMainFile)
+        DirectDeps.push_back(KV.second->ID);
+
+    addModuleMapFiles(CI, DirectDeps);
+    addModuleFiles(CI, DirectDeps);
+
+    for (const auto &PrebuiltModule : DirectPrebuiltDeps)
+      CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
+  }
+
+  std::string Executable = CI.getCodeGenOpts().Argv0;
+  Consumer.handleBuildCommand(std::move(Executable), CI.getCC1CommandLine());
+}
+
+static void getModuleIDKeyString(const ModuleID &ID, SmallVectorImpl<char> &O) {
+  O.reserve(ID.ModuleName.size() + ID.ContextHash.size() + 1);
+  O.append(ID.ModuleName.begin(), ID.ModuleName.end());
+  O.push_back(':');
+  O.append(ID.ContextHash.begin(), ID.ContextHash.end());
+}
+
+ModuleDeps *ModuleDepCollector::getModuleDepsForID(const ModuleID &ID) const {
+  SmallString<64> Key;
+  getModuleIDKeyString(ID, Key);
+  return ModuleDepsByID.lookup(Key);
+}
+
+void ModuleDepCollector::setModuleDepsForID(const ModuleID &ID,
+                                            ModuleDeps *MD) {
+  SmallString<64> Key;
+  getModuleIDKeyString(ID, Key);
+  assert(ModuleDepsByID.count(Key) == 0);
+  ModuleDepsByID[Key] = MD;
 }
 
 static std::string getModuleContextHash(const ModuleDeps &MD,
+                                        const CompilerInvocation &CI,
                                         bool EagerLoadModules) {
   llvm::HashBuilder<llvm::TruncatedBLAKE3<16>,
                     llvm::support::endianness::native>
@@ -188,7 +241,7 @@
 
   // Hash the BuildInvocation without any input files.
   SmallVector<const char *, 32> DummyArgs;
-  MD.BuildInvocation.generateCC1CommandLine(DummyArgs, [&](const Twine &Arg) {
+  CI.generateCC1CommandLine(DummyArgs, [&](const Twine &Arg) {
     Scratch.clear();
     StringRef Str = Arg.toStringRef(Scratch);
     HashBuilder.add(Str);
@@ -296,8 +349,12 @@
   for (auto &&I : MDC.FileDeps)
     MDC.Consumer.handleFileDependency(I);
 
-  for (auto &&I : DirectPrebuiltModularDeps)
-    MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
+  for (auto &&I : DirectPrebuiltModularDeps) {
+    MDC.DirectPrebuiltDeps.emplace_back(I);
+    MDC.Consumer.handlePrebuiltModuleDependency(MDC.DirectPrebuiltDeps.back());
+  }
+
+  MDC.handleInvocation(MDC.OriginalInvocation);
 }
 
 ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
@@ -390,7 +447,7 @@
   llvm::DenseSet<const Module *> AddedModules;
   addAllSubmoduleDeps(M, MD, AddedModules);
 
-  MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutOutputs(
+  auto CI = MDC.makeInvocationForModuleBuildWithoutOutputs(
       MD, [&](CompilerInvocation &BuildInvocation) {
         if (MDC.OptimizeArgs)
           optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
@@ -398,9 +455,13 @@
       });
 
   // Compute the context hash from the inputs. Requires dependencies.
-  MD.ID.ContextHash = getModuleContextHash(MD, MDC.EagerLoadModules);
+  MD.ID.ContextHash = getModuleContextHash(MD, CI, MDC.EagerLoadModules);
   // Finish the compiler invocation. Requires dependencies and the context hash.
-  MDC.addOutputPaths(MD);
+  MDC.addOutputPaths(CI, MD);
+
+  MD.BuildArguments = CI.getCC1CommandLine();
+
+  MDC.setModuleDepsForID(MD.ID, &MD);
   return MD.ID;
 }
 
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Job.h"
+#include "clang/Driver/Tool.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendActions.h"
@@ -17,6 +22,7 @@
 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
 #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Host.h"
 
 using namespace clang;
 using namespace tooling;
@@ -230,9 +236,10 @@
               std::move(Opts), WorkingDirectory, Consumer));
       break;
     case ScanningOutputFormat::Full:
-      ScanInstance.addDependencyCollector(std::make_shared<ModuleDepCollector>(
+      MDC = std::make_shared<ModuleDepCollector>(
           std::move(Opts), ScanInstance, Consumer,
-          std::move(OriginalInvocation), OptimizeArgs, EagerLoadModules));
+          std::move(OriginalInvocation), OptimizeArgs, EagerLoadModules);
+      ScanInstance.addDependencyCollector(MDC);
       break;
     }
 
@@ -256,6 +263,10 @@
     return Result;
   }
 
+  std::shared_ptr<ModuleDepCollector> getModuleDepCollector() const {
+    return MDC;
+  }
+
 private:
   StringRef WorkingDirectory;
   DependencyConsumer &Consumer;
@@ -265,6 +276,7 @@
   bool EagerLoadModules;
   bool DisableFree;
   llvm::Optional<StringRef> ModuleName;
+  std::shared_ptr<ModuleDepCollector> MDC;
 };
 
 } // end anonymous namespace
@@ -313,6 +325,36 @@
                                              llvm::inconvertibleErrorCode());
 }
 
+static bool forEachDriverJob(
+    ArrayRef<std::string> Args, DiagnosticsEngine &Diags, FileManager &FM,
+    llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
+  std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
+      Args[0], llvm::sys::getDefaultTargetTriple(), Diags,
+      "clang LLVM compiler", &FM.getVirtualFileSystem());
+  Driver->setTitle("clang_based_tool");
+
+  std::vector<const char *> Argv;
+  for (const std::string &Arg : Args)
+    Argv.push_back(Arg.c_str());
+
+  // The "input file not found" diagnostics from the driver are useful.
+  // The driver is only aware of the VFS working directory, but some clients
+  // change this at the FileManager level instead.
+  // In this case the checks have false positives, so skip them.
+  if (!FM.getFileSystemOpts().WorkingDir.empty())
+    Driver->setCheckInputsExist(false);
+  const std::unique_ptr<driver::Compilation> Compilation(
+      Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
+  if (!Compilation)
+    return false;
+
+  for (const driver::Command &Job : Compilation->getJobs()) {
+    if (!Callback(Job))
+      return false;
+  }
+  return true;
+}
+
 llvm::Error DependencyScanningWorker::computeDependencies(
     StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
     DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
@@ -338,25 +380,86 @@
   llvm::transform(CommandLine, FinalCCommandLine.begin(),
                   [](const std::string &Str) { return Str.c_str(); });
 
-  return runWithDiags(CreateAndPopulateDiagOpts(FinalCCommandLine).release(),
-                      [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) {
-                        // DisableFree is modified by Tooling for running
-                        // in-process; preserve the original value, which is
-                        // always true for a driver invocation.
-                        bool DisableFree = true;
-                        DependencyScanningAction Action(
-                            WorkingDirectory, Consumer, DepFS, Format,
-                            OptimizeArgs, EagerLoadModules, DisableFree,
-                            ModuleName);
-                        // Create an invocation that uses the underlying file
-                        // system to ensure that any file system requests that
-                        // are made by the driver do not go through the
-                        // dependency scanning filesystem.
-                        ToolInvocation Invocation(FinalCommandLine, &Action,
-                                                  CurrentFiles.get(),
-                                                  PCHContainerOps);
-                        Invocation.setDiagnosticConsumer(&DC);
-                        Invocation.setDiagnosticOptions(&DiagOpts);
-                        return Invocation.run();
-                      });
+  return runWithDiags(
+      CreateAndPopulateDiagOpts(FinalCCommandLine).release(),
+      [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) {
+        IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+            CompilerInstance::createDiagnostics(&DiagOpts, &DC, false);
+        // Although `Diagnostics` are used only for command-line parsing, the
+        // custom `DiagConsumer` might expect a `SourceManager` to be present.
+        SourceManager SrcMgr(*Diags, *CurrentFiles);
+        Diags->setSourceManager(&SrcMgr);
+        bool Scanned = false;
+        std::shared_ptr<ModuleDepCollector> ScannerCollector;
+        bool Success = forEachDriverJob(FinalCommandLine, *Diags, *CurrentFiles,
+                                        [&](const driver::Command &Cmd) {
+                                          return computeDependenciesForCommand(
+                                              Cmd, WorkingDirectory, ModuleName,
+                                              Consumer, *CurrentFiles, *Diags,
+                                              ScannerCollector, Scanned);
+                                        });
+
+        if (Success && !Scanned) {
+          Diags->Report(diag::err_fe_expected_compiler_job)
+              << llvm::join(FinalCommandLine, " ");
+        }
+        return Success && Scanned;
+      });
+}
+
+bool DependencyScanningWorker::computeDependenciesForCommand(
+    const driver::Command &Cmd, StringRef WorkingDirectory,
+    llvm::Optional<StringRef> ModuleName, DependencyConsumer &Consumer,
+    FileManager &FM, DiagnosticsEngine &Diags,
+    std::shared_ptr<ModuleDepCollector> &MDC, bool &Scanned) {
+  if (StringRef(Cmd.getCreator().getName()) != "clang") {
+    // Non-clang command. Just pass through to the consumer.
+    Consumer.handleBuildCommand(
+        Cmd.getExecutable(),
+        {Cmd.getArguments().begin(), Cmd.getArguments().end()});
+    return true;
+  }
+
+  if (Scanned) {
+    if (MDC) {
+      // Already scanned an upstream clang command. Apply the necessary changes
+      // to the compiler invocation using the original ModuleDepCollector.
+      CompilerInvocation CI;
+      if (!CompilerInvocation::CreateFromArgs(CI, Cmd.getArguments(), Diags,
+                                              Cmd.getExecutable()))
+        return false;
+      MDC->handleInvocation(std::move(CI));
+    } else {
+      // Already scanned, but there are no changes to make. Just pass through to
+      // the consumer.
+      Consumer.handleBuildCommand(
+          Cmd.getExecutable(),
+          {Cmd.getArguments().begin(), Cmd.getArguments().end()});
+    }
+    return true;
+  }
+
+  Scanned = true;
+  // DisableFree is modified by Tooling for running
+  // in-process; preserve the original value, which is
+  // always true for a driver invocation.
+  bool DisableFree = true;
+  DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, Format,
+                                  OptimizeArgs, EagerLoadModules, DisableFree,
+                                  ModuleName);
+
+  std::vector<std::string> Argv;
+  Argv.push_back(Cmd.getExecutable());
+  Argv.insert(Argv.end(), Cmd.getArguments().begin(), Cmd.getArguments().end());
+
+  // Create an invocation that uses the underlying file
+  // system to ensure that any file system requests that
+  // are made by the driver do not go through the
+  // dependency scanning filesystem.
+  ToolInvocation Invocation(std::move(Argv), &Action, &FM, PCHContainerOps);
+  Invocation.setDiagnosticConsumer(Diags.getClient());
+  Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
+  bool Result = Invocation.run();
+  MDC = Action.getModuleDepCollector();
+  return Result;
 }
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -45,6 +45,9 @@
   /// Prints out all of the gathered dependencies into a string.
   class MakeDependencyPrinterConsumer : public DependencyConsumer {
   public:
+    void handleBuildCommand(std::string Executable,
+                            std::vector<std::string> Args) override {}
+
     void
     handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
       this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
@@ -120,14 +123,53 @@
       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
   if (Result)
     return std::move(Result);
-  return Consumer.getFullDependencies(CommandLine);
+  return Consumer.takeFullDependencies();
+}
+
+llvm::Expected<FullDependenciesResult>
+DependencyScanningTool::getFullDependenciesLegacyDriverCommand(
+    const std::vector<std::string> &CommandLine, StringRef CWD,
+    const llvm::StringSet<> &AlreadySeen,
+    LookupModuleOutputCallback LookupModuleOutput,
+    llvm::Optional<StringRef> ModuleName) {
+  FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
+                                  Worker.shouldEagerLoadModules());
+  llvm::Error Result =
+      Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
+  if (Result)
+    return std::move(Result);
+  return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine);
+}
+
+FullDependenciesResult FullDependencyConsumer::takeFullDependencies() {
+  FullDependenciesResult FDR;
+  FullDependencies &FD = FDR.FullDeps;
+
+  FD.ID.ContextHash = std::move(ContextHash);
+  FD.FileDeps = std::move(Dependencies);
+  FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
+  FD.Commands = std::move(Commands);
+
+  for (auto &&M : ClangModuleDeps) {
+    auto &MD = M.second;
+    if (MD.ImportedByMainFile)
+      FD.ClangModuleDeps.push_back(MD.ID);
+    // TODO: Avoid handleModuleDependency even being called for modules
+    //   we've already seen.
+    if (AlreadySeen.count(M.first))
+      continue;
+    FDR.DiscoveredModules.push_back(std::move(MD));
+  }
+
+  return FDR;
 }
 
-FullDependenciesResult FullDependencyConsumer::getFullDependencies(
+FullDependenciesResult
+FullDependencyConsumer::getFullDependenciesLegacyDriverCommand(
     const std::vector<std::string> &OriginalCommandLine) const {
   FullDependencies FD;
 
-  FD.CommandLine = makeTUCommandLineWithoutPaths(
+  FD.DriverCommandLine = makeTUCommandLineWithoutPaths(
       ArrayRef<std::string>(OriginalCommandLine).slice(1));
 
   FD.ID.ContextHash = std::move(ContextHash);
@@ -135,7 +177,7 @@
   FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
 
   for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
-    FD.CommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
+    FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
 
   for (auto &&M : ClangModuleDeps) {
     auto &MD = M.second;
@@ -143,11 +185,12 @@
       FD.ClangModuleDeps.push_back(MD.ID);
       auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile);
       if (EagerLoadModules) {
-        FD.CommandLine.push_back("-fmodule-file=" + PCMPath);
+        FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath);
       } else {
-        FD.CommandLine.push_back("-fmodule-map-file=" + MD.ClangModuleMapFile);
-        FD.CommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName + "=" +
-                                 PCMPath);
+        FD.DriverCommandLine.push_back("-fmodule-map-file=" +
+                                       MD.ClangModuleMapFile);
+        FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName +
+                                       "=" + PCMPath);
       }
     }
   }
Index: clang/include/clang/Tooling/Tooling.h
===================================================================
--- clang/include/clang/Tooling/Tooling.h
+++ clang/include/clang/Tooling/Tooling.h
@@ -508,7 +508,7 @@
 
 /// Creates a \c CompilerInvocation.
 CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
-                                  const llvm::opt::ArgStringList &CC1Args,
+                                  ArrayRef<const char *> CC1Args,
                                   const char *const BinaryName);
 
 } // namespace tooling
Index: clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -119,11 +119,9 @@
   // the primary TU.
   bool ImportedByMainFile = false;
 
-  /// Compiler invocation that can be used to build this module (without paths).
-  CompilerInvocation BuildInvocation;
-
-  /// Gets the canonical command line suitable for passing to clang.
-  std::vector<std::string> getCanonicalCommandLine() const;
+  /// Compiler invocation that can be used to build this module. Does not
+  /// include argv[0].
+  std::vector<std::string> BuildArguments;
 };
 
 class ModuleDepCollector;
@@ -190,6 +188,14 @@
   void attachToPreprocessor(Preprocessor &PP) override;
   void attachToASTReader(ASTReader &R) override;
 
+  /// Apply any modules-related changes to the given invocation and pass it to
+  /// the \c DependencyConsumer.
+  ///
+  /// This is called automatically for the original invocation, but it can also
+  /// be used by clients that wish to apply the same changes to another
+  /// invocation, for example when there are multiple chained invocations.
+  void handleInvocation(CompilerInvocation CI);
+
 private:
   friend ModuleDepCollectorPP;
 
@@ -206,6 +212,12 @@
   std::vector<std::string> FileDeps;
   /// Direct and transitive modular dependencies of the main source file.
   llvm::MapVector<const Module *, std::unique_ptr<ModuleDeps>> ModularDeps;
+  /// Secondary mapping for \c ModularDeps allowing lookup by ModuleID without
+  /// a preprocessor. Storage owned by \c ModularDeps.
+  llvm::StringMap<ModuleDeps *> ModuleDepsByID;
+  /// Directly imported prebuilt deps.
+  std::vector<PrebuiltModuleDep> DirectPrebuiltDeps;
+
   /// Options that control the dependency output generation.
   std::unique_ptr<DependencyOutputOptions> Opts;
   /// The original Clang invocation passed to dependency scanner.
@@ -223,6 +235,9 @@
   /// Adds \p Path to \c MD.FileDeps, making it absolute if necessary.
   void addFileDep(ModuleDeps &MD, StringRef Path);
 
+  ModuleDeps *getModuleDepsForID(const ModuleID &ID) const;
+  void setModuleDepsForID(const ModuleID &ID, ModuleDeps *MD);
+
   /// Constructs a CompilerInvocation that can be used to build the given
   /// module, excluding paths to discovered modular dependencies that are yet to
   /// be built.
@@ -230,8 +245,15 @@
       const ModuleDeps &Deps,
       llvm::function_ref<void(CompilerInvocation &)> Optimize) const;
 
+  /// Add module map files to the invocation, if needed.
+  void addModuleMapFiles(CompilerInvocation &CI,
+                         ArrayRef<ModuleID> ClangModuleDeps) const;
+  /// Add module files (pcm) to the invocation, if needed.
+  void addModuleFiles(CompilerInvocation &CI,
+                      ArrayRef<ModuleID> ClangModuleDeps) const;
+
   /// Add paths that require looking up outputs to the given dependencies.
-  void addOutputPaths(ModuleDeps &Deps);
+  void addOutputPaths(CompilerInvocation &CI, const ModuleDeps &Deps);
 };
 
 } // end namespace dependencies
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -23,6 +23,10 @@
 
 class DependencyOutputOptions;
 
+namespace driver {
+class Command;
+}
+
 namespace tooling {
 namespace dependencies {
 
@@ -32,6 +36,9 @@
 public:
   virtual ~DependencyConsumer() {}
 
+  virtual void handleBuildCommand(std::string Executable,
+                                  std::vector<std::string> Args) = 0;
+
   virtual void
   handleDependencyOutputOpts(const DependencyOutputOptions &Opts) = 0;
 
@@ -72,6 +79,20 @@
 
   bool shouldEagerLoadModules() const { return EagerLoadModules; }
 
+private:
+  /// Helper for \c computeDependencies that handles computing dependencies for
+  /// a single driver command \p Cmd, or forwarding the invocation to
+  /// \p Consumer if it is downstream from a command that is already scaned.
+  ///
+  /// \returns true on success.
+  bool computeDependenciesForCommand(const driver::Command &Cmd,
+                                     StringRef WorkingDirectory,
+                                     llvm::Optional<StringRef> ModuleName,
+                                     DependencyConsumer &Consumer,
+                                     FileManager &FM, DiagnosticsEngine &Diags,
+                                     std::shared_ptr<ModuleDepCollector> &MDC,
+                                     bool &Scanned);
+
 private:
   std::shared_ptr<PCHContainerOperations> PCHContainerOps;
 
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -27,6 +27,14 @@
 using LookupModuleOutputCallback =
     llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>;
 
+/// A command-line tool invocation that is part of building a TU.
+///
+/// \see FullDependencies::Commands.
+struct Command {
+  std::string Executable;
+  std::vector<std::string> Arguments;
+};
+
 /// The full dependencies and module graph for a specific input.
 struct FullDependencies {
   /// The identifier of the C++20 module this translation unit exports.
@@ -49,8 +57,16 @@
   /// determined that the differences are benign for this compilation.
   std::vector<ModuleID> ClangModuleDeps;
 
-  /// The command line of the TU (excluding the compiler executable).
-  std::vector<std::string> CommandLine;
+  /// The sequence of commands required to build the translation unit. Commands
+  /// should be executed in order.
+  ///
+  /// FIXME: If we add support for multi-arch builds in clang-scan-deps, we
+  /// should make the dependencies between commands explicit to enable parallel
+  /// builds of each architecture.
+  std::vector<Command> Commands;
+
+  /// Deprecated driver command-line. This will be removed in a future version.
+  std::vector<std::string> DriverCommandLine;
 };
 
 struct FullDependenciesResult {
@@ -99,6 +115,12 @@
                       LookupModuleOutputCallback LookupModuleOutput,
                       llvm::Optional<StringRef> ModuleName = None);
 
+  llvm::Expected<FullDependenciesResult> getFullDependenciesLegacyDriverCommand(
+      const std::vector<std::string> &CommandLine, StringRef CWD,
+      const llvm::StringSet<> &AlreadySeen,
+      LookupModuleOutputCallback LookupModuleOutput,
+      llvm::Optional<StringRef> ModuleName = None);
+
 private:
   DependencyScanningWorker Worker;
 };
@@ -111,6 +133,11 @@
       : AlreadySeen(AlreadySeen), LookupModuleOutput(LookupModuleOutput),
         EagerLoadModules(EagerLoadModules) {}
 
+  void handleBuildCommand(std::string Executable,
+                          std::vector<std::string> Args) override {
+    Commands.push_back({std::move(Executable), std::move(Args)});
+  }
+
   void handleDependencyOutputOpts(const DependencyOutputOptions &) override {}
 
   void handleFileDependency(StringRef File) override {
@@ -134,14 +161,17 @@
     return LookupModuleOutput(ID, Kind);
   }
 
-  FullDependenciesResult getFullDependencies(
+  FullDependenciesResult getFullDependenciesLegacyDriverCommand(
       const std::vector<std::string> &OriginalCommandLine) const;
 
+  FullDependenciesResult takeFullDependencies();
+
 private:
   std::vector<std::string> Dependencies;
   std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
   llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>>
       ClangModuleDeps;
+  std::vector<Command> Commands;
   std::string ContextHash;
   std::vector<std::string> OutputPaths;
   const llvm::StringSet<> &AlreadySeen;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to