• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/PreprocessorOptions.h"
12 #include "clang/Basic/FileManager.h"
13 #include "clang/Basic/Diagnostic.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/raw_ostream.h"
18 #include <fstream>
19 
20 using namespace clang;
21 using namespace arcmt;
22 
FileRemapper()23 FileRemapper::FileRemapper() {
24   FileMgr.reset(new FileManager(FileSystemOptions()));
25 }
26 
~FileRemapper()27 FileRemapper::~FileRemapper() {
28   clear();
29 }
30 
clear(StringRef outputDir)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 
getRemapInfoFile(StringRef outputDir)44 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
45   assert(!outputDir.empty());
46   llvm::sys::Path dir(outputDir);
47   llvm::sys::Path infoFile = dir;
48   infoFile.appendComponent("remap");
49   return infoFile.str();
50 }
51 
initFromDisk(StringRef outputDir,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)52 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
53                                 bool ignoreIfFilesChanged) {
54   std::string infoFile = getRemapInfoFile(outputDir);
55   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
56 }
57 
initFromFile(StringRef filePath,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)58 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
59                                 bool ignoreIfFilesChanged) {
60   assert(FromToMappings.empty() &&
61          "initFromDisk should be called before any remap calls");
62   std::string infoFile = filePath;
63   bool fileExists = false;
64   llvm::sys::fs::exists(infoFile, fileExists);
65   if (!fileExists)
66     return false;
67 
68   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
69 
70   OwningPtr<llvm::MemoryBuffer> fileBuf;
71   if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf))
72     return report("Error opening file: " + infoFile, Diag);
73 
74   SmallVector<StringRef, 64> lines;
75   fileBuf->getBuffer().split(lines, "\n");
76 
77   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
78     StringRef fromFilename = lines[idx];
79     unsigned long long timeModified;
80     if (lines[idx+1].getAsInteger(10, timeModified))
81       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
82                     Diag);
83     StringRef toFilename = lines[idx+2];
84 
85     const FileEntry *origFE = FileMgr->getFile(fromFilename);
86     if (!origFE) {
87       if (ignoreIfFilesChanged)
88         continue;
89       return report("File does not exist: " + fromFilename, Diag);
90     }
91     const FileEntry *newFE = FileMgr->getFile(toFilename);
92     if (!newFE) {
93       if (ignoreIfFilesChanged)
94         continue;
95       return report("File does not exist: " + toFilename, Diag);
96     }
97 
98     if ((uint64_t)origFE->getModificationTime() != timeModified) {
99       if (ignoreIfFilesChanged)
100         continue;
101       return report("File was modified: " + fromFilename, Diag);
102     }
103 
104     pairs.push_back(std::make_pair(origFE, newFE));
105   }
106 
107   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
108     remap(pairs[i].first, pairs[i].second);
109 
110   return false;
111 }
112 
flushToDisk(StringRef outputDir,DiagnosticsEngine & Diag)113 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
114   using namespace llvm::sys;
115 
116   bool existed;
117   if (fs::create_directory(outputDir, existed) != llvm::errc::success)
118     return report("Could not create directory: " + outputDir, Diag);
119 
120   std::string infoFile = getRemapInfoFile(outputDir);
121   return flushToFile(infoFile, Diag);
122 }
123 
flushToFile(StringRef outputPath,DiagnosticsEngine & Diag)124 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
125   using namespace llvm::sys;
126 
127   std::string errMsg;
128   std::string infoFile = outputPath;
129   llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
130                                llvm::raw_fd_ostream::F_Binary);
131   if (!errMsg.empty())
132     return report(errMsg, Diag);
133 
134   for (MappingsTy::iterator
135          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
136 
137     const FileEntry *origFE = I->first;
138     SmallString<200> origPath = StringRef(origFE->getName());
139     fs::make_absolute(origPath);
140     infoOut << origPath << '\n';
141     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
142 
143     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
144       SmallString<200> newPath = StringRef(FE->getName());
145       fs::make_absolute(newPath);
146       infoOut << newPath << '\n';
147     } else {
148 
149       SmallString<64> tempPath;
150       tempPath = path::filename(origFE->getName());
151       tempPath += "-%%%%%%%%";
152       tempPath += path::extension(origFE->getName());
153       int fd;
154       if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success)
155         return report("Could not create file: " + tempPath.str(), Diag);
156 
157       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
158       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
159       newOut.write(mem->getBufferStart(), mem->getBufferSize());
160       newOut.close();
161 
162       const FileEntry *newE = FileMgr->getFile(tempPath);
163       remap(origFE, newE);
164       infoOut << newE->getName() << '\n';
165     }
166   }
167 
168   infoOut.close();
169   return false;
170 }
171 
overwriteOriginal(DiagnosticsEngine & Diag,StringRef outputDir)172 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
173                                      StringRef outputDir) {
174   using namespace llvm::sys;
175 
176   for (MappingsTy::iterator
177          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
178     const FileEntry *origFE = I->first;
179     if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) {
180       if (fs::copy_file(newFE->getName(), origFE->getName(),
181                  fs::copy_option::overwrite_if_exists) != llvm::errc::success)
182         return report(StringRef("Could not copy file '") + newFE->getName() +
183                       "' to file '" + origFE->getName() + "'", Diag);
184     } else {
185 
186       bool fileExists = false;
187       fs::exists(origFE->getName(), fileExists);
188       if (!fileExists)
189         return report(StringRef("File does not exist: ") + origFE->getName(),
190                       Diag);
191 
192       std::string errMsg;
193       llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
194                                llvm::raw_fd_ostream::F_Binary);
195       if (!errMsg.empty())
196         return report(errMsg, Diag);
197 
198       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
199       Out.write(mem->getBufferStart(), mem->getBufferSize());
200       Out.close();
201     }
202   }
203 
204   clear(outputDir);
205   return false;
206 }
207 
applyMappings(PreprocessorOptions & PPOpts) const208 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
209   for (MappingsTy::const_iterator
210          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
211     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
212       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
213     } else {
214       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
215       PPOpts.addRemappedFile(I->first->getName(), mem);
216     }
217   }
218 
219   PPOpts.RetainRemappedFileBuffers = true;
220 }
221 
transferMappingsAndClear(PreprocessorOptions & PPOpts)222 void FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
223   for (MappingsTy::iterator
224          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
225     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
226       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
227     } else {
228       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
229       PPOpts.addRemappedFile(I->first->getName(), mem);
230     }
231     I->second = Target();
232   }
233 
234   PPOpts.RetainRemappedFileBuffers = false;
235   clear();
236 }
237 
remap(StringRef filePath,llvm::MemoryBuffer * memBuf)238 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
239   remap(getOriginalFile(filePath), memBuf);
240 }
241 
remap(StringRef filePath,StringRef newPath)242 void FileRemapper::remap(StringRef filePath, StringRef newPath) {
243   const FileEntry *file = getOriginalFile(filePath);
244   const FileEntry *newfile = FileMgr->getFile(newPath);
245   remap(file, newfile);
246 }
247 
remap(const FileEntry * file,llvm::MemoryBuffer * memBuf)248 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
249   assert(file);
250   Target &targ = FromToMappings[file];
251   resetTarget(targ);
252   targ = memBuf;
253 }
254 
remap(const FileEntry * file,const FileEntry * newfile)255 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
256   assert(file && newfile);
257   Target &targ = FromToMappings[file];
258   resetTarget(targ);
259   targ = newfile;
260   ToFromMappings[newfile] = file;
261 }
262 
getOriginalFile(StringRef filePath)263 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
264   const FileEntry *file = FileMgr->getFile(filePath);
265   // If we are updating a file that overriden an original file,
266   // actually update the original file.
267   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
268     I = ToFromMappings.find(file);
269   if (I != ToFromMappings.end()) {
270     file = I->second;
271     assert(FromToMappings.find(file) != FromToMappings.end() &&
272            "Original file not in mappings!");
273   }
274   return file;
275 }
276 
resetTarget(Target & targ)277 void FileRemapper::resetTarget(Target &targ) {
278   if (!targ)
279     return;
280 
281   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
282     delete oldmem;
283   } else {
284     const FileEntry *toFE = targ.get<const FileEntry *>();
285     ToFromMappings.erase(toFE);
286   }
287 }
288 
report(const Twine & err,DiagnosticsEngine & Diag)289 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
290   SmallString<128> buf;
291   unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
292                                                          err.toStringRef(buf));
293   Diag.Report(ID);
294   return true;
295 }
296