Skip to content

Commit 81070b4

Browse files
[DependencyScanning] Add ability to scan TU with a buffer input
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 6a23220 commit 81070b4

File tree

8 files changed

+326
-84
lines changed

8 files changed

+326
-84
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,17 @@ class DependencyScanningTool {
174174
/// \param LookupModuleOutput This function is called to fill in
175175
/// "-fmodule-file=", "-o" and other output
176176
/// arguments for dependencies.
177+
/// \param TUBuffer Optional memory buffer for translation unit input. If
178+
/// TUBuffer is nullopt, the input should be included in the
179+
/// Commandline already.
177180
///
178181
/// \returns a \c StringError with the diagnostic output if clang errors
179182
/// occurred, \c TranslationUnitDeps otherwise.
180-
llvm::Expected<TranslationUnitDeps>
181-
getTranslationUnitDependencies(const std::vector<std::string> &CommandLine,
182-
StringRef CWD,
183-
const llvm::DenseSet<ModuleID> &AlreadySeen,
184-
LookupModuleOutputCallback LookupModuleOutput);
183+
llvm::Expected<TranslationUnitDeps> getTranslationUnitDependencies(
184+
const std::vector<std::string> &CommandLine, StringRef CWD,
185+
const llvm::DenseSet<ModuleID> &AlreadySeen,
186+
LookupModuleOutputCallback LookupModuleOutput,
187+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
185188

186189
/// Given a compilation context specified via the Clang driver command-line,
187190
/// 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
@@ -18,6 +18,7 @@
1818
#include "llvm/CAS/CachingOnDiskFileSystem.h"
1919
#include "llvm/Support/Error.h"
2020
#include "llvm/Support/FileSystem.h"
21+
#include "llvm/Support/MemoryBufferRef.h"
2122
#include <optional>
2223
#include <string>
2324

@@ -119,9 +120,21 @@ class DependencyScanningWorker {
119120
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
120121

121122
/// Run the dependency scanning tool for a given clang driver command-line,
122-
/// and report the discovered dependencies to the provided consumer. If \p
123-
/// ModuleName isn't empty, this function reports the dependencies of module
124-
/// \p ModuleName.
123+
/// and report the discovered dependencies to the provided consumer. If
124+
/// TUBuffer is not nullopt, it is used as TU input for the dependency
125+
/// scanning. Otherwise, the input should be included as part of the
126+
/// command-line.
127+
///
128+
/// \returns false if clang errors occurred (with diagnostics reported to
129+
/// \c DiagConsumer), true otherwise.
130+
bool computeDependencies(
131+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
132+
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
133+
DiagnosticConsumer &DiagConsumer,
134+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
135+
136+
/// Run the dependency scanning tool for a given clang driver command-line
137+
/// for a specific module.
125138
///
126139
/// \returns false if clang errors occurred (with diagnostics reported to
127140
/// \c DiagConsumer), true otherwise.
@@ -130,13 +143,28 @@ class DependencyScanningWorker {
130143
DependencyConsumer &DepConsumer,
131144
DependencyActionController &Controller,
132145
DiagnosticConsumer &DiagConsumer,
133-
std::optional<StringRef> ModuleName = std::nullopt);
146+
StringRef ModuleName);
147+
148+
/// Run the dependency scanning tool for a given clang driver command-line
149+
/// for a specific translation unit via file system or memory buffer.
150+
///
134151
/// \returns A \c StringError with the diagnostic output if clang errors
135152
/// occurred, success otherwise.
136153
llvm::Error computeDependencies(
137154
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
138155
DependencyConsumer &Consumer, DependencyActionController &Controller,
139-
std::optional<StringRef> ModuleName = std::nullopt);
156+
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
157+
158+
/// Run the dependency scanning tool for a given clang driver command-line
159+
/// for a specific module.
160+
///
161+
/// \returns A \c StringError with the diagnostic output if clang errors
162+
/// occurred, success otherwise.
163+
llvm::Error computeDependencies(StringRef WorkingDirectory,
164+
const std::vector<std::string> &CommandLine,
165+
DependencyConsumer &Consumer,
166+
DependencyActionController &Controller,
167+
StringRef ModuleName);
140168

141169
/// Scan from a compiler invocation.
142170
/// If \p DiagGenerationAsCompilation is true it will generate error
@@ -186,6 +214,15 @@ class DependencyScanningWorker {
186214
llvm::IntrusiveRefCntPtr<DependencyScanningCASFilesystem> DepCASFS;
187215
CASOptions CASOpts;
188216
std::shared_ptr<cas::ObjectStore> CAS;
217+
218+
/// Private helper functions.
219+
bool scanDependencies(StringRef WorkingDirectory,
220+
const std::vector<std::string> &CommandLine,
221+
DependencyConsumer &Consumer,
222+
DependencyActionController &Controller,
223+
DiagnosticConsumer &DC,
224+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
225+
std::optional<StringRef> ModuleName);
189226
};
190227

191228
} // end namespace dependencies

clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,12 @@ llvm::Expected<TranslationUnitDeps>
268268
DependencyScanningTool::getTranslationUnitDependencies(
269269
const std::vector<std::string> &CommandLine, StringRef CWD,
270270
const llvm::DenseSet<ModuleID> &AlreadySeen,
271-
LookupModuleOutputCallback LookupModuleOutput) {
271+
LookupModuleOutputCallback LookupModuleOutput,
272+
std::optional<llvm::MemoryBufferRef> TUBuffer) {
272273
FullDependencyConsumer Consumer(AlreadySeen);
273274
auto Controller = createActionController(LookupModuleOutput);
274-
llvm::Error Result =
275-
Worker.computeDependencies(CWD, CommandLine, Consumer, *Controller);
275+
llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer,
276+
*Controller, TUBuffer);
276277
if (Result)
277278
return std::move(Result);
278279
return Consumer.takeTranslationUnitDeps();

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 119 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
2929
#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h"
3030
#include "clang/Tooling/Tooling.h"
31+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
3132
#include "llvm/ADT/ScopeExit.h"
3233
#include "llvm/CAS/CASProvidingFileSystem.h"
3334
#include "llvm/CAS/CachingOnDiskFileSystem.h"
3435
#include "llvm/CAS/ObjectStore.h"
3536
#include "llvm/Support/Allocator.h"
3637
#include "llvm/Support/Error.h"
38+
#include "llvm/Support/MemoryBuffer.h"
3739
#include "llvm/Support/PrefixMapper.h"
3840
#include "llvm/TargetParser/Host.h"
3941
#include <optional>
@@ -744,20 +746,43 @@ DependencyScanningWorker::getOrCreateFileManager() const {
744746
return new FileManager(FileSystemOptions(), BaseFS);
745747
}
746748

747-
llvm::Error DependencyScanningWorker::computeDependencies(
748-
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
749-
DependencyConsumer &Consumer, DependencyActionController &Controller,
750-
std::optional<StringRef> ModuleName) {
749+
static std::unique_ptr<DiagnosticOptions>
750+
createDiagOptions(const std::vector<std::string> &CommandLine) {
751751
std::vector<const char *> CLI;
752752
for (const std::string &Arg : CommandLine)
753753
CLI.push_back(Arg.c_str());
754754
auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
755755
sanitizeDiagOpts(*DiagOpts);
756+
return DiagOpts;
757+
}
756758

759+
llvm::Error DependencyScanningWorker::computeDependencies(
760+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
761+
DependencyConsumer &Consumer, DependencyActionController &Controller,
762+
std::optional<llvm::MemoryBufferRef> TUBuffer) {
757763
// Capture the emitted diagnostics and report them to the client
758764
// in the case of a failure.
759765
std::string DiagnosticOutput;
760766
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
767+
auto DiagOpts = createDiagOptions(CommandLine);
768+
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
769+
770+
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
771+
DiagPrinter, TUBuffer))
772+
return llvm::Error::success();
773+
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
774+
llvm::inconvertibleErrorCode());
775+
}
776+
777+
llvm::Error DependencyScanningWorker::computeDependencies(
778+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
779+
DependencyConsumer &Consumer, DependencyActionController &Controller,
780+
StringRef ModuleName) {
781+
// Capture the emitted diagnostics and report them to the client
782+
// in the case of a failure.
783+
std::string DiagnosticOutput;
784+
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
785+
auto DiagOpts = createDiagOptions(CommandLine);
761786
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
762787

763788
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
@@ -828,59 +853,18 @@ static bool createAndRunToolInvocation(
828853
return true;
829854
}
830855

831-
bool DependencyScanningWorker::computeDependencies(
856+
bool DependencyScanningWorker::scanDependencies(
832857
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
833858
DependencyConsumer &Consumer, DependencyActionController &Controller,
834-
DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
835-
// Reset what might have been modified in the previous worker invocation.
836-
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
837-
838-
std::optional<std::vector<std::string>> ModifiedCommandLine;
839-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
840-
841-
// If we're scanning based on a module name alone, we don't expect the client
842-
// to provide us with an input file. However, the driver really wants to have
843-
// one. Let's just make it up to make the driver happy.
844-
if (ModuleName) {
845-
auto OverlayFS =
846-
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
847-
auto InMemoryFS =
848-
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
849-
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
850-
851-
SmallString<128> FakeInputPath;
852-
// TODO: We should retry the creation if the path already exists.
853-
llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
854-
FakeInputPath,
855-
/*MakeAbsolute=*/false);
856-
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
857-
858-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay =
859-
InMemoryFS;
860-
// If we are using a CAS but not dependency CASFS, we need to provide the
861-
// fake input file in a CASProvidingFS for include-tree.
862-
if (CAS && !DepCASFS)
863-
InMemoryOverlay =
864-
llvm::cas::createCASProvidingFileSystem(CAS, std::move(InMemoryFS));
865-
866-
OverlayFS->pushOverlay(InMemoryOverlay);
867-
ModifiedFS = OverlayFS;
868-
ModifiedCommandLine = CommandLine;
869-
ModifiedCommandLine->emplace_back(FakeInputPath);
870-
}
871-
872-
const std::vector<std::string> &FinalCommandLine =
873-
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
874-
auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
875-
859+
DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
860+
std::optional<StringRef> ModuleName) {
876861
auto FileMgr =
877-
llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
862+
llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FS);
878863

879-
std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
880-
llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
864+
std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
865+
llvm::transform(CommandLine, CCommandLine.begin(),
881866
[](const std::string &Str) { return Str.c_str(); });
882-
883-
auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
867+
auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
884868
sanitizeDiagOpts(*DiagOpts);
885869
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
886870
CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
@@ -902,12 +886,12 @@ bool DependencyScanningWorker::computeDependencies(
902886
/*DiagGenerationAsCompilation=*/false, getCASOpts(),
903887
ModuleName);
904888
bool Success = false;
905-
if (FinalCommandLine[1] == "-cc1") {
906-
Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
889+
if (CommandLine[1] == "-cc1") {
890+
Success = createAndRunToolInvocation(CommandLine, Action, *FileMgr,
907891
PCHContainerOps, *Diags, Consumer);
908892
} else {
909893
Success = forEachDriverJob(
910-
FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
894+
CommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
911895
if (StringRef(Cmd.getCreator().getName()) != "clang") {
912896
// Non-clang command. Just pass through to the dependency
913897
// consumer.
@@ -935,10 +919,89 @@ bool DependencyScanningWorker::computeDependencies(
935919

936920
if (Success && !Action.hasScanned())
937921
Diags->Report(diag::err_fe_expected_compiler_job)
938-
<< llvm::join(FinalCommandLine, " ");
922+
<< llvm::join(CommandLine, " ");
939923
return Success && Action.hasScanned();
940924
}
941925

926+
bool DependencyScanningWorker::computeDependencies(
927+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
928+
DependencyConsumer &Consumer, DependencyActionController &Controller,
929+
DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
930+
// Reset what might have been modified in the previous worker invocation.
931+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
932+
933+
std::optional<std::vector<std::string>> ModifiedCommandLine;
934+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
935+
936+
// If we're scanning based on a module name alone, we don't expect the client
937+
// to provide us with an input file. However, the driver really wants to have
938+
// one. Let's just make it up to make the driver happy.
939+
if (TUBuffer) {
940+
auto OverlayFS =
941+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
942+
auto InMemoryFS =
943+
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
944+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
945+
auto InputPath = TUBuffer->getBufferIdentifier();
946+
InMemoryFS->addFile(
947+
InputPath, 0,
948+
llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
949+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay =
950+
InMemoryFS;
951+
// If we are using a CAS but not dependency CASFS, we need to provide the
952+
// fake input file in a CASProvidingFS for include-tree.
953+
if (CAS && !DepCASFS)
954+
InMemoryOverlay =
955+
llvm::cas::createCASProvidingFileSystem(CAS, std::move(InMemoryFS));
956+
957+
OverlayFS->pushOverlay(InMemoryOverlay);
958+
ModifiedFS = OverlayFS;
959+
ModifiedCommandLine = CommandLine;
960+
ModifiedCommandLine->emplace_back(InputPath);
961+
}
962+
963+
const std::vector<std::string> &FinalCommandLine =
964+
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
965+
auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
966+
967+
return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
968+
Controller, DC, FinalFS, /*ModuleName=*/std::nullopt);
969+
}
970+
971+
bool DependencyScanningWorker::computeDependencies(
972+
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
973+
DependencyConsumer &Consumer, DependencyActionController &Controller,
974+
DiagnosticConsumer &DC, StringRef ModuleName) {
975+
// Reset what might have been modified in the previous worker invocation.
976+
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
977+
978+
// If we're scanning based on a module name alone, we don't expect the client
979+
// to provide us with an input file. However, the driver really wants to have
980+
// one. Let's just make it up to make the driver happy.
981+
auto OverlayFS =
982+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
983+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
984+
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
985+
SmallString<128> FakeInputPath;
986+
// TODO: We should retry the creation if the path already exists.
987+
llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
988+
/*MakeAbsolute=*/false);
989+
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
990+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
991+
// If we are using a CAS but not dependency CASFS, we need to provide the
992+
// fake input file in a CASProvidingFS for include-tree.
993+
if (CAS && !DepCASFS)
994+
InMemoryOverlay =
995+
llvm::cas::createCASProvidingFileSystem(CAS, std::move(InMemoryFS));
996+
997+
OverlayFS->pushOverlay(InMemoryOverlay);
998+
auto ModifiedCommandLine = CommandLine;
999+
ModifiedCommandLine.emplace_back(FakeInputPath);
1000+
1001+
return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
1002+
Controller, DC, OverlayFS, ModuleName);
1003+
}
1004+
9421005
DependencyActionController::~DependencyActionController() {}
9431006

9441007
void DependencyScanningWorker::computeDependenciesFromCompilerInvocation(

0 commit comments

Comments
 (0)