Skip to content

Commit b464157

Browse files
rebase and review comments
Created using spr 1.3.5
2 parents e575483 + 2809c30 commit b464157

23 files changed

+3545
-133
lines changed

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/StringRef.h"
2525
#include "llvm/Support/BuryPointer.h"
2626
#include "llvm/Support/FileSystem.h"
27+
#include "llvm/Support/VirtualOutputBackend.h"
2728
#include <cassert>
2829
#include <list>
2930
#include <memory>
@@ -92,6 +93,9 @@ class CompilerInstance : public ModuleLoader {
9293
/// The file manager.
9394
IntrusiveRefCntPtr<FileManager> FileMgr;
9495

96+
/// The output context.
97+
IntrusiveRefCntPtr<llvm::vfs::OutputBackend> TheOutputBackend;
98+
9599
/// The source manager.
96100
IntrusiveRefCntPtr<SourceManager> SourceMgr;
97101

@@ -182,22 +186,8 @@ class CompilerInstance : public ModuleLoader {
182186
/// The stream for verbose output.
183187
raw_ostream *VerboseOutputStream = &llvm::errs();
184188

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

202192
/// Force an output buffer.
203193
std::unique_ptr<llvm::raw_pwrite_stream> OutputStream;
@@ -459,6 +449,22 @@ class CompilerInstance : public ModuleLoader {
459449

460450
/// Replace the current file manager and virtual file system.
461451
void setFileManager(FileManager *Value);
452+
/// @name Output Backend.
453+
/// {
454+
455+
/// Set the output backend.
456+
void
457+
setOutputBackend(IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs);
458+
459+
/// Create an output manager.
460+
void createOutputBackend();
461+
462+
bool hasOutputBackend() const { return bool(TheOutputBackend); }
463+
464+
llvm::vfs::OutputBackend &getOutputBackend();
465+
llvm::vfs::OutputBackend &getOrCreateOutputBackend();
466+
467+
/// }
462468

463469
/// @}
464470
/// @name Source Manager

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 56 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#include "llvm/Support/Signals.h"
5555
#include "llvm/Support/TimeProfiler.h"
5656
#include "llvm/Support/Timer.h"
57+
#include "llvm/Support/VirtualOutputBackends.h"
58+
#include "llvm/Support/VirtualOutputError.h"
5759
#include "llvm/Support/raw_ostream.h"
5860
#include "llvm/TargetParser/Host.h"
5961
#include <optional>
@@ -514,6 +516,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
514516
collectVFSEntries(*this, ModuleDepCollector);
515517
}
516518

519+
// Modules need an output manager.
520+
if (!hasOutputBackend())
521+
createOutputBackend();
522+
517523
for (auto &Listener : DependencyCollectors)
518524
Listener->attachToPreprocessor(*PP);
519525

@@ -769,32 +775,19 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind,
769775
void CompilerInstance::clearOutputFiles(bool EraseFiles) {
770776
// The ASTConsumer can own streams that write to the output files.
771777
assert(!hasASTConsumer() && "ASTConsumer should be reset");
772-
// Ignore errors that occur when trying to discard the temp file.
773-
for (OutputFile &OF : OutputFiles) {
774-
if (EraseFiles) {
775-
if (OF.File)
776-
consumeError(OF.File->discard());
777-
if (!OF.Filename.empty())
778-
llvm::sys::fs::remove(OF.Filename);
779-
continue;
780-
}
781-
782-
if (!OF.File)
783-
continue;
784-
785-
if (OF.File->TmpName.empty()) {
786-
consumeError(OF.File->discard());
787-
continue;
788-
}
789-
790-
llvm::Error E = OF.File->keep(OF.Filename);
791-
if (!E)
792-
continue;
793-
794-
getDiagnostics().Report(diag::err_unable_to_rename_temp)
795-
<< OF.File->TmpName << OF.Filename << std::move(E);
796-
797-
llvm::sys::fs::remove(OF.File->TmpName);
778+
if (!EraseFiles) {
779+
for (auto &O : OutputFiles)
780+
llvm::handleAllErrors(
781+
O.keep(),
782+
[&](const llvm::vfs::TempFileOutputError &E) {
783+
getDiagnostics().Report(diag::err_unable_to_rename_temp)
784+
<< E.getTempPath() << E.getOutputPath()
785+
<< E.convertToErrorCode().message();
786+
},
787+
[&](const llvm::vfs::OutputError &E) {
788+
getDiagnostics().Report(diag::err_fe_unable_to_open_output)
789+
<< E.getOutputPath() << E.convertToErrorCode().message();
790+
});
798791
}
799792
OutputFiles.clear();
800793
if (DeleteBuiltModules) {
@@ -828,6 +821,29 @@ std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() {
828821
return std::make_unique<llvm::raw_null_ostream>();
829822
}
830823

824+
void CompilerInstance::setOutputBackend(
825+
IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs) {
826+
assert(!TheOutputBackend && "Already has an output manager");
827+
TheOutputBackend = std::move(NewOutputs);
828+
}
829+
830+
void CompilerInstance::createOutputBackend() {
831+
assert(!TheOutputBackend && "Already has an output manager");
832+
TheOutputBackend =
833+
llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
834+
}
835+
836+
llvm::vfs::OutputBackend &CompilerInstance::getOutputBackend() {
837+
assert(TheOutputBackend);
838+
return *TheOutputBackend;
839+
}
840+
841+
llvm::vfs::OutputBackend &CompilerInstance::getOrCreateOutputBackend() {
842+
if (!hasOutputBackend())
843+
createOutputBackend();
844+
return getOutputBackend();
845+
}
846+
831847
std::unique_ptr<raw_pwrite_stream>
832848
CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary,
833849
bool RemoveFileOnSignal, bool UseTemporary,
@@ -862,98 +878,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary,
862878
OutputPath = *AbsPath;
863879
}
864880

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

959897
// Initialization Utilities
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//===- HashingOutputBackends.h - Hashing output backends --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_SUPPORT_HASHINGOUTPUTBACKEND_H
10+
#define LLVM_SUPPORT_HASHINGOUTPUTBACKEND_H
11+
12+
#include "llvm/ADT/StringExtras.h"
13+
#include "llvm/ADT/StringMap.h"
14+
#include "llvm/Support/Endian.h"
15+
#include "llvm/Support/HashBuilder.h"
16+
#include "llvm/Support/VirtualOutputBackend.h"
17+
#include "llvm/Support/VirtualOutputConfig.h"
18+
#include "llvm/Support/raw_ostream.h"
19+
20+
namespace llvm::vfs {
21+
22+
/// raw_pwrite_stream that writes to a hasher.
23+
template <typename HasherT>
24+
class HashingStream : public llvm::raw_pwrite_stream {
25+
private:
26+
SmallVector<char> Buffer;
27+
raw_svector_ostream OS;
28+
29+
using HashBuilderT = HashBuilder<HasherT, support::endianness::native>;
30+
HashBuilderT Builder;
31+
32+
void write_impl(const char *Ptr, size_t Size) override {
33+
OS.write(Ptr, Size);
34+
}
35+
36+
void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override {
37+
OS.pwrite(Ptr, Size, Offset);
38+
}
39+
40+
uint64_t current_pos() const override { return OS.str().size(); }
41+
42+
public:
43+
HashingStream() : OS(Buffer) { SetUnbuffered(); }
44+
45+
auto final() {
46+
Builder.update(OS.str());
47+
return Builder.final();
48+
}
49+
};
50+
51+
template <typename HasherT> class HashingOutputFile;
52+
53+
/// An output backend that only generates the hash for outputs.
54+
template <typename HasherT> class HashingOutputBackend : public OutputBackend {
55+
private:
56+
friend class HashingOutputFile<HasherT>;
57+
void addOutputFile(StringRef Path, StringRef Hash) {
58+
OutputHashes[Path] = std::string(Hash);
59+
}
60+
61+
protected:
62+
IntrusiveRefCntPtr<OutputBackend> cloneImpl() const override {
63+
return const_cast<HashingOutputBackend<HasherT> *>(this);
64+
}
65+
66+
Expected<std::unique_ptr<OutputFileImpl>>
67+
createFileImpl(StringRef Path, std::optional<OutputConfig> Config) override {
68+
return std::make_unique<HashingOutputFile<HasherT>>(Path, *this);
69+
}
70+
71+
public:
72+
/// Iterator for all the output file names.
73+
auto outputFiles() const { return OutputHashes.keys(); }
74+
75+
/// Get hash value for the output files in hex representation.
76+
/// Return None if the requested path is not generated.
77+
std::optional<std::string> getHashValueForFile(StringRef Path) {
78+
auto F = OutputHashes.find(Path);
79+
if (F == OutputHashes.end())
80+
return std::nullopt;
81+
return toHex(F->second);
82+
}
83+
84+
private:
85+
StringMap<std::string> OutputHashes;
86+
};
87+
88+
/// HashingOutputFile.
89+
template <typename HasherT>
90+
class HashingOutputFile final : public OutputFileImpl {
91+
public:
92+
Error keep() override {
93+
auto Result = OS.final();
94+
Backend.addOutputFile(OutputPath, toStringRef(Result));
95+
return Error::success();
96+
}
97+
Error discard() override { return Error::success(); }
98+
raw_pwrite_stream &getOS() override { return OS; }
99+
100+
HashingOutputFile(StringRef OutputPath,
101+
HashingOutputBackend<HasherT> &Backend)
102+
: OutputPath(OutputPath.str()), Backend(Backend) {}
103+
104+
private:
105+
const std::string OutputPath;
106+
HashingStream<HasherT> OS;
107+
HashingOutputBackend<HasherT> &Backend;
108+
};
109+
110+
} // namespace llvm::vfs
111+
112+
#endif // LLVM_SUPPORT_HASHINGOUTPUTBACKEND_H

0 commit comments

Comments
 (0)