|
54 | 54 | #include "llvm/Support/Signals.h" |
55 | 55 | #include "llvm/Support/TimeProfiler.h" |
56 | 56 | #include "llvm/Support/Timer.h" |
| 57 | +#include "llvm/Support/VirtualOutputBackends.h" |
| 58 | +#include "llvm/Support/VirtualOutputError.h" |
57 | 59 | #include "llvm/Support/raw_ostream.h" |
58 | 60 | #include "llvm/TargetParser/Host.h" |
59 | 61 | #include <optional> |
@@ -514,6 +516,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { |
514 | 516 | collectVFSEntries(*this, ModuleDepCollector); |
515 | 517 | } |
516 | 518 |
|
| 519 | + // Modules need an output manager. |
| 520 | + if (!hasOutputBackend()) |
| 521 | + createOutputBackend(); |
| 522 | + |
517 | 523 | for (auto &Listener : DependencyCollectors) |
518 | 524 | Listener->attachToPreprocessor(*PP); |
519 | 525 |
|
@@ -769,32 +775,19 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, |
769 | 775 | void CompilerInstance::clearOutputFiles(bool EraseFiles) { |
770 | 776 | // The ASTConsumer can own streams that write to the output files. |
771 | 777 | 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 | + }); |
798 | 791 | } |
799 | 792 | OutputFiles.clear(); |
800 | 793 | if (DeleteBuiltModules) { |
@@ -828,6 +821,29 @@ std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() { |
828 | 821 | return std::make_unique<llvm::raw_null_ostream>(); |
829 | 822 | } |
830 | 823 |
|
| 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 | + |
831 | 847 | std::unique_ptr<raw_pwrite_stream> |
832 | 848 | CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, |
833 | 849 | bool RemoveFileOnSignal, bool UseTemporary, |
@@ -862,98 +878,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary, |
862 | 878 | OutputPath = *AbsPath; |
863 | 879 | } |
864 | 880 |
|
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(); |
957 | 895 | } |
958 | 896 |
|
959 | 897 | // Initialization Utilities |
|
0 commit comments