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