/// The frontend timer
llvm::OwningPtr<llvm::Timer> FrontendTimer;
+ /// \brief 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;
+ llvm::raw_ostream *OS;
+
+ OutputFile(const std::string &filename, const std::string &tempFilename,
+ llvm::raw_ostream *os)
+ : Filename(filename), TempFilename(tempFilename), OS(os) { }
+ };
+
/// The list of active output files.
- std::list< std::pair<std::string, llvm::raw_ostream*> > OutputFiles;
+ std::list<OutputFile> OutputFiles;
void operator=(const CompilerInstance &); // DO NOT IMPLEMENT
CompilerInstance(const CompilerInstance&); // DO NOT IMPLEMENT
/// addOutputFile - Add an output file onto the list of tracked output files.
///
- /// \param Path - The path to the output file, or empty.
- /// \param OS - The output stream, which should be non-null.
- void addOutputFile(llvm::StringRef Path, llvm::raw_ostream *OS);
+ /// \param OutFile - The output file info.
+ void addOutputFile(const OutputFile &OutFile);
/// clearOutputFiles - Clear the output file list, destroying the contained
/// output streams.
///
/// If \arg OutputPath is empty, then createOutputFile will derive an output
/// path location as \arg BaseInput, with any suffix removed, and \arg
- /// Extension appended.
+ /// Extension appended. If OutputPath is not stdout createOutputFile will
+ /// create a new temporary file that must be renamed to OutputPath in the end.
///
/// \param OutputPath - If given, the path to the output file.
/// \param Error [out] - On failure, the error message.
/// \param Binary - The mode to open the file in.
/// \param ResultPathName [out] - If given, the result path name will be
/// stored here on success.
+ /// \param TempPathName [out] - If given, the temporary file path name
+ /// will be stored here on success.
static llvm::raw_fd_ostream *
createOutputFile(llvm::StringRef OutputPath, std::string &Error,
bool Binary = true, llvm::StringRef BaseInput = "",
llvm::StringRef Extension = "",
- std::string *ResultPathName = 0);
+ std::string *ResultPathName = 0,
+ std::string *TempPathName = 0);
/// }
/// @name Initialization Utility Methods
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/System/Program.h"
+#include "llvm/System/Signals.h"
using namespace clang;
CompilerInstance::CompilerInstance()
// Output Files
-void CompilerInstance::addOutputFile(llvm::StringRef Path,
- llvm::raw_ostream *OS) {
- assert(OS && "Attempt to add empty stream to output list!");
- OutputFiles.push_back(std::make_pair(Path, OS));
+void CompilerInstance::addOutputFile(const OutputFile &OutFile) {
+ assert(OutFile.OS && "Attempt to add empty stream to output list!");
+ OutputFiles.push_back(OutFile);
}
void CompilerInstance::clearOutputFiles(bool EraseFiles) {
- for (std::list< std::pair<std::string, llvm::raw_ostream*> >::iterator
+ for (std::list<OutputFile>::iterator
it = OutputFiles.begin(), ie = OutputFiles.end(); it != ie; ++it) {
- delete it->second;
- if (EraseFiles && !it->first.empty())
- llvm::sys::Path(it->first).eraseFromDisk();
+ delete it->OS;
+ if (!it->TempFilename.empty()) {
+ llvm::sys::Path TempPath(it->TempFilename);
+ if (EraseFiles)
+ TempPath.eraseFromDisk();
+ else {
+ std::string Error;
+ if (TempPath.renamePathOnDisk(llvm::sys::Path(it->Filename), &Error)) {
+ getDiagnostics().Report(diag::err_fe_unable_to_rename_temp)
+ << it->TempFilename << it->Filename << Error;
+ TempPath.eraseFromDisk();
+ }
+ }
+ } else if (!it->Filename.empty() && EraseFiles)
+ llvm::sys::Path(it->Filename).eraseFromDisk();
+
}
OutputFiles.clear();
}
bool Binary,
llvm::StringRef InFile,
llvm::StringRef Extension) {
- std::string Error, OutputPathName;
+ std::string Error, OutputPathName, TempPathName;
llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary,
InFile, Extension,
- &OutputPathName);
+ &OutputPathName,
+ &TempPathName);
if (!OS) {
getDiagnostics().Report(diag::err_fe_unable_to_open_output)
<< OutputPath << Error;
// Add the output file -- but don't try to remove "-", since this means we are
// using stdin.
- addOutputFile((OutputPathName != "-") ? OutputPathName : "", OS);
+ addOutputFile(OutputFile((OutputPathName != "-") ? OutputPathName : "",
+ TempPathName, OS));
return OS;
}
bool Binary,
llvm::StringRef InFile,
llvm::StringRef Extension,
- std::string *ResultPathName) {
- std::string OutFile;
+ std::string *ResultPathName,
+ std::string *TempPathName) {
+ std::string OutFile, TempFile;
if (!OutputPath.empty()) {
OutFile = OutputPath;
} else if (InFile == "-") {
} else {
OutFile = "-";
}
+
+ if (OutFile != "-") {
+ llvm::sys::Path OutPath(OutFile);
+ // Only create the temporary if we can actually write to OutPath, otherwise
+ // we want to fail early.
+ if (!OutPath.exists() ||
+ (OutPath.isRegularFile() && OutPath.canWrite())) {
+ // Create a temporary file.
+ llvm::sys::Path TempPath(OutFile);
+ if (!TempPath.createTemporaryFileOnDisk())
+ TempFile = TempPath.str();
+ }
+ }
+
+ std::string OSFile = OutFile;
+ if (!TempFile.empty())
+ OSFile = TempFile;
llvm::OwningPtr<llvm::raw_fd_ostream> OS(
- new llvm::raw_fd_ostream(OutFile.c_str(), Error,
+ new llvm::raw_fd_ostream(OSFile.c_str(), Error,
(Binary ? llvm::raw_fd_ostream::F_Binary : 0)));
if (!Error.empty())
return 0;
+ // Make sure the out stream file gets removed if we crash.
+ llvm::sys::RemoveFileOnSignal(llvm::sys::Path(OSFile));
+
if (ResultPathName)
*ResultPathName = OutFile;
+ if (TempPathName)
+ *TempPathName = TempFile;
return OS.take();
}