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());
46263508Sdim  SmallString<128> InfoFile = outputDir;
47263508Sdim  llvm::sys::path::append(InfoFile, "remap");
48263508Sdim  return InfoFile.str();
49224135Sdim}
50224135Sdim
51226633Sdimbool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
52224135Sdim                                bool ignoreIfFilesChanged) {
53234353Sdim  std::string infoFile = getRemapInfoFile(outputDir);
54234353Sdim  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
55234353Sdim}
56234353Sdim
57234353Sdimbool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
58234353Sdim                                bool ignoreIfFilesChanged) {
59224135Sdim  assert(FromToMappings.empty() &&
60224135Sdim         "initFromDisk should be called before any remap calls");
61234353Sdim  std::string infoFile = filePath;
62224135Sdim  bool fileExists = false;
63224135Sdim  llvm::sys::fs::exists(infoFile, fileExists);
64224135Sdim  if (!fileExists)
65224135Sdim    return false;
66224135Sdim
67224135Sdim  std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
68226633Sdim
69234353Sdim  OwningPtr<llvm::MemoryBuffer> fileBuf;
70234353Sdim  if (llvm::MemoryBuffer::getFile(infoFile.c_str(), fileBuf))
71226633Sdim    return report("Error opening file: " + infoFile, Diag);
72226633Sdim
73226633Sdim  SmallVector<StringRef, 64> lines;
74226633Sdim  fileBuf->getBuffer().split(lines, "\n");
75224135Sdim
76226633Sdim  for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
77226633Sdim    StringRef fromFilename = lines[idx];
78226633Sdim    unsigned long long timeModified;
79239462Sdim    if (lines[idx+1].getAsInteger(10, timeModified))
80239462Sdim      return report("Invalid file data: '" + lines[idx+1] + "' not a number",
81239462Sdim                    Diag);
82226633Sdim    StringRef toFilename = lines[idx+2];
83226633Sdim
84224135Sdim    const FileEntry *origFE = FileMgr->getFile(fromFilename);
85224135Sdim    if (!origFE) {
86224135Sdim      if (ignoreIfFilesChanged)
87224135Sdim        continue;
88226633Sdim      return report("File does not exist: " + fromFilename, Diag);
89224135Sdim    }
90224135Sdim    const FileEntry *newFE = FileMgr->getFile(toFilename);
91224135Sdim    if (!newFE) {
92224135Sdim      if (ignoreIfFilesChanged)
93224135Sdim        continue;
94226633Sdim      return report("File does not exist: " + toFilename, Diag);
95224135Sdim    }
96224135Sdim
97224135Sdim    if ((uint64_t)origFE->getModificationTime() != timeModified) {
98224135Sdim      if (ignoreIfFilesChanged)
99224135Sdim        continue;
100226633Sdim      return report("File was modified: " + fromFilename, Diag);
101224135Sdim    }
102224135Sdim
103224135Sdim    pairs.push_back(std::make_pair(origFE, newFE));
104224135Sdim  }
105224135Sdim
106224135Sdim  for (unsigned i = 0, e = pairs.size(); i != e; ++i)
107224135Sdim    remap(pairs[i].first, pairs[i].second);
108224135Sdim
109224135Sdim  return false;
110224135Sdim}
111224135Sdim
112226633Sdimbool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
113224135Sdim  using namespace llvm::sys;
114224135Sdim
115224135Sdim  bool existed;
116224135Sdim  if (fs::create_directory(outputDir, existed) != llvm::errc::success)
117226633Sdim    return report("Could not create directory: " + outputDir, Diag);
118224135Sdim
119234353Sdim  std::string infoFile = getRemapInfoFile(outputDir);
120234353Sdim  return flushToFile(infoFile, Diag);
121234353Sdim}
122234353Sdim
123234353Sdimbool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
124234353Sdim  using namespace llvm::sys;
125234353Sdim
126224135Sdim  std::string errMsg;
127234353Sdim  std::string infoFile = outputPath;
128224135Sdim  llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg,
129263508Sdim                               llvm::sys::fs::F_Binary);
130224135Sdim  if (!errMsg.empty())
131224135Sdim    return report(errMsg, Diag);
132224135Sdim
133224135Sdim  for (MappingsTy::iterator
134224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
135224135Sdim
136224135Sdim    const FileEntry *origFE = I->first;
137234353Sdim    SmallString<200> origPath = StringRef(origFE->getName());
138224135Sdim    fs::make_absolute(origPath);
139224135Sdim    infoOut << origPath << '\n';
140224135Sdim    infoOut << (uint64_t)origFE->getModificationTime() << '\n';
141224135Sdim
142224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
143234353Sdim      SmallString<200> newPath = StringRef(FE->getName());
144224135Sdim      fs::make_absolute(newPath);
145224135Sdim      infoOut << newPath << '\n';
146224135Sdim    } else {
147224135Sdim
148234353Sdim      SmallString<64> tempPath;
149224135Sdim      int fd;
150263508Sdim      if (fs::createTemporaryFile(path::filename(origFE->getName()),
151263508Sdim                                  path::extension(origFE->getName()), fd,
152263508Sdim                                  tempPath))
153226633Sdim        return report("Could not create file: " + tempPath.str(), Diag);
154224135Sdim
155224135Sdim      llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
156224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
157224135Sdim      newOut.write(mem->getBufferStart(), mem->getBufferSize());
158224135Sdim      newOut.close();
159224135Sdim
160224135Sdim      const FileEntry *newE = FileMgr->getFile(tempPath);
161224135Sdim      remap(origFE, newE);
162224135Sdim      infoOut << newE->getName() << '\n';
163224135Sdim    }
164224135Sdim  }
165224135Sdim
166224135Sdim  infoOut.close();
167224135Sdim  return false;
168224135Sdim}
169224135Sdim
170226633Sdimbool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
171226633Sdim                                     StringRef outputDir) {
172224135Sdim  using namespace llvm::sys;
173224135Sdim
174224135Sdim  for (MappingsTy::iterator
175224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
176224135Sdim    const FileEntry *origFE = I->first;
177263508Sdim    assert(I->second.is<llvm::MemoryBuffer *>());
178263508Sdim    bool fileExists = false;
179263508Sdim    fs::exists(origFE->getName(), fileExists);
180263508Sdim    if (!fileExists)
181263508Sdim      return report(StringRef("File does not exist: ") + origFE->getName(),
182263508Sdim                    Diag);
183224135Sdim
184263508Sdim    std::string errMsg;
185263508Sdim    llvm::raw_fd_ostream Out(origFE->getName(), errMsg,
186263508Sdim                             llvm::sys::fs::F_Binary);
187263508Sdim    if (!errMsg.empty())
188263508Sdim      return report(errMsg, Diag);
189224135Sdim
190263508Sdim    llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
191263508Sdim    Out.write(mem->getBufferStart(), mem->getBufferSize());
192263508Sdim    Out.close();
193224135Sdim  }
194224135Sdim
195224135Sdim  clear(outputDir);
196224135Sdim  return false;
197224135Sdim}
198224135Sdim
199234353Sdimvoid FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
200224135Sdim  for (MappingsTy::const_iterator
201224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
202224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
203224135Sdim      PPOpts.addRemappedFile(I->first->getName(), FE->getName());
204224135Sdim    } else {
205224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
206224135Sdim      PPOpts.addRemappedFile(I->first->getName(), mem);
207224135Sdim    }
208224135Sdim  }
209224135Sdim
210224135Sdim  PPOpts.RetainRemappedFileBuffers = true;
211224135Sdim}
212224135Sdim
213234353Sdimvoid FileRemapper::transferMappingsAndClear(PreprocessorOptions &PPOpts) {
214224135Sdim  for (MappingsTy::iterator
215224135Sdim         I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
216224135Sdim    if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
217224135Sdim      PPOpts.addRemappedFile(I->first->getName(), FE->getName());
218224135Sdim    } else {
219224135Sdim      llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
220224135Sdim      PPOpts.addRemappedFile(I->first->getName(), mem);
221224135Sdim    }
222224135Sdim    I->second = Target();
223224135Sdim  }
224224135Sdim
225224135Sdim  PPOpts.RetainRemappedFileBuffers = false;
226224135Sdim  clear();
227224135Sdim}
228224135Sdim
229226633Sdimvoid FileRemapper::remap(StringRef filePath, llvm::MemoryBuffer *memBuf) {
230224135Sdim  remap(getOriginalFile(filePath), memBuf);
231224135Sdim}
232224135Sdim
233224135Sdimvoid FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) {
234224135Sdim  assert(file);
235224135Sdim  Target &targ = FromToMappings[file];
236224135Sdim  resetTarget(targ);
237224135Sdim  targ = memBuf;
238224135Sdim}
239224135Sdim
240224135Sdimvoid FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
241224135Sdim  assert(file && newfile);
242224135Sdim  Target &targ = FromToMappings[file];
243224135Sdim  resetTarget(targ);
244224135Sdim  targ = newfile;
245224135Sdim  ToFromMappings[newfile] = file;
246224135Sdim}
247224135Sdim
248226633Sdimconst FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
249224135Sdim  const FileEntry *file = FileMgr->getFile(filePath);
250224135Sdim  // If we are updating a file that overriden an original file,
251224135Sdim  // actually update the original file.
252224135Sdim  llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
253224135Sdim    I = ToFromMappings.find(file);
254224135Sdim  if (I != ToFromMappings.end()) {
255224135Sdim    file = I->second;
256224135Sdim    assert(FromToMappings.find(file) != FromToMappings.end() &&
257224135Sdim           "Original file not in mappings!");
258224135Sdim  }
259224135Sdim  return file;
260224135Sdim}
261224135Sdim
262224135Sdimvoid FileRemapper::resetTarget(Target &targ) {
263224135Sdim  if (!targ)
264224135Sdim    return;
265224135Sdim
266224135Sdim  if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
267224135Sdim    delete oldmem;
268224135Sdim  } else {
269224135Sdim    const FileEntry *toFE = targ.get<const FileEntry *>();
270234353Sdim    ToFromMappings.erase(toFE);
271224135Sdim  }
272224135Sdim}
273224135Sdim
274226633Sdimbool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
275234353Sdim  SmallString<128> buf;
276224135Sdim  unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
277226633Sdim                                                         err.toStringRef(buf));
278224135Sdim  Diag.Report(ID);
279224135Sdim  return true;
280224135Sdim}
281