]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/FileRemapper.cpp
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / llvm / tools / clang / lib / ARCMigrate / FileRemapper.cpp
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::sys::fs::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       int fd;
150       if (fs::createTemporaryFile(path::filename(origFE->getName()),
151                                   path::extension(origFE->getName()), fd,
152                                   tempPath))
153         return report("Could not create file: " + tempPath.str(), Diag);
154
155       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
156       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
157       newOut.write(mem->getBufferStart(), mem->getBufferSize());
158       newOut.close();
159       
160       const FileEntry *newE = FileMgr->getFile(tempPath);
161       remap(origFE, newE);
162       infoOut << newE->getName() << '\n';
163     }
164   }
165
166   infoOut.close();
167   return false;
168 }
169
170 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
171                                      StringRef outputDir) {
172   using namespace llvm::sys;
173
174   for (MappingsTy::iterator
175          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
176     const FileEntry *origFE = I->first;
177     assert(I->second.is<llvm::MemoryBuffer *>());
178     bool fileExists = false;
179     fs::exists(origFE->getName(), fileExists);
180     if (!fileExists)
181       return report(StringRef("File does not exist: ") + origFE->getName(),
182                     Diag);
183
184     std::string errMsg;
185     llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
186                              llvm::sys::fs::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   clear(outputDir);
196   return false;
197 }
198
199 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
200   for (MappingsTy::const_iterator
201          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
202     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
203       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
204     } else {
205       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
206       PPOpts.addRemappedFile(I->first->getName(), mem);
207     }
208   }
209
210   PPOpts.RetainRemappedFileBuffers = true;
211 }
212
213 void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
214   for (MappingsTy::iterator
215          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
216     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
217       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
218     } else {
219       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
220       PPOpts.addRemappedFile(I->first->getName(), mem);
221     }
222     I->second = Target();
223   }
224
225   PPOpts.RetainRemappedFileBuffers = false;
226   clear();
227 }
228
229 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
230   remap(getOriginalFile(filePath), memBuf);
231 }
232
233 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
234   assert(file);
235   Target &targ = FromToMappings[file];
236   resetTarget(targ);
237   targ = memBuf;
238 }
239
240 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
241   assert(file && newfile);
242   Target &targ = FromToMappings[file];
243   resetTarget(targ);
244   targ = newfile;
245   ToFromMappings[newfile] = file;
246 }
247
248 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
249   const FileEntry *file = FileMgr->getFile(filePath);
250   // If we are updating a file that overriden an original file,
251   // actually update the original file.
252   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
253     I = ToFromMappings.find(file);
254   if (I != ToFromMappings.end()) {
255     file = I->second;
256     assert(FromToMappings.find(file) != FromToMappings.end() &&
257            "Original file not in mappings!");
258   }
259   return file;
260 }
261
262 void FileRemapper::resetTarget(Target &targ) {
263   if (!targ)
264     return;
265
266   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
267     delete oldmem;
268   } else {
269     const FileEntry *toFE = targ.get<const FileEntry *>();
270     ToFromMappings.erase(toFE);
271   }
272 }
273
274 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
275   SmallString<128> buf;
276   unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
277                                                          err.toStringRef(buf));
278   Diag.Report(ID);
279   return true;
280 }