|
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> |
@@ -516,6 +518,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { |
516 | 518 | collectVFSEntries(*this, ModuleDepCollector); |
517 | 519 | } |
518 | 520 |
|
| 521 | + // Modules need an output manager. |
| 522 | + if (!hasOutputBackend()) |
| 523 | + createOutputBackend(); |
| 524 | + |
519 | 525 | for (auto &Listener : DependencyCollectors) |
520 | 526 | Listener->attachToPreprocessor(*PP); |
521 | 527 |
|
@@ -759,32 +765,19 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, |
759 | 765 | void CompilerInstance::clearOutputFiles(bool EraseFiles) { |
760 | 766 | // The ASTConsumer can own streams that write to the output files. |
761 | 767 | assert(!hasASTConsumer() && "ASTConsumer should be reset"); |
762 | | - // Ignore errors that occur when trying to discard the temp file. |
763 | | - for (OutputFile &OF : OutputFiles) { |
764 | | - if (EraseFiles) { |
765 | | - if (OF.File) |
766 | | - consumeError(OF.File->discard()); |
767 | | - if (!OF.Filename.empty()) |
768 | | - llvm::sys::fs::remove(OF.Filename); |
769 | | - continue; |
770 | | - } |
771 | | - |
772 | | - if (!OF.File) |
773 | | - continue; |
774 | | - |
775 | | - if (OF.File->TmpName.empty()) { |
776 | | - consumeError(OF.File->discard()); |
777 | | - continue; |
778 | | - } |
779 | | - |
780 | | - llvm::Error E = OF.File->keep(OF.Filename); |
781 | | - if (!E) |
782 | | - continue; |
783 | | - |
784 | | - getDiagnostics().Report(diag::err_unable_to_rename_temp) |
785 | | - << OF.File->TmpName << OF.Filename << std::move(E); |
786 | | - |
787 | | - llvm::sys::fs::remove(OF.File->TmpName); |
| 768 | + if (!EraseFiles) { |
| 769 | + for (auto &O : OutputFiles) |
| 770 | + llvm::handleAllErrors( |
| 771 | + O.keep(), |
| 772 | + [&](const llvm::vfs::TempFileOutputError &E) { |
| 773 | + getDiagnostics().Report(diag::err_unable_to_rename_temp) |
| 774 | + << E.getTempPath() << E.getOutputPath() |
| 775 | + << E.convertToErrorCode().message(); |
| 776 | + }, |
| 777 | + [&](const llvm::vfs::OutputError &E) { |
| 778 | + getDiagnostics().Report(diag::err_fe_unable_to_open_output) |
| 779 | + << E.getOutputPath() << E.convertToErrorCode().message(); |
| 780 | + }); |
788 | 781 | } |
789 | 782 | OutputFiles.clear(); |
790 | 783 | if (DeleteBuiltModules) { |
@@ -818,6 +811,29 @@ std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() { |
818 | 811 | return std::make_unique<llvm::raw_null_ostream>(); |
819 | 812 | } |
820 | 813 |
|
| 814 | +void CompilerInstance::setOutputBackend( |
| 815 | + IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs) { |
| 816 | + assert(!TheOutputBackend && "Already has an output manager"); |
| 817 | + TheOutputBackend = std::move(NewOutputs); |
| 818 | +} |
| 819 | + |
| 820 | +void CompilerInstance::createOutputBackend() { |
| 821 | + assert(!TheOutputBackend && "Already has an output manager"); |
| 822 | + TheOutputBackend = |
| 823 | + llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>(); |
| 824 | +} |
| 825 | + |
| 826 | +llvm::vfs::OutputBackend &CompilerInstance::getOutputBackend() { |
| 827 | + assert(TheOutputBackend); |
| 828 | + return *TheOutputBackend; |
| 829 | +} |
| 830 | + |
| 831 | +llvm::vfs::OutputBackend &CompilerInstance::getOrCreateOutputBackend() { |
| 832 | + if (!hasOutputBackend()) |
| 833 | + createOutputBackend(); |
| 834 | + return getOutputBackend(); |
| 835 | +} |
| 836 | + |
821 | 837 | std::unique_ptr<raw_pwrite_stream> |
822 | 838 | CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, |
823 | 839 | bool RemoveFileOnSignal, bool UseTemporary, |
@@ -852,98 +868,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary, |
852 | 868 | OutputPath = *AbsPath; |
853 | 869 | } |
854 | 870 |
|
855 | | - std::unique_ptr<llvm::raw_fd_ostream> OS; |
856 | | - std::optional<StringRef> OSFile; |
857 | | - |
858 | | - if (UseTemporary) { |
859 | | - if (OutputPath == "-") |
860 | | - UseTemporary = false; |
861 | | - else { |
862 | | - llvm::sys::fs::file_status Status; |
863 | | - llvm::sys::fs::status(OutputPath, Status); |
864 | | - if (llvm::sys::fs::exists(Status)) { |
865 | | - // Fail early if we can't write to the final destination. |
866 | | - if (!llvm::sys::fs::can_write(OutputPath)) |
867 | | - return llvm::errorCodeToError( |
868 | | - make_error_code(llvm::errc::operation_not_permitted)); |
869 | | - |
870 | | - // Don't use a temporary if the output is a special file. This handles |
871 | | - // things like '-o /dev/null' |
872 | | - if (!llvm::sys::fs::is_regular_file(Status)) |
873 | | - UseTemporary = false; |
874 | | - } |
875 | | - } |
876 | | - } |
877 | | - |
878 | | - std::optional<llvm::sys::fs::TempFile> Temp; |
879 | | - if (UseTemporary) { |
880 | | - // Create a temporary file. |
881 | | - // Insert -%%%%%%%% before the extension (if any), and because some tools |
882 | | - // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build |
883 | | - // artifacts, also append .tmp. |
884 | | - StringRef OutputExtension = llvm::sys::path::extension(OutputPath); |
885 | | - SmallString<128> TempPath = |
886 | | - StringRef(OutputPath).drop_back(OutputExtension.size()); |
887 | | - TempPath += "-%%%%%%%%"; |
888 | | - TempPath += OutputExtension; |
889 | | - TempPath += ".tmp"; |
890 | | - llvm::sys::fs::OpenFlags BinaryFlags = |
891 | | - Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text; |
892 | | - Expected<llvm::sys::fs::TempFile> ExpectedFile = |
893 | | - llvm::sys::fs::TempFile::create( |
894 | | - TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, |
895 | | - BinaryFlags); |
896 | | - |
897 | | - llvm::Error E = handleErrors( |
898 | | - ExpectedFile.takeError(), [&](const llvm::ECError &E) -> llvm::Error { |
899 | | - std::error_code EC = E.convertToErrorCode(); |
900 | | - if (CreateMissingDirectories && |
901 | | - EC == llvm::errc::no_such_file_or_directory) { |
902 | | - StringRef Parent = llvm::sys::path::parent_path(OutputPath); |
903 | | - EC = llvm::sys::fs::create_directories(Parent); |
904 | | - if (!EC) { |
905 | | - ExpectedFile = llvm::sys::fs::TempFile::create( |
906 | | - TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, |
907 | | - BinaryFlags); |
908 | | - if (!ExpectedFile) |
909 | | - return llvm::errorCodeToError( |
910 | | - llvm::errc::no_such_file_or_directory); |
911 | | - } |
912 | | - } |
913 | | - return llvm::errorCodeToError(EC); |
914 | | - }); |
915 | | - |
916 | | - if (E) { |
917 | | - consumeError(std::move(E)); |
918 | | - } else { |
919 | | - Temp = std::move(ExpectedFile.get()); |
920 | | - OS.reset(new llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false)); |
921 | | - OSFile = Temp->TmpName; |
922 | | - } |
923 | | - // If we failed to create the temporary, fallback to writing to the file |
924 | | - // directly. This handles the corner case where we cannot write to the |
925 | | - // directory, but can write to the file. |
926 | | - } |
927 | | - |
928 | | - if (!OS) { |
929 | | - OSFile = OutputPath; |
930 | | - std::error_code EC; |
931 | | - OS.reset(new llvm::raw_fd_ostream( |
932 | | - *OSFile, EC, |
933 | | - (Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); |
934 | | - if (EC) |
935 | | - return llvm::errorCodeToError(EC); |
936 | | - } |
937 | | - |
938 | | - // Add the output file -- but don't try to remove "-", since this means we are |
939 | | - // using stdin. |
940 | | - OutputFiles.emplace_back(((OutputPath != "-") ? OutputPath : "").str(), |
941 | | - std::move(Temp)); |
942 | | - |
943 | | - if (!Binary || OS->supportsSeeking()) |
944 | | - return std::move(OS); |
945 | | - |
946 | | - return std::make_unique<llvm::buffer_unique_ostream>(std::move(OS)); |
| 871 | + using namespace llvm::vfs; |
| 872 | + Expected<OutputFile> O = getOrCreateOutputBackend().createFile( |
| 873 | + OutputPath, |
| 874 | + OutputConfig() |
| 875 | + .setTextWithCRLF(!Binary) |
| 876 | + .setDiscardOnSignal(RemoveFileOnSignal) |
| 877 | + .setAtomicWrite(UseTemporary) |
| 878 | + .setImplyCreateDirectories(UseTemporary && CreateMissingDirectories)); |
| 879 | + if (!O) |
| 880 | + return O.takeError(); |
| 881 | + |
| 882 | + O->discardOnDestroy([](llvm::Error E) { consumeError(std::move(E)); }); |
| 883 | + OutputFiles.push_back(std::move(*O)); |
| 884 | + return OutputFiles.back().createProxy(); |
947 | 885 | } |
948 | 886 |
|
949 | 887 | // Initialization Utilities |
|
0 commit comments