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