| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | #include "clang/ARCMigrate/FileRemapper.h" |
| 10 | #include "clang/Basic/Diagnostic.h" |
| 11 | #include "clang/Basic/FileManager.h" |
| 12 | #include "clang/Lex/PreprocessorOptions.h" |
| 13 | #include "llvm/Support/FileSystem.h" |
| 14 | #include "llvm/Support/MemoryBuffer.h" |
| 15 | #include "llvm/Support/Path.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 | llvm::sys::fs::remove(infoFile); |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { |
| 43 | assert(!outputDir.empty()); |
| 44 | SmallString<128> InfoFile = outputDir; |
| 45 | llvm::sys::path::append(InfoFile, "remap"); |
| 46 | return InfoFile.str(); |
| 47 | } |
| 48 | |
| 49 | bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, |
| 50 | bool ignoreIfFilesChanged) { |
| 51 | std::string infoFile = getRemapInfoFile(outputDir); |
| 52 | return initFromFile(infoFile, Diag, ignoreIfFilesChanged); |
| 53 | } |
| 54 | |
| 55 | bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, |
| 56 | bool ignoreIfFilesChanged) { |
| 57 | (0) . __assert_fail ("FromToMappings.empty() && \"initFromDisk should be called before any remap calls\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 58, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FromToMappings.empty() && |
| 58 | (0) . __assert_fail ("FromToMappings.empty() && \"initFromDisk should be called before any remap calls\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 58, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "initFromDisk should be called before any remap calls"); |
| 59 | std::string infoFile = filePath; |
| 60 | if (!llvm::sys::fs::exists(infoFile)) |
| 61 | return false; |
| 62 | |
| 63 | std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; |
| 64 | |
| 65 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = |
| 66 | llvm::MemoryBuffer::getFile(infoFile); |
| 67 | if (!fileBuf) |
| 68 | return report("Error opening file: " + infoFile, Diag); |
| 69 | |
| 70 | SmallVector<StringRef, 64> lines; |
| 71 | fileBuf.get()->getBuffer().split(lines, "\n"); |
| 72 | |
| 73 | for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { |
| 74 | StringRef fromFilename = lines[idx]; |
| 75 | unsigned long long timeModified; |
| 76 | if (lines[idx+1].getAsInteger(10, timeModified)) |
| 77 | return report("Invalid file data: '" + lines[idx+1] + "' not a number", |
| 78 | Diag); |
| 79 | StringRef toFilename = lines[idx+2]; |
| 80 | |
| 81 | const FileEntry *origFE = FileMgr->getFile(fromFilename); |
| 82 | if (!origFE) { |
| 83 | if (ignoreIfFilesChanged) |
| 84 | continue; |
| 85 | return report("File does not exist: " + fromFilename, Diag); |
| 86 | } |
| 87 | const FileEntry *newFE = FileMgr->getFile(toFilename); |
| 88 | if (!newFE) { |
| 89 | if (ignoreIfFilesChanged) |
| 90 | continue; |
| 91 | return report("File does not exist: " + toFilename, Diag); |
| 92 | } |
| 93 | |
| 94 | if ((uint64_t)origFE->getModificationTime() != timeModified) { |
| 95 | if (ignoreIfFilesChanged) |
| 96 | continue; |
| 97 | return report("File was modified: " + fromFilename, Diag); |
| 98 | } |
| 99 | |
| 100 | pairs.push_back(std::make_pair(origFE, newFE)); |
| 101 | } |
| 102 | |
| 103 | for (unsigned i = 0, e = pairs.size(); i != e; ++i) |
| 104 | remap(pairs[i].first, pairs[i].second); |
| 105 | |
| 106 | return false; |
| 107 | } |
| 108 | |
| 109 | bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { |
| 110 | using namespace llvm::sys; |
| 111 | |
| 112 | if (fs::create_directory(outputDir)) |
| 113 | return report("Could not create directory: " + outputDir, Diag); |
| 114 | |
| 115 | std::string infoFile = getRemapInfoFile(outputDir); |
| 116 | return flushToFile(infoFile, Diag); |
| 117 | } |
| 118 | |
| 119 | bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { |
| 120 | using namespace llvm::sys; |
| 121 | |
| 122 | std::error_code EC; |
| 123 | std::string infoFile = outputPath; |
| 124 | llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None); |
| 125 | if (EC) |
| 126 | return report(EC.message(), Diag); |
| 127 | |
| 128 | for (MappingsTy::iterator |
| 129 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { |
| 130 | |
| 131 | const FileEntry *origFE = I->first; |
| 132 | SmallString<200> origPath = StringRef(origFE->getName()); |
| 133 | fs::make_absolute(origPath); |
| 134 | infoOut << origPath << '\n'; |
| 135 | infoOut << (uint64_t)origFE->getModificationTime() << '\n'; |
| 136 | |
| 137 | if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
| 138 | SmallString<200> newPath = StringRef(FE->getName()); |
| 139 | fs::make_absolute(newPath); |
| 140 | infoOut << newPath << '\n'; |
| 141 | } else { |
| 142 | |
| 143 | SmallString<64> tempPath; |
| 144 | int fd; |
| 145 | if (fs::createTemporaryFile(path::filename(origFE->getName()), |
| 146 | path::extension(origFE->getName()).drop_front(), fd, |
| 147 | tempPath)) |
| 148 | return report("Could not create file: " + tempPath.str(), Diag); |
| 149 | |
| 150 | llvm::raw_fd_ostream newOut(fd, ); |
| 151 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
| 152 | newOut.write(mem->getBufferStart(), mem->getBufferSize()); |
| 153 | newOut.close(); |
| 154 | |
| 155 | const FileEntry *newE = FileMgr->getFile(tempPath); |
| 156 | remap(origFE, newE); |
| 157 | infoOut << newE->getName() << '\n'; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | infoOut.close(); |
| 162 | return false; |
| 163 | } |
| 164 | |
| 165 | bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, |
| 166 | StringRef outputDir) { |
| 167 | using namespace llvm::sys; |
| 168 | |
| 169 | for (MappingsTy::iterator |
| 170 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { |
| 171 | const FileEntry *origFE = I->first; |
| 172 | second.is()", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 172, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(I->second.is<llvm::MemoryBuffer *>()); |
| 173 | if (!fs::exists(origFE->getName())) |
| 174 | return report(StringRef("File does not exist: ") + origFE->getName(), |
| 175 | Diag); |
| 176 | |
| 177 | std::error_code EC; |
| 178 | llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None); |
| 179 | if (EC) |
| 180 | return report(EC.message(), Diag); |
| 181 | |
| 182 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
| 183 | Out.write(mem->getBufferStart(), mem->getBufferSize()); |
| 184 | Out.close(); |
| 185 | } |
| 186 | |
| 187 | clear(outputDir); |
| 188 | return false; |
| 189 | } |
| 190 | |
| 191 | void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { |
| 192 | for (MappingsTy::const_iterator |
| 193 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { |
| 194 | if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
| 195 | PPOpts.addRemappedFile(I->first->getName(), FE->getName()); |
| 196 | } else { |
| 197 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
| 198 | PPOpts.addRemappedFile(I->first->getName(), mem); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | PPOpts.RetainRemappedFileBuffers = true; |
| 203 | } |
| 204 | |
| 205 | void FileRemapper::remap(StringRef filePath, |
| 206 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
| 207 | remap(getOriginalFile(filePath), std::move(memBuf)); |
| 208 | } |
| 209 | |
| 210 | void FileRemapper::remap(const FileEntry *file, |
| 211 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
| 212 | assert(file); |
| 213 | Target &targ = FromToMappings[file]; |
| 214 | resetTarget(targ); |
| 215 | targ = memBuf.release(); |
| 216 | } |
| 217 | |
| 218 | void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { |
| 219 | assert(file && newfile); |
| 220 | Target &targ = FromToMappings[file]; |
| 221 | resetTarget(targ); |
| 222 | targ = newfile; |
| 223 | ToFromMappings[newfile] = file; |
| 224 | } |
| 225 | |
| 226 | const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { |
| 227 | const FileEntry *file = FileMgr->getFile(filePath); |
| 228 | |
| 229 | |
| 230 | llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator |
| 231 | I = ToFromMappings.find(file); |
| 232 | if (I != ToFromMappings.end()) { |
| 233 | file = I->second; |
| 234 | (0) . __assert_fail ("FromToMappings.find(file) != FromToMappings.end() && \"Original file not in mappings!\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 235, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FromToMappings.find(file) != FromToMappings.end() && |
| 235 | (0) . __assert_fail ("FromToMappings.find(file) != FromToMappings.end() && \"Original file not in mappings!\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 235, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "Original file not in mappings!"); |
| 236 | } |
| 237 | return file; |
| 238 | } |
| 239 | |
| 240 | void FileRemapper::resetTarget(Target &targ) { |
| 241 | if (!targ) |
| 242 | return; |
| 243 | |
| 244 | if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { |
| 245 | delete oldmem; |
| 246 | } else { |
| 247 | const FileEntry *toFE = targ.get<const FileEntry *>(); |
| 248 | ToFromMappings.erase(toFE); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { |
| 253 | Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) |
| 254 | << err.str(); |
| 255 | return true; |
| 256 | } |
| 257 | |