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