Skip to content

Commit 7dd63e1

Browse files
Frontend: Adopt llvm::vfs::OutputBackend in CompilerInstance (llvm#113364)
Adopt new virtual output backend in CompilerInstance.
1 parent e9aee33 commit 7dd63e1

File tree

3 files changed

+97
-141
lines changed

3 files changed

+97
-141
lines changed

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Support/BuryPointer.h"
2727
#include "llvm/Support/FileSystem.h"
2828
#include "llvm/Support/VirtualFileSystem.h"
29+
#include "llvm/Support/VirtualOutputBackend.h"
2930
#include <cassert>
3031
#include <list>
3132
#include <memory>
@@ -97,6 +98,9 @@ class CompilerInstance : public ModuleLoader {
9798
/// The file manager.
9899
IntrusiveRefCntPtr<FileManager> FileMgr;
99100

101+
/// The output manager.
102+
IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutputMgr;
103+
100104
/// The source manager.
101105
IntrusiveRefCntPtr<SourceManager> SourceMgr;
102106

@@ -180,22 +184,8 @@ class CompilerInstance : public ModuleLoader {
180184
/// The stream for verbose output.
181185
raw_ostream *VerboseOutputStream = &llvm::errs();
182186

183-
/// Holds information about the output file.
184-
///
185-
/// If TempFilename is not empty we must rename it to Filename at the end.
186-
/// TempFilename may be empty and Filename non-empty if creating the temporary
187-
/// failed.
188-
struct OutputFile {
189-
std::string Filename;
190-
std::optional<llvm::sys::fs::TempFile> File;
191-
192-
OutputFile(std::string filename,
193-
std::optional<llvm::sys::fs::TempFile> file)
194-
: Filename(std::move(filename)), File(std::move(file)) {}
195-
};
196-
197187
/// The list of active output files.
198-
std::list<OutputFile> OutputFiles;
188+
std::list<llvm::vfs::OutputFile> OutputFiles;
199189

200190
/// Force an output buffer.
201191
std::unique_ptr<llvm::raw_pwrite_stream> OutputStream;
@@ -448,6 +438,22 @@ class CompilerInstance : public ModuleLoader {
448438
/// Replace the current file manager and virtual file system.
449439
void setFileManager(IntrusiveRefCntPtr<FileManager> Value);
450440

441+
/// @}
442+
/// @name Output Manager
443+
/// @{
444+
445+
/// Set the output manager.
446+
void
447+
setOutputManager(IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs);
448+
449+
/// Create an output manager.
450+
void createOutputManager();
451+
452+
bool hasOutputManager() const { return bool(OutputMgr); }
453+
454+
llvm::vfs::OutputBackend &getOutputManager();
455+
llvm::vfs::OutputBackend &getOrCreateOutputManager();
456+
451457
/// @}
452458
/// @name Source Manager
453459
/// @{

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 61 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
#include "llvm/Support/TimeProfiler.h"
5858
#include "llvm/Support/Timer.h"
5959
#include "llvm/Support/VirtualFileSystem.h"
60+
#include "llvm/Support/VirtualOutputBackends.h"
61+
#include "llvm/Support/VirtualOutputError.h"
6062
#include "llvm/Support/raw_ostream.h"
6163
#include "llvm/TargetParser/Host.h"
6264
#include <optional>
@@ -522,6 +524,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
522524
collectVFSEntries(*this, ModuleDepCollector);
523525
}
524526

527+
// Modules need an output manager.
528+
if (!hasOutputManager())
529+
createOutputManager();
530+
525531
for (auto &Listener : DependencyCollectors)
526532
Listener->attachToPreprocessor(*PP);
527533

@@ -778,32 +784,23 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind,
778784
void CompilerInstance::clearOutputFiles(bool EraseFiles) {
779785
// The ASTConsumer can own streams that write to the output files.
780786
assert(!hasASTConsumer() && "ASTConsumer should be reset");
781-
// Ignore errors that occur when trying to discard the temp file.
782-
for (OutputFile &OF : OutputFiles) {
783-
if (EraseFiles) {
784-
if (OF.File)
785-
consumeError(OF.File->discard());
786-
if (!OF.Filename.empty())
787-
llvm::sys::fs::remove(OF.Filename);
788-
continue;
789-
}
790-
791-
if (!OF.File)
792-
continue;
793-
794-
if (OF.File->TmpName.empty()) {
795-
consumeError(OF.File->discard());
796-
continue;
797-
}
798-
799-
llvm::Error E = OF.File->keep(OF.Filename);
800-
if (!E)
801-
continue;
802-
803-
getDiagnostics().Report(diag::err_unable_to_rename_temp)
804-
<< OF.File->TmpName << OF.Filename << std::move(E);
805-
806-
llvm::sys::fs::remove(OF.File->TmpName);
787+
if (!EraseFiles) {
788+
for (auto &O : OutputFiles)
789+
llvm::handleAllErrors(
790+
O.keep(),
791+
[&](const llvm::vfs::TempFileOutputError &E) {
792+
getDiagnostics().Report(diag::err_unable_to_rename_temp)
793+
<< E.getTempPath() << E.getOutputPath()
794+
<< E.convertToErrorCode().message();
795+
},
796+
[&](const llvm::vfs::OutputError &E) {
797+
getDiagnostics().Report(diag::err_fe_unable_to_open_output)
798+
<< E.getOutputPath() << E.convertToErrorCode().message();
799+
},
800+
[&](const llvm::ErrorInfoBase &EIB) { // Handle any remaining error
801+
getDiagnostics().Report(diag::err_fe_unable_to_open_output)
802+
<< O.getPath() << EIB.message();
803+
});
807804
}
808805
OutputFiles.clear();
809806
if (DeleteBuiltModules) {
@@ -837,6 +834,30 @@ std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() {
837834
return std::make_unique<llvm::raw_null_ostream>();
838835
}
839836

837+
// Output Manager
838+
839+
void CompilerInstance::setOutputManager(
840+
IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs) {
841+
assert(!OutputMgr && "Already has an output manager");
842+
OutputMgr = std::move(NewOutputs);
843+
}
844+
845+
void CompilerInstance::createOutputManager() {
846+
assert(!OutputMgr && "Already has an output manager");
847+
OutputMgr = llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
848+
}
849+
850+
llvm::vfs::OutputBackend &CompilerInstance::getOutputManager() {
851+
assert(OutputMgr);
852+
return *OutputMgr;
853+
}
854+
855+
llvm::vfs::OutputBackend &CompilerInstance::getOrCreateOutputManager() {
856+
if (!hasOutputManager())
857+
createOutputManager();
858+
return getOutputManager();
859+
}
860+
840861
std::unique_ptr<raw_pwrite_stream>
841862
CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary,
842863
bool RemoveFileOnSignal, bool UseTemporary,
@@ -871,98 +892,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary,
871892
OutputPath = *AbsPath;
872893
}
873894

874-
std::unique_ptr<llvm::raw_fd_ostream> OS;
875-
std::optional<StringRef> OSFile;
876-
877-
if (UseTemporary) {
878-
if (OutputPath == "-")
879-
UseTemporary = false;
880-
else {
881-
llvm::sys::fs::file_status Status;
882-
llvm::sys::fs::status(OutputPath, Status);
883-
if (llvm::sys::fs::exists(Status)) {
884-
// Fail early if we can't write to the final destination.
885-
if (!llvm::sys::fs::can_write(OutputPath))
886-
return llvm::errorCodeToError(
887-
make_error_code(llvm::errc::operation_not_permitted));
888-
889-
// Don't use a temporary if the output is a special file. This handles
890-
// things like '-o /dev/null'
891-
if (!llvm::sys::fs::is_regular_file(Status))
892-
UseTemporary = false;
893-
}
894-
}
895-
}
896-
897-
std::optional<llvm::sys::fs::TempFile> Temp;
898-
if (UseTemporary) {
899-
// Create a temporary file.
900-
// Insert -%%%%%%%% before the extension (if any), and because some tools
901-
// (noticeable, clang's own GlobalModuleIndex.cpp) glob for build
902-
// artifacts, also append .tmp.
903-
StringRef OutputExtension = llvm::sys::path::extension(OutputPath);
904-
SmallString<128> TempPath =
905-
StringRef(OutputPath).drop_back(OutputExtension.size());
906-
TempPath += "-%%%%%%%%";
907-
TempPath += OutputExtension;
908-
TempPath += ".tmp";
909-
llvm::sys::fs::OpenFlags BinaryFlags =
910-
Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text;
911-
Expected<llvm::sys::fs::TempFile> ExpectedFile =
912-
llvm::sys::fs::TempFile::create(
913-
TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write,
914-
BinaryFlags);
915-
916-
llvm::Error E = handleErrors(
917-
ExpectedFile.takeError(), [&](const llvm::ECError &E) -> llvm::Error {
918-
std::error_code EC = E.convertToErrorCode();
919-
if (CreateMissingDirectories &&
920-
EC == llvm::errc::no_such_file_or_directory) {
921-
StringRef Parent = llvm::sys::path::parent_path(OutputPath);
922-
EC = llvm::sys::fs::create_directories(Parent);
923-
if (!EC) {
924-
ExpectedFile = llvm::sys::fs::TempFile::create(
925-
TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write,
926-
BinaryFlags);
927-
if (!ExpectedFile)
928-
return llvm::errorCodeToError(
929-
llvm::errc::no_such_file_or_directory);
930-
}
931-
}
932-
return llvm::errorCodeToError(EC);
933-
});
934-
935-
if (E) {
936-
consumeError(std::move(E));
937-
} else {
938-
Temp = std::move(ExpectedFile.get());
939-
OS.reset(new llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false));
940-
OSFile = Temp->TmpName;
941-
}
942-
// If we failed to create the temporary, fallback to writing to the file
943-
// directly. This handles the corner case where we cannot write to the
944-
// directory, but can write to the file.
945-
}
946-
947-
if (!OS) {
948-
OSFile = OutputPath;
949-
std::error_code EC;
950-
OS.reset(new llvm::raw_fd_ostream(
951-
*OSFile, EC,
952-
(Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
953-
if (EC)
954-
return llvm::errorCodeToError(EC);
955-
}
956-
957-
// Add the output file -- but don't try to remove "-", since this means we are
958-
// using stdin.
959-
OutputFiles.emplace_back(((OutputPath != "-") ? OutputPath : "").str(),
960-
std::move(Temp));
961-
962-
if (!Binary || OS->supportsSeeking())
963-
return std::move(OS);
964-
965-
return std::make_unique<llvm::buffer_unique_ostream>(std::move(OS));
895+
using namespace llvm::vfs;
896+
Expected<OutputFile> O = getOrCreateOutputManager().createFile(
897+
OutputPath,
898+
OutputConfig()
899+
.setTextWithCRLF(!Binary)
900+
.setDiscardOnSignal(RemoveFileOnSignal)
901+
.setAtomicWrite(UseTemporary)
902+
.setImplyCreateDirectories(UseTemporary && CreateMissingDirectories));
903+
if (!O)
904+
return O.takeError();
905+
906+
O->discardOnDestroy([](llvm::Error E) { consumeError(std::move(E)); });
907+
OutputFiles.push_back(std::move(*O));
908+
return OutputFiles.back().createProxy();
966909
}
967910

968911
// Initialization Utilities

clang/tools/clang-installapi/ClangInstallAPI.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,15 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
153153
return EXIT_FAILURE;
154154

155155
// After symbols have been collected, prepare to write output.
156-
auto Out = CI->createOutputFile(Ctx.OutputLoc, /*Binary=*/false,
157-
/*RemoveFileOnSignal=*/false,
158-
/*UseTemporary=*/false,
159-
/*CreateMissingDirectories=*/false);
160-
if (!Out)
156+
auto Out = CI->getOrCreateOutputManager().createFile(
157+
Ctx.OutputLoc, llvm::vfs::OutputConfig()
158+
.setTextWithCRLF()
159+
.setNoImplyCreateDirectories()
160+
.setNoAtomicWrite());
161+
if (!Out) {
162+
Diag->Report(diag::err_cannot_open_file) << Ctx.OutputLoc;
161163
return EXIT_FAILURE;
164+
}
162165

163166
// Assign attributes for serialization.
164167
InterfaceFile IF(Ctx.Verifier->takeExports());
@@ -186,11 +189,15 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
186189
if (auto Err = TextAPIWriter::writeToStream(*Out, IF, Ctx.FT)) {
187190
Diag->Report(diag::err_cannot_write_file)
188191
<< Ctx.OutputLoc << std::move(Err);
189-
CI->clearOutputFiles(/*EraseFiles=*/true);
192+
if (auto Err = Out->discard())
193+
llvm::consumeError(std::move(Err));
194+
return EXIT_FAILURE;
195+
}
196+
if (auto Err = Out->keep()) {
197+
Diag->Report(diag::err_cannot_write_file)
198+
<< Ctx.OutputLoc << std::move(Err);
190199
return EXIT_FAILURE;
191200
}
192-
193-
CI->clearOutputFiles(/*EraseFiles=*/false);
194201
return EXIT_SUCCESS;
195202
}
196203

0 commit comments

Comments
 (0)