Index: clang/include/clang/Frontend/CompilerInstance.h =================================================================== --- clang/include/clang/Frontend/CompilerInstance.h +++ clang/include/clang/Frontend/CompilerInstance.h @@ -22,6 +22,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/BuryPointer.h" +#include "llvm/Support/OutputManager.h" #include #include #include @@ -86,6 +87,9 @@ /// The file manager. IntrusiveRefCntPtr FileMgr; + /// The output context. + std::shared_ptr TheOutputManager; + /// The source manager. IntrusiveRefCntPtr SourceMgr; @@ -158,22 +162,8 @@ /// The stream for verbose output. raw_ostream *VerboseOutputStream = &llvm::errs(); - /// Holds information about the output file. - /// - /// If TempFilename is not empty we must rename it to Filename at the end. - /// TempFilename may be empty and Filename non-empty if creating the temporary - /// failed. - struct OutputFile { - std::string Filename; - std::string TempFilename; - - OutputFile(std::string filename, std::string tempFilename) - : Filename(std::move(filename)), TempFilename(std::move(tempFilename)) { - } - }; - /// The list of active output files. - std::list OutputFiles; + std::list> OutputFiles; /// Force an output buffer. std::unique_ptr OutputStream; @@ -408,6 +398,20 @@ /// Replace the current file manager and virtual file system. void setFileManager(FileManager *Value); + /// Set the output manager. + void setOutputManager(std::shared_ptr NewOutputs); + + /// Create an output manager. + void createOutputManager(); + + bool hasOutputManager() const { return bool(TheOutputManager); } + + std::shared_ptr getSharedOutputManager() { + return TheOutputManager; + } + llvm::vfs::OutputManager &getOutputManager(); + llvm::vfs::OutputManager &getOrCreateOutputManager(); + /// } /// @name Source Manager /// { Index: clang/lib/Frontend/CompilerInstance.cpp =================================================================== --- clang/lib/Frontend/CompilerInstance.cpp +++ clang/lib/Frontend/CompilerInstance.cpp @@ -459,6 +459,10 @@ collectVFSEntries(*this, ModuleDepCollector); } + // Modules need an output manager. + if (!hasOutputManager()) + createOutputManager(); + for (auto &Listener : DependencyCollectors) Listener->attachToPreprocessor(*PP); @@ -647,27 +651,19 @@ // Output Files void CompilerInstance::clearOutputFiles(bool EraseFiles) { - for (OutputFile &OF : OutputFiles) { - if (EraseFiles) { - if (!OF.TempFilename.empty()) { - llvm::sys::fs::remove(OF.TempFilename); - continue; - } - if (!OF.Filename.empty()) - llvm::sys::fs::remove(OF.Filename); - continue; - } - - if (OF.TempFilename.empty()) - continue; - - std::error_code EC = llvm::sys::fs::rename(OF.TempFilename, OF.Filename); - if (!EC) - continue; - getDiagnostics().Report(diag::err_unable_to_rename_temp) - << OF.TempFilename << OF.Filename << EC.message(); - - llvm::sys::fs::remove(OF.TempFilename); + if (!EraseFiles) { + for (auto &O : OutputFiles) + llvm::handleAllErrors( + O->close(), + [&](const llvm::vfs::OnDiskOutputRenameTempError &E) { + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << E.getTempPath() << E.getOutputPath() + << E.getErrorCode().message(); + }, + [&](const llvm::vfs::OutputError &E) { + getDiagnostics().Report(diag::err_fe_unable_to_open_output) + << E.getOutputPath() << E.getErrorCode().message(); + }); } OutputFiles.clear(); if (DeleteBuiltModules) { @@ -704,6 +700,28 @@ return std::make_unique(); } +void CompilerInstance::setOutputManager( + std::shared_ptr NewOutputs) { + assert(!TheOutputManager && "Already has an output manager"); + TheOutputManager = std::move(NewOutputs); +} + +void CompilerInstance::createOutputManager() { + assert(!TheOutputManager && "Already has an output manager"); + TheOutputManager = std::make_unique(); +} + +llvm::vfs::OutputManager &CompilerInstance::getOutputManager() { + assert(TheOutputManager); + return *TheOutputManager; +} + +llvm::vfs::OutputManager &CompilerInstance::getOrCreateOutputManager() { + if (!hasOutputManager()) + createOutputManager(); + return getOutputManager(); +} + std::unique_ptr CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, bool UseTemporary, @@ -735,86 +753,21 @@ OutputPath = *AbsPath; } - std::unique_ptr OS; - Optional OSFile; + using namespace llvm::vfs; + Expected> O = getOrCreateOutputManager().createOutput( + OutputPath, + PartialOutputConfig() + .set(ClientIntentOutputConfig::NeedsSeeking, Binary) + .set(OnDiskOutputConfig::OpenFlagText, !Binary) + .set(OnDiskOutputConfig::RemoveFileOnSignal, RemoveFileOnSignal) + .set(OnDiskOutputConfig::UseTemporary, UseTemporary) + .set(OnDiskOutputConfig::UseTemporaryCreateMissingDirectories, + CreateMissingDirectories)); + if (!O) + return O.takeError(); - if (UseTemporary) { - if (OutputPath == "-") - UseTemporary = false; - else { - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(OutputPath, Status); - if (llvm::sys::fs::exists(Status)) { - // Fail early if we can't write to the final destination. - if (!llvm::sys::fs::can_write(OutputPath)) - return llvm::errorCodeToError( - make_error_code(llvm::errc::operation_not_permitted)); - - // Don't use a temporary if the output is a special file. This handles - // things like '-o /dev/null' - if (!llvm::sys::fs::is_regular_file(Status)) - UseTemporary = false; - } - } - } - - std::string TempFile; - if (UseTemporary) { - // Create a temporary file. - // Insert -%%%%%%%% before the extension (if any), and because some tools - // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build - // artifacts, also append .tmp. - StringRef OutputExtension = llvm::sys::path::extension(OutputPath); - SmallString<128> TempPath = - StringRef(OutputPath).drop_back(OutputExtension.size()); - TempPath += "-%%%%%%%%"; - TempPath += OutputExtension; - TempPath += ".tmp"; - int fd; - std::error_code EC = - llvm::sys::fs::createUniqueFile(TempPath, fd, TempPath); - - if (CreateMissingDirectories && - EC == llvm::errc::no_such_file_or_directory) { - StringRef Parent = llvm::sys::path::parent_path(OutputPath); - EC = llvm::sys::fs::create_directories(Parent); - if (!EC) { - EC = llvm::sys::fs::createUniqueFile(TempPath, fd, TempPath); - } - } - - if (!EC) { - OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); - OSFile = TempFile = std::string(TempPath.str()); - } - // If we failed to create the temporary, fallback to writing to the file - // directly. This handles the corner case where we cannot write to the - // directory, but can write to the file. - } - - if (!OS) { - OSFile = OutputPath; - std::error_code EC; - OS.reset(new llvm::raw_fd_ostream( - *OSFile, EC, - (Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text))); - if (EC) - return llvm::errorCodeToError(EC); - } - - // Make sure the out stream file gets removed if we crash. - if (RemoveFileOnSignal) - llvm::sys::RemoveFileOnSignal(*OSFile); - - // Add the output file -- but don't try to remove "-", since this means we are - // using stdin. - OutputFiles.emplace_back(((OutputPath != "-") ? OutputPath : "").str(), - std::move(TempFile)); - - if (!Binary || OS->supportsSeeking()) - return std::move(OS); - - return std::make_unique(std::move(OS)); + OutputFiles.push_back(std::move(*O)); + return OutputFiles.back()->takeOS(); } // Initialization Utilities @@ -1117,6 +1070,11 @@ SourceMgr.pushModuleBuildStack(ModuleName, FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + // Share an output manager. + assert(ImportingInstance.hasOutputManager() && + "Expected an output manager to already be set up"); + Instance.setOutputManager(ImportingInstance.getSharedOutputManager()); + // If we're collecting module dependencies, we need to share a collector // between all of the module CompilerInstances. Other than that, we don't // want to produce any dependency output from the module build.