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()); 46224135Sdim llvm::sys::Path dir(outputDir); 47224135Sdim llvm::sys::Path infoFile = dir; 48224135Sdim infoFile.appendComponent("remap"); 49224135Sdim return infoFile.str(); 50224135Sdim} 51224135Sdim 52226633Sdimbool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, 53224135Sdim bool ignoreIfFilesChanged) { 54234353Sdim std::string infoFile = getRemapInfoFile(outputDir); 55234353Sdim return initFromFile(infoFile, Diag, ignoreIfFilesChanged); 56234353Sdim} 57234353Sdim 58234353Sdimbool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, 59234353Sdim bool ignoreIfFilesChanged) { 60224135Sdim assert(FromToMappings.empty() && 61224135Sdim "initFromDisk should be called before any remap calls"); 62234353Sdim std::string infoFile = filePath; 63224135Sdim bool fileExists = false; 64224135Sdim llvm::sys::fs::exists(infoFile, fileExists); 65224135Sdim if (!fileExists) 66224135Sdim return false; 67224135Sdim 68224135Sdim std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; 69226633Sdim 70234353Sdim OwningPtr<llvm::MemoryBuffer> fileBuf; 71234353Sdim if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf)) 72226633Sdim return report("Error opening file: " + infoFile, Diag); 73226633Sdim 74226633Sdim SmallVector<StringRef, 64> lines; 75226633Sdim fileBuf->getBuffer().split(lines, "\n"); 76224135Sdim 77226633Sdim for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { 78226633Sdim StringRef fromFilename = lines[idx]; 79226633Sdim unsigned long long timeModified; 80239462Sdim if (lines[idx+1].getAsInteger(10, timeModified)) 81239462Sdim return report("Invalid file data: '" + lines[idx+1] + "' not a number", 82239462Sdim Diag); 83226633Sdim StringRef toFilename = lines[idx+2]; 84226633Sdim 85224135Sdim const FileEntry *origFE = FileMgr->getFile(fromFilename); 86224135Sdim if (!origFE) { 87224135Sdim if (ignoreIfFilesChanged) 88224135Sdim continue; 89226633Sdim return report("File does not exist: " + fromFilename, Diag); 90224135Sdim } 91224135Sdim const FileEntry *newFE = FileMgr->getFile(toFilename); 92224135Sdim if (!newFE) { 93224135Sdim if (ignoreIfFilesChanged) 94224135Sdim continue; 95226633Sdim return report("File does not exist: " + toFilename, Diag); 96224135Sdim } 97224135Sdim 98224135Sdim if ((uint64_t)origFE->getModificationTime() != timeModified) { 99224135Sdim if (ignoreIfFilesChanged) 100224135Sdim continue; 101226633Sdim return report("File was modified: " + fromFilename, Diag); 102224135Sdim } 103224135Sdim 104224135Sdim pairs.push_back(std::make_pair(origFE, newFE)); 105224135Sdim } 106224135Sdim 107224135Sdim for (unsigned i = 0, e = pairs.size(); i != e; ++i) 108224135Sdim remap(pairs[i].first, pairs[i].second); 109224135Sdim 110224135Sdim return false; 111224135Sdim} 112224135Sdim 113226633Sdimbool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { 114224135Sdim using namespace llvm::sys; 115224135Sdim 116224135Sdim bool existed; 117224135Sdim if (fs::create_directory(outputDir, existed) != llvm::errc::success) 118226633Sdim return report("Could not create directory: " + outputDir, Diag); 119224135Sdim 120234353Sdim std::string infoFile = getRemapInfoFile(outputDir); 121234353Sdim return flushToFile(infoFile, Diag); 122234353Sdim} 123234353Sdim 124234353Sdimbool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { 125234353Sdim using namespace llvm::sys; 126234353Sdim 127224135Sdim std::string errMsg; 128234353Sdim std::string infoFile = outputPath; 129224135Sdim llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, 130224135Sdim llvm::raw_fd_ostream::F_Binary); 131224135Sdim if (!errMsg.empty()) 132224135Sdim return report(errMsg, Diag); 133224135Sdim 134224135Sdim for (MappingsTy::iterator 135224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 136224135Sdim 137224135Sdim const FileEntry *origFE = I->first; 138234353Sdim SmallString<200> origPath = StringRef(origFE->getName()); 139224135Sdim fs::make_absolute(origPath); 140224135Sdim infoOut << origPath << '\n'; 141224135Sdim infoOut << (uint64_t)origFE->getModificationTime() << '\n'; 142224135Sdim 143224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 144234353Sdim SmallString<200> newPath = StringRef(FE->getName()); 145224135Sdim fs::make_absolute(newPath); 146224135Sdim infoOut << newPath << '\n'; 147224135Sdim } else { 148224135Sdim 149234353Sdim SmallString<64> tempPath; 150224135Sdim tempPath = path::filename(origFE->getName()); 151224135Sdim tempPath += "-%%%%%%%%"; 152224135Sdim tempPath += path::extension(origFE->getName()); 153224135Sdim int fd; 154224135Sdim if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success) 155226633Sdim return report("Could not create file: " + tempPath.str(), Diag); 156224135Sdim 157224135Sdim llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 158224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 159224135Sdim newOut.write(mem->getBufferStart(), mem->getBufferSize()); 160224135Sdim newOut.close(); 161224135Sdim 162224135Sdim const FileEntry *newE = FileMgr->getFile(tempPath); 163224135Sdim remap(origFE, newE); 164224135Sdim infoOut << newE->getName() << '\n'; 165224135Sdim } 166224135Sdim } 167224135Sdim 168224135Sdim infoOut.close(); 169224135Sdim return false; 170224135Sdim} 171224135Sdim 172226633Sdimbool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 173226633Sdim StringRef outputDir) { 174224135Sdim using namespace llvm::sys; 175224135Sdim 176224135Sdim for (MappingsTy::iterator 177224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 178224135Sdim const FileEntry *origFE = I->first; 179224135Sdim if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) { 180224135Sdim if (fs::copy_file(newFE->getName(), origFE->getName(), 181226633Sdim fs::copy_option::overwrite_if_exists) != llvm::errc::success) 182226633Sdim return report(StringRef("Could not copy file '") + newFE->getName() + 183226633Sdim "' to file '" + origFE->getName() + "'", Diag); 184224135Sdim } else { 185224135Sdim 186224135Sdim bool fileExists = false; 187224135Sdim fs::exists(origFE->getName(), fileExists); 188224135Sdim if (!fileExists) 189226633Sdim return report(StringRef("File does not exist: ") + origFE->getName(), 190224135Sdim Diag); 191224135Sdim 192224135Sdim std::string errMsg; 193224135Sdim llvm::raw_fd_ostream Out(origFE->getName(), errMsg, 194224135Sdim llvm::raw_fd_ostream::F_Binary); 195224135Sdim if (!errMsg.empty()) 196224135Sdim return report(errMsg, Diag); 197224135Sdim 198224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 199224135Sdim Out.write(mem->getBufferStart(), mem->getBufferSize()); 200224135Sdim Out.close(); 201224135Sdim } 202224135Sdim } 203224135Sdim 204224135Sdim clear(outputDir); 205224135Sdim return false; 206224135Sdim} 207224135Sdim 208234353Sdimvoid FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 209224135Sdim for (MappingsTy::const_iterator 210224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 211224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 212224135Sdim PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 213224135Sdim } else { 214224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 215224135Sdim PPOpts.addRemappedFile(I->first->getName(), mem); 216224135Sdim } 217224135Sdim } 218224135Sdim 219224135Sdim PPOpts.RetainRemappedFileBuffers = true; 220224135Sdim} 221224135Sdim 222234353Sdimvoid FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) { 223224135Sdim for (MappingsTy::iterator 224224135Sdim I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 225224135Sdim if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 226224135Sdim PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 227224135Sdim } else { 228224135Sdim llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 229224135Sdim PPOpts.addRemappedFile(I->first->getName(), mem); 230224135Sdim } 231224135Sdim I->second = Target(); 232224135Sdim } 233224135Sdim 234224135Sdim PPOpts.RetainRemappedFileBuffers = false; 235224135Sdim clear(); 236224135Sdim} 237224135Sdim 238226633Sdimvoid FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) { 239224135Sdim remap(getOriginalFile(filePath), memBuf); 240224135Sdim} 241224135Sdim 242226633Sdimvoid FileRemapper::remap(StringRef filePath, StringRef newPath) { 243224135Sdim const FileEntry *file = getOriginalFile(filePath); 244224135Sdim const FileEntry *newfile = FileMgr->getFile(newPath); 245224135Sdim remap(file, newfile); 246224135Sdim} 247224135Sdim 248224135Sdimvoid FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) { 249224135Sdim assert(file); 250224135Sdim Target &targ = FromToMappings[file]; 251224135Sdim resetTarget(targ); 252224135Sdim targ = memBuf; 253224135Sdim} 254224135Sdim 255224135Sdimvoid FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { 256224135Sdim assert(file && newfile); 257224135Sdim Target &targ = FromToMappings[file]; 258224135Sdim resetTarget(targ); 259224135Sdim targ = newfile; 260224135Sdim ToFromMappings[newfile] = file; 261224135Sdim} 262224135Sdim 263226633Sdimconst FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { 264224135Sdim const FileEntry *file = FileMgr->getFile(filePath); 265224135Sdim // If we are updating a file that overriden an original file, 266224135Sdim // actually update the original file. 267224135Sdim llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator 268224135Sdim I = ToFromMappings.find(file); 269224135Sdim if (I != ToFromMappings.end()) { 270224135Sdim file = I->second; 271224135Sdim assert(FromToMappings.find(file) != FromToMappings.end() && 272224135Sdim "Original file not in mappings!"); 273224135Sdim } 274224135Sdim return file; 275224135Sdim} 276224135Sdim 277224135Sdimvoid FileRemapper::resetTarget(Target &targ) { 278224135Sdim if (!targ) 279224135Sdim return; 280224135Sdim 281224135Sdim if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { 282224135Sdim delete oldmem; 283224135Sdim } else { 284224135Sdim const FileEntry *toFE = targ.get<const FileEntry *>(); 285234353Sdim ToFromMappings.erase(toFE); 286224135Sdim } 287224135Sdim} 288224135Sdim 289226633Sdimbool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 290234353Sdim SmallString<128> buf; 291224135Sdim unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, 292226633Sdim err.toStringRef(buf)); 293224135Sdim Diag.Report(ID); 294224135Sdim return true; 295224135Sdim} 296