mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user