| 
57 | 57 | #include "llvm/Support/TimeProfiler.h"  | 
58 | 58 | #include "llvm/Support/Timer.h"  | 
59 | 59 | #include "llvm/Support/VirtualFileSystem.h"  | 
 | 60 | +#include "llvm/Support/VirtualOutputBackends.h"  | 
 | 61 | +#include "llvm/Support/VirtualOutputError.h"  | 
60 | 62 | #include "llvm/Support/raw_ostream.h"  | 
61 | 63 | #include "llvm/TargetParser/Host.h"  | 
62 | 64 | #include <optional>  | 
@@ -522,6 +524,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {  | 
522 | 524 |     collectVFSEntries(*this, ModuleDepCollector);  | 
523 | 525 |   }  | 
524 | 526 | 
 
  | 
 | 527 | +  // Modules need an output manager.  | 
 | 528 | +  if (!hasOutputManager())  | 
 | 529 | +    createOutputManager();  | 
 | 530 | + | 
525 | 531 |   for (auto &Listener : DependencyCollectors)  | 
526 | 532 |     Listener->attachToPreprocessor(*PP);  | 
527 | 533 | 
 
  | 
@@ -778,32 +784,23 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind,  | 
778 | 784 | void CompilerInstance::clearOutputFiles(bool EraseFiles) {  | 
779 | 785 |   // The ASTConsumer can own streams that write to the output files.  | 
780 | 786 |   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 | +          });  | 
807 | 804 |   }  | 
808 | 805 |   OutputFiles.clear();  | 
809 | 806 |   if (DeleteBuiltModules) {  | 
@@ -837,6 +834,30 @@ std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() {  | 
837 | 834 |   return std::make_unique<llvm::raw_null_ostream>();  | 
838 | 835 | }  | 
839 | 836 | 
 
  | 
 | 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 | + | 
840 | 861 | std::unique_ptr<raw_pwrite_stream>  | 
841 | 862 | CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary,  | 
842 | 863 |                                    bool RemoveFileOnSignal, bool UseTemporary,  | 
@@ -871,98 +892,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary,  | 
871 | 892 |     OutputPath = *AbsPath;  | 
872 | 893 |   }  | 
873 | 894 | 
 
  | 
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();  | 
966 | 909 | }  | 
967 | 910 | 
 
  | 
968 | 911 | // Initialization Utilities  | 
 | 
0 commit comments