1224135Sdim//===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
2224135Sdim//
3224135Sdim//                     The LLVM Compiler Infrastructure
4224135Sdim//
5224135Sdim// This file is distributed under the University of Illinois Open Source
6224135Sdim// License. See LICENSE.TXT for details.
7224135Sdim//
8224135Sdim//===----------------------------------------------------------------------===//
9224135Sdim
10224135Sdim#include "clang/ARCMigrate/FileRemapper.h"
11249423Sdim#include "clang/Basic/Diagnostic.h"
12249423Sdim#include "clang/Basic/FileManager.h"
13243830Sdim#include "clang/Lex/PreprocessorOptions.h"
14249423Sdim#include "llvm/Support/FileSystem.h"
15224135Sdim#include "llvm/Support/MemoryBuffer.h"
16224135Sdim#include "llvm/Support/Path.h"
17224135Sdim#include "llvm/Support/raw_ostream.h"
18224135Sdim#include <fstream>
19224135Sdim
20224135Sdimusing namespace clang;
21224135Sdimusing namespace arcmt;
22224135Sdim
23224135SdimFileRemapper::FileRemapper() {
24224135Sdim  FileMgr.reset(new FileManager(FileSystemOptions()));
25224135Sdim}
26224135Sdim
27224135SdimFileRemapper::~FileRemapper() {
28224135Sdim  clear();
29224135Sdim}
30224135Sdim
31226633Sdimvoid FileRemapper::clear(StringRef outputDir) {
32224135Sdim  for (MappingsTy::iterator
33224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
34224135Sdim    resetTarget(I->second);
35224135Sdim  FromToMappings.clear();
36224135Sdim  assert(ToFromMappings.empty());
37224135Sdim  if (!outputDir.empty()) {
38224135Sdim    std::string infoFile = getRemapInfoFile(outputDir);
39224135Sdim    bool existed;
40224135Sdim    llvm::sys::fs::remove(infoFile, existed);
41224135Sdim  }
42224135Sdim}
43224135Sdim
44226633Sdimstd::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
45224135Sdim  assert(!outputDir.empty());
46224135Sdim  llvm::sys::Path dir(outputDir);
47224135Sdim  llvm::sys::Path infoFile = dir;
48224135Sdim  infoFile.appendComponent("remap");
49224135Sdim  return infoFile.str();
50224135Sdim}
51224135Sdim
52226633Sdimbool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
53224135Sdim                                bool ignoreIfFilesChanged) {
54234353Sdim  std::string infoFile = getRemapInfoFile(outputDir);
55234353Sdim  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
56234353Sdim}
57234353Sdim
58234353Sdimbool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
59234353Sdim                                bool ignoreIfFilesChanged) {
60224135Sdim  assert(FromToMappings.empty() &&
61224135Sdim         "initFromDisk should be called before any remap calls");
62234353Sdim  std::string infoFile = filePath;
63224135Sdim  bool fileExists = false;
64224135Sdim  llvm::sys::fs::exists(infoFile, fileExists);
65224135Sdim  if (!fileExists)
66224135Sdim    return false;
67224135Sdim
68224135Sdim  std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
69226633Sdim
70234353Sdim  OwningPtr<llvm::MemoryBuffer> fileBuf;
71234353Sdim  if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf))
72226633Sdim    return report("Error opening file: " + infoFile, Diag);
73226633Sdim
74226633Sdim  SmallVector<StringRef, 64> lines;
75226633Sdim  fileBuf->getBuffer().split(lines, "\n");
76224135Sdim
77226633Sdim  for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
78226633Sdim    StringRef fromFilename = lines[idx];
79226633Sdim    unsigned long long timeModified;
80239462Sdim    if (lines[idx+1].getAsInteger(10, timeModified))
81239462Sdim      return report("Invalid file data: '" + lines[idx+1] + "' not a number",
82239462Sdim                    Diag);
83226633Sdim    StringRef toFilename = lines[idx+2];
84226633Sdim
85224135Sdim    const FileEntry *origFE = FileMgr->getFile(fromFilename);
86224135Sdim    if (!origFE) {
87224135Sdim      if (ignoreIfFilesChanged)
88224135Sdim        continue;
89226633Sdim      return report("File does not exist: " + fromFilename, Diag);
90224135Sdim    }
91224135Sdim    const FileEntry *newFE = FileMgr->getFile(toFilename);
92224135Sdim    if (!newFE) {
93224135Sdim      if (ignoreIfFilesChanged)
94224135Sdim        continue;
95226633Sdim      return report("File does not exist: " + toFilename, Diag);
96224135Sdim    }
97224135Sdim
98224135Sdim    if ((uint64_t)origFE->getModificationTime() != timeModified) {
99224135Sdim      if (ignoreIfFilesChanged)
100224135Sdim        continue;
101226633Sdim      return report("File was modified: " + fromFilename, Diag);
102224135Sdim    }
103224135Sdim
104224135Sdim    pairs.push_back(std::make_pair(origFE, newFE));
105224135Sdim  }
106224135Sdim
107224135Sdim  for (unsigned i = 0, e = pairs.size(); i != e; ++i)
108224135Sdim    remap(pairs[i].first, pairs[i].second);
109224135Sdim
110224135Sdim  return false;
111224135Sdim}
112224135Sdim
113226633Sdimbool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
114224135Sdim  using namespace llvm::sys;
115224135Sdim
116224135Sdim  bool existed;
117224135Sdim  if (fs::create_directory(outputDir, existed) != llvm::errc::success)
118226633Sdim    return report("Could not create directory: " + outputDir, Diag);
119224135Sdim
120234353Sdim  std::string infoFile = getRemapInfoFile(outputDir);
121234353Sdim  return flushToFile(infoFile, Diag);
122234353Sdim}
123234353Sdim
124234353Sdimbool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
125234353Sdim  using namespace llvm::sys;
126234353Sdim
127224135Sdim  std::string errMsg;
128234353Sdim  std::string infoFile = outputPath;
129224135Sdim  llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
130224135Sdim                               llvm::raw_fd_ostream::F_Binary);
131224135Sdim  if (!errMsg.empty())
132224135Sdim    return report(errMsg, Diag);
133224135Sdim
134224135Sdim  for (MappingsTy::iterator
135224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
136224135Sdim
137224135Sdim    const FileEntry *origFE = I->first;
138234353Sdim    SmallString<200> origPath = StringRef(origFE->getName());
139224135Sdim    fs::make_absolute(origPath);
140224135Sdim    infoOut << origPath << '\n';
141224135Sdim    infoOut << (uint64_t)origFE->getModificationTime() << '\n';
142224135Sdim
143224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
144234353Sdim      SmallString<200> newPath = StringRef(FE->getName());
145224135Sdim      fs::make_absolute(newPath);
146224135Sdim      infoOut << newPath << '\n';
147224135Sdim    } else {
148224135Sdim
149234353Sdim      SmallString<64> tempPath;
150224135Sdim      tempPath = path::filename(origFE->getName());
151224135Sdim      tempPath += "-%%%%%%%%";
152224135Sdim      tempPath += path::extension(origFE->getName());
153224135Sdim      int fd;
154224135Sdim      if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success)
155226633Sdim        return report("Could not create file: " + tempPath.str(), Diag);
156224135Sdim
157224135Sdim      llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
158224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
159224135Sdim      newOut.write(mem->getBufferStart(), mem->getBufferSize());
160224135Sdim      newOut.close();
161224135Sdim
162224135Sdim      const FileEntry *newE = FileMgr->getFile(tempPath);
163224135Sdim      remap(origFE, newE);
164224135Sdim      infoOut << newE->getName() << '\n';
165224135Sdim    }
166224135Sdim  }
167224135Sdim
168224135Sdim  infoOut.close();
169224135Sdim  return false;
170224135Sdim}
171224135Sdim
172226633Sdimbool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
173226633Sdim                                     StringRef outputDir) {
174224135Sdim  using namespace llvm::sys;
175224135Sdim
176224135Sdim  for (MappingsTy::iterator
177224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
178224135Sdim    const FileEntry *origFE = I->first;
179224135Sdim    if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) {
180224135Sdim      if (fs::copy_file(newFE->getName(), origFE->getName(),
181226633Sdim                 fs::copy_option::overwrite_if_exists) != llvm::errc::success)
182226633Sdim        return report(StringRef("Could not copy file '") + newFE->getName() +
183226633Sdim                      "' to file '" + origFE->getName() + "'", Diag);
184224135Sdim    } else {
185224135Sdim
186224135Sdim      bool fileExists = false;
187224135Sdim      fs::exists(origFE->getName(), fileExists);
188224135Sdim      if (!fileExists)
189226633Sdim        return report(StringRef("File does not exist: ") + origFE->getName(),
190224135Sdim                      Diag);
191224135Sdim
192224135Sdim      std::string errMsg;
193224135Sdim      llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
194224135Sdim                               llvm::raw_fd_ostream::F_Binary);
195224135Sdim      if (!errMsg.empty())
196224135Sdim        return report(errMsg, Diag);
197224135Sdim
198224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
199224135Sdim      Out.write(mem->getBufferStart(), mem->getBufferSize());
200224135Sdim      Out.close();
201224135Sdim    }
202224135Sdim  }
203224135Sdim
204224135Sdim  clear(outputDir);
205224135Sdim  return false;
206224135Sdim}
207224135Sdim
208234353Sdimvoid FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
209224135Sdim  for (MappingsTy::const_iterator
210224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
211224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
212224135Sdim      PPOpts.addRemappedFile(I->first->getName(), FE->getName());
213224135Sdim    } else {
214224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
215224135Sdim      PPOpts.addRemappedFile(I->first->getName(), mem);
216224135Sdim    }
217224135Sdim  }
218224135Sdim
219224135Sdim  PPOpts.RetainRemappedFileBuffers = true;
220224135Sdim}
221224135Sdim
222234353Sdimvoid FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
223224135Sdim  for (MappingsTy::iterator
224224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
225224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
226224135Sdim      PPOpts.addRemappedFile(I->first->getName(), FE->getName());
227224135Sdim    } else {
228224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
229224135Sdim      PPOpts.addRemappedFile(I->first->getName(), mem);
230224135Sdim    }
231224135Sdim    I->second = Target();
232224135Sdim  }
233224135Sdim
234224135Sdim  PPOpts.RetainRemappedFileBuffers = false;
235224135Sdim  clear();
236224135Sdim}
237224135Sdim
238226633Sdimvoid FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
239224135Sdim  remap(getOriginalFile(filePath), memBuf);
240224135Sdim}
241224135Sdim
242226633Sdimvoid FileRemapper::remap(StringRef filePath, StringRef newPath) {
243224135Sdim  const FileEntry *file = getOriginalFile(filePath);
244224135Sdim  const FileEntry *newfile = FileMgr->getFile(newPath);
245224135Sdim  remap(file, newfile);
246224135Sdim}
247224135Sdim
248224135Sdimvoid FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
249224135Sdim  assert(file);
250224135Sdim  Target &targ = FromToMappings[file];
251224135Sdim  resetTarget(targ);
252224135Sdim  targ = memBuf;
253224135Sdim}
254224135Sdim
255224135Sdimvoid FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
256224135Sdim  assert(file && newfile);
257224135Sdim  Target &targ = FromToMappings[file];
258224135Sdim  resetTarget(targ);
259224135Sdim  targ = newfile;
260224135Sdim  ToFromMappings[newfile] = file;
261224135Sdim}
262224135Sdim
263226633Sdimconst FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
264224135Sdim  const FileEntry *file = FileMgr->getFile(filePath);
265224135Sdim  // If we are updating a file that overriden an original file,
266224135Sdim  // actually update the original file.
267224135Sdim  llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
268224135Sdim    I = ToFromMappings.find(file);
269224135Sdim  if (I != ToFromMappings.end()) {
270224135Sdim    file = I->second;
271224135Sdim    assert(FromToMappings.find(file) != FromToMappings.end() &&
272224135Sdim           "Original file not in mappings!");
273224135Sdim  }
274224135Sdim  return file;
275224135Sdim}
276224135Sdim
277224135Sdimvoid FileRemapper::resetTarget(Target &targ) {
278224135Sdim  if (!targ)
279224135Sdim    return;
280224135Sdim
281224135Sdim  if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
282224135Sdim    delete oldmem;
283224135Sdim  } else {
284224135Sdim    const FileEntry *toFE = targ.get<const FileEntry *>();
285234353Sdim    ToFromMappings.erase(toFE);
286224135Sdim  }
287224135Sdim}
288224135Sdim
289226633Sdimbool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
290234353Sdim  SmallString<128> buf;
291224135Sdim  unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
292226633Sdim                                                         err.toStringRef(buf));
293224135Sdim  Diag.Report(ID);
294224135Sdim  return true;
295224135Sdim}
296