Skip to content
5 changes: 5 additions & 0 deletions clang/include/clang-c/Dependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,11 @@ const char *clang_experimental_DepGraph_getTUContextHash(CXDepGraph);
CINDEX_LINKAGE
CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph);

CINDEX_LINKAGE
CXCStringArray
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
CXDependencyScannerService);

/**
* @}
*/
Expand Down
1 change: 1 addition & 0 deletions clang/test/ClangScanDeps/error-c-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

// CHECK: error: failed to get dependencies
// CHECK-NEXT: 'missing.h' file not found
// CHECK-NEXT: number of invalid negatively stat cached paths: 0
8 changes: 8 additions & 0 deletions clang/tools/c-index-test/core_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,14 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
clang_disposeString(Spelling);
clang_disposeDiagnostic(Diag);
}

CXCStringArray InvalidNegativeStatCachedPaths =
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
Service);

llvm::errs() << "note: number of invalid negatively stat cached paths: "
<< InvalidNegativeStatCachedPaths.Count << "\n";

return 1;
}

Expand Down
135 changes: 86 additions & 49 deletions clang/tools/libclang/CDependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,57 @@ struct DependencyScannerServiceOptions {

ScanningOutputFormat getFormat() const;
};

struct CStringsManager {
SmallVector<std::unique_ptr<std::vector<const char *>>> OwnedCStr;
SmallVector<std::unique_ptr<std::vector<std::string>>> OwnedStdStr;

/// Doesn't own the string contents.
CXCStringArray createCStringsRef(ArrayRef<std::string> Strings) {
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
std::vector<const char *> &CStrings = *OwnedCStr.back();
CStrings.reserve(Strings.size());
for (const auto &String : Strings)
CStrings.push_back(String.c_str());
return {CStrings.data(), CStrings.size()};
}

/// Doesn't own the string contents.
CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) {
std::vector<StringRef> Strings;

for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
SI != SE; ++SI)
Strings.push_back(SI->getKey());

llvm::sort(Strings);

OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
std::vector<const char *> &CStrings = *OwnedCStr.back();
CStrings.reserve(Strings.size());
for (const auto &String : Strings)
CStrings.push_back(String.data());
return {CStrings.data(), CStrings.size()};
}

/// Gets ownership of string contents.
CXCStringArray createCStringsOwned(std::vector<std::string> &&Strings) {
OwnedStdStr.push_back(
std::make_unique<std::vector<std::string>>(std::move(Strings)));
return createCStringsRef(*OwnedStdStr.back());
}
};

struct DependencyScannerService {
DependencyScanningService Service;
CStringsManager StrMgr{};
};
} // end anonymous namespace

DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerServiceOptions,
CXDependencyScannerServiceOptions)

DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningService,
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerService,
CXDependencyScannerService)
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningWorker,
CXDependencyScannerWorker)
Expand Down Expand Up @@ -127,9 +172,9 @@ clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format) {
// FIXME: Pass default CASOpts and nullptr as CachingOnDiskFileSystem now.
CASOptions CASOpts;
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> FS;
return wrap(new DependencyScanningService(
return wrap(new DependencyScannerService{DependencyScanningService(
ScanningMode::DependencyDirectivesScan, unwrap(Format), CASOpts,
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS));
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS)});
}

ScanningOutputFormat DependencyScannerServiceOptions::getFormat() const {
Expand Down Expand Up @@ -165,10 +210,10 @@ clang_experimental_DependencyScannerService_create_v1(
FS = llvm::cantFail(
llvm::cas::createCachingOnDiskFileSystem(CAS));
}
return wrap(new DependencyScanningService(
return wrap(new DependencyScannerService{DependencyScanningService(
ScanningMode::DependencyDirectivesScan, Format, unwrap(Opts)->CASOpts,
std::move(CAS), std::move(Cache), std::move(FS),
unwrap(Opts)->OptimizeArgs));
unwrap(Opts)->OptimizeArgs)});
}

void clang_experimental_DependencyScannerService_dispose_v0(
Expand All @@ -177,17 +222,17 @@ void clang_experimental_DependencyScannerService_dispose_v0(
}

CXDependencyScannerWorker clang_experimental_DependencyScannerWorker_create_v0(
CXDependencyScannerService Service) {
ScanningOutputFormat Format = unwrap(Service)->getFormat();
CXDependencyScannerService S) {
ScanningOutputFormat Format = unwrap(S)->Service.getFormat();
bool IsIncludeTreeOutput = Format == ScanningOutputFormat::IncludeTree ||
Format == ScanningOutputFormat::FullIncludeTree;
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::createPhysicalFileSystem();
if (IsIncludeTreeOutput)
FS = llvm::cas::createCASProvidingFileSystem(unwrap(Service)->getCAS(),
FS = llvm::cas::createCASProvidingFileSystem(unwrap(S)->Service.getCAS(),
std::move(FS));

return wrap(new DependencyScanningWorker(*unwrap(Service), FS));
return wrap(new DependencyScanningWorker(unwrap(S)->Service, FS));
}

void clang_experimental_DependencyScannerWorker_dispose_v0(
Expand Down Expand Up @@ -223,46 +268,6 @@ struct DependencyScannerWorkerScanSettings {
MLO;
};

struct CStringsManager {
SmallVector<std::unique_ptr<std::vector<const char *>>> OwnedCStr;
SmallVector<std::unique_ptr<std::vector<std::string>>> OwnedStdStr;

/// Doesn't own the string contents.
CXCStringArray createCStringsRef(ArrayRef<std::string> Strings) {
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
std::vector<const char *> &CStrings = *OwnedCStr.back();
CStrings.reserve(Strings.size());
for (const auto &String : Strings)
CStrings.push_back(String.c_str());
return {CStrings.data(), CStrings.size()};
}

/// Doesn't own the string contents.
CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) {
std::vector<StringRef> Strings;

for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
SI != SE; ++SI)
Strings.push_back(SI->getKey());

llvm::sort(Strings);

OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
std::vector<const char *> &CStrings = *OwnedCStr.back();
CStrings.reserve(Strings.size());
for (const auto &String : Strings)
CStrings.push_back(String.data());
return {CStrings.data(), CStrings.size()};
}

/// Gets ownership of string contents.
CXCStringArray createCStringsOwned(std::vector<std::string> &&Strings) {
OwnedStdStr.push_back(
std::make_unique<std::vector<std::string>>(std::move(Strings)));
return createCStringsRef(*OwnedStdStr.back());
}
};

struct DependencyGraph {
TranslationUnitDeps TUDeps;
SmallString<256> SerialDiagBuf;
Expand Down Expand Up @@ -561,6 +566,38 @@ CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph Graph) {
return unwrap(Graph)->getDiagnosticSet();
}

CXCStringArray
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
CXDependencyScannerService S) {
DependencyScanningService &Service = unwrap(S)->Service;
CStringsManager &StrMgr = unwrap(S)->StrMgr;

// FIXME: CAS currently does not use the shared cache, and cannot produce
// the same diagnostics. We should add such a diagnostics to CAS as well.
if (Service.useCASFS())
return {nullptr, 0};

DependencyScanningFilesystemSharedCache &SharedCache =
Service.getSharedCache();

// Note that it is critical that this FS is the same as the default virtual
// file system we pass to the DependencyScanningWorkers.
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::createPhysicalFileSystem();

auto InvaidNegStatCachedPaths =
SharedCache.getInvalidNegativeStatCachedPaths(*FS);

// FIXME: This code here creates copies of strings from
// InvaidNegStatCachedPaths. It is acceptable because this C-API is expected
// to be called only at the end of a CXDependencyScannerService's lifetime.
// In other words, it is called very infrequently. We can change
// CStringsManager's interface to accommodate handling arbitrary StringRefs
// (which may not be null terminated) if we want to avoid copying.
return StrMgr.createCStringsOwned(
{InvaidNegStatCachedPaths.begin(), InvaidNegStatCachedPaths.end()});
}

static std::string
lookupModuleOutput(const ModuleDeps &MD, ModuleOutputKind MOK, void *MLOContext,
std::variant<CXModuleLookupOutputCallback *,
Expand Down
10 changes: 5 additions & 5 deletions clang/tools/libclang/CXString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ CXStringSet *createSet(const std::vector<std::string> &Strings) {

CXStringSet *createSet(const llvm::StringSet<> &StringsUnordered) {
std::vector<StringRef> Strings;
for (auto SI = StringsUnordered.begin(),
SE = StringsUnordered.end(); SI != SE; ++SI)

for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
SI != SE; ++SI)
Strings.push_back(SI->getKey());

llvm::sort(Strings);

CXStringSet *Set = new CXStringSet;
Set->Count = Strings.size();
Set->Strings = new CXString[Set->Count];
Expand Down
1 change: 1 addition & 0 deletions clang/tools/libclang/libclang.map
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ LLVM_21 {
clang_experimental_DepGraphModule_isCWDIgnored;
clang_experimental_DepGraphModule_isInStableDirs;
clang_getFullyQualifiedName;
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths;
};

# Example of how to add a new symbol version entry. If you do add a new symbol
Expand Down