Home | History | Annotate | Download | only in ARCMigrate
      1 //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/ARCMigrate/FileRemapper.h"
     11 #include "clang/Basic/Diagnostic.h"
     12 #include "clang/Basic/FileManager.h"
     13 #include "clang/Lex/PreprocessorOptions.h"
     14 #include "llvm/Support/FileSystem.h"
     15 #include "llvm/Support/MemoryBuffer.h"
     16 #include "llvm/Support/Path.h"
     17 #include "llvm/Support/raw_ostream.h"
     18 #include <fstream>
     19 
     20 using namespace clang;
     21 using namespace arcmt;
     22 
     23 FileRemapper::FileRemapper() {
     24   FileMgr.reset(new FileManager(FileSystemOptions()));
     25 }
     26 
     27 FileRemapper::~FileRemapper() {
     28   clear();
     29 }
     30 
     31 void FileRemapper::clear(StringRef outputDir) {
     32   for (MappingsTy::iterator
     33          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
     34     resetTarget(I->second);
     35   FromToMappings.clear();
     36   assert(ToFromMappings.empty());
     37   if (!outputDir.empty()) {
     38     std::string infoFile = getRemapInfoFile(outputDir);
     39     llvm::sys::fs::remove(infoFile);
     40   }
     41 }
     42 
     43 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
     44   assert(!outputDir.empty());
     45   SmallString<128> InfoFile = outputDir;
     46   llvm::sys::path::append(InfoFile, "remap");
     47   return InfoFile.str();
     48 }
     49 
     50 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
     51                                 bool ignoreIfFilesChanged) {
     52   std::string infoFile = getRemapInfoFile(outputDir);
     53   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
     54 }
     55 
     56 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
     57                                 bool ignoreIfFilesChanged) {
     58   assert(FromToMappings.empty() &&
     59          "initFromDisk should be called before any remap calls");
     60   std::string infoFile = filePath;
     61   bool fileExists = false;
     62   llvm::sys::fs::exists(infoFile, fileExists);
     63   if (!fileExists)
     64     return false;
     65 
     66   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
     67 
     68   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
     69       llvm::MemoryBuffer::getFile(infoFile.c_str());
     70   if (!fileBuf)
     71     return report("Error opening file: " + infoFile, Diag);
     72 
     73   SmallVector<StringRef, 64> lines;
     74   fileBuf.get()->getBuffer().split(lines, "\n");
     75 
     76   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
     77     StringRef fromFilename = lines[idx];
     78     unsigned long long timeModified;
     79     if (lines[idx+1].getAsInteger(10, timeModified))
     80       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
     81                     Diag);
     82     StringRef toFilename = lines[idx+2];
     83 
     84     const FileEntry *origFE = FileMgr->getFile(fromFilename);
     85     if (!origFE) {
     86       if (ignoreIfFilesChanged)
     87         continue;
     88       return report("File does not exist: " + fromFilename, Diag);
     89     }
     90     const FileEntry *newFE = FileMgr->getFile(toFilename);
     91     if (!newFE) {
     92       if (ignoreIfFilesChanged)
     93         continue;
     94       return report("File does not exist: " + toFilename, Diag);
     95     }
     96 
     97     if ((uint64_t)origFE->getModificationTime() != timeModified) {
     98       if (ignoreIfFilesChanged)
     99         continue;
    100       return report("File was modified: " + fromFilename, Diag);
    101     }
    102 
    103     pairs.push_back(std::make_pair(origFE, newFE));
    104   }
    105 
    106   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
    107     remap(pairs[i].first, pairs[i].second);
    108 
    109   return false;
    110 }
    111 
    112 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
    113   using namespace llvm::sys;
    114 
    115   if (fs::create_directory(outputDir))
    116     return report("Could not create directory: " + outputDir, Diag);
    117 
    118   std::string infoFile = getRemapInfoFile(outputDir);
    119   return flushToFile(infoFile, Diag);
    120 }
    121 
    122 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
    123   using namespace llvm::sys;
    124 
    125   std::string errMsg;
    126   std::string infoFile = outputPath;
    127   llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, llvm::sys::fs::F_None);
    128   if (!errMsg.empty())
    129     return report(errMsg, Diag);
    130 
    131   for (MappingsTy::iterator
    132          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    133 
    134     const FileEntry *origFE = I->first;
    135     SmallString<200> origPath = StringRef(origFE->getName());
    136     fs::make_absolute(origPath);
    137     infoOut << origPath << '\n';
    138     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
    139 
    140     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
    141       SmallString<200> newPath = StringRef(FE->getName());
    142       fs::make_absolute(newPath);
    143       infoOut << newPath << '\n';
    144     } else {
    145 
    146       SmallString<64> tempPath;
    147       int fd;
    148       if (fs::createTemporaryFile(path::filename(origFE->getName()),
    149                                   path::extension(origFE->getName()), fd,
    150                                   tempPath))
    151         return report("Could not create file: " + tempPath.str(), Diag);
    152 
    153       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
    154       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    155       newOut.write(mem->getBufferStart(), mem->getBufferSize());
    156       newOut.close();
    157 
    158       const FileEntry *newE = FileMgr->getFile(tempPath);
    159       remap(origFE, newE);
    160       infoOut << newE->getName() << '\n';
    161     }
    162   }
    163 
    164   infoOut.close();
    165   return false;
    166 }
    167 
    168 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
    169                                      StringRef outputDir) {
    170   using namespace llvm::sys;
    171 
    172   for (MappingsTy::iterator
    173          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    174     const FileEntry *origFE = I->first;
    175     assert(I->second.is<llvm::MemoryBuffer *>());
    176     bool fileExists = false;
    177     fs::exists(origFE->getName(), fileExists);
    178     if (!fileExists)
    179       return report(StringRef("File does not exist: ") + origFE->getName(),
    180                     Diag);
    181 
    182     std::string errMsg;
    183     llvm::raw_fd_ostream Out(origFE->getName(), errMsg, llvm::sys::fs::F_None);
    184     if (!errMsg.empty())
    185       return report(errMsg, Diag);
    186 
    187     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    188     Out.write(mem->getBufferStart(), mem->getBufferSize());
    189     Out.close();
    190   }
    191 
    192   clear(outputDir);
    193   return false;
    194 }
    195 
    196 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
    197   for (MappingsTy::const_iterator
    198          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    199     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
    200       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
    201     } else {
    202       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    203       PPOpts.addRemappedFile(I->first->getName(), mem);
    204     }
    205   }
    206 
    207   PPOpts.RetainRemappedFileBuffers = true;
    208 }
    209 
    210 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
    211   remap(getOriginalFile(filePath), memBuf);
    212 }
    213 
    214 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
    215   assert(file);
    216   Target &targ = FromToMappings[file];
    217   resetTarget(targ);
    218   targ = memBuf;
    219 }
    220 
    221 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
    222   assert(file && newfile);
    223   Target &targ = FromToMappings[file];
    224   resetTarget(targ);
    225   targ = newfile;
    226   ToFromMappings[newfile] = file;
    227 }
    228 
    229 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
    230   const FileEntry *file = FileMgr->getFile(filePath);
    231   // If we are updating a file that overriden an original file,
    232   // actually update the original file.
    233   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
    234     I = ToFromMappings.find(file);
    235   if (I != ToFromMappings.end()) {
    236     file = I->second;
    237     assert(FromToMappings.find(file) != FromToMappings.end() &&
    238            "Original file not in mappings!");
    239   }
    240   return file;
    241 }
    242 
    243 void FileRemapper::resetTarget(Target &targ) {
    244   if (!targ)
    245     return;
    246 
    247   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
    248     delete oldmem;
    249   } else {
    250     const FileEntry *toFE = targ.get<const FileEntry *>();
    251     ToFromMappings.erase(toFE);
    252   }
    253 }
    254 
    255 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
    256   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
    257       << err.str();
    258   return true;
    259 }
    260