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/Frontend/PreprocessorOptions.h"
00012 #include "clang/Basic/FileManager.h"
00013 #include "clang/Basic/Diagnostic.h"
00014 #include "llvm/Support/MemoryBuffer.h"
00015 #include "llvm/Support/Path.h"
00016 #include "llvm/Support/FileSystem.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     bool existed;
00040     llvm::sys::fs::remove(infoFile, existed);
00041   }
00042 }
00043 
00044 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
00045   assert(!outputDir.empty());
00046   llvm::sys::Path dir(outputDir);
00047   llvm::sys::Path infoFile = dir;
00048   infoFile.appendComponent("remap");
00049   return infoFile.str();
00050 }
00051 
00052 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
00053                                 bool ignoreIfFilesChanged) {
00054   std::string infoFile = getRemapInfoFile(outputDir);
00055   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
00056 }
00057 
00058 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
00059                                 bool ignoreIfFilesChanged) {
00060   assert(FromToMappings.empty() &&
00061          "initFromDisk should be called before any remap calls");
00062   std::string infoFile = filePath;
00063   bool fileExists = false;
00064   llvm::sys::fs::exists(infoFile, fileExists);
00065   if (!fileExists)
00066     return false;
00067 
00068   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
00069   
00070   OwningPtr<llvm::MemoryBuffer> fileBuf;
00071   if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf))
00072     return report("Error opening file: " + infoFile, Diag);
00073   
00074   SmallVector<StringRef, 64> lines;
00075   fileBuf->getBuffer().split(lines, "\n");
00076 
00077   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
00078     StringRef fromFilename = lines[idx];
00079     unsigned long long timeModified;
00080     if (lines[idx+1].getAsInteger(10, timeModified))
00081       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
00082                     Diag);
00083     StringRef toFilename = lines[idx+2];
00084     
00085     const FileEntry *origFE = FileMgr->getFile(fromFilename);
00086     if (!origFE) {
00087       if (ignoreIfFilesChanged)
00088         continue;
00089       return report("File does not exist: " + fromFilename, Diag);
00090     }
00091     const FileEntry *newFE = FileMgr->getFile(toFilename);
00092     if (!newFE) {
00093       if (ignoreIfFilesChanged)
00094         continue;
00095       return report("File does not exist: " + toFilename, Diag);
00096     }
00097 
00098     if ((uint64_t)origFE->getModificationTime() != timeModified) {
00099       if (ignoreIfFilesChanged)
00100         continue;
00101       return report("File was modified: " + fromFilename, Diag);
00102     }
00103 
00104     pairs.push_back(std::make_pair(origFE, newFE));
00105   }
00106 
00107   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
00108     remap(pairs[i].first, pairs[i].second);
00109 
00110   return false;
00111 }
00112 
00113 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
00114   using namespace llvm::sys;
00115 
00116   bool existed;
00117   if (fs::create_directory(outputDir, existed) != llvm::errc::success)
00118     return report("Could not create directory: " + outputDir, Diag);
00119 
00120   std::string infoFile = getRemapInfoFile(outputDir);
00121   return flushToFile(infoFile, Diag);
00122 }
00123 
00124 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
00125   using namespace llvm::sys;
00126 
00127   std::string errMsg;
00128   std::string infoFile = outputPath;
00129   llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
00130                                llvm::raw_fd_ostream::F_Binary);
00131   if (!errMsg.empty())
00132     return report(errMsg, Diag);
00133 
00134   for (MappingsTy::iterator
00135          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00136 
00137     const FileEntry *origFE = I->first;
00138     SmallString<200> origPath = StringRef(origFE->getName());
00139     fs::make_absolute(origPath);
00140     infoOut << origPath << '\n';
00141     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
00142 
00143     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
00144       SmallString<200> newPath = StringRef(FE->getName());
00145       fs::make_absolute(newPath);
00146       infoOut << newPath << '\n';
00147     } else {
00148 
00149       SmallString<64> tempPath;
00150       tempPath = path::filename(origFE->getName());
00151       tempPath += "-%%%%%%%%";
00152       tempPath += path::extension(origFE->getName());
00153       int fd;
00154       if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success)
00155         return report("Could not create file: " + tempPath.str(), Diag);
00156 
00157       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
00158       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00159       newOut.write(mem->getBufferStart(), mem->getBufferSize());
00160       newOut.close();
00161       
00162       const FileEntry *newE = FileMgr->getFile(tempPath);
00163       remap(origFE, newE);
00164       infoOut << newE->getName() << '\n';
00165     }
00166   }
00167 
00168   infoOut.close();
00169   return false;
00170 }
00171 
00172 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
00173                                      StringRef outputDir) {
00174   using namespace llvm::sys;
00175 
00176   for (MappingsTy::iterator
00177          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00178     const FileEntry *origFE = I->first;
00179     if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) {
00180       if (fs::copy_file(newFE->getName(), origFE->getName(),
00181                  fs::copy_option::overwrite_if_exists) != llvm::errc::success)
00182         return report(StringRef("Could not copy file '") + newFE->getName() +
00183                       "' to file '" + origFE->getName() + "'", Diag);
00184     } else {
00185 
00186       bool fileExists = false;
00187       fs::exists(origFE->getName(), fileExists);
00188       if (!fileExists)
00189         return report(StringRef("File does not exist: ") + origFE->getName(),
00190                       Diag);
00191 
00192       std::string errMsg;
00193       llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
00194                                llvm::raw_fd_ostream::F_Binary);
00195       if (!errMsg.empty())
00196         return report(errMsg, Diag);
00197 
00198       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00199       Out.write(mem->getBufferStart(), mem->getBufferSize());
00200       Out.close();
00201     }
00202   }
00203 
00204   clear(outputDir);
00205   return false;
00206 }
00207 
00208 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
00209   for (MappingsTy::const_iterator
00210          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00211     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
00212       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
00213     } else {
00214       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00215       PPOpts.addRemappedFile(I->first->getName(), mem);
00216     }
00217   }
00218 
00219   PPOpts.RetainRemappedFileBuffers = true;
00220 }
00221 
00222 void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
00223   for (MappingsTy::iterator
00224          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
00225     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
00226       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
00227     } else {
00228       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
00229       PPOpts.addRemappedFile(I->first->getName(), mem);
00230     }
00231     I->second = Target();
00232   }
00233 
00234   PPOpts.RetainRemappedFileBuffers = false;
00235   clear();
00236 }
00237 
00238 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
00239   remap(getOriginalFile(filePath), memBuf);
00240 }
00241 
00242 void FileRemapper::remap(StringRef filePath, StringRef newPath) {
00243   const FileEntry *file = getOriginalFile(filePath);
00244   const FileEntry *newfile = FileMgr->getFile(newPath);
00245   remap(file, newfile);
00246 }
00247 
00248 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
00249   assert(file);
00250   Target &targ = FromToMappings[file];
00251   resetTarget(targ);
00252   targ = memBuf;
00253 }
00254 
00255 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
00256   assert(file && newfile);
00257   Target &targ = FromToMappings[file];
00258   resetTarget(targ);
00259   targ = newfile;
00260   ToFromMappings[newfile] = file;
00261 }
00262 
00263 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
00264   const FileEntry *file = FileMgr->getFile(filePath);
00265   // If we are updating a file that overriden an original file,
00266   // actually update the original file.
00267   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
00268     I = ToFromMappings.find(file);
00269   if (I != ToFromMappings.end()) {
00270     file = I->second;
00271     assert(FromToMappings.find(file) != FromToMappings.end() &&
00272            "Original file not in mappings!");
00273   }
00274   return file;
00275 }
00276 
00277 void FileRemapper::resetTarget(Target &targ) {
00278   if (!targ)
00279     return;
00280 
00281   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
00282     delete oldmem;
00283   } else {
00284     const FileEntry *toFE = targ.get<const FileEntry *>();
00285     ToFromMappings.erase(toFE);
00286   }
00287 }
00288 
00289 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
00290   SmallString<128> buf;
00291   unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
00292                                                          err.toStringRef(buf));
00293   Diag.Report(ID);
00294   return true;
00295 }