clang API Documentation

FileRemapper.cpp
Go to the documentation of this file.
00001 //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "clang/ARCMigrate/FileRemapper.h"
00011 #include "clang/Basic/Diagnostic.h"
00012 #include "clang/Basic/FileManager.h"
00013 #include "clang/Lex/PreprocessorOptions.h"
00014 #include "llvm/Support/FileSystem.h"
00015 #include "llvm/Support/MemoryBuffer.h"
00016 #include "llvm/Support/Path.h"
00017 #include "llvm/Support/raw_ostream.h"
00018 #include <fstream>
00019 
00020 using namespace clang;
00021 using namespace arcmt;
00022 
00023 FileRemapper::FileRemapper() {
00024   FileMgr.reset(new FileManager(FileSystemOptions()));
00025 }
00026 
00027 FileRemapper::~FileRemapper() {
00028   clear();
00029 }
00030 
00031 void FileRemapper::clear(StringRef outputDir) {
00032   for (MappingsTy::iterator
00033          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
00034     resetTarget(I->second);
00035   FromToMappings.clear();
00036   assert(ToFromMappings.empty());
00037   if (!outputDir.empty()) {
00038     std::string infoFile = getRemapInfoFile(outputDir);
00039     llvm::sys::fs::remove(infoFile);
00040   }
00041 }
00042 
00043 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
00044   assert(!outputDir.empty());
00045   SmallString<128> InfoFile = outputDir;
00046   llvm::sys::path::append(InfoFile, "remap");
00047   return InfoFile.str();
00048 }
00049 
00050 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
00051                                 bool ignoreIfFilesChanged) {
00052   std::string infoFile = getRemapInfoFile(outputDir);
00053   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
00054 }
00055 
00056 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
00057                                 bool ignoreIfFilesChanged) {
00058   assert(FromToMappings.empty() &&
00059          "initFromDisk should be called before any remap calls");
00060   std::string infoFile = filePath;
00061   if (!llvm::sys::fs::exists(infoFile))
00062     return false;
00063 
00064   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
00065 
00066   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
00067       llvm::MemoryBuffer::getFile(infoFile.c_str());
00068   if (!fileBuf)
00069     return report("Error opening file: " + infoFile, Diag);
00070   
00071   SmallVector<StringRef, 64> lines;
00072   fileBuf.get()->getBuffer().split(lines, "\n");
00073 
00074   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
00075     StringRef fromFilename = lines[idx];
00076     unsigned long long timeModified;
00077     if (lines[idx+1].getAsInteger(10, timeModified))
00078       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
00079                     Diag);
00080     StringRef toFilename = lines[idx+2];
00081     
00082     const FileEntry *origFE = FileMgr->getFile(fromFilename);
00083     if (!origFE) {
00084       if (ignoreIfFilesChanged)
00085         continue;
00086       return report("File does not exist: " + fromFilename, Diag);
00087     }
00088     const FileEntry *newFE = FileMgr->getFile(toFilename);
00089     if (!newFE) {
00090       if (ignoreIfFilesChanged)
00091         continue;
00092       return report("File does not exist: " + toFilename, Diag);
00093     }
00094 
00095     if ((uint64_t)origFE->getModificationTime() != timeModified) {
00096       if (ignoreIfFilesChanged)
00097         continue;
00098       return report("File was modified: " + fromFilename, Diag);
00099     }
00100 
00101     pairs.push_back(std::make_pair(origFE, newFE));
00102   }
00103 
00104   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
00105     remap(pairs[i].first, pairs[i].second);
00106 
00107   return false;
00108 }
00109 
00110 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
00111   using namespace llvm::sys;
00112 
00113   if (fs::create_directory(outputDir))
00114     return report("Could not create directory: " + outputDir, Diag);
00115 
00116   std::string infoFile = getRemapInfoFile(outputDir);
00117   return flushToFile(infoFile, Diag);
00118 }
00119 
00120 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
00121   using namespace llvm::sys;
00122 
00123   std::error_code EC;
00124   std::string infoFile = outputPath;
00125   llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None);
00126   if (EC)
00127     return report(EC.message(), Diag);
00128 
00129   for (MappingsTy::iterator
00130          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00131 
00132     const FileEntry *origFE = I->first;
00133     SmallString<200> origPath = StringRef(origFE->getName());
00134     fs::make_absolute(origPath);
00135     infoOut << origPath << '\n';
00136     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
00137 
00138     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
00139       SmallString<200> newPath = StringRef(FE->getName());
00140       fs::make_absolute(newPath);
00141       infoOut << newPath << '\n';
00142     } else {
00143 
00144       SmallString<64> tempPath;
00145       int fd;
00146       if (fs::createTemporaryFile(path::filename(origFE->getName()),
00147                                   path::extension(origFE->getName()), fd,
00148                                   tempPath))
00149         return report("Could not create file: " + tempPath.str(), Diag);
00150 
00151       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
00152       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00153       newOut.write(mem->getBufferStart(), mem->getBufferSize());
00154       newOut.close();
00155       
00156       const FileEntry *newE = FileMgr->getFile(tempPath);
00157       remap(origFE, newE);
00158       infoOut << newE->getName() << '\n';
00159     }
00160   }
00161 
00162   infoOut.close();
00163   return false;
00164 }
00165 
00166 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
00167                                      StringRef outputDir) {
00168   using namespace llvm::sys;
00169 
00170   for (MappingsTy::iterator
00171          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00172     const FileEntry *origFE = I->first;
00173     assert(I->second.is<llvm::MemoryBuffer *>());
00174     if (!fs::exists(origFE->getName()))
00175       return report(StringRef("File does not exist: ") + origFE->getName(),
00176                     Diag);
00177 
00178     std::error_code EC;
00179     llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None);
00180     if (EC)
00181       return report(EC.message(), Diag);
00182 
00183     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00184     Out.write(mem->getBufferStart(), mem->getBufferSize());
00185     Out.close();
00186   }
00187 
00188   clear(outputDir);
00189   return false;
00190 }
00191 
00192 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
00193   for (MappingsTy::const_iterator
00194          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00195     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
00196       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
00197     } else {
00198       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00199       PPOpts.addRemappedFile(I->first->getName(), mem);
00200     }
00201   }
00202 
00203   PPOpts.RetainRemappedFileBuffers = true;
00204 }
00205 
00206 void FileRemapper::remap(StringRef filePath,
00207                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
00208   remap(getOriginalFile(filePath), std::move(memBuf));
00209 }
00210 
00211 void FileRemapper::remap(const FileEntry *file,
00212                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
00213   assert(file);
00214   Target &targ = FromToMappings[file];
00215   resetTarget(targ);
00216   targ = memBuf.release();
00217 }
00218 
00219 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
00220   assert(file && newfile);
00221   Target &targ = FromToMappings[file];
00222   resetTarget(targ);
00223   targ = newfile;
00224   ToFromMappings[newfile] = file;
00225 }
00226 
00227 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
00228   const FileEntry *file = FileMgr->getFile(filePath);
00229   // If we are updating a file that overriden an original file,
00230   // actually update the original file.
00231   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
00232     I = ToFromMappings.find(file);
00233   if (I != ToFromMappings.end()) {
00234     file = I->second;
00235     assert(FromToMappings.find(file) != FromToMappings.end() &&
00236            "Original file not in mappings!");
00237   }
00238   return file;
00239 }
00240 
00241 void FileRemapper::resetTarget(Target &targ) {
00242   if (!targ)
00243     return;
00244 
00245   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
00246     delete oldmem;
00247   } else {
00248     const FileEntry *toFE = targ.get<const FileEntry *>();
00249     ToFromMappings.erase(toFE);
00250   }
00251 }
00252 
00253 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
00254   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
00255       << err.str();
00256   return true;
00257 }