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