Remove temoprary files.

Previously, we created temporary files using llvm::sys::fs::createTemporaryFile
and removed them using llvm::FileRemover. This is error-prone as it is easy to
forget creating FileRemover instances after creating temporary files.
There is actually a temporary file leak bug.

This patch introduces a new class, TemporaryFile, to manage temporary files
in the RAII style.

Differential Revision: https://reviews.llvm.org/D24176

llvm-svn: 280510
This commit is contained in:
Rui Ueyama 2016-09-02 17:34:17 +00:00
parent 31b88cb030
commit fa12b8bd1a
1 changed files with 60 additions and 36 deletions

View File

@ -279,18 +279,54 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) {
}
}
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
public:
TemporaryFile(StringRef Prefix, StringRef Extn) {
SmallString<128> S;
if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
fatal(EC, "cannot create a temporary file");
Path = S.str();
}
TemporaryFile(TemporaryFile &&Obj) {
std::swap(Path, Obj.Path);
}
~TemporaryFile() {
if (Path.empty())
return;
if (sys::fs::remove(Path))
fatal("failed to remove " + Path);
}
// Returns a memory buffer of this temporary file.
// Note that this function does not leave the file open,
// so it is safe to remove the file immediately after this function
// is called (you cannot remove an opened file on Windows.)
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
// IsVolatileSize=true forces MemoryBuffer to not use mmap().
return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false,
/*IsVolatileSize=*/true),
"could not open " + Path);
}
std::string Path;
};
}
// Create the default manifest file as a temporary file.
static std::string createDefaultXml() {
TemporaryFile createDefaultXml() {
// Create a temporary file.
SmallString<128> Path;
if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path))
fatal(EC, "cannot create a temporary file");
TemporaryFile File("defaultxml", "manifest");
// Open the temporary file for writing.
std::error_code EC;
llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text);
llvm::raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
if (EC)
fatal(EC, "failed to open " + Path);
fatal(EC, "failed to open " + File.Path);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
@ -316,56 +352,48 @@ static std::string createDefaultXml() {
}
OS << "</assembly>\n";
OS.close();
return StringRef(Path);
return File;
}
static std::string readFile(StringRef Path) {
std::unique_ptr<MemoryBuffer> MB =
check(MemoryBuffer::getFile(Path), "could not open " + Path);
std::unique_ptr<MemoryBuffer> Buf(std::move(MB));
return Buf->getBuffer();
return MB->getBuffer();
}
static std::string createManifestXml() {
// Create the default manifest file.
std::string Path1 = createDefaultXml();
TemporaryFile File1 = createDefaultXml();
if (Config->ManifestInput.empty())
return readFile(Path1);
return readFile(File1.Path);
// If manifest files are supplied by the user using /MANIFESTINPUT
// option, we need to merge them with the default manifest.
SmallString<128> Path2;
if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path2))
fatal(EC, "cannot create a temporary file");
FileRemover Remover1(Path1);
FileRemover Remover2(Path2);
TemporaryFile File2("user", "manifest");
Executor E("mt.exe");
E.add("/manifest");
E.add(Path1);
E.add(File1.Path);
for (StringRef Filename : Config->ManifestInput) {
E.add("/manifest");
E.add(Filename);
}
E.add("/nologo");
E.add("/out:" + StringRef(Path2));
E.add("/out:" + StringRef(File2.Path));
E.run();
return readFile(Path2);
return readFile(File2.Path);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
// Create a temporary file for the resource script file.
SmallString<128> RCPath;
if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath))
fatal(EC, "cannot create a temporary file");
FileRemover RCRemover(RCPath);
TemporaryFile RCFile("manifest", "rc");
// Open the temporary file for writing.
std::error_code EC;
llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text);
llvm::raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
if (EC)
fatal(EC, "failed to open " + RCPath);
fatal(EC, "failed to open " + RCFile.Path);
// Write resource script to the RC file.
Out << "#define LANG_ENGLISH 9\n"
@ -379,17 +407,15 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
Out.close();
// Create output resource file.
SmallString<128> ResPath;
if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath))
fatal(EC, "cannot create a temporary file");
TemporaryFile ResFile("output-resource", "res");
Executor E("rc.exe");
E.add("/fo");
E.add(ResPath.str());
E.add(ResFile.Path);
E.add("/nologo");
E.add(RCPath.str());
E.add(RCFile.Path);
E.run();
return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath);
return ResFile.getMemoryBuffer();
}
void createSideBySideManifest() {
@ -555,20 +581,18 @@ void checkFailIfMismatch(StringRef Arg) {
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
// Create an output file path.
SmallString<128> Path;
if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path))
fatal(EC, "could not create temporary file");
TemporaryFile File("resource-file", "obj");
// Execute cvtres.exe.
Executor E("cvtres.exe");
E.add("/machine:" + machineToStr(Config->Machine));
E.add("/readonly");
E.add("/nologo");
E.add("/out:" + Path);
E.add("/out:" + Twine(File.Path));
for (MemoryBufferRef MB : MBs)
E.add(MB.getBufferIdentifier());
E.run();
return check(MemoryBuffer::getFile(Path), "could not open " + Path);
return File.getMemoryBuffer();
}
// Create OptTable