clang API Documentation
00001 //===--- FileRemapper.cpp - File Remapping Helper -------------------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 00010 #include "clang/ARCMigrate/FileRemapper.h" 00011 #include "clang/Basic/Diagnostic.h" 00012 #include "clang/Basic/FileManager.h" 00013 #include "clang/Lex/PreprocessorOptions.h" 00014 #include "llvm/Support/FileSystem.h" 00015 #include "llvm/Support/MemoryBuffer.h" 00016 #include "llvm/Support/Path.h" 00017 #include "llvm/Support/raw_ostream.h" 00018 #include <fstream> 00019 00020 using namespace clang; 00021 using namespace arcmt; 00022 00023 FileRemapper::FileRemapper() { 00024 FileMgr.reset(new FileManager(FileSystemOptions())); 00025 } 00026 00027 FileRemapper::~FileRemapper() { 00028 clear(); 00029 } 00030 00031 void FileRemapper::clear(StringRef outputDir) { 00032 for (MappingsTy::iterator 00033 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) 00034 resetTarget(I->second); 00035 FromToMappings.clear(); 00036 assert(ToFromMappings.empty()); 00037 if (!outputDir.empty()) { 00038 std::string infoFile = getRemapInfoFile(outputDir); 00039 llvm::sys::fs::remove(infoFile); 00040 } 00041 } 00042 00043 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { 00044 assert(!outputDir.empty()); 00045 SmallString<128> InfoFile = outputDir; 00046 llvm::sys::path::append(InfoFile, "remap"); 00047 return InfoFile.str(); 00048 } 00049 00050 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, 00051 bool ignoreIfFilesChanged) { 00052 std::string infoFile = getRemapInfoFile(outputDir); 00053 return initFromFile(infoFile, Diag, ignoreIfFilesChanged); 00054 } 00055 00056 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, 00057 bool ignoreIfFilesChanged) { 00058 assert(FromToMappings.empty() && 00059 "initFromDisk should be called before any remap calls"); 00060 std::string infoFile = filePath; 00061 if (!llvm::sys::fs::exists(infoFile)) 00062 return false; 00063 00064 std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; 00065 00066 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = 00067 llvm::MemoryBuffer::getFile(infoFile.c_str()); 00068 if (!fileBuf) 00069 return report("Error opening file: " + infoFile, Diag); 00070 00071 SmallVector<StringRef, 64> lines; 00072 fileBuf.get()->getBuffer().split(lines, "\n"); 00073 00074 for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { 00075 StringRef fromFilename = lines[idx]; 00076 unsigned long long timeModified; 00077 if (lines[idx+1].getAsInteger(10, timeModified)) 00078 return report("Invalid file data: '" + lines[idx+1] + "' not a number", 00079 Diag); 00080 StringRef toFilename = lines[idx+2]; 00081 00082 const FileEntry *origFE = FileMgr->getFile(fromFilename); 00083 if (!origFE) { 00084 if (ignoreIfFilesChanged) 00085 continue; 00086 return report("File does not exist: " + fromFilename, Diag); 00087 } 00088 const FileEntry *newFE = FileMgr->getFile(toFilename); 00089 if (!newFE) { 00090 if (ignoreIfFilesChanged) 00091 continue; 00092 return report("File does not exist: " + toFilename, Diag); 00093 } 00094 00095 if ((uint64_t)origFE->getModificationTime() != timeModified) { 00096 if (ignoreIfFilesChanged) 00097 continue; 00098 return report("File was modified: " + fromFilename, Diag); 00099 } 00100 00101 pairs.push_back(std::make_pair(origFE, newFE)); 00102 } 00103 00104 for (unsigned i = 0, e = pairs.size(); i != e; ++i) 00105 remap(pairs[i].first, pairs[i].second); 00106 00107 return false; 00108 } 00109 00110 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { 00111 using namespace llvm::sys; 00112 00113 if (fs::create_directory(outputDir)) 00114 return report("Could not create directory: " + outputDir, Diag); 00115 00116 std::string infoFile = getRemapInfoFile(outputDir); 00117 return flushToFile(infoFile, Diag); 00118 } 00119 00120 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { 00121 using namespace llvm::sys; 00122 00123 std::error_code EC; 00124 std::string infoFile = outputPath; 00125 llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None); 00126 if (EC) 00127 return report(EC.message(), Diag); 00128 00129 for (MappingsTy::iterator 00130 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00131 00132 const FileEntry *origFE = I->first; 00133 SmallString<200> origPath = StringRef(origFE->getName()); 00134 fs::make_absolute(origPath); 00135 infoOut << origPath << '\n'; 00136 infoOut << (uint64_t)origFE->getModificationTime() << '\n'; 00137 00138 if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 00139 SmallString<200> newPath = StringRef(FE->getName()); 00140 fs::make_absolute(newPath); 00141 infoOut << newPath << '\n'; 00142 } else { 00143 00144 SmallString<64> tempPath; 00145 int fd; 00146 if (fs::createTemporaryFile(path::filename(origFE->getName()), 00147 path::extension(origFE->getName()), fd, 00148 tempPath)) 00149 return report("Could not create file: " + tempPath.str(), Diag); 00150 00151 llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 00152 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00153 newOut.write(mem->getBufferStart(), mem->getBufferSize()); 00154 newOut.close(); 00155 00156 const FileEntry *newE = FileMgr->getFile(tempPath); 00157 remap(origFE, newE); 00158 infoOut << newE->getName() << '\n'; 00159 } 00160 } 00161 00162 infoOut.close(); 00163 return false; 00164 } 00165 00166 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 00167 StringRef outputDir) { 00168 using namespace llvm::sys; 00169 00170 for (MappingsTy::iterator 00171 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00172 const FileEntry *origFE = I->first; 00173 assert(I->second.is<llvm::MemoryBuffer *>()); 00174 if (!fs::exists(origFE->getName())) 00175 return report(StringRef("File does not exist: ") + origFE->getName(), 00176 Diag); 00177 00178 std::error_code EC; 00179 llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None); 00180 if (EC) 00181 return report(EC.message(), Diag); 00182 00183 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00184 Out.write(mem->getBufferStart(), mem->getBufferSize()); 00185 Out.close(); 00186 } 00187 00188 clear(outputDir); 00189 return false; 00190 } 00191 00192 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 00193 for (MappingsTy::const_iterator 00194 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00195 if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 00196 PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 00197 } else { 00198 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00199 PPOpts.addRemappedFile(I->first->getName(), mem); 00200 } 00201 } 00202 00203 PPOpts.RetainRemappedFileBuffers = true; 00204 } 00205 00206 void FileRemapper::remap(StringRef filePath, 00207 std::unique_ptr<llvm::MemoryBuffer> memBuf) { 00208 remap(getOriginalFile(filePath), std::move(memBuf)); 00209 } 00210 00211 void FileRemapper::remap(const FileEntry *file, 00212 std::unique_ptr<llvm::MemoryBuffer> memBuf) { 00213 assert(file); 00214 Target &targ = FromToMappings[file]; 00215 resetTarget(targ); 00216 targ = memBuf.release(); 00217 } 00218 00219 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { 00220 assert(file && newfile); 00221 Target &targ = FromToMappings[file]; 00222 resetTarget(targ); 00223 targ = newfile; 00224 ToFromMappings[newfile] = file; 00225 } 00226 00227 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { 00228 const FileEntry *file = FileMgr->getFile(filePath); 00229 // If we are updating a file that overriden an original file, 00230 // actually update the original file. 00231 llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator 00232 I = ToFromMappings.find(file); 00233 if (I != ToFromMappings.end()) { 00234 file = I->second; 00235 assert(FromToMappings.find(file) != FromToMappings.end() && 00236 "Original file not in mappings!"); 00237 } 00238 return file; 00239 } 00240 00241 void FileRemapper::resetTarget(Target &targ) { 00242 if (!targ) 00243 return; 00244 00245 if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { 00246 delete oldmem; 00247 } else { 00248 const FileEntry *toFE = targ.get<const FileEntry *>(); 00249 ToFromMappings.erase(toFE); 00250 } 00251 } 00252 00253 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 00254 Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 00255 << err.str(); 00256 return true; 00257 }