[libclang] Add clang_ModuleCache_prune (#190067)
This allows a build system to direct Clang to prune a module cache directory using the same method Clang does internally. This also changes `clang::maybePruneImpl` to clean up files directly in the directory, not just subdirectories.
This commit is contained in:
parent
bf50e847fb
commit
b1ef47f459
@ -18,6 +18,7 @@
|
||||
#include "clang-c/CXString.h"
|
||||
#include "clang-c/ExternC.h"
|
||||
#include "clang-c/Platform.h"
|
||||
#include <time.h>
|
||||
|
||||
LLVM_CLANG_C_EXTERN_C_BEGIN
|
||||
|
||||
@ -143,6 +144,23 @@ clang_ModuleMapDescriptor_writeToBuffer(CXModuleMapDescriptor, unsigned options,
|
||||
*/
|
||||
CINDEX_LINKAGE void clang_ModuleMapDescriptor_dispose(CXModuleMapDescriptor);
|
||||
|
||||
/**
|
||||
* Prune module files in the module cache directory that haven't been accessed
|
||||
* in a long time.
|
||||
*
|
||||
* \param Path the path to the module cache directory.
|
||||
*
|
||||
* \param PruneInterval the minimum time in seconds between two prune
|
||||
* operations. If the timestamp file is newer than this, pruning is skipped.
|
||||
*
|
||||
* \param PruneAfter the time in seconds after which unused module files are
|
||||
* removed.
|
||||
*
|
||||
*/
|
||||
CINDEX_LINKAGE void clang_ModuleCache_prune(const char *Path,
|
||||
time_t PruneInterval,
|
||||
time_t PruneAfter);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@ -60,38 +60,48 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
|
||||
// Walk the entire module cache, looking for unused module files and module
|
||||
// indices.
|
||||
std::error_code EC;
|
||||
auto TryPruneFile = [&](StringRef FilePath) {
|
||||
// We only care about module and global module index files.
|
||||
StringRef Filename = llvm::sys::path::filename(FilePath);
|
||||
StringRef Extension = llvm::sys::path::extension(FilePath);
|
||||
if (Extension != ".pcm" && Extension != ".timestamp" &&
|
||||
Filename != "modules.idx")
|
||||
return;
|
||||
|
||||
// Don't prune the pruning timestamp file.
|
||||
if (Filename == "modules.timestamp")
|
||||
return;
|
||||
|
||||
// Look at this file. If we can't stat it, there's nothing interesting
|
||||
// there.
|
||||
if (llvm::sys::fs::status(FilePath, StatBuf))
|
||||
return;
|
||||
|
||||
// If the file has been used recently enough, leave it there.
|
||||
time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime());
|
||||
if (CurrentTime - FileAccessTime <= PruneAfter)
|
||||
return;
|
||||
|
||||
// Remove the file.
|
||||
llvm::sys::fs::remove(FilePath);
|
||||
|
||||
// Remove the timestamp file created by implicit module builds.
|
||||
std::string TimestampFilename = FilePath.str() + ".timestamp";
|
||||
llvm::sys::fs::remove(TimestampFilename);
|
||||
};
|
||||
|
||||
for (llvm::sys::fs::directory_iterator Dir(Path, EC), DirEnd;
|
||||
Dir != DirEnd && !EC; Dir.increment(EC)) {
|
||||
// If we don't have a directory, there's nothing to look into.
|
||||
if (!llvm::sys::fs::is_directory(Dir->path()))
|
||||
// If we don't have a directory, try to prune it as a file in the root.
|
||||
if (!llvm::sys::fs::is_directory(Dir->path())) {
|
||||
TryPruneFile(Dir->path());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Walk all the files within this directory.
|
||||
for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd;
|
||||
File != FileEnd && !EC; File.increment(EC)) {
|
||||
// We only care about module and global module index files.
|
||||
StringRef Extension = llvm::sys::path::extension(File->path());
|
||||
if (Extension != ".pcm" && Extension != ".timestamp" &&
|
||||
llvm::sys::path::filename(File->path()) != "modules.idx")
|
||||
continue;
|
||||
|
||||
// Look at this file. If we can't stat it, there's nothing interesting
|
||||
// there.
|
||||
if (llvm::sys::fs::status(File->path(), StatBuf))
|
||||
continue;
|
||||
|
||||
// If the file has been used recently enough, leave it there.
|
||||
time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime());
|
||||
if (CurrentTime - FileAccessTime <= PruneAfter)
|
||||
continue;
|
||||
|
||||
// Remove the file.
|
||||
llvm::sys::fs::remove(File->path());
|
||||
|
||||
// Remove the timestamp file.
|
||||
std::string TimpestampFilename = File->path() + ".timestamp";
|
||||
llvm::sys::fs::remove(TimpestampFilename);
|
||||
}
|
||||
File != FileEnd && !EC; File.increment(EC))
|
||||
TryPruneFile(File->path());
|
||||
|
||||
// If we removed all the files in the directory, remove the directory
|
||||
// itself.
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#include "clang-c/BuildSystem.h"
|
||||
#include "CXString.h"
|
||||
#include "clang/Serialization/ModuleCache.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/CBindingWrapping.h"
|
||||
#include "llvm/Support/Chrono.h"
|
||||
@ -150,3 +151,9 @@ clang_ModuleMapDescriptor_writeToBuffer(CXModuleMapDescriptor MMD, unsigned,
|
||||
void clang_ModuleMapDescriptor_dispose(CXModuleMapDescriptor MMD) {
|
||||
delete MMD;
|
||||
}
|
||||
|
||||
void clang_ModuleCache_prune(const char *Path, time_t PruneInterval,
|
||||
time_t PruneAfter) {
|
||||
if (Path)
|
||||
clang::maybePruneImpl(Path, PruneInterval, PruneAfter);
|
||||
}
|
||||
|
||||
@ -457,6 +457,11 @@ LLVM_21 {
|
||||
clang_Cursor_isGCCAssemblyVolatile;
|
||||
};
|
||||
|
||||
LLVM_23 {
|
||||
global:
|
||||
clang_ModuleCache_prune;
|
||||
};
|
||||
|
||||
# Example of how to add a new symbol version entry. If you do add a new symbol
|
||||
# version, please update the example to depend on the version you added.
|
||||
# LLVM_X {
|
||||
|
||||
@ -7,9 +7,11 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TestUtils.h"
|
||||
#include "clang-c/BuildSystem.h"
|
||||
#include "clang-c/Index.h"
|
||||
#include "clang-c/Rewrite.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Chrono.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
@ -356,6 +358,70 @@ TEST(libclang, ModuleMapDescriptor) {
|
||||
clang_ModuleMapDescriptor_dispose(MMD);
|
||||
}
|
||||
|
||||
TEST(libclang, ModuleCache_prune) {
|
||||
llvm::SmallString<256> CacheDir;
|
||||
ASSERT_FALSE(
|
||||
llvm::sys::fs::createUniqueDirectory("libclang-module-cache", CacheDir));
|
||||
|
||||
auto writeFile = [](const llvm::Twine &Path, llvm::StringRef Content = "") {
|
||||
std::error_code EC;
|
||||
llvm::raw_fd_ostream OS(Path.str(), EC);
|
||||
ASSERT_FALSE(EC);
|
||||
OS << Content;
|
||||
};
|
||||
|
||||
auto setOldAccessTime = [](const llvm::Twine &Path) {
|
||||
int FD;
|
||||
std::error_code EC = llvm::sys::fs::openFileForReadWrite(
|
||||
Path, FD, llvm::sys::fs::CD_OpenExisting, llvm::sys::fs::OF_None);
|
||||
ASSERT_FALSE(EC);
|
||||
// Set the access time to be old enough to get pruned.
|
||||
time_t OldTime = time(nullptr) - 86400;
|
||||
EC = llvm::sys::fs::setLastAccessAndModificationTime(
|
||||
FD, llvm::sys::toTimePoint(OldTime));
|
||||
ASSERT_FALSE(EC);
|
||||
::close(FD);
|
||||
};
|
||||
|
||||
// Create a subdirectory with a .pcm file and a .timestamp file.
|
||||
llvm::SmallString<256> SubDir(CacheDir);
|
||||
llvm::sys::path::append(SubDir, "ABCDEF");
|
||||
ASSERT_FALSE(llvm::sys::fs::create_directory(SubDir));
|
||||
|
||||
llvm::SmallString<256> PCMFile(SubDir);
|
||||
llvm::sys::path::append(PCMFile, "Foo.pcm");
|
||||
writeFile(PCMFile, "fake pcm");
|
||||
|
||||
// Also create a .pcm file in the root of the cache directory.
|
||||
llvm::SmallString<256> RootPCMFile(CacheDir);
|
||||
llvm::sys::path::append(RootPCMFile, "Bar.pcm");
|
||||
writeFile(RootPCMFile, "fake pcm");
|
||||
|
||||
setOldAccessTime(PCMFile);
|
||||
setOldAccessTime(RootPCMFile);
|
||||
|
||||
// Create the modules.timestamp file with an old modification time so the
|
||||
// prune interval check passes.
|
||||
llvm::SmallString<256> ModulesTimestamp(CacheDir);
|
||||
llvm::sys::path::append(ModulesTimestamp, "modules.timestamp");
|
||||
writeFile(ModulesTimestamp);
|
||||
setOldAccessTime(ModulesTimestamp);
|
||||
|
||||
// Verify the files exist before pruning.
|
||||
EXPECT_TRUE(llvm::sys::fs::exists(PCMFile));
|
||||
EXPECT_TRUE(llvm::sys::fs::exists(RootPCMFile));
|
||||
|
||||
clang_ModuleCache_prune(CacheDir.c_str(), /*PruneInterval=*/1,
|
||||
/*PruneAfter=*/1);
|
||||
|
||||
// The old .pcm files should have been removed from both the subdirectory
|
||||
// and the root.
|
||||
EXPECT_FALSE(llvm::sys::fs::exists(PCMFile));
|
||||
EXPECT_FALSE(llvm::sys::fs::exists(RootPCMFile));
|
||||
|
||||
llvm::sys::fs::remove_directories(CacheDir);
|
||||
}
|
||||
|
||||
TEST_F(LibclangParseTest, GlobalOptions) {
|
||||
EXPECT_EQ(clang_CXIndex_getGlobalOptions(Index), CXGlobalOpt_None);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user