FileRemapper.cpp revision 263508
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
20using namespace clang;
21using namespace arcmt;
22
23FileRemapper::FileRemapper() {
24  FileMgr.reset(new FileManager(FileSystemOptions()));
25}
26
27FileRemapper::~FileRemapper() {
28  clear();
29}
30
31void 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
44std::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
51bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
52                                bool ignoreIfFilesChanged) {
53  std::string infoFile = getRemapInfoFile(outputDir);
54  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
55}
56
57bool 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
112bool 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
123bool 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
170bool 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
199void 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
213void 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
229void FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
230  remap(getOriginalFile(filePath), memBuf);
231}
232
233void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
234  assert(file);
235  Target &targ = FromToMappings[file];
236  resetTarget(targ);
237  targ = memBuf;
238}
239
240void 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
248const 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
262void 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
274bool 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}
281