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