mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #25653 from harlanhaskins/one-small-dep-for-man
[ModuleInterfaces] Rebuild if a dependency is missing
This commit is contained in:
@@ -223,27 +223,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();
|
||||
@@ -293,6 +285,7 @@ class swift::ParseableInterfaceBuilder {
|
||||
const SourceLoc diagnosticLoc;
|
||||
DependencyTracker *const dependencyTracker;
|
||||
CompilerInvocation subInvocation;
|
||||
SmallVector<StringRef, 3> extraDependencies;
|
||||
|
||||
void configureSubInvocationInputsAndOutputs(StringRef OutPath) {
|
||||
auto &SubFEOpts = subInvocation.getFrontendOptions();
|
||||
@@ -406,6 +399,8 @@ class swift::ParseableInterfaceBuilder {
|
||||
auto DTDeps = SubInstance.getDependencyTracker()->getDependencies();
|
||||
SmallVector<StringRef, 16> InitialDepNames(DTDeps.begin(), DTDeps.end());
|
||||
InitialDepNames.push_back(interfacePath);
|
||||
InitialDepNames.insert(InitialDepNames.end(),
|
||||
extraDependencies.begin(), extraDependencies.end());
|
||||
llvm::StringSet<> AllDepNames;
|
||||
SmallString<128> Scratch;
|
||||
|
||||
@@ -433,8 +428,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;
|
||||
|
||||
@@ -444,8 +438,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();
|
||||
}
|
||||
@@ -498,6 +491,12 @@ public:
|
||||
return subInvocation;
|
||||
}
|
||||
|
||||
/// Ensures the requested file name is added as a dependency of the resulting
|
||||
/// module.
|
||||
void addExtraDependency(StringRef path) {
|
||||
extraDependencies.push_back(path);
|
||||
}
|
||||
|
||||
bool buildSwiftModule(StringRef OutPath, bool ShouldSerializeDeps,
|
||||
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer) {
|
||||
bool SubError = false;
|
||||
@@ -656,6 +655,7 @@ struct ModuleRebuildInfo {
|
||||
Optional<serialization::Status> serializationStatus;
|
||||
ModuleKind kind;
|
||||
SmallVector<std::string, 10> outOfDateDependencies;
|
||||
SmallVector<std::string, 10> missingDependencies;
|
||||
};
|
||||
SmallVector<OutOfDateModule, 3> outOfDateModules;
|
||||
|
||||
@@ -663,7 +663,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();
|
||||
}
|
||||
|
||||
@@ -686,6 +686,22 @@ 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);
|
||||
}
|
||||
|
||||
/// Determines if we saw the given module path and registered is as out of
|
||||
/// date.
|
||||
bool sawOutOfDateModule(StringRef modulePath) {
|
||||
for (auto &mod : outOfDateModules)
|
||||
if (mod.path == modulePath)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *invalidModuleReason(serialization::Status status) {
|
||||
using namespace serialization;
|
||||
switch (status) {
|
||||
@@ -722,6 +738,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) {
|
||||
@@ -835,31 +856,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
|
||||
@@ -869,13 +903,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;
|
||||
}
|
||||
@@ -885,7 +925,12 @@ class ParseableInterfaceModuleLoaderImpl {
|
||||
bool serializedASTBufferIsUpToDate(
|
||||
StringRef path, const llvm::MemoryBuffer &buf,
|
||||
SmallVectorImpl<FileDependency> &allDeps) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "Validating deps of " << modulePath << "\n");
|
||||
|
||||
// Clear the existing dependencies, because we're going to re-fill them
|
||||
// in validateSerializedAST.
|
||||
allDeps.clear();
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Validating deps of " << path << "\n");
|
||||
auto validationInfo = serialization::validateSerializedAST(
|
||||
buf.getBuffer(), /*ExtendedValidationInfo=*/nullptr, &allDeps);
|
||||
|
||||
@@ -916,6 +961,13 @@ class ParseableInterfaceModuleLoaderImpl {
|
||||
StringRef path, const ForwardingModule &fwd,
|
||||
SmallVectorImpl<FileDependency> &deps,
|
||||
std::unique_ptr<llvm::MemoryBuffer> &moduleBuffer) {
|
||||
|
||||
// Clear the existing dependencies, because we're going to re-fill them
|
||||
// from the forwarding module.
|
||||
deps.clear();
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Validating deps of " << path << "\n");
|
||||
|
||||
// First, make sure the underlying module path exists and is valid.
|
||||
auto modBuf = fs.getBufferForFile(fwd.underlyingModulePath);
|
||||
if (!modBuf || !serializedASTLooksValid(*modBuf.get()))
|
||||
@@ -1160,6 +1212,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);
|
||||
}
|
||||
|
||||
@@ -1306,6 +1361,14 @@ class ParseableInterfaceModuleLoaderImpl {
|
||||
interfacePath);
|
||||
}
|
||||
|
||||
// If we found an out-of-date .swiftmodule, we still want to add it as
|
||||
// a dependency of the .swiftinterface. That way if it's updated, but
|
||||
// the .swiftinterface remains the same, we invalidate the cache and
|
||||
// check the new .swiftmodule, because it likely has more information
|
||||
// about the state of the world.
|
||||
if (rebuildInfo.sawOutOfDateModule(modulePath))
|
||||
builder.addExtraDependency(modulePath);
|
||||
|
||||
if (builder.buildSwiftModule(cachedOutputPath, /*shouldSerializeDeps*/true,
|
||||
&moduleBuffer))
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
|
||||
Reference in New Issue
Block a user