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