1224135Sdim//===--- FileRemapper.cpp - File Remapping Helper -------------------------===// 2224135Sdim// 3224135Sdim// The LLVM Compiler Infrastructure 4224135Sdim// 5224135Sdim// This file is distributed under the University of Illinois Open Source 6224135Sdim// License. See LICENSE.TXT for details. 7224135Sdim// 8224135Sdim//===----------------------------------------------------------------------===// 9224135Sdim 10224135Sdim#include "clang/ARCMigrate/FileRemapper.h" 11249423Sdim#include "clang/Basic/Diagnostic.h" 12249423Sdim#include "clang/Basic/FileManager.h" 13243830Sdim#include "clang/Lex/PreprocessorOptions.h" 14249423Sdim#include "llvm/Support/FileSystem.h" 15224135Sdim#include "llvm/Support/MemoryBuffer.h" 16224135Sdim#include "llvm/Support/Path.h" 17224135Sdim#include "llvm/Support/raw_ostream.h" 18224135Sdim#include <fstream> 19224135Sdim 20224135Sdimusing namespace clang; 21224135Sdimusing namespace arcmt; 22224135Sdim 23224135SdimFileRemapper::FileRemapper() { 24224135Sdim FileMgr.reset(new FileManager(FileSystemOptions())); 25224135Sdim} 26224135Sdim 27224135SdimFileRemapper::~FileRemapper() { 28224135Sdim clear(); 29224135Sdim} 30224135Sdim 31226633Sdimvoid FileRemapper::clear(StringRef outputDir) { 32224135Sdim for (MappingsTy::iterator 33224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) 34224135Sdim resetTarget(I->second); 35224135Sdim FromToMappings.clear(); 36224135Sdim assert(ToFromMappings.empty()); 37224135Sdim if (!outputDir.empty()) { 38224135Sdim std::string infoFile = getRemapInfoFile(outputDir); 39224135Sdim bool existed; 40224135Sdim llvm::sys::fs::remove(infoFile, existed); 41224135Sdim } 42224135Sdim} 43224135Sdim 44226633Sdimstd::string FileRemapper::getRemapInfoFile(StringRef outputDir) { 45224135Sdim assert(!outputDir.empty()); 46263508Sdim SmallString<128> InfoFile = outputDir; 47263508Sdim llvm::sys::path::append(InfoFile, "remap"); 48263508Sdim return InfoFile.str(); 49224135Sdim} 50224135Sdim 51226633Sdimbool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, 52224135Sdim bool ignoreIfFilesChanged) { 53234353Sdim std::string infoFile = getRemapInfoFile(outputDir); 54234353Sdim return initFromFile(infoFile, Diag, ignoreIfFilesChanged); 55234353Sdim} 56234353Sdim 57234353Sdimbool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, 58234353Sdim bool ignoreIfFilesChanged) { 59224135Sdim assert(FromToMappings.empty() && 60224135Sdim "initFromDisk should be called before any remap calls"); 61234353Sdim std::string infoFile = filePath; 62224135Sdim bool fileExists = false; 63224135Sdim llvm::sys::fs::exists(infoFile, fileExists); 64224135Sdim if (!fileExists) 65224135Sdim return false; 66224135Sdim 67224135Sdim std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; 68226633Sdim 69234353Sdim OwningPtr<llvm::MemoryBuffer> fileBuf; 70234353Sdim if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf)) 71226633Sdim return report("Error opening file: " + infoFile, Diag); 72226633Sdim 73226633Sdim SmallVector<StringRef, 64> lines; 74226633Sdim fileBuf->getBuffer().split(lines, "\n"); 75224135Sdim 76226633Sdim for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { 77226633Sdim StringRef fromFilename = lines[idx]; 78226633Sdim unsigned long long timeModified; 79239462Sdim if (lines[idx+1].getAsInteger(10, timeModified)) 80239462Sdim return report("Invalid file data: '" + lines[idx+1] + "' not a number", 81239462Sdim Diag); 82226633Sdim StringRef toFilename = lines[idx+2]; 83226633Sdim 84224135Sdim const FileEntry *origFE = FileMgr->getFile(fromFilename); 85224135Sdim if (!origFE) { 86224135Sdim if (ignoreIfFilesChanged) 87224135Sdim continue; 88226633Sdim return report("File does not exist: " + fromFilename, Diag); 89224135Sdim } 90224135Sdim const FileEntry *newFE = FileMgr->getFile(toFilename); 91224135Sdim if (!newFE) { 92224135Sdim if (ignoreIfFilesChanged) 93224135Sdim continue; 94226633Sdim return report("File does not exist: " + toFilename, Diag); 95224135Sdim } 96224135Sdim 97224135Sdim if ((uint64_t)origFE->getModificationTime() != timeModified) { 98224135Sdim if (ignoreIfFilesChanged) 99224135Sdim continue; 100226633Sdim return report("File was modified: " + fromFilename, Diag); 101224135Sdim } 102224135Sdim 103224135Sdim pairs.push_back(std::make_pair(origFE, newFE)); 104224135Sdim } 105224135Sdim 106224135Sdim for (unsigned i = 0, e = pairs.size(); i != e; ++i) 107224135Sdim remap(pairs[i].first, pairs[i].second); 108224135Sdim 109224135Sdim return false; 110224135Sdim} 111224135Sdim 112226633Sdimbool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { 113224135Sdim using namespace llvm::sys; 114224135Sdim 115224135Sdim bool existed; 116224135Sdim if (fs::create_directory(outputDir, existed) != llvm::errc::success) 117226633Sdim return report("Could not create directory: " + outputDir, Diag); 118224135Sdim 119234353Sdim std::string infoFile = getRemapInfoFile(outputDir); 120234353Sdim return flushToFile(infoFile, Diag); 121234353Sdim} 122234353Sdim 123234353Sdimbool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { 124234353Sdim using namespace llvm::sys; 125234353Sdim 126224135Sdim std::string errMsg; 127234353Sdim std::string infoFile = outputPath; 128224135Sdim llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, 129263508Sdim llvm::sys::fs::F_Binary); 130224135Sdim if (!errMsg.empty()) 131224135Sdim return report(errMsg, Diag); 132224135Sdim 133224135Sdim for (MappingsTy::iterator 134224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 135224135Sdim 136224135Sdim const FileEntry *origFE = I->first; 137234353Sdim SmallString<200> origPath = StringRef(origFE->getName()); 138224135Sdim fs::make_absolute(origPath); 139224135Sdim infoOut << origPath << '\n'; 140224135Sdim infoOut << (uint64_t)origFE->getModificationTime() << '\n'; 141224135Sdim 142224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 143234353Sdim SmallString<200> newPath = StringRef(FE->getName()); 144224135Sdim fs::make_absolute(newPath); 145224135Sdim infoOut << newPath << '\n'; 146224135Sdim } else { 147224135Sdim 148234353Sdim SmallString<64> tempPath; 149224135Sdim int fd; 150263508Sdim if (fs::createTemporaryFile(path::filename(origFE->getName()), 151263508Sdim path::extension(origFE->getName()), fd, 152263508Sdim tempPath)) 153226633Sdim return report("Could not create file: " + tempPath.str(), Diag); 154224135Sdim 155224135Sdim llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 156224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 157224135Sdim newOut.write(mem->getBufferStart(), mem->getBufferSize()); 158224135Sdim newOut.close(); 159224135Sdim 160224135Sdim const FileEntry *newE = FileMgr->getFile(tempPath); 161224135Sdim remap(origFE, newE); 162224135Sdim infoOut << newE->getName() << '\n'; 163224135Sdim } 164224135Sdim } 165224135Sdim 166224135Sdim infoOut.close(); 167224135Sdim return false; 168224135Sdim} 169224135Sdim 170226633Sdimbool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 171226633Sdim StringRef outputDir) { 172224135Sdim using namespace llvm::sys; 173224135Sdim 174224135Sdim for (MappingsTy::iterator 175224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 176224135Sdim const FileEntry *origFE = I->first; 177263508Sdim assert(I->second.is<llvm::MemoryBuffer *>()); 178263508Sdim bool fileExists = false; 179263508Sdim fs::exists(origFE->getName(), fileExists); 180263508Sdim if (!fileExists) 181263508Sdim return report(StringRef("File does not exist: ") + origFE->getName(), 182263508Sdim Diag); 183224135Sdim 184263508Sdim std::string errMsg; 185263508Sdim llvm::raw_fd_ostream Out(origFE->getName(), errMsg, 186263508Sdim llvm::sys::fs::F_Binary); 187263508Sdim if (!errMsg.empty()) 188263508Sdim return report(errMsg, Diag); 189224135Sdim 190263508Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 191263508Sdim Out.write(mem->getBufferStart(), mem->getBufferSize()); 192263508Sdim Out.close(); 193224135Sdim } 194224135Sdim 195224135Sdim clear(outputDir); 196224135Sdim return false; 197224135Sdim} 198224135Sdim 199234353Sdimvoid FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 200224135Sdim for (MappingsTy::const_iterator 201224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 202224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 203224135Sdim PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 204224135Sdim } else { 205224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 206224135Sdim PPOpts.addRemappedFile(I->first->getName(), mem); 207224135Sdim } 208224135Sdim } 209224135Sdim 210224135Sdim PPOpts.RetainRemappedFileBuffers = true; 211224135Sdim} 212224135Sdim 213234353Sdimvoid FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) { 214224135Sdim for (MappingsTy::iterator 215224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 216224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 217224135Sdim PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 218224135Sdim } else { 219224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 220224135Sdim PPOpts.addRemappedFile(I->first->getName(), mem); 221224135Sdim } 222224135Sdim I->second = Target(); 223224135Sdim } 224224135Sdim 225224135Sdim PPOpts.RetainRemappedFileBuffers = false; 226224135Sdim clear(); 227224135Sdim} 228224135Sdim 229226633Sdimvoid FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) { 230224135Sdim remap(getOriginalFile(filePath), memBuf); 231224135Sdim} 232224135Sdim 233224135Sdimvoid FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) { 234224135Sdim assert(file); 235224135Sdim Target &targ = FromToMappings[file]; 236224135Sdim resetTarget(targ); 237224135Sdim targ = memBuf; 238224135Sdim} 239224135Sdim 240224135Sdimvoid FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { 241224135Sdim assert(file && newfile); 242224135Sdim Target &targ = FromToMappings[file]; 243224135Sdim resetTarget(targ); 244224135Sdim targ = newfile; 245224135Sdim ToFromMappings[newfile] = file; 246224135Sdim} 247224135Sdim 248226633Sdimconst FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { 249224135Sdim const FileEntry *file = FileMgr->getFile(filePath); 250224135Sdim // If we are updating a file that overriden an original file, 251224135Sdim // actually update the original file. 252224135Sdim llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator 253224135Sdim I = ToFromMappings.find(file); 254224135Sdim if (I != ToFromMappings.end()) { 255224135Sdim file = I->second; 256224135Sdim assert(FromToMappings.find(file) != FromToMappings.end() && 257224135Sdim "Original file not in mappings!"); 258224135Sdim } 259224135Sdim return file; 260224135Sdim} 261224135Sdim 262224135Sdimvoid FileRemapper::resetTarget(Target &targ) { 263224135Sdim if (!targ) 264224135Sdim return; 265224135Sdim 266224135Sdim if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { 267224135Sdim delete oldmem; 268224135Sdim } else { 269224135Sdim const FileEntry *toFE = targ.get<const FileEntry *>(); 270234353Sdim ToFromMappings.erase(toFE); 271224135Sdim } 272224135Sdim} 273224135Sdim 274226633Sdimbool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 275234353Sdim SmallString<128> buf; 276224135Sdim unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, 277226633Sdim err.toStringRef(buf)); 278224135Sdim Diag.Report(ID); 279224135Sdim return true; 280224135Sdim} 281