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