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"); 46*7a6dacacSDimitry Andric return std::string(InfoFile); 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"); 595ffd83dbSDimitry Andric std::string infoFile = std::string(filePath); 600b57cec5SDimitry Andric if (!llvm::sys::fs::exists(infoFile)) 610b57cec5SDimitry Andric return false; 620b57cec5SDimitry Andric 635f757f3fSDimitry Andric std::vector<std::pair<FileEntryRef, FileEntryRef>> pairs; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = 66fe6060f1SDimitry Andric llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true); 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 815f757f3fSDimitry Andric auto origFE = FileMgr->getOptionalFileRef(fromFilename); 820b57cec5SDimitry Andric if (!origFE) { 830b57cec5SDimitry Andric if (ignoreIfFilesChanged) 840b57cec5SDimitry Andric continue; 850b57cec5SDimitry Andric return report("File does not exist: " + fromFilename, Diag); 860b57cec5SDimitry Andric } 875f757f3fSDimitry Andric auto newFE = FileMgr->getOptionalFileRef(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 945f757f3fSDimitry 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 100a7dea167SDimitry 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; 1235ffd83dbSDimitry Andric std::string infoFile = std::string(outputPath); 124fe6060f1SDimitry Andric llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text); 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 1315f757f3fSDimitry Andric FileEntryRef origFE = I->first; 1325f757f3fSDimitry Andric SmallString<200> origPath = StringRef(origFE.getName()); 1330b57cec5SDimitry Andric fs::make_absolute(origPath); 1340b57cec5SDimitry Andric infoOut << origPath << '\n'; 1355f757f3fSDimitry Andric infoOut << (uint64_t)origFE.getModificationTime() << '\n'; 1360b57cec5SDimitry Andric 1375f757f3fSDimitry Andric if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) { 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; 145fe6060f1SDimitry Andric if (fs::createTemporaryFile( 1465f757f3fSDimitry Andric path::filename(origFE.getName()), 1475f757f3fSDimitry Andric path::extension(origFE.getName()).drop_front(), fd, tempPath, 148fe6060f1SDimitry Andric llvm::sys::fs::OF_Text)) 1490b57cec5SDimitry Andric return report("Could not create file: " + tempPath.str(), Diag); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 1525f757f3fSDimitry Andric llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second); 1530b57cec5SDimitry Andric newOut.write(mem->getBufferStart(), mem->getBufferSize()); 1540b57cec5SDimitry Andric newOut.close(); 1550b57cec5SDimitry Andric 1565f757f3fSDimitry Andric auto newE = FileMgr->getOptionalFileRef(tempPath); 157a7dea167SDimitry Andric if (newE) { 158a7dea167SDimitry Andric remap(origFE, *newE); 1595f757f3fSDimitry Andric infoOut << newE->getName() << '\n'; 160a7dea167SDimitry Andric } 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric } 1630b57cec5SDimitry Andric 1640b57cec5SDimitry Andric infoOut.close(); 1650b57cec5SDimitry Andric return false; 1660b57cec5SDimitry Andric } 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 1690b57cec5SDimitry Andric StringRef outputDir) { 1700b57cec5SDimitry Andric using namespace llvm::sys; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric for (MappingsTy::iterator 1730b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 1745f757f3fSDimitry Andric FileEntryRef origFE = I->first; 1755f757f3fSDimitry Andric assert(std::holds_alternative<llvm::MemoryBuffer *>(I->second)); 1765f757f3fSDimitry Andric if (!fs::exists(origFE.getName())) 1775f757f3fSDimitry Andric return report(StringRef("File does not exist: ") + origFE.getName(), 1780b57cec5SDimitry Andric Diag); 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric std::error_code EC; 1815f757f3fSDimitry Andric llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None); 1820b57cec5SDimitry Andric if (EC) 1830b57cec5SDimitry Andric return report(EC.message(), Diag); 1840b57cec5SDimitry Andric 1855f757f3fSDimitry Andric llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second); 1860b57cec5SDimitry Andric Out.write(mem->getBufferStart(), mem->getBufferSize()); 1870b57cec5SDimitry Andric Out.close(); 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric clear(outputDir); 1910b57cec5SDimitry Andric return false; 1920b57cec5SDimitry Andric } 1930b57cec5SDimitry Andric 194e8d8bef9SDimitry Andric void FileRemapper::forEachMapping( 195e8d8bef9SDimitry Andric llvm::function_ref<void(StringRef, StringRef)> CaptureFile, 196e8d8bef9SDimitry Andric llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)> 197e8d8bef9SDimitry Andric CaptureBuffer) const { 198e8d8bef9SDimitry Andric for (auto &Mapping : FromToMappings) { 1995f757f3fSDimitry Andric if (const auto *FE = std::get_if<FileEntryRef>(&Mapping.second)) { 2005f757f3fSDimitry Andric CaptureFile(Mapping.first.getName(), FE->getName()); 201e8d8bef9SDimitry Andric continue; 202e8d8bef9SDimitry Andric } 203e8d8bef9SDimitry Andric CaptureBuffer( 2045f757f3fSDimitry Andric Mapping.first.getName(), 2055f757f3fSDimitry Andric std::get<llvm::MemoryBuffer *>(Mapping.second)->getMemBufferRef()); 206e8d8bef9SDimitry Andric } 207e8d8bef9SDimitry Andric } 208e8d8bef9SDimitry Andric 2090b57cec5SDimitry Andric void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 2100b57cec5SDimitry Andric for (MappingsTy::const_iterator 2110b57cec5SDimitry Andric I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 2125f757f3fSDimitry Andric if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) { 2135f757f3fSDimitry Andric PPOpts.addRemappedFile(I->first.getName(), FE->getName()); 2140b57cec5SDimitry Andric } else { 2155f757f3fSDimitry Andric llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second); 2165f757f3fSDimitry Andric PPOpts.addRemappedFile(I->first.getName(), mem); 2170b57cec5SDimitry Andric } 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric PPOpts.RetainRemappedFileBuffers = true; 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric 2230b57cec5SDimitry Andric void FileRemapper::remap(StringRef filePath, 2240b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> memBuf) { 2255f757f3fSDimitry Andric OptionalFileEntryRef File = getOriginalFile(filePath); 2265f757f3fSDimitry Andric assert(File); 2275f757f3fSDimitry Andric remap(*File, std::move(memBuf)); 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric 2305f757f3fSDimitry Andric void FileRemapper::remap(FileEntryRef File, 2315f757f3fSDimitry Andric std::unique_ptr<llvm::MemoryBuffer> MemBuf) { 2325f757f3fSDimitry Andric auto [It, New] = FromToMappings.insert({File, nullptr}); 2335f757f3fSDimitry Andric if (!New) 2345f757f3fSDimitry Andric resetTarget(It->second); 2355f757f3fSDimitry Andric It->second = MemBuf.release(); 2360b57cec5SDimitry Andric } 2370b57cec5SDimitry Andric 2385f757f3fSDimitry Andric void FileRemapper::remap(FileEntryRef File, FileEntryRef NewFile) { 2395f757f3fSDimitry Andric auto [It, New] = FromToMappings.insert({File, nullptr}); 2405f757f3fSDimitry Andric if (!New) 2415f757f3fSDimitry Andric resetTarget(It->second); 2425f757f3fSDimitry Andric It->second = NewFile; 2435f757f3fSDimitry Andric ToFromMappings.insert({NewFile, File}); 2440b57cec5SDimitry Andric } 2450b57cec5SDimitry Andric 2465f757f3fSDimitry Andric OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) { 2475f757f3fSDimitry Andric OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath); 2485f757f3fSDimitry Andric if (!File) 2495f757f3fSDimitry Andric return std::nullopt; 2500b57cec5SDimitry Andric // If we are updating a file that overridden an original file, 2510b57cec5SDimitry Andric // actually update the original file. 2525f757f3fSDimitry Andric auto I = ToFromMappings.find(*File); 2530b57cec5SDimitry Andric if (I != ToFromMappings.end()) { 2545f757f3fSDimitry Andric *File = I->second; 2555f757f3fSDimitry Andric assert(FromToMappings.contains(*File) && "Original file not in mappings!"); 2560b57cec5SDimitry Andric } 2575f757f3fSDimitry Andric return File; 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric 2600b57cec5SDimitry Andric void FileRemapper::resetTarget(Target &targ) { 2615f757f3fSDimitry Andric if (std::holds_alternative<llvm::MemoryBuffer *>(targ)) { 2625f757f3fSDimitry Andric llvm::MemoryBuffer *oldmem = std::get<llvm::MemoryBuffer *>(targ); 2630b57cec5SDimitry Andric delete oldmem; 2640b57cec5SDimitry Andric } else { 2655f757f3fSDimitry Andric FileEntryRef toFE = std::get<FileEntryRef>(targ); 2660b57cec5SDimitry Andric ToFromMappings.erase(toFE); 2670b57cec5SDimitry Andric } 2680b57cec5SDimitry Andric } 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 2710b57cec5SDimitry Andric Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 2720b57cec5SDimitry Andric << err.str(); 2730b57cec5SDimitry Andric return true; 2740b57cec5SDimitry Andric } 275