[clang] [clang][deps] Use `ModuleDepCollector` for Make output (PR #182063)
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 18 09:04:24 PST 2026
https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/182063
>From a4bd1e4234e3c2ef81d1fe01ac78ba7163ec4e8b Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Tue, 17 Feb 2026 21:21:35 -0800
Subject: [PATCH 1/2] [clang][deps] Don't treat the 'make' output format
specially
---
.../DependencyScannerImpl.h | 12 ----
.../DependencyScanning/ModuleDepCollector.h | 3 +
.../clang/Tooling/DependencyScanningTool.h | 1 +
.../DependencyScannerImpl.cpp | 66 ++++---------------
.../DependencyScanning/ModuleDepCollector.cpp | 12 ++--
clang/lib/Tooling/DependencyScanningTool.cpp | 26 +++++---
clang/test/ClangScanDeps/modules-cc1.cpp | 2 +-
.../modules-has-include-umbrella-header.c | 3 +-
clang/tools/clang-scan-deps/ClangScanDeps.cpp | 4 +-
.../Tooling/DependencyScannerTest.cpp | 10 +--
10 files changed, 54 insertions(+), 85 deletions(-)
diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
index d50371512667d..3d55759d60d83 100644
--- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
+++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h
@@ -106,18 +106,6 @@ std::optional<PrebuiltModulesAttrsMap>
computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
SmallVector<StringRef> &StableDirs);
-/// Create the dependency collector that will collect the produced
-/// dependencies. May return the created ModuleDepCollector depending
-/// on the scanning format.
-std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
- CompilerInstance &ScanInstance,
- std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
- StringRef WorkingDirectory, DependencyConsumer &Consumer,
- DependencyScanningService &Service, CompilerInvocation &Inv,
- DependencyActionController &Controller,
- PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
- llvm::SmallVector<StringRef> &StableDirs);
-
class CompilerInstanceWithContext {
// Context
DependencyScanningWorker &Worker;
diff --git a/clang/include/clang/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
index 8f665daf03c69..2a581d72274f1 100644
--- a/clang/include/clang/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/DependencyScanning/ModuleDepCollector.h
@@ -225,6 +225,9 @@ class ModuleDepCollectorPP final : public PPCallbacks {
void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
SrcMgr::CharacteristicKind FileType, FileID PrevFID,
SourceLocation Loc) override;
+ void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
+ OptionalFileEntryRef File,
+ SrcMgr::CharacteristicKind FileType) override;
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
diff --git a/clang/include/clang/Tooling/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanningTool.h
index 96d21866dd7f9..5a94025191513 100644
--- a/clang/include/clang/Tooling/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanningTool.h
@@ -47,6 +47,7 @@ class DependencyScanningTool {
/// dependency file contents otherwise.
std::optional<std::string>
getDependencyFile(ArrayRef<std::string> CommandLine, StringRef CWD,
+ dependencies::LookupModuleOutputCallback LookupModuleOutput,
DiagnosticConsumer &DiagConsumer);
/// Collect the module dependency in P1689 format for C++20 named modules.
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index 861e6e0042265..8d580012aae78 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -27,32 +27,6 @@
using namespace clang;
using namespace dependencies;
-namespace {
-/// Forwards the gatherered dependencies to the consumer.
-class DependencyConsumerForwarder : public DependencyFileGenerator {
-public:
- DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
- StringRef WorkingDirectory, DependencyConsumer &C)
- : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
- Opts(std::move(Opts)), C(C) {}
-
- void finishedMainFile(DiagnosticsEngine &Diags) override {
- C.handleDependencyOutputOpts(*Opts);
- llvm::SmallString<256> CanonPath;
- for (const auto &File : getDependencies()) {
- CanonPath = File;
- llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
- llvm::sys::path::make_absolute(WorkingDirectory, CanonPath);
- C.handleFileDependency(CanonPath);
- }
- }
-
-private:
- StringRef WorkingDirectory;
- std::unique_ptr<DependencyOutputOptions> Opts;
- DependencyConsumer &C;
-};
-
static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
const HeaderSearchOptions &ExistingHSOpts,
DiagnosticsEngine *Diags,
@@ -77,6 +51,8 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
return false;
}
+namespace {
+
using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
/// A listener that collects the imported modules and the input
@@ -527,31 +503,18 @@ createDependencyOutputOptions(const CompilerInvocation &Invocation) {
return Opts;
}
-std::shared_ptr<ModuleDepCollector>
-dependencies::initializeScanInstanceDependencyCollector(
+static std::shared_ptr<ModuleDepCollector>
+initializeScanInstanceDependencyCollector(
CompilerInstance &ScanInstance,
std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
- StringRef WorkingDirectory, DependencyConsumer &Consumer,
- DependencyScanningService &Service, CompilerInvocation &Inv,
- DependencyActionController &Controller,
+ DependencyConsumer &Consumer, DependencyScanningService &Service,
+ CompilerInvocation &Inv, DependencyActionController &Controller,
PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
- llvm::SmallVector<StringRef> &StableDirs) {
- std::shared_ptr<ModuleDepCollector> MDC;
- switch (Service.getOpts().Format) {
- case ScanningOutputFormat::Make:
- ScanInstance.addDependencyCollector(
- std::make_shared<DependencyConsumerForwarder>(
- std::move(DepOutputOpts), WorkingDirectory, Consumer));
- break;
- case ScanningOutputFormat::P1689:
- case ScanningOutputFormat::Full:
- MDC = std::make_shared<ModuleDepCollector>(
- Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller,
- Inv, std::move(PrebuiltModulesASTMap), StableDirs);
- ScanInstance.addDependencyCollector(MDC);
- break;
- }
-
+ SmallVector<StringRef> &StableDirs) {
+ auto MDC = std::make_shared<ModuleDepCollector>(
+ Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller,
+ Inv, std::move(PrebuiltModulesASTMap), StableDirs);
+ ScanInstance.addDependencyCollector(MDC);
return MDC;
}
@@ -790,9 +753,8 @@ bool DependencyScanningAction::runInvocation(
auto DepOutputOpts = createDependencyOutputOptions(*OriginalInvocation);
MDC = initializeScanInstanceDependencyCollector(
- ScanInstance, std::move(DepOutputOpts), WorkingDirectory, Consumer,
- Service, *OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap,
- StableDirs);
+ ScanInstance, std::move(DepOutputOpts), Consumer, Service,
+ *OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap, StableDirs);
std::unique_ptr<FrontendAction> Action;
@@ -901,7 +863,7 @@ bool CompilerInstanceWithContext::computeDependencies(
});
auto MDC = initializeScanInstanceDependencyCollector(
- CI, std::make_unique<DependencyOutputOptions>(*OutputOpts), CWD, Consumer,
+ CI, std::make_unique<DependencyOutputOptions>(*OutputOpts), Consumer,
Worker.Service,
/* The MDC's constructor makes a copy of the OriginalInvocation, so
we can pass it in without worrying that it might be changed across
diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index 46a0f79bfd38e..ef56c894657e6 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -549,6 +549,13 @@ void ModuleDepCollectorPP::LexedFileChanged(FileID FID,
MDC.addFileDep(llvm::sys::path::remove_leading_dotslash(*Filename));
}
+void ModuleDepCollectorPP::HasInclude(SourceLocation Loc, StringRef FileName,
+ bool IsAngled, OptionalFileEntryRef File,
+ SrcMgr::CharacteristicKind FileType) {
+ if (File)
+ MDC.addFileDep(File->getName());
+}
+
void ModuleDepCollectorPP::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
@@ -950,12 +957,9 @@ void ModuleDepCollector::addVisibleModules() {
static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
SmallVectorImpl<char> &Storage) {
- if (llvm::sys::path::is_absolute(Path) &&
- !llvm::sys::path::is_style_windows(llvm::sys::path::Style::native))
- return Path;
Storage.assign(Path.begin(), Path.end());
CI.getFileManager().makeAbsolutePath(Storage);
- llvm::sys::path::make_preferred(Storage);
+ llvm::sys::path::remove_dots(Storage, /*remove_dot_dot=*/true);
return StringRef(Storage.data(), Storage.size());
}
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp
index 2ae149e8fb2cf..56f4e56fd13a1 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -40,7 +40,11 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
// set of deps, and handleFileDependency handles enough for implicitly
// built modules to work.
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
- void handleModuleDependency(ModuleDeps MD) override {}
+ void handleModuleDependency(ModuleDeps MD) override {
+ MD.forEachFileDep([this](StringRef File) {
+ ModuleDependencies.push_back(std::string(File));
+ });
+ }
void handleDirectModuleDependency(ModuleID ID) override {}
void handleVisibleModule(std::string ModuleName) override {}
void handleContextHash(std::string Hash) override {}
@@ -51,10 +55,13 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
class DependencyPrinter : public DependencyFileGenerator {
public:
DependencyPrinter(DependencyOutputOptions &Opts,
- ArrayRef<std::string> Dependencies)
- : DependencyFileGenerator(Opts) {
+ ArrayRef<std::string> Dependencies,
+ ArrayRef<std::string> ModuleDependencies)
+ : DependencyFileGenerator(Opts) {
for (const auto &Dep : Dependencies)
addDependency(Dep);
+ for (const auto &Dep : ModuleDependencies)
+ addDependency(Dep);
}
void printDependencies(std::string &S) {
@@ -63,13 +70,14 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
}
};
- DependencyPrinter Generator(*Opts, Dependencies);
+ DependencyPrinter Generator(*Opts, Dependencies, ModuleDependencies);
Generator.printDependencies(S);
}
protected:
std::unique_ptr<DependencyOutputOptions> Opts;
std::vector<std::string> Dependencies;
+ std::vector<std::string> ModuleDependencies;
};
} // anonymous namespace
@@ -182,12 +190,12 @@ bool tooling::computeDependencies(
Controller, DiagConsumer, OverlayFS);
}
-std::optional<std::string>
-DependencyScanningTool::getDependencyFile(ArrayRef<std::string> CommandLine,
- StringRef CWD,
- DiagnosticConsumer &DiagConsumer) {
+std::optional<std::string> DependencyScanningTool::getDependencyFile(
+ ArrayRef<std::string> CommandLine, StringRef CWD,
+ LookupModuleOutputCallback LookupModuleOutput,
+ DiagnosticConsumer &DiagConsumer) {
MakeDependencyPrinterConsumer DepConsumer;
- CallbackActionController Controller(nullptr);
+ CallbackActionController Controller(LookupModuleOutput);
if (!computeDependencies(Worker, CWD, CommandLine, DepConsumer, Controller,
DiagConsumer))
return std::nullopt;
diff --git a/clang/test/ClangScanDeps/modules-cc1.cpp b/clang/test/ClangScanDeps/modules-cc1.cpp
index 04a365249f379..28fc020847d56 100644
--- a/clang/test/ClangScanDeps/modules-cc1.cpp
+++ b/clang/test/ClangScanDeps/modules-cc1.cpp
@@ -16,7 +16,7 @@ module header1 { header "header.h" }
[{
"file": "DIR/modules_cc1.cpp",
"directory": "DIR",
- "command": "clang -cc1 DIR/modules_cc1.cpp -fimplicit-module-maps -o modules_cc1.o"
+ "command": "clang -cc1 DIR/modules_cc1.cpp -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -o modules_cc1.o"
}]
// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
diff --git a/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c b/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
index 022c59ca65db2..f7f804794ab41 100644
--- a/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
+++ b/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
@@ -65,7 +65,8 @@ module Dependency { header "dependency.h" }
// CHECK-NEXT: "command-line": [
// CHECK: ],
// CHECK: "file-deps": [
-// CHECK-NEXT: "[[PREFIX]]/tu.c"
+// CHECK-NEXT: "[[PREFIX]]/tu.c",
+// CHECK-NEXT: "[[PREFIX]]/frameworks/FW.framework/PrivateHeaders/B.h"
// CHECK-NEXT: ],
// CHECK-NEXT: "input-file": "[[PREFIX]]/tu.c"
// CHECK-NEXT: }
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 30fb8d47f8d5d..7f909ae34e595 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -1009,8 +1009,8 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
// Run the tool on it.
if (Format == ScanningOutputFormat::Make) {
- auto MaybeFile =
- WorkerTool.getDependencyFile(Input->CommandLine, CWD, DiagConsumer);
+ auto MaybeFile = WorkerTool.getDependencyFile(
+ Input->CommandLine, CWD, LookupOutput, DiagConsumer);
handleDiagnostics(Filename, S, Errs);
if (MaybeFile)
DependencyOS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
diff --git a/clang/unittests/Tooling/DependencyScannerTest.cpp b/clang/unittests/Tooling/DependencyScannerTest.cpp
index 79fd5a312d2b9..86d4e0ee1b8c5 100644
--- a/clang/unittests/Tooling/DependencyScannerTest.cpp
+++ b/clang/unittests/Tooling/DependencyScannerTest.cpp
@@ -235,8 +235,9 @@ TEST(DependencyScanner, ScanDepsWithFS) {
DependencyScanningTool ScanTool(Service);
TextDiagnosticBuffer DiagConsumer;
- std::optional<std::string> DepFile =
- ScanTool.getDependencyFile(CommandLine, CWD, DiagConsumer);
+ std::optional<std::string> DepFile = ScanTool.getDependencyFile(
+ CommandLine, CWD, CallbackActionController::lookupUnreachableModuleOutput,
+ DiagConsumer);
ASSERT_TRUE(DepFile.has_value());
EXPECT_EQ(llvm::sys::path::convert_to_slash(*DepFile),
"test.cpp.o: /root/test.cpp /root/header.h\n");
@@ -297,8 +298,9 @@ TEST(DependencyScanner, ScanDepsWithModuleLookup) {
// matter, the point of the test is to check that files are not read
// unnecessarily.
TextDiagnosticBuffer DiagConsumer;
- std::optional<std::string> DepFile =
- ScanTool.getDependencyFile(CommandLine, CWD, DiagConsumer);
+ std::optional<std::string> DepFile = ScanTool.getDependencyFile(
+ CommandLine, CWD, CallbackActionController::lookupUnreachableModuleOutput,
+ DiagConsumer);
ASSERT_FALSE(DepFile.has_value());
EXPECT_TRUE(!llvm::is_contained(InterceptFS->StatPaths, OtherPath));
>From de42f7769b6208108493ff7cfdbeff5d55ae0e35 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Wed, 18 Feb 2026 09:04:11 -0800
Subject: [PATCH 2/2] clang-format
---
clang/lib/Tooling/DependencyScanningTool.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp
index 56f4e56fd13a1..082417eba3b20 100644
--- a/clang/lib/Tooling/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanningTool.cpp
@@ -57,7 +57,7 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
DependencyPrinter(DependencyOutputOptions &Opts,
ArrayRef<std::string> Dependencies,
ArrayRef<std::string> ModuleDependencies)
- : DependencyFileGenerator(Opts) {
+ : DependencyFileGenerator(Opts) {
for (const auto &Dep : Dependencies)
addDependency(Dep);
for (const auto &Dep : ModuleDependencies)
More information about the cfe-commits
mailing list