https://github.com/qiongsiwu updated 
https://github.com/llvm/llvm-project/pull/211966

>From b1d0246f45ebfa2e9aea29530dd1acceb7fafed5 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <[email protected]>
Date: Fri, 24 Jul 2026 16:13:14 -0700
Subject: [PATCH 1/5] Add a clang driver option to enable dependency scanning
 logging.

---
 clang/include/clang/Basic/AtomicLineLogger.h  |  9 ++-
 .../DependencyScanningWorker.h                |  2 +
 clang/include/clang/Options/Options.td        |  7 ++
 clang/lib/Basic/AtomicLineLogger.cpp          | 76 ++++++++++++++-----
 clang/lib/Tooling/DependencyScanningTool.cpp  | 20 +++--
 .../test/ClangScanDeps/logging-driver-flag.c  | 47 ++++++++++++
 6 files changed, 132 insertions(+), 29 deletions(-)
 create mode 100644 clang/test/ClangScanDeps/logging-driver-flag.c

diff --git a/clang/include/clang/Basic/AtomicLineLogger.h 
b/clang/include/clang/Basic/AtomicLineLogger.h
index 3d06ddfce0262..ddcd5993b060e 100644
--- a/clang/include/clang/Basic/AtomicLineLogger.h
+++ b/clang/include/clang/Basic/AtomicLineLogger.h
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/raw_ostream.h"
 #include <atomic>
+#include <mutex>
 #include <optional>
 #include <string>
 
@@ -60,9 +61,11 @@ class LogLine {
 };
 
 class AtomicLineLogger {
-  int FD = -1;
+  std::atomic<int> FD{-1};
   std::string LogPath;
   std::atomic<uint64_t> DroppedLines{0};
+  std::mutex EnableMtx;
+  bool WarnedConflict = false;
 
 public:
   AtomicLineLogger() {}
@@ -75,6 +78,10 @@ class AtomicLineLogger {
 
   ~AtomicLineLogger();
 
+  // Enables the logger if it is not already enabled. Thread safe.
+  // If the logger is already enabled, call to enable is a no-op.
+  void enable(StringRef LogFilePath);
+
   LogLine log();
 };
 
diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h 
b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
index 638c317d9ce64..2c5c7fbb8f9a0 100644
--- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
+++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h
@@ -90,6 +90,8 @@ class DependencyScanningWorker {
     return TracingFS.get();
   }
 
+  DependencyScanningService &getService() const { return Service; }
+
   // MaxNumOfByNameQueries is the upper limit of the number of names the 
by-name
   // scanning API (computeDependenciesByName) can drain per call. At the time 
of
   // this commit, the estimated number of total unique importable names is
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index cf66ee3e52f2d..f8651a7bc42a5 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3781,6 +3781,13 @@ def fno_modules_driver :
   Group<f_Group>,
   Visibility<[ClangOption]>,
   HelpText<"Disable support for driver managed module builds (experimental)">;
+def fdepscan_log_path : Joined<["-"], "fdepscan-log-path=">,
+  Group<f_Group>,
+  Visibility<[ClangOption]>,
+  Flags<[NoArgumentUnused]>,
+  MetaVarName<"<file>">,
+  HelpText<"Log the timing of dependency scanning actions to <file>. Only "
+           "takes effect while running the dependency scanner.">;
 
 def fincremental_extensions :
   Flag<["-"], "fincremental-extensions">,
diff --git a/clang/lib/Basic/AtomicLineLogger.cpp 
b/clang/lib/Basic/AtomicLineLogger.cpp
index dc0f2bf5adc6c..4bf26f0cb756d 100644
--- a/clang/lib/Basic/AtomicLineLogger.cpp
+++ b/clang/lib/Basic/AtomicLineLogger.cpp
@@ -41,20 +41,43 @@ static uint64_t getTimestampMillis() {
 #endif
 }
 
+#ifdef _WIN32
+// Write to files opened with OF_Append may not be guaranteed to be atomic
+// on Windows. Both openLogFile and writeLineToFD are noops on Windows.
+static int openLogFile(StringRef Path) {
+  (void)Path;
+  return -1;
+}
+
+static bool writeLineToFD(int FD, const char *Data, size_t Size) {
+  (void)FD, (void)Data, (void)Size;
+  return false;
+}
+
+#else
+
+static int openLogFile(StringRef Path) {
+  int FD = -1;
+  std::error_code EC = llvm::sys::fs::openFileForWrite(
+      Path, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append);
+  if (EC) {
+    llvm::errs() << "warning: unable to open log file '" << Path
+                 << "': " << EC.message() << "\n";
+    return -1;
+  }
+  return FD;
+}
+
 // Writes the whole line into an FD that is opened with OF_Append.
 // This function only does one write (up to retry due to interrupts), and the
 // single write is blocking and atomic on POSIX systems.
 static bool writeLineToFD(int FD, const char *Data, size_t Size) {
-#ifndef _WIN32
   ssize_t Written = llvm::sys::RetryAfterSignal(-1, write, FD, Data, Size);
   return Written >= 0 && (static_cast<size_t>(Written) == Size);
-#else
-  (void)FD, (void)Data, (void)Size;
-  llvm_unreachable("Logging not supported on Windows!");
-  return false;
-#endif
 }
 
+#endif
+
 LogLine::LogLine(int FD, std::atomic<uint64_t> *DroppedLines)
     : FormattingOS(Buffer), FD(FD), DroppedLines(DroppedLines) {
   auto Millis = getTimestampMillis();
@@ -83,33 +106,44 @@ LogLine::~LogLine() {
     DroppedLines->fetch_add(1, std::memory_order_relaxed);
 }
 
-AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath)
-    : LogPath(LogFilePath.str()) {
-#ifndef _WIN32
+AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) {
   if (LogFilePath.empty())
     return;
+  LogPath = LogFilePath.str();
+  FD.store(openLogFile(LogFilePath), std::memory_order_release);
+}
 
-  std::error_code EC = llvm::sys::fs::openFileForWrite(
-      LogFilePath, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append);
-  if (EC) {
-    llvm::errs() << "warning: unable to open log file '" << LogFilePath
-                 << "': " << EC.message() << "\n";
-    FD = -1;
+void AtomicLineLogger::enable(StringRef LogFilePath) {
+  if (LogFilePath.empty())
+    return;
+  std::lock_guard<std::mutex> Lock(EnableMtx);
+  if (FD.load(std::memory_order_relaxed) != -1) {
+    if (LogFilePath != LogPath && !WarnedConflict) {
+      llvm::errs() << "warning: dependency scanning log path '" << LogFilePath
+                   << "' ignored; already logging to '" << LogPath << "'\n";
+      WarnedConflict = true;
+    }
     return;
   }
-#endif
-  // Write to files opened with OF_Append may not be guaranteed to be atomic
-  // on Windows, so we do not enable logging on Windows.
+
+  int NewFD = openLogFile(LogFilePath);
+  if (NewFD == -1)
+    return;
+  LogPath = LogFilePath.str();
+  FD.store(NewFD, std::memory_order_relaxed);
+  return;
 }
 
 LogLine AtomicLineLogger::log() {
-  if (FD != -1)
-    return LogLine(FD, &DroppedLines);
+  int CurFD = FD.load(std::memory_order_relaxed);
+  if (CurFD != -1)
+    return LogLine(CurFD, &DroppedLines);
   return LogLine();
 }
 
 AtomicLineLogger::~AtomicLineLogger() {
-  if (FD == -1)
+  int CurFD = FD.load(std::memory_order_relaxed);
+  if (CurFD == -1)
     return;
   if (uint64_t Dropped = DroppedLines.load(std::memory_order_relaxed))
     llvm::errs() << "warning: log '" << LogPath
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp 
b/clang/lib/Tooling/DependencyScanningTool.cpp
index b3332d9d49cd8..d0e01bd788ce2 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/DependencyScanningTool.h"
+#include "clang/Basic/AtomicLineLogger.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/DependencyScanning/DependencyScanningWorker.h"
@@ -15,6 +16,7 @@
 #include "clang/Driver/Tool.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/Utils.h"
+#include "clang/Options/Options.h"
 #include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/ADT/iterator.h"
 #include "llvm/TargetParser/Host.h"
@@ -90,7 +92,7 @@ static std::pair<std::unique_ptr<driver::Driver>,
                  std::unique_ptr<driver::Compilation>>
 buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
                  IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
-                 llvm::BumpPtrAllocator &Alloc) {
+                 llvm::BumpPtrAllocator &Alloc, AtomicLineLogger &Logger) {
   SmallVector<const char *, 256> Argv;
   Argv.reserve(ArgStrs.size());
   for (const std::string &Arg : ArgStrs)
@@ -125,6 +127,9 @@ buildCompilation(ArrayRef<std::string> ArgStrs, 
DiagnosticsEngine &Diags,
     return std::make_pair(nullptr, nullptr);
   }
 
+  Logger.enable(
+      Compilation->getArgs().getLastArgValue(options::OPT_fdepscan_log_path));
+
   return std::make_pair(std::move(Driver), std::move(Compilation));
 }
 
@@ -155,8 +160,8 @@ static bool computeDependenciesForDriverCommandLine(
   auto DiagEngine =
       CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DiagConsumer,
                                           /*ShouldOwnClient=*/false);
-  const auto [Driver, Compilation] =
-      buildCompilation(CommandLine, *DiagEngine, FS, Alloc);
+  const auto [Driver, Compilation] = buildCompilation(
+      CommandLine, *DiagEngine, FS, Alloc, Worker.getService().getLogger());
   if (!Compilation)
     return false;
 
@@ -326,13 +331,14 @@ DependencyScanningTool::getTranslationUnitDependencies(
 static std::optional<SmallVector<std::string, 0>>
 getFirstCC1CommandLine(ArrayRef<std::string> CommandLine,
                        DiagnosticsEngine &Diags,
-                       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
+                       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
+                       AtomicLineLogger &Logger) {
   // Compilation holds a non-owning a reference to the Driver, hence we need to
   // keep the Driver alive when we use Compilation. Arguments to commands may 
be
   // owned by Alloc when expanded from response files.
   llvm::BumpPtrAllocator Alloc;
   const auto [Driver, Compilation] =
-      buildCompilation(CommandLine, Diags, std::move(FS), Alloc);
+      buildCompilation(CommandLine, Diags, std::move(FS), Alloc, Logger);
   if (!Compilation)
     return std::nullopt;
 
@@ -362,8 +368,8 @@ bool DependencyScanningTool::getByNameDependencies(
     auto DiagEngine =
         CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DiagConsumer,
                                             /*ShouldOwnClient=*/false);
-    auto MaybeFirstCC1 =
-        getFirstCC1CommandLine(ModifiedCommandLine, *DiagEngine, FS);
+    auto MaybeFirstCC1 = getFirstCC1CommandLine(
+        ModifiedCommandLine, *DiagEngine, FS, Worker.getService().getLogger());
     if (!MaybeFirstCC1)
       return false;
     CC1CommandLine.assign(MaybeFirstCC1->begin(), MaybeFirstCC1->end());
diff --git a/clang/test/ClangScanDeps/logging-driver-flag.c 
b/clang/test/ClangScanDeps/logging-driver-flag.c
new file mode 100644
index 0000000000000..b2d99a98e5f56
--- /dev/null
+++ b/clang/test/ClangScanDeps/logging-driver-flag.c
@@ -0,0 +1,47 @@
+// UNSUPPORTED: system-windows
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb-by-name.json.template > %t/cdb-by-name.json
+
+// Translation-unit scanning: logging enabled via the driver command line in
+// computeDependenciesForDriverCommandLine -> buildCompilation.
+// RUN: clang-scan-deps -compilation-database %t/cdb.json \
+// RUN:   -format experimental-full -j 1 -o %t/deps.json
+// RUN: FileCheck %s --check-prefix=TU --input-file %t/tu.log
+
+// TU: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: starting scanning 
command:{{.*}}tu.c
+// TU: [{{[0-9]+\.[0-9]+}}] {{.*}}: pcm_write: {{.*}}.pcm
+// TU: [{{[0-9]+\.[0-9]+}}] {{.*}}: finished scanning command:{{.*}}tu.c
+
+// By-name scanning: logging enabled via getFirstCC1CommandLine ->
+// buildCompilation.
+// RUN: clang-scan-deps -compilation-database %t/cdb-by-name.json \
+// RUN:   -format experimental-full -j 1 -module-names=A
+// RUN: FileCheck %s --check-prefix=BY-NAME --input-file %t/by-name.log
+
+// BY-NAME: [{{[0-9]+\.[0-9]+}}] {{.*}}: start scan_by_name: A
+// BY-NAME: [{{[0-9]+\.[0-9]+}}] {{.*}}: finish scan_by_name: A
+
+//--- cdb.json.template
+[{
+  "directory": "DIR",
+  "command": "clang -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps 
-fmodules-cache-path=DIR/cache -fbuild-session-timestamp=1
+-fmodules-validate-once-per-build-session -fdepscan-log-path=DIR/tu.log",
+  "file": "DIR/tu.c"
+}]
+
+//--- cdb-by-name.json.template
+[{
+  "directory": "DIR",
+  "command": "clang -fmodules -fimplicit-module-maps 
-fmodules-cache-path=DIR/cache -I DIR -x c -fdepscan-log-path=DIR/by-name.log",
+  "file": ""
+}]
+
+//--- module.modulemap
+module A { header "A.h" }
+//--- A.h
+void A_func(void);
+//--- tu.c
+#include "A.h"
+void foo(void) { A_func(); }

>From d44bb662a5ea9f91b2b492220bf47623ab6bb371 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <[email protected]>
Date: Tue, 11 Aug 2026 16:08:09 -0700
Subject: [PATCH 2/5] Adding logging_start and logging_end events.

---
 clang/include/clang/Basic/AtomicLineLogger.h  |  2 +
 clang/lib/Basic/AtomicLineLogger.cpp          | 13 ++--
 .../ClangScanDeps/logging-simple-by-name.c    |  4 +-
 clang/test/ClangScanDeps/logging-simple.c     |  4 +-
 .../unittests/Basic/AtomicLineLoggerTest.cpp  | 61 ++++++++++++++-----
 5 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Basic/AtomicLineLogger.h 
b/clang/include/clang/Basic/AtomicLineLogger.h
index ddcd5993b060e..a2b1f9abdaa6d 100644
--- a/clang/include/clang/Basic/AtomicLineLogger.h
+++ b/clang/include/clang/Basic/AtomicLineLogger.h
@@ -67,6 +67,8 @@ class AtomicLineLogger {
   std::mutex EnableMtx;
   bool WarnedConflict = false;
 
+  void setFD(StringRef Path);
+
 public:
   AtomicLineLogger() {}
   AtomicLineLogger(StringRef LogFilePath);
diff --git a/clang/lib/Basic/AtomicLineLogger.cpp 
b/clang/lib/Basic/AtomicLineLogger.cpp
index 4bf26f0cb756d..c32f3551dcc0e 100644
--- a/clang/lib/Basic/AtomicLineLogger.cpp
+++ b/clang/lib/Basic/AtomicLineLogger.cpp
@@ -106,11 +106,16 @@ LogLine::~LogLine() {
     DroppedLines->fetch_add(1, std::memory_order_relaxed);
 }
 
+void AtomicLineLogger::setFD(StringRef Path) {
+  LogPath = Path.str();
+  FD.store(openLogFile(Path), std::memory_order_release);
+  log() << "logging_start";
+}
+
 AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) {
   if (LogFilePath.empty())
     return;
-  LogPath = LogFilePath.str();
-  FD.store(openLogFile(LogFilePath), std::memory_order_release);
+  setFD(LogFilePath);
 }
 
 void AtomicLineLogger::enable(StringRef LogFilePath) {
@@ -129,8 +134,7 @@ void AtomicLineLogger::enable(StringRef LogFilePath) {
   int NewFD = openLogFile(LogFilePath);
   if (NewFD == -1)
     return;
-  LogPath = LogFilePath.str();
-  FD.store(NewFD, std::memory_order_relaxed);
+  setFD(LogFilePath);
   return;
 }
 
@@ -145,6 +149,7 @@ AtomicLineLogger::~AtomicLineLogger() {
   int CurFD = FD.load(std::memory_order_relaxed);
   if (CurFD == -1)
     return;
+  log() << "logging_end";
   if (uint64_t Dropped = DroppedLines.load(std::memory_order_relaxed))
     llvm::errs() << "warning: log '" << LogPath
                  << "' is incomplete: " << Dropped
diff --git a/clang/test/ClangScanDeps/logging-simple-by-name.c 
b/clang/test/ClangScanDeps/logging-simple-by-name.c
index 954d25acdd1f7..b02fad5117ea9 100644
--- a/clang/test/ClangScanDeps/logging-simple-by-name.c
+++ b/clang/test/ClangScanDeps/logging-simple-by-name.c
@@ -17,7 +17,8 @@
 // RUN:   -module-names=M,N -o %t/deps.json
 // RUN: FileCheck %s < %t/scan.log
 
-// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID1:]]: 
init_compiler_instance_with_context:{{.*}}
+// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID1:]]: logging_start
+// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: 
init_compiler_instance_with_context:{{.*}}
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: start scan_by_name: M
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: timestamp_read: 
{{.*}}[[MPCMFILE:.*\.pcm]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: pcm_read_cached: 
{{.*}}[[MPCMFILE]]
@@ -46,6 +47,7 @@
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: timestamp_read: 
{{.*}}[[NPCMFILE]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: pcm_finalized: 
{{.*}}[[NPCMFILE]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: finish scan_by_name: N
+// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID1]]: logging_end
 
 //--- cdb.json.template
 [{
diff --git a/clang/test/ClangScanDeps/logging-simple.c 
b/clang/test/ClangScanDeps/logging-simple.c
index 0022fb97cbc73..444fdf1ab6e20 100644
--- a/clang/test/ClangScanDeps/logging-simple.c
+++ b/clang/test/ClangScanDeps/logging-simple.c
@@ -13,7 +13,8 @@
 // build a single scanning pcm. We should only log these events, no more and no
 // less, strictly in this order. Changes to this list should be intentional.
 
-// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: starting scanning 
command:{{.*}}tu.c
+// CHECK: [{{[0-9]+\.[0-9]+}}] [[#PID:]] [[#TID:]]: logging_start
+// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: starting scanning 
command:{{.*}}tu.c
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: 
init_compiler_instance_with_context:{{.*}}
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: timestamp_read: 
{{.*}}[[PCMFILE:.*\.pcm]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_cached: 
{{.*}}[[PCMFILE]]
@@ -26,6 +27,7 @@
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_read_cached: 
{{.*}}[[PCMFILE]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: pcm_finalized: 
{{.*}}[[PCMFILE]]
 // CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: finished scanning 
command:{{.*}}tu.c
+// CHECK-NEXT: [{{[0-9]+\.[0-9]+}}] [[#PID]] [[#TID]]: logging_end
 
 //--- cdb.json.template
 [{
diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp 
b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
index b1dfb616d08c2..654ebf1149668 100644
--- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp
+++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
@@ -16,6 +16,21 @@
 
 using namespace clang;
 
+static StringRef logBody(StringRef Line) { return Line.split(": ").second; }
+
+static SmallVector<StringRef> logBodyLines(StringRef Content) {
+  SmallVector<StringRef> Lines;
+  Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+  if (Lines.size() < 2) {
+    ADD_FAILURE() << "log not framed by logging_start/logging_end; got "
+                  << Lines.size() << " line(s)";
+    return {};
+  }
+  EXPECT_EQ(logBody(Lines.front()), "logging_start");
+  EXPECT_EQ(logBody(Lines.back()), "logging_end");
+  return SmallVector<StringRef>(ArrayRef(Lines).drop_front().drop_back());
+}
+
 TEST(AtomicLineLoggerTest, DisabledLoggerDoesNotCrash) {
   AtomicLineLogger Logger;
   Logger.log() << "this goes nowhere";
@@ -41,9 +56,10 @@ TEST(AtomicLineLoggerTest, LogLineMoveConstructor) {
   ASSERT_TRUE(BufOrErr) << "Failed to read log file";
   StringRef Content = (*BufOrErr)->getBuffer();
 
-  // Only one line should be written (from Moved, not from Original).
-  EXPECT_EQ(Content.count('\n'), 1u);
-  EXPECT_TRUE(Content.contains("after_move"));
+  // Only one log body line should be written (from Moved, not from Original).
+  auto Body = logBodyLines(Content);
+  ASSERT_EQ(Body.size(), 1u);
+  EXPECT_EQ(logBody(Body.front()), "after_move");
 }
 
 TEST(AtomicLineLoggerTest, LogLinePIDTIDMsg) {
@@ -60,17 +76,18 @@ TEST(AtomicLineLoggerTest, LogLinePIDTIDMsg) {
   ASSERT_TRUE(BufOrErr) << "Failed to read log file";
   StringRef Content = (*BufOrErr)->getBuffer();
 
-  // Ends with message + newline.
-  EXPECT_TRUE(Content.ends_with("test_event: some_file.pcm\n"));
+  auto Body = logBodyLines(Content);
+  ASSERT_EQ(Body.size(), 1u);
+  EXPECT_EQ(logBody(Body.front()), "test_event: some_file.pcm");
 
   // Prefix has the form: "<timestamp> <pid> <tid>: "
   // Verify PID matches this process.
   std::string ExpectedPID = std::to_string(llvm::sys::Process::getProcessId());
-  EXPECT_TRUE(Content.contains(ExpectedPID));
+  EXPECT_TRUE(Body.front().contains(ExpectedPID));
 
   // Verify TID is present.
   std::string ExpectedTID = std::to_string(llvm::get_threadid());
-  EXPECT_TRUE(Content.contains(ExpectedTID));
+  EXPECT_TRUE(Body.front().contains(ExpectedTID));
 }
 
 TEST(AtomicLineLoggerTest, LogLineLogArray) {
@@ -149,11 +166,9 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) {
   StringRef Content = (*BufOrErr)->getBuffer();
 
   // Verify the message is present and the line ends with a newline.
-  EXPECT_TRUE(Content.contains("pcm_write: module.pcm"));
-  EXPECT_TRUE(Content.ends_with("\n"));
-
-  // Verify there is exactly one line.
-  EXPECT_EQ(Content.count('\n'), 1u);
+  auto Body = logBodyLines(Content);
+  ASSERT_EQ(Body.size(), 1u);
+  EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm");
 }
 
 TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) {
@@ -167,6 +182,8 @@ TEST(AtomicLineLoggerTest, 
ConcurrentWritesProduceCompleteLines) {
   constexpr unsigned NumThreads = 8;
   constexpr unsigned LinesPerThread = 100;
   constexpr unsigned MessageLen = 32;
+  // One LoggerEven and one LoggerOdd.
+  constexpr unsigned NumLoggers = 2;
 
   {
     // Creating two loggers based on the same file to make sure
@@ -204,9 +221,25 @@ TEST(AtomicLineLoggerTest, 
ConcurrentWritesProduceCompleteLines) {
   SmallVector<StringRef> Lines;
   Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
 
-  EXPECT_EQ(Lines.size(), (size_t)(NumThreads * LinesPerThread));
+  SmallVector<StringRef> MessageLines;
+  unsigned Starts = 0, Ends = 0;
+  for (StringRef Line : Lines) {
+    StringRef Body = logBody(Line);
+    if (Body == "logging_start") {
+      ++Starts;
+      continue;
+    }
+    if (Body == "logging_end") {
+      ++Ends;
+      continue;
+    }
+    MessageLines.push_back(Line);
+  }
+  EXPECT_EQ(Starts, NumLoggers);
+  EXPECT_EQ(Ends, NumLoggers);
+  EXPECT_EQ(MessageLines.size(), (size_t)(NumThreads * LinesPerThread));
 
-  for (const auto &Line : Lines) {
+  for (const auto &Line : MessageLines) {
     // For each line, we check the separator, message length, message start and
     // the prefix format to make sure no lines are interleved.
 

>From ddbbd74d750e7b27d2872a07779547e1df7fa2e7 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <[email protected]>
Date: Wed, 19 Aug 2026 13:04:18 -0700
Subject: [PATCH 3/5] Address review comments.

---
 clang/include/clang/Basic/AtomicLineLogger.h  |  2 +-
 clang/lib/Basic/AtomicLineLogger.cpp          | 44 +++++++++----------
 .../unittests/Basic/AtomicLineLoggerTest.cpp  | 44 ++++++++++++++++++-
 3 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/Basic/AtomicLineLogger.h 
b/clang/include/clang/Basic/AtomicLineLogger.h
index a2b1f9abdaa6d..1e8593578054d 100644
--- a/clang/include/clang/Basic/AtomicLineLogger.h
+++ b/clang/include/clang/Basic/AtomicLineLogger.h
@@ -67,7 +67,7 @@ class AtomicLineLogger {
   std::mutex EnableMtx;
   bool WarnedConflict = false;
 
-  void setFD(StringRef Path);
+  void initialize(StringRef LogFilePath);
 
 public:
   AtomicLineLogger() {}
diff --git a/clang/lib/Basic/AtomicLineLogger.cpp 
b/clang/lib/Basic/AtomicLineLogger.cpp
index c32f3551dcc0e..08bc4ef9b397c 100644
--- a/clang/lib/Basic/AtomicLineLogger.cpp
+++ b/clang/lib/Basic/AtomicLineLogger.cpp
@@ -14,6 +14,7 @@
 #include "clang/Basic/AtomicLineLogger.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errno.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/Process.h"
@@ -41,22 +42,16 @@ static uint64_t getTimestampMillis() {
 #endif
 }
 
-#ifdef _WIN32
-// Write to files opened with OF_Append may not be guaranteed to be atomic
-// on Windows. Both openLogFile and writeLineToFD are noops on Windows.
 static int openLogFile(StringRef Path) {
+#ifdef _WIN32
+  // Logging is always disabled on Windows. openLogFile implements this policy
+  // by never returning a valid FD, so the logger and the LogLines it creates
+  // stay dormant (FD == -1). The reason is that writes to files opened with
+  // OF_Append are not guaranteed atomic on Windows. If a use case arises we'll
+  // need a different strategy to write LogLines atomically.
   (void)Path;
   return -1;
-}
-
-static bool writeLineToFD(int FD, const char *Data, size_t Size) {
-  (void)FD, (void)Data, (void)Size;
-  return false;
-}
-
 #else
-
-static int openLogFile(StringRef Path) {
   int FD = -1;
   std::error_code EC = llvm::sys::fs::openFileForWrite(
       Path, FD, llvm::sys::fs::CD_OpenAlways, llvm::sys::fs::OF_Append);
@@ -66,17 +61,21 @@ static int openLogFile(StringRef Path) {
     return -1;
   }
   return FD;
+#endif
 }
 
 // Writes the whole line into an FD that is opened with OF_Append.
 // This function only does one write (up to retry due to interrupts), and the
 // single write is blocking and atomic on POSIX systems.
 static bool writeLineToFD(int FD, const char *Data, size_t Size) {
+#ifdef _WIN32
+  (void)FD, (void)Data, (void)Size;
+  llvm_unreachable("dependency scanning logging is unsupported on Windows");
+#else
   ssize_t Written = llvm::sys::RetryAfterSignal(-1, write, FD, Data, Size);
   return Written >= 0 && (static_cast<size_t>(Written) == Size);
-}
-
 #endif
+}
 
 LogLine::LogLine(int FD, std::atomic<uint64_t> *DroppedLines)
     : FormattingOS(Buffer), FD(FD), DroppedLines(DroppedLines) {
@@ -106,16 +105,19 @@ LogLine::~LogLine() {
     DroppedLines->fetch_add(1, std::memory_order_relaxed);
 }
 
-void AtomicLineLogger::setFD(StringRef Path) {
-  LogPath = Path.str();
-  FD.store(openLogFile(Path), std::memory_order_release);
+void AtomicLineLogger::initialize(StringRef LogFilePath) {
+  int NewFD = openLogFile(LogFilePath);
+  if (NewFD == -1)
+    return;
+  LogPath = LogFilePath.str();
+  FD.store(NewFD, std::memory_order_relaxed);
   log() << "logging_start";
 }
 
 AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) {
   if (LogFilePath.empty())
     return;
-  setFD(LogFilePath);
+  initialize(LogFilePath);
 }
 
 void AtomicLineLogger::enable(StringRef LogFilePath) {
@@ -131,11 +133,7 @@ void AtomicLineLogger::enable(StringRef LogFilePath) {
     return;
   }
 
-  int NewFD = openLogFile(LogFilePath);
-  if (NewFD == -1)
-    return;
-  setFD(LogFilePath);
-  return;
+  initialize(LogFilePath);
 }
 
 LogLine AtomicLineLogger::log() {
diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp 
b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
index 654ebf1149668..5e0b4c9a88cd0 100644
--- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp
+++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
@@ -16,6 +16,7 @@
 
 using namespace clang;
 
+#ifndef _WIN32
 static StringRef logBody(StringRef Line) { return Line.split(": ").second; }
 
 static SmallVector<StringRef> logBodyLines(StringRef Content) {
@@ -39,7 +40,6 @@ TEST(AtomicLineLoggerTest, DisabledLoggerDoesNotCrash) {
   EXPECT_TRUE(true);
 }
 
-#ifndef _WIN32
 TEST(AtomicLineLoggerTest, LogLineMoveConstructor) {
   llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true);
   SmallString<128> LogPath(Dir.path());
@@ -171,6 +171,48 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) {
   EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm");
 }
 
+TEST(AtomicLineLoggerTest, EnableWithConflictingPathIsIgnoredAndWarnsOnce) {
+  llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true);
+  SmallString<128> PathA(Dir.path());
+  llvm::sys::path::append(PathA, "a.log");
+  SmallString<128> PathB(Dir.path());
+  llvm::sys::path::append(PathB, "b.log");
+
+  std::string Err;
+  {
+    AtomicLineLogger Logger;
+    Logger.enable(PathA);
+
+    testing::internal::CaptureStderr();
+    // Enable PathB twice to check if only one warning shows up.
+    Logger.enable(PathB);
+    Logger.enable(PathB);
+    // Enabling the path already in effect is a no-op.
+    Logger.enable(PathA);
+    Err = testing::internal::GetCapturedStderr();
+
+    StringRef ErrRef(Err);
+    EXPECT_EQ(ErrRef.count("already logging to"), 1u);
+    EXPECT_TRUE(ErrRef.contains(PathB));
+    EXPECT_TRUE(ErrRef.contains(PathA));
+
+    Logger.log() << "still_on_a";
+  }
+  // Logger destroyed here. Log file is written to disk.
+
+  EXPECT_EQ(Err,
+            ("warning: dependency scanning log path '" + Twine(PathB.str()) +
+             "' ignored; already logging to '" + Twine(PathA.str()) + "'\n")
+                .str());
+
+  auto ABuf = llvm::MemoryBuffer::getFile(PathA);
+  ASSERT_TRUE(ABuf) << "Failed to read log file A";
+  EXPECT_TRUE((*ABuf)->getBuffer().contains("still_on_a"));
+
+  EXPECT_FALSE(llvm::sys::fs::exists(PathB))
+      << "Conflicting path B should never have been opened";
+}
+
 TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) {
   llvm::unittest::TempDir Dir("atomic-logger-concurrent", /*Unique=*/true);
   SmallString<128> LogPath(Dir.path());

>From cb2353acc3eec5509a46d48c03acf56730a94118 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <[email protected]>
Date: Wed, 19 Aug 2026 16:09:35 -0700
Subject: [PATCH 4/5] Hooking up the logger option for modules driver.

---
 clang/lib/Driver/ModulesDriver.cpp              | 10 +++++++---
 .../test/Driver/modules-driver-depscan-log.cpp  | 17 +++++++++++++++++
 2 files changed, 24 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Driver/modules-driver-depscan-log.cpp

diff --git a/clang/lib/Driver/ModulesDriver.cpp 
b/clang/lib/Driver/ModulesDriver.cpp
index f0f312769fab1..607705014c42d 100644
--- a/clang/lib/Driver/ModulesDriver.cpp
+++ b/clang/lib/Driver/ModulesDriver.cpp
@@ -607,7 +607,7 @@ static std::optional<DependencyScanResult> scanDependencies(
     ArrayRef<std::unique_ptr<Command>> Jobs,
     llvm::DenseMap<StringRef, const StdModuleManifest::Module *> 
ManifestLookup,
     StringRef ModuleCachePath, StringRef WorkingDirectory,
-    DiagnosticsEngine &Diags) {
+    StringRef DepScanLogPath, DiagnosticsEngine &Diags) {
   llvm::PrettyStackTraceString CrashInfo("Performing module dependency scan.");
 
   // Classify the jobs based on scan eligibility.
@@ -645,6 +645,7 @@ static std::optional<DependencyScanResult> scanDependencies(
   const bool HasStdlibModuleInputs = !StdlibModuleScanIndexByID.empty();
 
   deps::DependencyScanningServiceOptions Opts;
+  Opts.LogPath = DepScanLogPath.str();
   deps::DependencyScanningService ScanningService(std::move(Opts));
 
   std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
@@ -1655,8 +1656,11 @@ void driver::modules::runModulesDriver(
   auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory();
   const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : ".";
 
-  auto MaybeScanResults = scanDependencies(Jobs, ManifestEntryBySource,
-                                           *MaybeModuleCachePath, CWD, Diags);
+  StringRef DepScanLogPath =
+      C.getArgs().getLastArgValue(options::OPT_fdepscan_log_path);
+  auto MaybeScanResults =
+      scanDependencies(Jobs, ManifestEntryBySource, *MaybeModuleCachePath, CWD,
+                       DepScanLogPath, Diags);
   if (!MaybeScanResults) {
     Diags.Report(diag::err_dependency_scan_failed);
     return;
diff --git a/clang/test/Driver/modules-driver-depscan-log.cpp 
b/clang/test/Driver/modules-driver-depscan-log.cpp
new file mode 100644
index 0000000000000..62f0ae859d56a
--- /dev/null
+++ b/clang/test/Driver/modules-driver-depscan-log.cpp
@@ -0,0 +1,17 @@
+// Check that -fdepscan-log-path enables dependency scanning logging.
+
+// UNSUPPORTED: system-windows
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang -c -std=c++23 -fmodules-driver -fdepscan-log-path=%t/scan.log \
+// RUN:   %t/A.cppm
+// RUN: FileCheck %s --input-file %t/scan.log
+
+// CHECK: logging_start
+// CHECK: starting scanning command:
+// CHECK: logging_end
+
+//--- A.cppm
+export module A;
+export int a() { return 0; }

>From 85c7164c93dd5e6bc868e0ac2e8170c41affa5d3 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <[email protected]>
Date: Wed, 2 Sep 2026 15:35:21 -0700
Subject: [PATCH 5/5] Adding error checks against enabling the logger
 inconsistently through driver commands.

---
 clang/include/clang/Basic/AtomicLineLogger.h  | 11 +++-
 .../clang/Basic/DiagnosticDriverKinds.td      |  6 ++
 clang/lib/Basic/AtomicLineLogger.cpp          | 25 ++++----
 clang/lib/Driver/ModulesDriver.cpp            |  9 ++-
 clang/lib/Tooling/DependencyScanningTool.cpp  | 19 +++++-
 clang/test/ClangScanDeps/depscan-log-path.c   | 64 +++++++++++++++++++
 .../unittests/Basic/AtomicLineLoggerTest.cpp  | 42 ------------
 7 files changed, 115 insertions(+), 61 deletions(-)
 create mode 100644 clang/test/ClangScanDeps/depscan-log-path.c

diff --git a/clang/include/clang/Basic/AtomicLineLogger.h 
b/clang/include/clang/Basic/AtomicLineLogger.h
index 1e8593578054d..b3a48d847868f 100644
--- a/clang/include/clang/Basic/AtomicLineLogger.h
+++ b/clang/include/clang/Basic/AtomicLineLogger.h
@@ -65,7 +65,8 @@ class AtomicLineLogger {
   std::string LogPath;
   std::atomic<uint64_t> DroppedLines{0};
   std::mutex EnableMtx;
-  bool WarnedConflict = false;
+  bool EnabledAtConstruction = false;
+  bool CommittedByFlag = false;
 
   void initialize(StringRef LogFilePath);
 
@@ -81,8 +82,12 @@ class AtomicLineLogger {
   ~AtomicLineLogger();
 
   // Enables the logger if it is not already enabled. Thread safe.
-  // If the logger is already enabled, call to enable is a no-op.
-  void enable(StringRef LogFilePath);
+  // Returns false only when the requested path conflicts with the
+  // already-committed path. In other words, returns false if
+  // the logger is not enabled consistently during its lifetime.
+  bool enable(StringRef RequestedLogPath);
+
+  StringRef getLogPath() const { return LogPath; }
 
   LogLine log();
 };
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td 
b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ae3eecc60fc78..bb2fa5c0fcec9 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -659,6 +659,12 @@ def remark_printing_module_graph : Remark<
 def err_module_defined_outside_of_module_source : Error<
   "module '%0' is defined in file '%1', but module declarations are only "
   "allowed in C++ module inputs; use the '.cppm' extension or '-x c++module'">;
+def err_drv_depscan_log_path_empty : Error<
+  "'-fdepscan-log-path=' requires a non-empty file path">;
+def err_drv_depscan_log_path_inconsistent : Error<
+  "'-fdepscan-log-path' set inconsistently within a dependency scan: "
+  "%select{no log path|log path '%1'}0 conflicts with "
+  "%select{no log path|log path '%3'}2 requested earlier">;
 
 def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
   "-fdelayed-template-parsing is deprecated after C++20">,
diff --git a/clang/lib/Basic/AtomicLineLogger.cpp 
b/clang/lib/Basic/AtomicLineLogger.cpp
index 08bc4ef9b397c..75b8914fd61ae 100644
--- a/clang/lib/Basic/AtomicLineLogger.cpp
+++ b/clang/lib/Basic/AtomicLineLogger.cpp
@@ -106,10 +106,10 @@ LogLine::~LogLine() {
 }
 
 void AtomicLineLogger::initialize(StringRef LogFilePath) {
+  LogPath = LogFilePath.str();
   int NewFD = openLogFile(LogFilePath);
   if (NewFD == -1)
     return;
-  LogPath = LogFilePath.str();
   FD.store(NewFD, std::memory_order_relaxed);
   log() << "logging_start";
 }
@@ -118,22 +118,21 @@ AtomicLineLogger::AtomicLineLogger(StringRef LogFilePath) 
{
   if (LogFilePath.empty())
     return;
   initialize(LogFilePath);
+  EnabledAtConstruction = true;
 }
 
-void AtomicLineLogger::enable(StringRef LogFilePath) {
-  if (LogFilePath.empty())
-    return;
+bool AtomicLineLogger::enable(StringRef RequestedLogPath) {
   std::lock_guard<std::mutex> Lock(EnableMtx);
-  if (FD.load(std::memory_order_relaxed) != -1) {
-    if (LogFilePath != LogPath && !WarnedConflict) {
-      llvm::errs() << "warning: dependency scanning log path '" << LogFilePath
-                   << "' ignored; already logging to '" << LogPath << "'\n";
-      WarnedConflict = true;
-    }
-    return;
-  }
+  if (EnabledAtConstruction)
+    return RequestedLogPath.empty() || RequestedLogPath == LogPath;
 
-  initialize(LogFilePath);
+  if (!CommittedByFlag) {
+    CommittedByFlag = true;
+    if (!RequestedLogPath.empty())
+      initialize(RequestedLogPath);
+    return true;
+  }
+  return RequestedLogPath == LogPath;
 }
 
 LogLine AtomicLineLogger::log() {
diff --git a/clang/lib/Driver/ModulesDriver.cpp 
b/clang/lib/Driver/ModulesDriver.cpp
index 607705014c42d..9e6984fa8ef6b 100644
--- a/clang/lib/Driver/ModulesDriver.cpp
+++ b/clang/lib/Driver/ModulesDriver.cpp
@@ -1656,8 +1656,15 @@ void driver::modules::runModulesDriver(
   auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory();
   const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : ".";
 
+  const llvm::opt::Arg *LogPathArg =
+      C.getArgs().getLastArg(options::OPT_fdepscan_log_path);
   StringRef DepScanLogPath =
-      C.getArgs().getLastArgValue(options::OPT_fdepscan_log_path);
+      LogPathArg ? StringRef(LogPathArg->getValue()).trim() : StringRef();
+  if (LogPathArg && DepScanLogPath.empty()) {
+    Diags.Report(diag::err_drv_depscan_log_path_empty);
+    return;
+  }
+
   auto MaybeScanResults =
       scanDependencies(Jobs, ManifestEntryBySource, *MaybeModuleCachePath, CWD,
                        DepScanLogPath, Diags);
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp 
b/clang/lib/Tooling/DependencyScanningTool.cpp
index d0e01bd788ce2..a937879706522 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -127,8 +127,23 @@ buildCompilation(ArrayRef<std::string> ArgStrs, 
DiagnosticsEngine &Diags,
     return std::make_pair(nullptr, nullptr);
   }
 
-  Logger.enable(
-      Compilation->getArgs().getLastArgValue(options::OPT_fdepscan_log_path));
+  const llvm::opt::Arg *LogPathArg =
+      Compilation->getArgs().getLastArg(options::OPT_fdepscan_log_path);
+  StringRef LogPath =
+      LogPathArg ? StringRef(LogPathArg->getValue()).trim() : StringRef();
+
+  // Forbid -fdepscan-log-path="".
+  if (LogPathArg && LogPath.empty()) {
+    Diags.Report(diag::err_drv_depscan_log_path_empty);
+    return std::make_pair(nullptr, nullptr);
+  }
+
+  if (!Logger.enable(LogPath)) {
+    Diags.Report(diag::err_drv_depscan_log_path_inconsistent)
+        << unsigned(!LogPath.empty()) << LogPath
+        << unsigned(!Logger.getLogPath().empty()) << Logger.getLogPath();
+    return std::make_pair(nullptr, nullptr);
+  }
 
   return std::make_pair(std::move(Driver), std::move(Compilation));
 }
diff --git a/clang/test/ClangScanDeps/depscan-log-path.c 
b/clang/test/ClangScanDeps/depscan-log-path.c
new file mode 100644
index 0000000000000..ea3993c80c8fa
--- /dev/null
+++ b/clang/test/ClangScanDeps/depscan-log-path.c
@@ -0,0 +1,64 @@
+// Diagnostics for the -fdepscan-log-path driver flag during dependency 
scanning:
+// an empty value is rejected, an inconsistent value across commands in one 
scan
+// is rejected, and a consistent value is accepted (and aggregated).
+
+// UNSUPPORTED: system-windows
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/empty.json.template        > %t/empty.json
+// RUN: sed -e "s|DIR|%/t|g" %t/inconsistent.json.template > 
%t/inconsistent.json
+// RUN: sed -e "s|DIR|%/t|g" %t/consistent.json.template   > %t/consistent.json
+
+// An explicitly empty value is rejected.
+// RUN: not clang-scan-deps -compilation-database %t/empty.json \
+// RUN:   -format experimental-full -j 1 2>&1 | FileCheck %s 
--check-prefix=EMPTY
+// EMPTY: error: '-fdepscan-log-path=' requires a non-empty file path
+
+// Different log paths across commands in one scan are rejected.
+// RUN: not clang-scan-deps -compilation-database %t/inconsistent.json \
+// RUN:   -format experimental-full -j 1 2>&1 | FileCheck %s 
--check-prefix=CONFLICT
+// CONFLICT: error: '-fdepscan-log-path' set inconsistently within a 
dependency scan
+
+// The same log path across commands is fine; both are aggregated into one log.
+// RUN: clang-scan-deps -compilation-database %t/consistent.json \
+// RUN:   -format experimental-full -j 1 -o %t/deps.json
+// RUN: FileCheck %s --check-prefix=OK --input-file %t/scan.log
+// OK: logging_start
+// OK: starting scanning command:{{.*}}tu.c
+// OK: starting scanning command:{{.*}}tu2.c
+// OK: logging_end
+
+// One command with a valid flag, the other does not have a flag in effect.
+// RUN: not clang-scan-deps -compilation-database %t/inconsistent.json \
+// RUN:   -format experimental-full -j 1 2>&1 | FileCheck %s 
--check-prefix=CONFLICT2
+// CONFLICT2: error: '-fdepscan-log-path' set inconsistently within a 
dependency scan
+
+//--- empty.json.template
+[{
+  "directory": "DIR",
+  "command": "clang -fsyntax-only DIR/tu.c -fdepscan-log-path=",
+  "file": "DIR/tu.c"
+}]
+
+//--- inconsistent.json.template
+[
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c 
-fdepscan-log-path=DIR/a.log",  "file": "DIR/tu.c" },
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c 
-fdepscan-log-path=DIR/b.log", "file": "DIR/tu2.c" }
+]
+
+//--- consistent.json.template
+[
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c 
-fdepscan-log-path=DIR/scan.log",  "file": "DIR/tu.c" },
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c 
-fdepscan-log-path=DIR/scan.log", "file": "DIR/tu2.c" }
+]
+
+//--- inconsistent2.json.template
+[
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu.c",  "file": 
"DIR/tu.c" },
+{ "directory": "DIR", "command": "clang -fsyntax-only DIR/tu2.c 
-fdepscan-log-path=DIR/b.log", "file": "DIR/tu2.c" }
+]
+
+//--- tu.c
+void foo(void) {}
+//--- tu2.c
+void bar(void) {}
diff --git a/clang/unittests/Basic/AtomicLineLoggerTest.cpp 
b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
index 5e0b4c9a88cd0..97e600a1a17eb 100644
--- a/clang/unittests/Basic/AtomicLineLoggerTest.cpp
+++ b/clang/unittests/Basic/AtomicLineLoggerTest.cpp
@@ -171,48 +171,6 @@ TEST(AtomicLineLoggerTest, SingleLineWrittenToFile) {
   EXPECT_EQ(logBody(Body.front()), "pcm_write: module.pcm");
 }
 
-TEST(AtomicLineLoggerTest, EnableWithConflictingPathIsIgnoredAndWarnsOnce) {
-  llvm::unittest::TempDir Dir("atomic-logger-test", /*Unique=*/true);
-  SmallString<128> PathA(Dir.path());
-  llvm::sys::path::append(PathA, "a.log");
-  SmallString<128> PathB(Dir.path());
-  llvm::sys::path::append(PathB, "b.log");
-
-  std::string Err;
-  {
-    AtomicLineLogger Logger;
-    Logger.enable(PathA);
-
-    testing::internal::CaptureStderr();
-    // Enable PathB twice to check if only one warning shows up.
-    Logger.enable(PathB);
-    Logger.enable(PathB);
-    // Enabling the path already in effect is a no-op.
-    Logger.enable(PathA);
-    Err = testing::internal::GetCapturedStderr();
-
-    StringRef ErrRef(Err);
-    EXPECT_EQ(ErrRef.count("already logging to"), 1u);
-    EXPECT_TRUE(ErrRef.contains(PathB));
-    EXPECT_TRUE(ErrRef.contains(PathA));
-
-    Logger.log() << "still_on_a";
-  }
-  // Logger destroyed here. Log file is written to disk.
-
-  EXPECT_EQ(Err,
-            ("warning: dependency scanning log path '" + Twine(PathB.str()) +
-             "' ignored; already logging to '" + Twine(PathA.str()) + "'\n")
-                .str());
-
-  auto ABuf = llvm::MemoryBuffer::getFile(PathA);
-  ASSERT_TRUE(ABuf) << "Failed to read log file A";
-  EXPECT_TRUE((*ABuf)->getBuffer().contains("still_on_a"));
-
-  EXPECT_FALSE(llvm::sys::fs::exists(PathB))
-      << "Conflicting path B should never have been opened";
-}
-
 TEST(AtomicLineLoggerTest, ConcurrentWritesProduceCompleteLines) {
   llvm::unittest::TempDir Dir("atomic-logger-concurrent", /*Unique=*/true);
   SmallString<128> LogPath(Dir.path());

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to