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   if (!llvm::sys::fs::exists(infoFile))
     62     return false;
     63 
     64   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
     65 
     66   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
     67       llvm::MemoryBuffer::getFile(infoFile.c_str());
     68   if (!fileBuf)
     69     return report("Error opening file: " + infoFile, Diag);
     70 
     71   SmallVector<StringRef, 64> lines;
     72   fileBuf.get()->getBuffer().split(lines, "\n");
     73 
     74   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
     75     StringRef fromFilename = lines[idx];
     76     unsigned long long timeModified;
     77     if (lines[idx+1].getAsInteger(10, timeModified))
     78       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
     79                     Diag);
     80     StringRef toFilename = lines[idx+2];
     81 
     82     const FileEntry *origFE = FileMgr->getFile(fromFilename);
     83     if (!origFE) {
     84       if (ignoreIfFilesChanged)
     85         continue;
     86       return report("File does not exist: " + fromFilename, Diag);
     87     }
     88     const FileEntry *newFE = FileMgr->getFile(toFilename);
     89     if (!newFE) {
     90       if (ignoreIfFilesChanged)
     91         continue;
     92       return report("File does not exist: " + toFilename, Diag);
     93     }
     94 
     95     if ((uint64_t)origFE->getModificationTime() != timeModified) {
     96       if (ignoreIfFilesChanged)
     97         continue;
     98       return report("File was modified: " + fromFilename, Diag);
     99     }
    100 
    101     pairs.push_back(std::make_pair(origFE, newFE));
    102   }
    103 
    104   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
    105     remap(pairs[i].first, pairs[i].second);
    106 
    107   return false;
    108 }
    109 
    110 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
    111   using namespace llvm::sys;
    112 
    113   if (fs::create_directory(outputDir))
    114     return report("Could not create directory: " + outputDir, Diag);
    115 
    116   std::string infoFile = getRemapInfoFile(outputDir);
    117   return flushToFile(infoFile, Diag);
    118 }
    119 
    120 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
    121   using namespace llvm::sys;
    122 
    123   std::error_code EC;
    124   std::string infoFile = outputPath;
    125   llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None);
    126   if (EC)
    127     return report(EC.message(), Diag);
    128 
    129   for (MappingsTy::iterator
    130          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    131 
    132     const FileEntry *origFE = I->first;
    133     SmallString<200> origPath = StringRef(origFE->getName());
    134     fs::make_absolute(origPath);
    135     infoOut << origPath << '\n';
    136     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
    137 
    138     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
    139       SmallString<200> newPath = StringRef(FE->getName());
    140       fs::make_absolute(newPath);
    141       infoOut << newPath << '\n';
    142     } else {
    143 
    144       SmallString<64> tempPath;
    145       int fd;
    146       if (fs::createTemporaryFile(path::filename(origFE->getName()),
    147                                   path::extension(origFE->getName()).drop_front(), fd,
    148                                   tempPath))
    149         return report("Could not create file: " + tempPath.str(), Diag);
    150 
    151       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
    152       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    153       newOut.write(mem->getBufferStart(), mem->getBufferSize());
    154       newOut.close();
    155 
    156       const FileEntry *newE = FileMgr->getFile(tempPath);
    157       remap(origFE, newE);
    158       infoOut << newE->getName() << '\n';
    159     }
    160   }
    161 
    162   infoOut.close();
    163   return false;
    164 }
    165 
    166 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
    167                                      StringRef outputDir) {
    168   using namespace llvm::sys;
    169 
    170   for (MappingsTy::iterator
    171          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    172     const FileEntry *origFE = I->first;
    173     assert(I->second.is<llvm::MemoryBuffer *>());
    174     if (!fs::exists(origFE->getName()))
    175       return report(StringRef("File does not exist: ") + origFE->getName(),
    176                     Diag);
    177 
    178     std::error_code EC;
    179     llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None);
    180     if (EC)
    181       return report(EC.message(), Diag);
    182 
    183     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    184     Out.write(mem->getBufferStart(), mem->getBufferSize());
    185     Out.close();
    186   }
    187 
    188   clear(outputDir);
    189   return false;
    190 }
    191 
    192 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
    193   for (MappingsTy::const_iterator
    194          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
    195     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
    196       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
    197     } else {
    198       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
    199       PPOpts.addRemappedFile(I->first->getName(), mem);
    200     }
    201   }
    202 
    203   PPOpts.RetainRemappedFileBuffers = true;
    204 }
    205 
    206 void FileRemapper::remap(StringRef filePath,
    207                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
    208   remap(getOriginalFile(filePath), std::move(memBuf));
    209 }
    210 
    211 void FileRemapper::remap(const FileEntry *file,
    212                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
    213   assert(file);
    214   Target &targ = FromToMappings[file];
    215   resetTarget(targ);
    216   targ = memBuf.release();
    217 }
    218 
    219 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
    220   assert(file && newfile);
    221   Target &targ = FromToMappings[file];
    222   resetTarget(targ);
    223   targ = newfile;
    224   ToFromMappings[newfile] = file;
    225 }
    226 
    227 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
    228   const FileEntry *file = FileMgr->getFile(filePath);
    229   // If we are updating a file that overriden an original file,
    230   // actually update the original file.
    231   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
    232     I = ToFromMappings.find(file);
    233   if (I != ToFromMappings.end()) {
    234     file = I->second;
    235     assert(FromToMappings.find(file) != FromToMappings.end() &&
    236            "Original file not in mappings!");
    237   }
    238   return file;
    239 }
    240 
    241 void FileRemapper::resetTarget(Target &targ) {
    242   if (!targ)
    243     return;
    244 
    245   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
    246     delete oldmem;
    247   } else {
    248     const FileEntry *toFE = targ.get<const FileEntry *>();
    249     ToFromMappings.erase(toFE);
    250   }
    251 }
    252 
    253 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
    254   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
    255       << err.str();
    256   return true;
    257 }
    258