Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 7a52b93

Browse files
[DependencyScanning] Add ability to scan TU with a buffer input (llvm#125111)
Update Dependency scanner so it can scan the dependency of a TU with a provided buffer rather than relying on the on disk file system to provide the input file.
1 parent c67148d commit 7a52b93

File tree

7 files changed

+306
-72
lines changed

7 files changed

+306
-72
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,17 @@ class DependencyScanningTool {
128128
/// \param LookupModuleOutput This function is called to fill in
129129
/// "-fmodule-file=", "-o" and other output
130130
/// arguments for dependencies.
131+
/// \param TUBuffer Optional memory buffer for translation unit input. If
132+
/// TUBuffer is nullopt, the input should be included in the
133+
/// Commandline already.
131134
///
132135
/// \returns a \c StringError with the diagnostic output if clang errors
133136
/// occurred, \c TranslationUnitDeps otherwise.
134-
llvm::Expected<TranslationUnitDeps>
135-
getTranslationUnitDependencies(const std::vector<std::string> &CommandLine,
136-
StringRef CWD,
137-
const llvm::DenseSet<ModuleID> &AlreadySeen,
138-
LookupModuleOutputCallback LookupModuleOutput);
137+
llvm::Expected<TranslationUnitDeps> getTranslationUnitDependencies(
138+
const std::vector<std::string> &CommandLine, StringRef CWD,
139+
const llvm::DenseSet<ModuleID> &AlreadySeen,
140+
LookupModuleOutputCallback LookupModuleOutput,
141+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
139142

140143
/// Given a compilation context specified via the Clang driver command-line,
141144
/// gather modular dependencies of module with the given name, and return the

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
1818
#include "llvm/Support/Error.h"
1919
#include "llvm/Support/FileSystem.h"
20+
#include "llvm/Support/MemoryBufferRef.h"
2021
#include <optional>
2122
#include <string>
2223

@@ -83,9 +84,21 @@ class DependencyScanningWorker {
8384
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
8485

8586
/// Run the dependency scanning tool for a given clang driver command-line,
86-
/// and report the discovered dependencies to the provided consumer. If \p
87-
/// ModuleName isn't empty, this function reports the dependencies of module
88-
/// \p ModuleName.
87+
/// and report the discovered dependencies to the provided consumer. If
88+
/// TUBuffer is not nullopt, it is used as TU input for the dependency
89+
/// scanning. Otherwise, the input should be included as part of the
90+
/// command-line.
91+
///
92+
/// \returns false if clang errors occurred (with diagnostics reported to
93+
/// \c DiagConsumer), true otherwise.
94+
bool computeDependencies(
95+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
96+
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
97+
DiagnosticConsumer &DiagConsumer,
98+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
99+
100+
/// Run the dependency scanning tool for a given clang driver command-line
101+
/// for a specific module.
89102
///
90103
/// \returns false if clang errors occurred (with diagnostics reported to
91104
/// \c DiagConsumer), true otherwise.
@@ -94,13 +107,28 @@ class DependencyScanningWorker {
94107
DependencyConsumer &DepConsumer,
95108
DependencyActionController &Controller,
96109
DiagnosticConsumer &DiagConsumer,
97-
std::optional<StringRef> ModuleName = std::nullopt);
110+
StringRef ModuleName);
111+
112+
/// Run the dependency scanning tool for a given clang driver command-line
113+
/// for a specific translation unit via file system or memory buffer.
114+
///
98115
/// \returns A \c StringError with the diagnostic output if clang errors
99116
/// occurred, success otherwise.
100117
llvm::Error computeDependencies(
101118
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
102119
DependencyConsumer &Consumer, DependencyActionController &Controller,
103-
std::optional<StringRef> ModuleName = std::nullopt);
120+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
121+
122+
/// Run the dependency scanning tool for a given clang driver command-line
123+
/// for a specific module.
124+
///
125+
/// \returns A \c StringError with the diagnostic output if clang errors
126+
/// occurred, success otherwise.
127+
llvm::Error computeDependencies(StringRef WorkingDirectory,
128+
const std::vector<std::string> &CommandLine,
129+
DependencyConsumer &Consumer,
130+
DependencyActionController &Controller,
131+
StringRef ModuleName);
104132

105133
bool shouldEagerLoadModules() const { return EagerLoadModules; }
106134

@@ -121,6 +149,15 @@ class DependencyScanningWorker {
121149
ScanningOptimizations OptimizeArgs;
122150
/// Whether to set up command-lines to load PCM files eagerly.
123151
bool EagerLoadModules;
152+
153+
/// Private helper functions.
154+
bool scanDependencies(StringRef WorkingDirectory,
155+
const std::vector<std::string> &CommandLine,
156+
DependencyConsumer &Consumer,
157+
DependencyActionController &Controller,
158+
DiagnosticConsumer &DC,
159+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
160+
std::optional<StringRef> ModuleName);
124161
};
125162

126163
} // end namespace dependencies

clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,13 @@ llvm::Expected<TranslationUnitDeps>
142142
DependencyScanningTool::getTranslationUnitDependencies(
143143
const std::vector<std::string> &CommandLine, StringRef CWD,
144144
const llvm::DenseSet<ModuleID> &AlreadySeen,
145-
LookupModuleOutputCallback LookupModuleOutput) {
145+
LookupModuleOutputCallback LookupModuleOutput,
146+
std::optional<llvm::MemoryBufferRef> TUBuffer) {
146147
FullDependencyConsumer Consumer(AlreadySeen);
147148
CallbackActionController Controller(LookupModuleOutput);
148-
llvm::Error Result =
149-
Worker.computeDependencies(CWD, CommandLine, Consumer, Controller);
149+
llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer,
150+
Controller, TUBuffer);
151+
150152
if (Result)
151153
return std::move(Result);
152154
return Consumer.takeTranslationUnitDeps();

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 111 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
2525
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
2626
#include "clang/Tooling/Tooling.h"
27+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2728
#include "llvm/ADT/ScopeExit.h"
2829
#include "llvm/Support/Allocator.h"
2930
#include "llvm/Support/Error.h"
31+
#include "llvm/Support/MemoryBuffer.h"
3032
#include "llvm/TargetParser/Host.h"
3133
#include <optional>
3234

@@ -521,20 +523,43 @@ DependencyScanningWorker::DependencyScanningWorker(
521523
}
522524
}
523525

524-
llvm::Error DependencyScanningWorker::computeDependencies(
525-
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
526-
DependencyConsumer &Consumer, DependencyActionController &Controller,
527-
std::optional<StringRef> ModuleName) {
526+
static std::unique_ptr<DiagnosticOptions>
527+
createDiagOptions(const std::vector<std::string> &CommandLine) {
528528
std::vector<const char *> CLI;
529529
for (const std::string &Arg : CommandLine)
530530
CLI.push_back(Arg.c_str());
531531
auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
532532
sanitizeDiagOpts(*DiagOpts);
533+
return DiagOpts;
534+
}
535+
536+
llvm::Error DependencyScanningWorker::computeDependencies(
537+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
538+
DependencyConsumer &Consumer, DependencyActionController &Controller,
539+
std::optional<llvm::MemoryBufferRef> TUBuffer) {
540+
// Capture the emitted diagnostics and report them to the client
541+
// in the case of a failure.
542+
std::string DiagnosticOutput;
543+
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
544+
auto DiagOpts = createDiagOptions(CommandLine);
545+
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
533546

547+
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
548+
DiagPrinter, TUBuffer))
549+
return llvm::Error::success();
550+
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
551+
llvm::inconvertibleErrorCode());
552+
}
553+
554+
llvm::Error DependencyScanningWorker::computeDependencies(
555+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
556+
DependencyConsumer &Consumer, DependencyActionController &Controller,
557+
StringRef ModuleName) {
534558
// Capture the emitted diagnostics and report them to the client
535559
// in the case of a failure.
536560
std::string DiagnosticOutput;
537561
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
562+
auto DiagOpts = createDiagOptions(CommandLine);
538563
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
539564

540565
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
@@ -604,54 +629,22 @@ static bool createAndRunToolInvocation(
604629
return true;
605630
}
606631

607-
bool DependencyScanningWorker::computeDependencies(
632+
bool DependencyScanningWorker::scanDependencies(
608633
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
609634
DependencyConsumer &Consumer, DependencyActionController &Controller,
610-
DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
611-
// Reset what might have been modified in the previous worker invocation.
612-
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
613-
614-
std::optional<std::vector<std::string>> ModifiedCommandLine;
615-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
616-
617-
// If we're scanning based on a module name alone, we don't expect the client
618-
// to provide us with an input file. However, the driver really wants to have
619-
// one. Let's just make it up to make the driver happy.
620-
if (ModuleName) {
621-
auto OverlayFS =
622-
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
623-
auto InMemoryFS =
624-
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
625-
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
626-
OverlayFS->pushOverlay(InMemoryFS);
627-
ModifiedFS = OverlayFS;
628-
629-
SmallString<128> FakeInputPath;
630-
// TODO: We should retry the creation if the path already exists.
631-
llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
632-
FakeInputPath,
633-
/*MakeAbsolute=*/false);
634-
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
635-
636-
ModifiedCommandLine = CommandLine;
637-
ModifiedCommandLine->emplace_back(FakeInputPath);
638-
}
639-
640-
const std::vector<std::string> &FinalCommandLine =
641-
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
642-
auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
643-
635+
DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
636+
std::optional<StringRef> ModuleName) {
644637
auto FileMgr =
645-
llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
638+
llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FS);
646639

647-
std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
648-
llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
640+
std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
641+
llvm::transform(CommandLine, CCommandLine.begin(),
649642
[](const std::string &Str) { return Str.c_str(); });
650-
651-
auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
643+
auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
652644
sanitizeDiagOpts(*DiagOpts);
653645
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
654-
CompilerInstance::createDiagnostics(*FinalFS, DiagOpts.release(), &DC,
646+
CompilerInstance::createDiagnostics(FileMgr->getVirtualFileSystem(),
647+
DiagOpts.release(), &DC,
655648
/*ShouldOwnClient=*/false);
656649

657650
// Although `Diagnostics` are used only for command-line parsing, the
@@ -667,12 +660,12 @@ bool DependencyScanningWorker::computeDependencies(
667660
DisableFree, ModuleName);
668661

669662
bool Success = false;
670-
if (FinalCommandLine[1] == "-cc1") {
671-
Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
663+
if (CommandLine[1] == "-cc1") {
664+
Success = createAndRunToolInvocation(CommandLine, Action, *FileMgr,
672665
PCHContainerOps, *Diags, Consumer);
673666
} else {
674667
Success = forEachDriverJob(
675-
FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
668+
CommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
676669
if (StringRef(Cmd.getCreator().getName()) != "clang") {
677670
// Non-clang command. Just pass through to the dependency
678671
// consumer.
@@ -699,8 +692,77 @@ bool DependencyScanningWorker::computeDependencies(
699692

700693
if (Success && !Action.hasScanned())
701694
Diags->Report(diag::err_fe_expected_compiler_job)
702-
<< llvm::join(FinalCommandLine, " ");
695+
<< llvm::join(CommandLine, " ");
703696
return Success && Action.hasScanned();
704697
}
705698

699+
bool DependencyScanningWorker::computeDependencies(
700+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
701+
DependencyConsumer &Consumer, DependencyActionController &Controller,
702+
DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
703+
// Reset what might have been modified in the previous worker invocation.
704+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
705+
706+
std::optional<std::vector<std::string>> ModifiedCommandLine;
707+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
708+
709+
// If we're scanning based on a module name alone, we don't expect the client
710+
// to provide us with an input file. However, the driver really wants to have
711+
// one. Let's just make it up to make the driver happy.
712+
if (TUBuffer) {
713+
auto OverlayFS =
714+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
715+
auto InMemoryFS =
716+
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
717+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
718+
auto InputPath = TUBuffer->getBufferIdentifier();
719+
InMemoryFS->addFile(
720+
InputPath, 0,
721+
llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
722+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay =
723+
InMemoryFS;
724+
725+
OverlayFS->pushOverlay(InMemoryOverlay);
726+
ModifiedFS = OverlayFS;
727+
ModifiedCommandLine = CommandLine;
728+
ModifiedCommandLine->emplace_back(InputPath);
729+
}
730+
731+
const std::vector<std::string> &FinalCommandLine =
732+
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
733+
auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
734+
735+
return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
736+
Controller, DC, FinalFS, /*ModuleName=*/std::nullopt);
737+
}
738+
739+
bool DependencyScanningWorker::computeDependencies(
740+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
741+
DependencyConsumer &Consumer, DependencyActionController &Controller,
742+
DiagnosticConsumer &DC, StringRef ModuleName) {
743+
// Reset what might have been modified in the previous worker invocation.
744+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
745+
746+
// If we're scanning based on a module name alone, we don't expect the client
747+
// to provide us with an input file. However, the driver really wants to have
748+
// one. Let's just make it up to make the driver happy.
749+
auto OverlayFS =
750+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
751+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
752+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
753+
SmallString<128> FakeInputPath;
754+
// TODO: We should retry the creation if the path already exists.
755+
llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
756+
/*MakeAbsolute=*/false);
757+
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
758+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
759+
760+
OverlayFS->pushOverlay(InMemoryOverlay);
761+
auto ModifiedCommandLine = CommandLine;
762+
ModifiedCommandLine.emplace_back(FakeInputPath);
763+
764+
return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
765+
Controller, DC, OverlayFS, ModuleName);
766+
}
767+
706768
DependencyActionController::~DependencyActionController() {}

0 commit comments

Comments
 (0)