[ModuleInterfaces] Rebuild if a dependency is missing

A dependency being removed is not a hard error, because the recovery step is to recompile the interface. That might fail because of the change in dependencies, but that is a more actionable error.

Fixes rdar://51959698
This commit is contained in:
Harlan Haskins
2019-06-20 16:44:42 -07:00
parent 815ae1f568
commit 33df36db20
2 changed files with 57 additions and 32 deletions

View File

@@ -222,27 +222,19 @@ static bool serializedASTLooksValid(const llvm::MemoryBuffer &buf) {
}
static std::unique_ptr<llvm::MemoryBuffer> getBufferOfDependency(
llvm::vfs::FileSystem &fs, StringRef depPath, StringRef interfacePath,
DiagnosticEngine &diags, SourceLoc diagnosticLoc) {
llvm::vfs::FileSystem &fs, StringRef depPath) {
auto depBuf = fs.getBufferForFile(depPath, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false);
if (!depBuf) {
diags.diagnose(diagnosticLoc,
diag::missing_dependency_of_module_interface,
depPath, interfacePath, depBuf.getError().message());
return nullptr;
}
return std::move(depBuf.get());
}
static Optional<llvm::vfs::Status> getStatusOfDependency(
llvm::vfs::FileSystem &fs, StringRef depPath, StringRef interfacePath,
DiagnosticEngine &diags, SourceLoc diagnosticLoc) {
llvm::vfs::FileSystem &fs, StringRef depPath) {
auto status = fs.status(depPath);
if (!status) {
diags.diagnose(diagnosticLoc,
diag::missing_dependency_of_module_interface,
depPath, interfacePath, status.getError().message());
return None;
}
return status.get();
@@ -432,8 +424,7 @@ class swift::ParseableInterfaceBuilder {
if (DepName.startswith(ResourcePath))
continue;
auto Status = getStatusOfDependency(fs, DepName, interfacePath,
diags, diagnosticLoc);
auto Status = getStatusOfDependency(fs, DepName);
if (!Status)
return true;
@@ -443,8 +434,7 @@ class swift::ParseableInterfaceBuilder {
std::unique_ptr<llvm::MemoryBuffer> DepBuf = nullptr;
auto getDepBuf = [&]() -> llvm::MemoryBuffer * {
if (DepBuf) return DepBuf.get();
if (auto Buf = getBufferOfDependency(fs, DepName, interfacePath,
diags, diagnosticLoc)) {
if (auto Buf = getBufferOfDependency(fs, DepName)) {
DepBuf = std::move(Buf);
return DepBuf.get();
}
@@ -644,6 +634,7 @@ struct ModuleRebuildInfo {
Optional<serialization::Status> serializationStatus;
ModuleKind kind;
SmallVector<std::string, 10> outOfDateDependencies;
SmallVector<std::string, 10> missingDependencies;
};
SmallVector<OutOfDateModule, 3> outOfDateModules;
@@ -651,7 +642,7 @@ struct ModuleRebuildInfo {
for (auto &mod : outOfDateModules) {
if (mod.path == path) return mod;
}
outOfDateModules.push_back({path, None, ModuleKind::Normal, {}});
outOfDateModules.push_back({path, None, ModuleKind::Normal, {}, {}});
return outOfDateModules.back();
}
@@ -674,6 +665,13 @@ struct ModuleRebuildInfo {
.outOfDateDependencies.push_back(depPath);
}
/// Registers a missing dependency at \c depPath for the module
/// at \c modulePath.
void addMissingDependency(StringRef modulePath, StringRef depPath) {
getOrInsertOutOfDateModule(modulePath)
.missingDependencies.push_back(depPath);
}
const char *invalidModuleReason(serialization::Status status) {
using namespace serialization;
switch (status) {
@@ -710,6 +708,11 @@ struct ModuleRebuildInfo {
dep);
}
// Diagnose any missing dependencies in this module.
for (auto &dep : mod.missingDependencies) {
ctx.Diags.diagnose(loc, diag::module_interface_dependency_missing, dep);
}
// If there was a compiled module that wasn't able to be read, diagnose
// the reason we couldn't read it.
if (auto status = mod.serializationStatus) {
@@ -823,31 +826,44 @@ class ParseableInterfaceModuleLoaderImpl {
return StringRef(scratch.data(), scratch.size());
}
enum class DependencyStatus {
UpToDate,
OutOfDate,
Missing
};
// Checks that a dependency read from the cached module is up to date compared
// to the interface file it represents.
bool dependencyIsUpToDate(const FileDependency &dep, StringRef fullPath) {
auto status = getStatusOfDependency(fs, fullPath, interfacePath,
diags, diagnosticLoc);
if (!status) return false;
DependencyStatus checkDependency(StringRef modulePath,
const FileDependency &dep,
StringRef fullPath) {
auto status = getStatusOfDependency(fs, fullPath);
if (!status)
return DependencyStatus::Missing;
// If the sizes differ, then we know the file has changed.
if (status->getSize() != dep.getSize()) return false;
if (status->getSize() != dep.getSize())
return DependencyStatus::OutOfDate;
// Otherwise, if this dependency is verified by modification time, check
// it vs. the modification time of the file.
if (dep.isModificationTimeBased()) {
uint64_t mtime =
status->getLastModificationTime().time_since_epoch().count();
return mtime == dep.getModificationTime();
return mtime == dep.getModificationTime() ?
DependencyStatus::UpToDate :
DependencyStatus::OutOfDate;
}
// Slow path: if the dependency is verified by content hash, check it vs.
// the hash of the file.
auto buf = getBufferOfDependency(fs, fullPath, interfacePath,
diags, diagnosticLoc);
if (!buf) return false;
auto buf = getBufferOfDependency(fs, fullPath);
if (!buf)
return DependencyStatus::Missing;
return xxHash64(buf->getBuffer()) == dep.getContentHash();
return xxHash64(buf->getBuffer()) == dep.getContentHash() ?
DependencyStatus::UpToDate :
DependencyStatus::OutOfDate;
}
// Check if all the provided file dependencies are up-to-date compared to
@@ -857,13 +873,19 @@ class ParseableInterfaceModuleLoaderImpl {
SmallString<128> SDKRelativeBuffer;
for (auto &in : deps) {
StringRef fullPath = getFullDependencyPath(in, SDKRelativeBuffer);
if (!dependencyIsUpToDate(in, fullPath)) {
LLVM_DEBUG(llvm::dbgs() << "Dep " << fullPath
<< " is directly out of date\n");
switch (checkDependency(modulePath, in, fullPath)) {
case DependencyStatus::UpToDate:
LLVM_DEBUG(llvm::dbgs() << "Dep " << fullPath << " is up to date\n");
break;
case DependencyStatus::OutOfDate:
LLVM_DEBUG(llvm::dbgs() << "Dep " << fullPath << " is out of date\n");
rebuildInfo.addOutOfDateDependency(modulePath, fullPath);
return false;
case DependencyStatus::Missing:
LLVM_DEBUG(llvm::dbgs() << "Dep " << fullPath << " is missing\n");
rebuildInfo.addMissingDependency(modulePath, fullPath);
return false;
}
LLVM_DEBUG(llvm::dbgs() << "Dep " << fullPath << " is up to date\n");
}
return true;
}
@@ -1148,6 +1170,9 @@ class ParseableInterfaceModuleLoaderImpl {
ModuleRebuildInfo::ModuleKind::Normal);
}
} else if (adjacentModuleBuffer.getError() != notFoundError) {
LLVM_DEBUG(llvm::dbgs() << "Found unreadable module at "
<< modulePath
<< "; deferring to serialized module loader\n");
return std::make_error_code(std::errc::not_supported);
}