[llvm-objcopy][Support] move writeToOutput helper function to Support.

writeToOutput function is useful when it is necessary to create different kinds
of streams(based on stream name) and when we need to use a temporary file
while writing(which would be renamed into the resulting file in a success case).
This patch moves the writeToStream helper into the Support library.

Differential Revision: https://reviews.llvm.org/D98426
This commit is contained in:
Alexey Lapshin 2021-03-04 12:51:30 +03:00
parent 9cd7c41306
commit 972b6a3a34
5 changed files with 116 additions and 39 deletions

View File

@ -714,6 +714,17 @@ public:
~buffer_unique_ostream() override { *OS << str(); }
};
class Error;
/// This helper creates an output stream and then passes it to \p Write.
/// The stream created is based on the specified \p OutputFileName:
/// llvm::outs for "-", raw_null_ostream for "/dev/null", and raw_fd_ostream
/// for other names. For raw_fd_ostream instances, the stream writes to
/// a temporary file. The final output file is atomically replaced with the
/// temporary file after the \p Write function is finished.
Error writeToOutput(StringRef OutputFileName,
std::function<Error(raw_ostream &)> Write);
} // end namespace llvm
#endif // LLVM_SUPPORT_RAW_OSTREAM_H

View File

@ -989,3 +989,31 @@ void raw_pwrite_stream::anchor() {}
void buffer_ostream::anchor() {}
void buffer_unique_ostream::anchor() {}
Error llvm::writeToOutput(StringRef OutputFileName,
std::function<Error(raw_ostream &)> Write) {
if (OutputFileName == "-")
return Write(outs());
if (OutputFileName == "/dev/null") {
raw_null_ostream Out;
return Write(Out);
}
unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe;
Expected<sys::fs::TempFile> Temp =
sys::fs::TempFile::create(OutputFileName + ".temp-stream-%%%%%%", Mode);
if (!Temp)
return createFileError(OutputFileName, Temp.takeError());
raw_fd_ostream Out(Temp->FD, false);
if (Error E = Write(Out)) {
if (Error DiscardError = Temp->discard())
return joinErrors(std::move(E), std::move(DiscardError));
return E;
}
Out.flush();
return Temp->keep(OutputFileName);
}

View File

@ -57,34 +57,6 @@
namespace llvm {
namespace objcopy {
Error writeToFile(StringRef OutputFileName,
std::function<Error(raw_ostream &)> Write) {
if (OutputFileName == "-")
return Write(outs());
if (OutputFileName == "/dev/null") {
raw_null_ostream Out;
return Write(Out);
}
unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe;
Expected<sys::fs::TempFile> Temp =
sys::fs::TempFile::create(OutputFileName + ".temp-objcopy-%%%%%%", Mode);
if (!Temp)
return createFileError(OutputFileName, Temp.takeError());
raw_fd_ostream Out(Temp->FD, false);
if (Error E = Write(Out)) {
if (Error DiscardError = Temp->discard())
return joinErrors(std::move(E), std::move(DiscardError));
return E;
}
Out.flush();
return Temp->keep(OutputFileName);
}
// The name this program was invoked as.
StringRef ToolName;
@ -369,21 +341,21 @@ static Error executeObjcopy(CopyConfig &Config) {
if (Config.SplitDWO.empty()) {
// Apply transformations described by Config and store result into
// Config.OutputFilename using specified ObjcopyFunc function.
if (Error E = writeToFile(Config.OutputFilename, ObjcopyFunc))
if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
return E;
} else {
Config.ExtractDWO = true;
Config.StripDWO = false;
// Copy .dwo tables from the Config.InputFilename into Config.SplitDWO
// file using specified ObjcopyFunc function.
if (Error E = writeToFile(Config.SplitDWO, ObjcopyFunc))
if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))
return E;
Config.ExtractDWO = false;
Config.StripDWO = true;
// Apply transformations described by Config, remove .dwo tables and
// store result into Config.OutputFilename using specified ObjcopyFunc
// function.
if (Error E = writeToFile(Config.OutputFilename, ObjcopyFunc))
if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
return E;
}
}

View File

@ -27,14 +27,6 @@ struct CopyConfig;
Expected<std::vector<NewArchiveMember>>
createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar);
/// A writeToFile helper creates an output stream, based on the specified
/// \p OutputFileName: std::outs for the "-", raw_null_ostream for
/// the "/dev/null", temporary file in the same directory as the final output
/// file for other names. The final output file is atomically replaced with
/// the temporary file after \p Write handler is finished.
Error writeToFile(StringRef OutputFileName,
std::function<Error(raw_ostream &)> Write);
} // end namespace objcopy
} // end namespace llvm

View File

@ -8,8 +8,11 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
@ -469,4 +472,75 @@ TEST(raw_ostreamTest, reserve_stream) {
OS.flush();
EXPECT_EQ("11111111111111111111hello1world", Str);
}
static void checkFileData(StringRef FileName, StringRef GoldenData) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFileOrSTDIN(FileName);
EXPECT_FALSE(BufOrErr.getError());
EXPECT_EQ((*BufOrErr)->getBufferSize(), GoldenData.size());
EXPECT_EQ(memcmp((*BufOrErr)->getBufferStart(), GoldenData.data(),
GoldenData.size()),
0);
}
TEST(raw_ostreamTest, writeToOutputFile) {
SmallString<64> Path;
int FD;
ASSERT_FALSE(sys::fs::createTemporaryFile("foo", "bar", FD, Path));
FileRemover Cleanup(Path);
ASSERT_THAT_ERROR(writeToOutput(Path,
[](raw_ostream &Out) -> Error {
Out << "HelloWorld";
return Error::success();
}),
Succeeded());
checkFileData(Path, "HelloWorld");
}
TEST(raw_ostreamTest, writeToNonexistingPath) {
StringRef FileName = "/_bad/_path";
std::string ErrorMessage = toString(createFileError(
FileName, make_error_code(errc::no_such_file_or_directory)));
EXPECT_THAT_ERROR(writeToOutput(FileName,
[](raw_ostream &Out) -> Error {
Out << "HelloWorld";
return Error::success();
}),
FailedWithMessage(ErrorMessage));
}
TEST(raw_ostreamTest, writeToDevNull) {
bool DevNullIsUsed = false;
EXPECT_THAT_ERROR(
writeToOutput("/dev/null",
[&](raw_ostream &Out) -> Error {
DevNullIsUsed =
testing::internal::CheckedDowncastToActualType<
raw_null_ostream, raw_ostream>(&Out);
return Error::success();
}),
Succeeded());
EXPECT_TRUE(DevNullIsUsed);
}
TEST(raw_ostreamTest, writeToStdOut) {
outs().flush();
testing::internal::CaptureStdout();
EXPECT_THAT_ERROR(writeToOutput("-",
[](raw_ostream &Out) -> Error {
Out << "HelloWorld";
return Error::success();
}),
Succeeded());
outs().flush();
std::string CapturedStdOut = testing::internal::GetCapturedStdout();
EXPECT_EQ(CapturedStdOut, "HelloWorld");
}
}