xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/ARCMigrate/FileRemapper.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg 
97330f729Sjoerg #include "clang/ARCMigrate/FileRemapper.h"
107330f729Sjoerg #include "clang/Basic/Diagnostic.h"
117330f729Sjoerg #include "clang/Basic/FileManager.h"
127330f729Sjoerg #include "clang/Lex/PreprocessorOptions.h"
137330f729Sjoerg #include "llvm/Support/FileSystem.h"
147330f729Sjoerg #include "llvm/Support/MemoryBuffer.h"
157330f729Sjoerg #include "llvm/Support/Path.h"
167330f729Sjoerg #include "llvm/Support/raw_ostream.h"
177330f729Sjoerg #include <fstream>
187330f729Sjoerg 
197330f729Sjoerg using namespace clang;
207330f729Sjoerg using namespace arcmt;
217330f729Sjoerg 
FileRemapper()227330f729Sjoerg FileRemapper::FileRemapper() {
237330f729Sjoerg   FileMgr.reset(new FileManager(FileSystemOptions()));
247330f729Sjoerg }
257330f729Sjoerg 
~FileRemapper()267330f729Sjoerg FileRemapper::~FileRemapper() {
277330f729Sjoerg   clear();
287330f729Sjoerg }
297330f729Sjoerg 
clear(StringRef outputDir)307330f729Sjoerg void FileRemapper::clear(StringRef outputDir) {
317330f729Sjoerg   for (MappingsTy::iterator
327330f729Sjoerg          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
337330f729Sjoerg     resetTarget(I->second);
347330f729Sjoerg   FromToMappings.clear();
357330f729Sjoerg   assert(ToFromMappings.empty());
367330f729Sjoerg   if (!outputDir.empty()) {
377330f729Sjoerg     std::string infoFile = getRemapInfoFile(outputDir);
387330f729Sjoerg     llvm::sys::fs::remove(infoFile);
397330f729Sjoerg   }
407330f729Sjoerg }
417330f729Sjoerg 
getRemapInfoFile(StringRef outputDir)427330f729Sjoerg std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
437330f729Sjoerg   assert(!outputDir.empty());
447330f729Sjoerg   SmallString<128> InfoFile = outputDir;
457330f729Sjoerg   llvm::sys::path::append(InfoFile, "remap");
46*e038c9c4Sjoerg   return std::string(InfoFile.str());
477330f729Sjoerg }
487330f729Sjoerg 
initFromDisk(StringRef outputDir,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)497330f729Sjoerg bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
507330f729Sjoerg                                 bool ignoreIfFilesChanged) {
517330f729Sjoerg   std::string infoFile = getRemapInfoFile(outputDir);
527330f729Sjoerg   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
537330f729Sjoerg }
547330f729Sjoerg 
initFromFile(StringRef filePath,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)557330f729Sjoerg bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
567330f729Sjoerg                                 bool ignoreIfFilesChanged) {
577330f729Sjoerg   assert(FromToMappings.empty() &&
587330f729Sjoerg          "initFromDisk should be called before any remap calls");
59*e038c9c4Sjoerg   std::string infoFile = std::string(filePath);
607330f729Sjoerg   if (!llvm::sys::fs::exists(infoFile))
617330f729Sjoerg     return false;
627330f729Sjoerg 
637330f729Sjoerg   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
647330f729Sjoerg 
657330f729Sjoerg   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
66*e038c9c4Sjoerg       llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);
677330f729Sjoerg   if (!fileBuf)
687330f729Sjoerg     return report("Error opening file: " + infoFile, Diag);
697330f729Sjoerg 
707330f729Sjoerg   SmallVector<StringRef, 64> lines;
717330f729Sjoerg   fileBuf.get()->getBuffer().split(lines, "\n");
727330f729Sjoerg 
737330f729Sjoerg   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
747330f729Sjoerg     StringRef fromFilename = lines[idx];
757330f729Sjoerg     unsigned long long timeModified;
767330f729Sjoerg     if (lines[idx+1].getAsInteger(10, timeModified))
777330f729Sjoerg       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
787330f729Sjoerg                     Diag);
797330f729Sjoerg     StringRef toFilename = lines[idx+2];
807330f729Sjoerg 
817330f729Sjoerg     llvm::ErrorOr<const FileEntry *> origFE = FileMgr->getFile(fromFilename);
827330f729Sjoerg     if (!origFE) {
837330f729Sjoerg       if (ignoreIfFilesChanged)
847330f729Sjoerg         continue;
857330f729Sjoerg       return report("File does not exist: " + fromFilename, Diag);
867330f729Sjoerg     }
877330f729Sjoerg     llvm::ErrorOr<const FileEntry *> newFE = FileMgr->getFile(toFilename);
887330f729Sjoerg     if (!newFE) {
897330f729Sjoerg       if (ignoreIfFilesChanged)
907330f729Sjoerg         continue;
917330f729Sjoerg       return report("File does not exist: " + toFilename, Diag);
927330f729Sjoerg     }
937330f729Sjoerg 
947330f729Sjoerg     if ((uint64_t)(*origFE)->getModificationTime() != timeModified) {
957330f729Sjoerg       if (ignoreIfFilesChanged)
967330f729Sjoerg         continue;
977330f729Sjoerg       return report("File was modified: " + fromFilename, Diag);
987330f729Sjoerg     }
997330f729Sjoerg 
1007330f729Sjoerg     pairs.push_back(std::make_pair(*origFE, *newFE));
1017330f729Sjoerg   }
1027330f729Sjoerg 
1037330f729Sjoerg   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
1047330f729Sjoerg     remap(pairs[i].first, pairs[i].second);
1057330f729Sjoerg 
1067330f729Sjoerg   return false;
1077330f729Sjoerg }
1087330f729Sjoerg 
flushToDisk(StringRef outputDir,DiagnosticsEngine & Diag)1097330f729Sjoerg bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
1107330f729Sjoerg   using namespace llvm::sys;
1117330f729Sjoerg 
1127330f729Sjoerg   if (fs::create_directory(outputDir))
1137330f729Sjoerg     return report("Could not create directory: " + outputDir, Diag);
1147330f729Sjoerg 
1157330f729Sjoerg   std::string infoFile = getRemapInfoFile(outputDir);
1167330f729Sjoerg   return flushToFile(infoFile, Diag);
1177330f729Sjoerg }
1187330f729Sjoerg 
flushToFile(StringRef outputPath,DiagnosticsEngine & Diag)1197330f729Sjoerg bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
1207330f729Sjoerg   using namespace llvm::sys;
1217330f729Sjoerg 
1227330f729Sjoerg   std::error_code EC;
123*e038c9c4Sjoerg   std::string infoFile = std::string(outputPath);
124*e038c9c4Sjoerg   llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);
1257330f729Sjoerg   if (EC)
1267330f729Sjoerg     return report(EC.message(), Diag);
1277330f729Sjoerg 
1287330f729Sjoerg   for (MappingsTy::iterator
1297330f729Sjoerg          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
1307330f729Sjoerg 
1317330f729Sjoerg     const FileEntry *origFE = I->first;
1327330f729Sjoerg     SmallString<200> origPath = StringRef(origFE->getName());
1337330f729Sjoerg     fs::make_absolute(origPath);
1347330f729Sjoerg     infoOut << origPath << '\n';
1357330f729Sjoerg     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
1367330f729Sjoerg 
1377330f729Sjoerg     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
1387330f729Sjoerg       SmallString<200> newPath = StringRef(FE->getName());
1397330f729Sjoerg       fs::make_absolute(newPath);
1407330f729Sjoerg       infoOut << newPath << '\n';
1417330f729Sjoerg     } else {
1427330f729Sjoerg 
1437330f729Sjoerg       SmallString<64> tempPath;
1447330f729Sjoerg       int fd;
145*e038c9c4Sjoerg       if (fs::createTemporaryFile(
146*e038c9c4Sjoerg               path::filename(origFE->getName()),
147*e038c9c4Sjoerg               path::extension(origFE->getName()).drop_front(), fd, tempPath,
148*e038c9c4Sjoerg               llvm::sys::fs::OF_Text))
1497330f729Sjoerg         return report("Could not create file: " + tempPath.str(), Diag);
1507330f729Sjoerg 
1517330f729Sjoerg       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
1527330f729Sjoerg       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
1537330f729Sjoerg       newOut.write(mem->getBufferStart(), mem->getBufferSize());
1547330f729Sjoerg       newOut.close();
1557330f729Sjoerg 
1567330f729Sjoerg       auto newE = FileMgr->getFile(tempPath);
1577330f729Sjoerg       if (newE) {
1587330f729Sjoerg         remap(origFE, *newE);
1597330f729Sjoerg         infoOut << (*newE)->getName() << '\n';
1607330f729Sjoerg       }
1617330f729Sjoerg     }
1627330f729Sjoerg   }
1637330f729Sjoerg 
1647330f729Sjoerg   infoOut.close();
1657330f729Sjoerg   return false;
1667330f729Sjoerg }
1677330f729Sjoerg 
overwriteOriginal(DiagnosticsEngine & Diag,StringRef outputDir)1687330f729Sjoerg bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
1697330f729Sjoerg                                      StringRef outputDir) {
1707330f729Sjoerg   using namespace llvm::sys;
1717330f729Sjoerg 
1727330f729Sjoerg   for (MappingsTy::iterator
1737330f729Sjoerg          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
1747330f729Sjoerg     const FileEntry *origFE = I->first;
1757330f729Sjoerg     assert(I->second.is<llvm::MemoryBuffer *>());
1767330f729Sjoerg     if (!fs::exists(origFE->getName()))
1777330f729Sjoerg       return report(StringRef("File does not exist: ") + origFE->getName(),
1787330f729Sjoerg                     Diag);
1797330f729Sjoerg 
1807330f729Sjoerg     std::error_code EC;
1817330f729Sjoerg     llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None);
1827330f729Sjoerg     if (EC)
1837330f729Sjoerg       return report(EC.message(), Diag);
1847330f729Sjoerg 
1857330f729Sjoerg     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
1867330f729Sjoerg     Out.write(mem->getBufferStart(), mem->getBufferSize());
1877330f729Sjoerg     Out.close();
1887330f729Sjoerg   }
1897330f729Sjoerg 
1907330f729Sjoerg   clear(outputDir);
1917330f729Sjoerg   return false;
1927330f729Sjoerg }
1937330f729Sjoerg 
forEachMapping(llvm::function_ref<void (StringRef,StringRef)> CaptureFile,llvm::function_ref<void (StringRef,const llvm::MemoryBufferRef &)> CaptureBuffer) const194*e038c9c4Sjoerg void FileRemapper::forEachMapping(
195*e038c9c4Sjoerg     llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
196*e038c9c4Sjoerg     llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
197*e038c9c4Sjoerg         CaptureBuffer) const {
198*e038c9c4Sjoerg   for (auto &Mapping : FromToMappings) {
199*e038c9c4Sjoerg     if (const FileEntry *FE = Mapping.second.dyn_cast<const FileEntry *>()) {
200*e038c9c4Sjoerg       CaptureFile(Mapping.first->getName(), FE->getName());
201*e038c9c4Sjoerg       continue;
202*e038c9c4Sjoerg     }
203*e038c9c4Sjoerg     CaptureBuffer(
204*e038c9c4Sjoerg         Mapping.first->getName(),
205*e038c9c4Sjoerg         Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef());
206*e038c9c4Sjoerg   }
207*e038c9c4Sjoerg }
208*e038c9c4Sjoerg 
applyMappings(PreprocessorOptions & PPOpts) const2097330f729Sjoerg void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
2107330f729Sjoerg   for (MappingsTy::const_iterator
2117330f729Sjoerg          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
2127330f729Sjoerg     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
2137330f729Sjoerg       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
2147330f729Sjoerg     } else {
2157330f729Sjoerg       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
2167330f729Sjoerg       PPOpts.addRemappedFile(I->first->getName(), mem);
2177330f729Sjoerg     }
2187330f729Sjoerg   }
2197330f729Sjoerg 
2207330f729Sjoerg   PPOpts.RetainRemappedFileBuffers = true;
2217330f729Sjoerg }
2227330f729Sjoerg 
remap(StringRef filePath,std::unique_ptr<llvm::MemoryBuffer> memBuf)2237330f729Sjoerg void FileRemapper::remap(StringRef filePath,
2247330f729Sjoerg                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
2257330f729Sjoerg   remap(getOriginalFile(filePath), std::move(memBuf));
2267330f729Sjoerg }
2277330f729Sjoerg 
remap(const FileEntry * file,std::unique_ptr<llvm::MemoryBuffer> memBuf)2287330f729Sjoerg void FileRemapper::remap(const FileEntry *file,
2297330f729Sjoerg                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
2307330f729Sjoerg   assert(file);
2317330f729Sjoerg   Target &targ = FromToMappings[file];
2327330f729Sjoerg   resetTarget(targ);
2337330f729Sjoerg   targ = memBuf.release();
2347330f729Sjoerg }
2357330f729Sjoerg 
remap(const FileEntry * file,const FileEntry * newfile)2367330f729Sjoerg void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
2377330f729Sjoerg   assert(file && newfile);
2387330f729Sjoerg   Target &targ = FromToMappings[file];
2397330f729Sjoerg   resetTarget(targ);
2407330f729Sjoerg   targ = newfile;
2417330f729Sjoerg   ToFromMappings[newfile] = file;
2427330f729Sjoerg }
2437330f729Sjoerg 
getOriginalFile(StringRef filePath)2447330f729Sjoerg const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
2457330f729Sjoerg   const FileEntry *file = nullptr;
2467330f729Sjoerg   if (auto fileOrErr = FileMgr->getFile(filePath))
2477330f729Sjoerg     file = *fileOrErr;
2487330f729Sjoerg   // If we are updating a file that overridden an original file,
2497330f729Sjoerg   // actually update the original file.
2507330f729Sjoerg   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
2517330f729Sjoerg     I = ToFromMappings.find(file);
2527330f729Sjoerg   if (I != ToFromMappings.end()) {
2537330f729Sjoerg     file = I->second;
2547330f729Sjoerg     assert(FromToMappings.find(file) != FromToMappings.end() &&
2557330f729Sjoerg            "Original file not in mappings!");
2567330f729Sjoerg   }
2577330f729Sjoerg   return file;
2587330f729Sjoerg }
2597330f729Sjoerg 
resetTarget(Target & targ)2607330f729Sjoerg void FileRemapper::resetTarget(Target &targ) {
2617330f729Sjoerg   if (!targ)
2627330f729Sjoerg     return;
2637330f729Sjoerg 
2647330f729Sjoerg   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
2657330f729Sjoerg     delete oldmem;
2667330f729Sjoerg   } else {
2677330f729Sjoerg     const FileEntry *toFE = targ.get<const FileEntry *>();
2687330f729Sjoerg     ToFromMappings.erase(toFE);
2697330f729Sjoerg   }
2707330f729Sjoerg }
2717330f729Sjoerg 
report(const Twine & err,DiagnosticsEngine & Diag)2727330f729Sjoerg bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
2737330f729Sjoerg   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
2747330f729Sjoerg       << err.str();
2757330f729Sjoerg   return true;
2767330f729Sjoerg }
277