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/Frontend/PreprocessorOptions.h" 00012 #include "clang/Basic/FileManager.h" 00013 #include "clang/Basic/Diagnostic.h" 00014 #include "llvm/Support/MemoryBuffer.h" 00015 #include "llvm/Support/Path.h" 00016 #include "llvm/Support/FileSystem.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 bool existed; 00040 llvm::sys::fs::remove(infoFile, existed); 00041 } 00042 } 00043 00044 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { 00045 assert(!outputDir.empty()); 00046 llvm::sys::Path dir(outputDir); 00047 llvm::sys::Path infoFile = dir; 00048 infoFile.appendComponent("remap"); 00049 return infoFile.str(); 00050 } 00051 00052 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, 00053 bool ignoreIfFilesChanged) { 00054 std::string infoFile = getRemapInfoFile(outputDir); 00055 return initFromFile(infoFile, Diag, ignoreIfFilesChanged); 00056 } 00057 00058 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, 00059 bool ignoreIfFilesChanged) { 00060 assert(FromToMappings.empty() && 00061 "initFromDisk should be called before any remap calls"); 00062 std::string infoFile = filePath; 00063 bool fileExists = false; 00064 llvm::sys::fs::exists(infoFile, fileExists); 00065 if (!fileExists) 00066 return false; 00067 00068 std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; 00069 00070 OwningPtr<llvm::MemoryBuffer> fileBuf; 00071 if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf)) 00072 return report("Error opening file: " + infoFile, Diag); 00073 00074 SmallVector<StringRef, 64> lines; 00075 fileBuf->getBuffer().split(lines, "\n"); 00076 00077 for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { 00078 StringRef fromFilename = lines[idx]; 00079 unsigned long long timeModified; 00080 if (lines[idx+1].getAsInteger(10, timeModified)) 00081 return report("Invalid file data: '" + lines[idx+1] + "' not a number", 00082 Diag); 00083 StringRef toFilename = lines[idx+2]; 00084 00085 const FileEntry *origFE = FileMgr->getFile(fromFilename); 00086 if (!origFE) { 00087 if (ignoreIfFilesChanged) 00088 continue; 00089 return report("File does not exist: " + fromFilename, Diag); 00090 } 00091 const FileEntry *newFE = FileMgr->getFile(toFilename); 00092 if (!newFE) { 00093 if (ignoreIfFilesChanged) 00094 continue; 00095 return report("File does not exist: " + toFilename, Diag); 00096 } 00097 00098 if ((uint64_t)origFE->getModificationTime() != timeModified) { 00099 if (ignoreIfFilesChanged) 00100 continue; 00101 return report("File was modified: " + fromFilename, Diag); 00102 } 00103 00104 pairs.push_back(std::make_pair(origFE, newFE)); 00105 } 00106 00107 for (unsigned i = 0, e = pairs.size(); i != e; ++i) 00108 remap(pairs[i].first, pairs[i].second); 00109 00110 return false; 00111 } 00112 00113 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { 00114 using namespace llvm::sys; 00115 00116 bool existed; 00117 if (fs::create_directory(outputDir, existed) != llvm::errc::success) 00118 return report("Could not create directory: " + outputDir, Diag); 00119 00120 std::string infoFile = getRemapInfoFile(outputDir); 00121 return flushToFile(infoFile, Diag); 00122 } 00123 00124 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { 00125 using namespace llvm::sys; 00126 00127 std::string errMsg; 00128 std::string infoFile = outputPath; 00129 llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, 00130 llvm::raw_fd_ostream::F_Binary); 00131 if (!errMsg.empty()) 00132 return report(errMsg, Diag); 00133 00134 for (MappingsTy::iterator 00135 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00136 00137 const FileEntry *origFE = I->first; 00138 SmallString<200> origPath = StringRef(origFE->getName()); 00139 fs::make_absolute(origPath); 00140 infoOut << origPath << '\n'; 00141 infoOut << (uint64_t)origFE->getModificationTime() << '\n'; 00142 00143 if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 00144 SmallString<200> newPath = StringRef(FE->getName()); 00145 fs::make_absolute(newPath); 00146 infoOut << newPath << '\n'; 00147 } else { 00148 00149 SmallString<64> tempPath; 00150 tempPath = path::filename(origFE->getName()); 00151 tempPath += "-%%%%%%%%"; 00152 tempPath += path::extension(origFE->getName()); 00153 int fd; 00154 if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success) 00155 return report("Could not create file: " + tempPath.str(), Diag); 00156 00157 llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); 00158 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00159 newOut.write(mem->getBufferStart(), mem->getBufferSize()); 00160 newOut.close(); 00161 00162 const FileEntry *newE = FileMgr->getFile(tempPath); 00163 remap(origFE, newE); 00164 infoOut << newE->getName() << '\n'; 00165 } 00166 } 00167 00168 infoOut.close(); 00169 return false; 00170 } 00171 00172 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, 00173 StringRef outputDir) { 00174 using namespace llvm::sys; 00175 00176 for (MappingsTy::iterator 00177 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00178 const FileEntry *origFE = I->first; 00179 if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) { 00180 if (fs::copy_file(newFE->getName(), origFE->getName(), 00181 fs::copy_option::overwrite_if_exists) != llvm::errc::success) 00182 return report(StringRef("Could not copy file '") + newFE->getName() + 00183 "' to file '" + origFE->getName() + "'", Diag); 00184 } else { 00185 00186 bool fileExists = false; 00187 fs::exists(origFE->getName(), fileExists); 00188 if (!fileExists) 00189 return report(StringRef("File does not exist: ") + origFE->getName(), 00190 Diag); 00191 00192 std::string errMsg; 00193 llvm::raw_fd_ostream Out(origFE->getName(), errMsg, 00194 llvm::raw_fd_ostream::F_Binary); 00195 if (!errMsg.empty()) 00196 return report(errMsg, Diag); 00197 00198 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00199 Out.write(mem->getBufferStart(), mem->getBufferSize()); 00200 Out.close(); 00201 } 00202 } 00203 00204 clear(outputDir); 00205 return false; 00206 } 00207 00208 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { 00209 for (MappingsTy::const_iterator 00210 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00211 if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 00212 PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 00213 } else { 00214 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00215 PPOpts.addRemappedFile(I->first->getName(), mem); 00216 } 00217 } 00218 00219 PPOpts.RetainRemappedFileBuffers = true; 00220 } 00221 00222 void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) { 00223 for (MappingsTy::iterator 00224 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { 00225 if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { 00226 PPOpts.addRemappedFile(I->first->getName(), FE->getName()); 00227 } else { 00228 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); 00229 PPOpts.addRemappedFile(I->first->getName(), mem); 00230 } 00231 I->second = Target(); 00232 } 00233 00234 PPOpts.RetainRemappedFileBuffers = false; 00235 clear(); 00236 } 00237 00238 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) { 00239 remap(getOriginalFile(filePath), memBuf); 00240 } 00241 00242 void FileRemapper::remap(StringRef filePath, StringRef newPath) { 00243 const FileEntry *file = getOriginalFile(filePath); 00244 const FileEntry *newfile = FileMgr->getFile(newPath); 00245 remap(file, newfile); 00246 } 00247 00248 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) { 00249 assert(file); 00250 Target &targ = FromToMappings[file]; 00251 resetTarget(targ); 00252 targ = memBuf; 00253 } 00254 00255 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { 00256 assert(file && newfile); 00257 Target &targ = FromToMappings[file]; 00258 resetTarget(targ); 00259 targ = newfile; 00260 ToFromMappings[newfile] = file; 00261 } 00262 00263 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { 00264 const FileEntry *file = FileMgr->getFile(filePath); 00265 // If we are updating a file that overriden an original file, 00266 // actually update the original file. 00267 llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator 00268 I = ToFromMappings.find(file); 00269 if (I != ToFromMappings.end()) { 00270 file = I->second; 00271 assert(FromToMappings.find(file) != FromToMappings.end() && 00272 "Original file not in mappings!"); 00273 } 00274 return file; 00275 } 00276 00277 void FileRemapper::resetTarget(Target &targ) { 00278 if (!targ) 00279 return; 00280 00281 if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { 00282 delete oldmem; 00283 } else { 00284 const FileEntry *toFE = targ.get<const FileEntry *>(); 00285 ToFromMappings.erase(toFE); 00286 } 00287 } 00288 00289 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { 00290 SmallString<128> buf; 00291 unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, 00292 err.toStringRef(buf)); 00293 Diag.Report(ID); 00294 return true; 00295 }