[Frontend]: Write out .swiftdeps files atomically

Modify ReferenceDependenciesEmitter::emit to write to a temporary file and then atomically rename it in place using the atomicallyWritingToFile helper function.
This resolves SR-8707, where .swiftdeps files could be truncated if the compiler job was killed in the middle of a write.
This commit is contained in:
Owen Voorhees
2018-10-05 19:11:23 -07:00
parent da8eb3c344
commit a676ab6d4c

View File

@@ -22,6 +22,7 @@
#include "swift/AST/NameLookup.h"
#include "swift/AST/ReferencedNameTracker.h"
#include "swift/AST/Types.h"
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/ReferenceDependencyKeys.h"
#include "swift/Frontend/FrontendOptions.h"
@@ -65,11 +66,6 @@ public:
llvm::raw_ostream &out);
private:
/// Opens file for reference dependencies. Emits diagnostic if needed.
///
/// \return nullptr on error
static std::unique_ptr<llvm::raw_fd_ostream> openFile(DiagnosticEngine &diags,
StringRef OutputPath);
/// Emits all the dependency information.
void emit() const;
@@ -189,35 +185,24 @@ static std::string escape(DeclBaseName name) {
return llvm::yaml::escape(name.userFacingName());
}
std::unique_ptr<llvm::raw_fd_ostream>
ReferenceDependenciesEmitter::openFile(DiagnosticEngine &diags,
StringRef outputPath) {
// Before writing to the dependencies file path, preserve any previous file
// that may have been there. No error handling -- this is just a nicety, it
// doesn't matter if it fails.
llvm::sys::fs::rename(outputPath, outputPath + "~");
std::error_code EC;
auto out = llvm::make_unique<llvm::raw_fd_ostream>(outputPath, EC,
llvm::sys::fs::F_None);
if (out->has_error() || EC) {
diags.diagnose(SourceLoc(), diag::error_opening_output, outputPath,
EC.message());
out->clear_error();
return nullptr;
}
return out;
}
bool ReferenceDependenciesEmitter::emit(DiagnosticEngine &diags,
SourceFile *const SF,
const DependencyTracker &depTracker,
StringRef outputPath) {
const std::unique_ptr<llvm::raw_ostream> out = openFile(diags, outputPath);
if (!out.get())
// Before writing to the dependencies file path, preserve any previous file
// that may have been there. No error handling -- this is just a nicety, it
// doesn't matter if it fails.
llvm::sys::fs::rename(outputPath, outputPath + "~");
std::error_code EC =
swift::atomicallyWritingToFile(outputPath,
[&](llvm::raw_pwrite_stream &out) {
ReferenceDependenciesEmitter::emit(SF, depTracker, out);
});
if (EC) {
diags.diagnose(SourceLoc(), diag::error_opening_output, outputPath,
EC.message());
return true;
ReferenceDependenciesEmitter::emit(SF, depTracker, *out);
}
return false;
}