• 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/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 
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     llvm::sys::fs::remove(infoFile);
40   }
41 }
42 
getRemapInfoFile(StringRef outputDir)43 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
44   assert(!outputDir.empty());
45   SmallString<128> InfoFile = outputDir;
46   llvm::sys::path::append(InfoFile, "remap");
47   return InfoFile.str();
48 }
49 
initFromDisk(StringRef outputDir,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)50 bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
51                                 bool ignoreIfFilesChanged) {
52   std::string infoFile = getRemapInfoFile(outputDir);
53   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
54 }
55 
initFromFile(StringRef filePath,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)56 bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
57                                 bool ignoreIfFilesChanged) {
58   assert(FromToMappings.empty() &&
59          "initFromDisk should be called before any remap calls");
60   std::string infoFile = filePath;
61   bool fileExists = false;
62   llvm::sys::fs::exists(infoFile, fileExists);
63   if (!fileExists)
64     return false;
65 
66   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
67 
68   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
69       llvm::MemoryBuffer::getFile(infoFile.c_str());
70   if (!fileBuf)
71     return report("Error opening file: " + infoFile, Diag);
72 
73   SmallVector<StringRef, 64> lines;
74   fileBuf.get()->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 
flushToDisk(StringRef outputDir,DiagnosticsEngine & Diag)112 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
113   using namespace llvm::sys;
114 
115   if (fs::create_directory(outputDir))
116     return report("Could not create directory: " + outputDir, Diag);
117 
118   std::string infoFile = getRemapInfoFile(outputDir);
119   return flushToFile(infoFile, Diag);
120 }
121 
flushToFile(StringRef outputPath,DiagnosticsEngine & Diag)122 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
123   using namespace llvm::sys;
124 
125   std::string errMsg;
126   std::string infoFile = outputPath;
127   llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, llvm::sys::fs::F_None);
128   if (!errMsg.empty())
129     return report(errMsg, Diag);
130 
131   for (MappingsTy::iterator
132          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
133 
134     const FileEntry *origFE = I->first;
135     SmallString<200> origPath = StringRef(origFE->getName());
136     fs::make_absolute(origPath);
137     infoOut << origPath << '\n';
138     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
139 
140     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
141       SmallString<200> newPath = StringRef(FE->getName());
142       fs::make_absolute(newPath);
143       infoOut << newPath << '\n';
144     } else {
145 
146       SmallString<64> tempPath;
147       int fd;
148       if (fs::createTemporaryFile(path::filename(origFE->getName()),
149                                   path::extension(origFE->getName()), fd,
150                                   tempPath))
151         return report("Could not create file: " + tempPath.str(), Diag);
152 
153       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
154       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
155       newOut.write(mem->getBufferStart(), mem->getBufferSize());
156       newOut.close();
157 
158       const FileEntry *newE = FileMgr->getFile(tempPath);
159       remap(origFE, newE);
160       infoOut << newE->getName() << '\n';
161     }
162   }
163 
164   infoOut.close();
165   return false;
166 }
167 
overwriteOriginal(DiagnosticsEngine & Diag,StringRef outputDir)168 bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
169                                      StringRef outputDir) {
170   using namespace llvm::sys;
171 
172   for (MappingsTy::iterator
173          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
174     const FileEntry *origFE = I->first;
175     assert(I->second.is<llvm::MemoryBuffer *>());
176     bool fileExists = false;
177     fs::exists(origFE->getName(), fileExists);
178     if (!fileExists)
179       return report(StringRef("File does not exist: ") + origFE->getName(),
180                     Diag);
181 
182     std::string errMsg;
183     llvm::raw_fd_ostream Out(origFE->getName(), errMsg, llvm::sys::fs::F_None);
184     if (!errMsg.empty())
185       return report(errMsg, Diag);
186 
187     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
188     Out.write(mem->getBufferStart(), mem->getBufferSize());
189     Out.close();
190   }
191 
192   clear(outputDir);
193   return false;
194 }
195 
applyMappings(PreprocessorOptions & PPOpts) const196 void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
197   for (MappingsTy::const_iterator
198          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
199     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
200       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
201     } else {
202       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
203       PPOpts.addRemappedFile(I->first->getName(), mem);
204     }
205   }
206 
207   PPOpts.RetainRemappedFileBuffers = true;
208 }
209 
remap(StringRef filePath,llvm::MemoryBuffer * memBuf)210 void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
211   remap(getOriginalFile(filePath), memBuf);
212 }
213 
remap(const FileEntry * file,llvm::MemoryBuffer * memBuf)214 void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
215   assert(file);
216   Target &targ = FromToMappings[file];
217   resetTarget(targ);
218   targ = memBuf;
219 }
220 
remap(const FileEntry * file,const FileEntry * newfile)221 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
222   assert(file && newfile);
223   Target &targ = FromToMappings[file];
224   resetTarget(targ);
225   targ = newfile;
226   ToFromMappings[newfile] = file;
227 }
228 
getOriginalFile(StringRef filePath)229 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
230   const FileEntry *file = FileMgr->getFile(filePath);
231   // If we are updating a file that overriden an original file,
232   // actually update the original file.
233   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
234     I = ToFromMappings.find(file);
235   if (I != ToFromMappings.end()) {
236     file = I->second;
237     assert(FromToMappings.find(file) != FromToMappings.end() &&
238            "Original file not in mappings!");
239   }
240   return file;
241 }
242 
resetTarget(Target & targ)243 void FileRemapper::resetTarget(Target &targ) {
244   if (!targ)
245     return;
246 
247   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
248     delete oldmem;
249   } else {
250     const FileEntry *toFE = targ.get<const FileEntry *>();
251     ToFromMappings.erase(toFE);
252   }
253 }
254 
report(const Twine & err,DiagnosticsEngine & Diag)255 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
256   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
257       << err.str();
258   return true;
259 }
260