10b57cec5SDimitry Andric //===--- FileRemapper.cpp - File Remapping Helper -------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "clang/ARCMigrate/FileRemapper.h" 100b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h" 110b57cec5SDimitry Andric #include "clang/Basic/FileManager.h" 120b57cec5SDimitry Andric #include "clang/Lex/PreprocessorOptions.h" 130b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 140b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 150b57cec5SDimitry Andric #include "llvm/Support/Path.h" 160b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 170b57cec5SDimitry Andric #include <fstream> 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric using namespace clang; 200b57cec5SDimitry Andric using namespace arcmt; 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric FileRemapper::FileRemapper() { 230b57cec5SDimitry Andric FileMgr.reset(new FileManager(FileSystemOptions())); 240b57cec5SDimitry Andric } 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric FileRemapper::~FileRemapper() { 270b57cec5SDimitry Andric clear(); 280b57cec5SDimitry Andric } 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric void FileRemapper::clear(StringRef outputDir) { 310b57cec5SDimitry Andric for (MappingsTy::iterator 320b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) 330b57cec5SDimitry Andric resetTarget(I->second); 340b57cec5SDimitry Andric FromToMappings.clear(); 350b57cec5SDimitry Andric assert(ToFromMappings.empty()); 360b57cec5SDimitry Andric if (!outputDir.empty()) { 370b57cec5SDimitry Andric std::string infoFile = getRemapInfoFile(outputDir); 380b57cec5SDimitry Andric llvm::sys::fs::remove(infoFile); 390b57cec5SDimitry Andric } 400b57cec5SDimitry Andric } 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { 430b57cec5SDimitry Andric assert(!outputDir.empty()); 440b57cec5SDimitry Andric SmallString<128> InfoFile = outputDir; 450b57cec5SDimitry Andric llvm::sys::path::append(InfoFile, "remap"); 460b57cec5SDimitry Andric return InfoFile.str(); 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, 500b57cec5SDimitry Andric bool ignoreIfFilesChanged) { 510b57cec5SDimitry Andric std::string infoFile = getRemapInfoFile(outputDir); 520b57cec5SDimitry Andric return initFromFile(infoFile, Diag, ignoreIfFilesChanged); 530b57cec5SDimitry Andric } 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, 560b57cec5SDimitry Andric bool ignoreIfFilesChanged) { 570b57cec5SDimitry Andric assert(FromToMappings.empty() && 580b57cec5SDimitry Andric "initFromDisk should be called before any remap calls"); 590b57cec5SDimitry Andric std::string infoFile = filePath; 600b57cec5SDimitry Andric if (!llvm::sys::fs::exists(infoFile)) 610b57cec5SDimitry Andric return false; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = 660b57cec5SDimitry Andric llvm::MemoryBuffer::getFile(infoFile); 670b57cec5SDimitry Andric if (!fileBuf) 680b57cec5SDimitry Andric return report("Error opening file: " + infoFile, Diag); 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric SmallVector<StringRef, 64> lines; 710b57cec5SDimitry Andric fileBuf.get()->getBuffer().split(lines, "\n"); 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { 740b57cec5SDimitry Andric StringRef fromFilename = lines[idx]; 750b57cec5SDimitry Andric unsigned long long timeModified; 760b57cec5SDimitry Andric if (lines[idx+1].getAsInteger(10, timeModified)) 770b57cec5SDimitry Andric return report("Invalid file data: '" + lines[idx+1] + "' not a number", 780b57cec5SDimitry Andric Diag); 790b57cec5SDimitry Andric StringRef toFilename = lines[idx+2]; 800b57cec5SDimitry Andric 81*a7dea167SDimitry Andric llvm::ErrorOr<const FileEntry *> origFE = FileMgr->getFile(fromFilename); 820b57cec5SDimitry Andric if (!origFE) { 830b57cec5SDimitry Andric if (ignoreIfFilesChanged) 840b57cec5SDimitry Andric continue; 850b57cec5SDimitry Andric return report("File does not exist: " + fromFilename, Diag); 860b57cec5SDimitry Andric } 87*a7dea167SDimitry Andric llvm::ErrorOr<const FileEntry *> newFE = FileMgr->getFile(toFilename); 880b57cec5SDimitry Andric if (!newFE) { 890b57cec5SDimitry Andric if (ignoreIfFilesChanged) 900b57cec5SDimitry Andric continue; 910b57cec5SDimitry Andric return report("File does not exist: " + toFilename, Diag); 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 94*a7dea167SDimitry Andric if ((uint64_t)(*origFE)->getModificationTime() != timeModified) { 950b57cec5SDimitry Andric if (ignoreIfFilesChanged) 960b57cec5SDimitry Andric continue; 970b57cec5SDimitry Andric return report("File was modified: " + fromFilename, Diag); 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric 100*a7dea167SDimitry Andric pairs.push_back(std::make_pair(*origFE, *newFE)); 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric for (unsigned i = 0, e = pairs.size(); i != e; ++i) 1040b57cec5SDimitry Andric remap(pairs[i].first, pairs[i].second); 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric return false; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { 1100b57cec5SDimitry Andric using namespace llvm::sys; 1110b57cec5SDimitry Andric 1120b57cec5SDimitry Andric if (fs::create_directory(outputDir)) 1130b57cec5SDimitry Andric return report("Could not create directory: " + outputDir, Diag); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric std::string infoFile = getRemapInfoFile(outputDir); 1160b57cec5SDimitry Andric return flushToFile(infoFile, Diag); 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { 1200b57cec5SDimitry Andric using namespace llvm::sys; 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric std::error_code EC; 1230b57cec5SDimitry Andric std::string infoFile = outputPath; 124*a7dea167SDimitry Andric llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_None); 1250b57cec5SDimitry Andric if (EC) 1260b57cec5SDimitry Andric return report(EC.message(), Diag); 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric for (MappingsTy::iterator 1290b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric const FileEntry *origFE = I->first; 1320b57cec5SDimitry Andric SmallString<200> origPath = StringRef(origFE->getName()); 1330b57cec5SDimitry Andric fs::make_absolute(origPath); 1340b57cec5SDimitry Andric infoOut << origPath << '\n'; 1350b57cec5SDimitry Andric infoOut << (uint64_t)origFE->getModificationTime() << '\n'; 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 1380b57cec5SDimitry Andric SmallString<200> newPath = StringRef(FE->getName()); 1390b57cec5SDimitry Andric fs::make_absolute(newPath); 1400b57cec5SDimitry Andric infoOut << newPath << '\n'; 1410b57cec5SDimitry Andric } else { 1420b57cec5SDimitry Andric 1430b57cec5SDimitry Andric SmallString<64> tempPath; 1440b57cec5SDimitry Andric int fd; 1450b57cec5SDimitry Andric if (fs::createTemporaryFile(path::filename(origFE->getName()), 1460b57cec5SDimitry Andric path::extension(origFE->getName()).drop_front(), fd, 1470b57cec5SDimitry Andric tempPath)) 1480b57cec5SDimitry Andric return report("Could not create file: " + tempPath.str(), Diag); 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 1510b57cec5SDimitry Andric llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 1520b57cec5SDimitry Andric newOut.write(mem->getBufferStart(), mem->getBufferSize()); 1530b57cec5SDimitry Andric newOut.close(); 1540b57cec5SDimitry Andric 155*a7dea167SDimitry Andric auto newE = FileMgr->getFile(tempPath); 156*a7dea167SDimitry Andric if (newE) { 157*a7dea167SDimitry Andric remap(origFE, *newE); 158*a7dea167SDimitry Andric infoOut << (*newE)->getName() << '\n'; 159*a7dea167SDimitry Andric } 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric infoOut.close(); 1640b57cec5SDimitry Andric return false; 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 1680b57cec5SDimitry Andric StringRef outputDir) { 1690b57cec5SDimitry Andric using namespace llvm::sys; 1700b57cec5SDimitry Andric 1710b57cec5SDimitry Andric for (MappingsTy::iterator 1720b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 1730b57cec5SDimitry Andric const FileEntry *origFE = I->first; 1740b57cec5SDimitry Andric assert(I->second.is<llvm::MemoryBuffer *>()); 1750b57cec5SDimitry Andric if (!fs::exists(origFE->getName())) 1760b57cec5SDimitry Andric return report(StringRef("File does not exist: ") + origFE->getName(), 1770b57cec5SDimitry Andric Diag); 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric std::error_code EC; 180*a7dea167SDimitry Andric llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None); 1810b57cec5SDimitry Andric if (EC) 1820b57cec5SDimitry Andric return report(EC.message(), Diag); 1830b57cec5SDimitry Andric 1840b57cec5SDimitry Andric llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 1850b57cec5SDimitry Andric Out.write(mem->getBufferStart(), mem->getBufferSize()); 1860b57cec5SDimitry Andric Out.close(); 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric clear(outputDir); 1900b57cec5SDimitry Andric return false; 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 1940b57cec5SDimitry Andric for (MappingsTy::const_iterator 1950b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 1960b57cec5SDimitry Andric if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 1970b57cec5SDimitry Andric PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 1980b57cec5SDimitry Andric } else { 1990b57cec5SDimitry Andric llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 2000b57cec5SDimitry Andric PPOpts.addRemappedFile(I->first->getName(), mem); 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric } 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric PPOpts.RetainRemappedFileBuffers = true; 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric void FileRemapper::remap(StringRef filePath, 2080b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> memBuf) { 2090b57cec5SDimitry Andric remap(getOriginalFile(filePath), std::move(memBuf)); 2100b57cec5SDimitry Andric } 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric void FileRemapper::remap(const FileEntry *file, 2130b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> memBuf) { 2140b57cec5SDimitry Andric assert(file); 2150b57cec5SDimitry Andric Target &targ = FromToMappings[file]; 2160b57cec5SDimitry Andric resetTarget(targ); 2170b57cec5SDimitry Andric targ = memBuf.release(); 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { 2210b57cec5SDimitry Andric assert(file && newfile); 2220b57cec5SDimitry Andric Target &targ = FromToMappings[file]; 2230b57cec5SDimitry Andric resetTarget(targ); 2240b57cec5SDimitry Andric targ = newfile; 2250b57cec5SDimitry Andric ToFromMappings[newfile] = file; 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { 229*a7dea167SDimitry Andric const FileEntry *file = nullptr; 230*a7dea167SDimitry Andric if (auto fileOrErr = FileMgr->getFile(filePath)) 231*a7dea167SDimitry Andric file = *fileOrErr; 2320b57cec5SDimitry Andric // If we are updating a file that overridden an original file, 2330b57cec5SDimitry Andric // actually update the original file. 2340b57cec5SDimitry Andric llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator 2350b57cec5SDimitry Andric I = ToFromMappings.find(file); 2360b57cec5SDimitry Andric if (I != ToFromMappings.end()) { 2370b57cec5SDimitry Andric file = I->second; 2380b57cec5SDimitry Andric assert(FromToMappings.find(file) != FromToMappings.end() && 2390b57cec5SDimitry Andric "Original file not in mappings!"); 2400b57cec5SDimitry Andric } 2410b57cec5SDimitry Andric return file; 2420b57cec5SDimitry Andric } 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric void FileRemapper::resetTarget(Target &targ) { 2450b57cec5SDimitry Andric if (!targ) 2460b57cec5SDimitry Andric return; 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { 2490b57cec5SDimitry Andric delete oldmem; 2500b57cec5SDimitry Andric } else { 2510b57cec5SDimitry Andric const FileEntry *toFE = targ.get<const FileEntry *>(); 2520b57cec5SDimitry Andric ToFromMappings.erase(toFE); 2530b57cec5SDimitry Andric } 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 2570b57cec5SDimitry Andric Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 2580b57cec5SDimitry Andric << err.str(); 2590b57cec5SDimitry Andric return true; 2600b57cec5SDimitry Andric } 261