clang  6.0.0svn
FileRemapper.cpp
Go to the documentation of this file.
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 
11 #include "clang/Basic/Diagnostic.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 
24  FileMgr.reset(new FileManager(FileSystemOptions()));
25 }
26 
28  clear();
29 }
30 
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 
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 
51  bool ignoreIfFilesChanged) {
52  std::string infoFile = getRemapInfoFile(outputDir);
53  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
54 }
55 
57  bool ignoreIfFilesChanged) {
58  assert(FromToMappings.empty() &&
59  "initFromDisk should be called before any remap calls");
60  std::string infoFile = filePath;
61  if (!llvm::sys::fs::exists(infoFile))
62  return false;
63 
64  std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
65 
66  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
67  llvm::MemoryBuffer::getFile(infoFile);
68  if (!fileBuf)
69  return report("Error opening file: " + infoFile, Diag);
70 
72  fileBuf.get()->getBuffer().split(lines, "\n");
73 
74  for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
75  StringRef fromFilename = lines[idx];
76  unsigned long long timeModified;
77  if (lines[idx+1].getAsInteger(10, timeModified))
78  return report("Invalid file data: '" + lines[idx+1] + "' not a number",
79  Diag);
80  StringRef toFilename = lines[idx+2];
81 
82  const FileEntry *origFE = FileMgr->getFile(fromFilename);
83  if (!origFE) {
84  if (ignoreIfFilesChanged)
85  continue;
86  return report("File does not exist: " + fromFilename, Diag);
87  }
88  const FileEntry *newFE = FileMgr->getFile(toFilename);
89  if (!newFE) {
90  if (ignoreIfFilesChanged)
91  continue;
92  return report("File does not exist: " + toFilename, Diag);
93  }
94 
95  if ((uint64_t)origFE->getModificationTime() != timeModified) {
96  if (ignoreIfFilesChanged)
97  continue;
98  return report("File was modified: " + fromFilename, Diag);
99  }
100 
101  pairs.push_back(std::make_pair(origFE, newFE));
102  }
103 
104  for (unsigned i = 0, e = pairs.size(); i != e; ++i)
105  remap(pairs[i].first, pairs[i].second);
106 
107  return false;
108 }
109 
110 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
111  using namespace llvm::sys;
112 
113  if (fs::create_directory(outputDir))
114  return report("Could not create directory: " + outputDir, Diag);
115 
116  std::string infoFile = getRemapInfoFile(outputDir);
117  return flushToFile(infoFile, Diag);
118 }
119 
120 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
121  using namespace llvm::sys;
122 
123  std::error_code EC;
124  std::string infoFile = outputPath;
125  llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None);
126  if (EC)
127  return report(EC.message(), Diag);
128 
129  for (MappingsTy::iterator
130  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
131 
132  const FileEntry *origFE = I->first;
133  SmallString<200> origPath = StringRef(origFE->getName());
134  fs::make_absolute(origPath);
135  infoOut << origPath << '\n';
136  infoOut << (uint64_t)origFE->getModificationTime() << '\n';
137 
138  if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
139  SmallString<200> newPath = StringRef(FE->getName());
140  fs::make_absolute(newPath);
141  infoOut << newPath << '\n';
142  } else {
143 
144  SmallString<64> tempPath;
145  int fd;
146  if (fs::createTemporaryFile(path::filename(origFE->getName()),
147  path::extension(origFE->getName()).drop_front(), fd,
148  tempPath))
149  return report("Could not create file: " + tempPath.str(), Diag);
150 
151  llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
152  llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
153  newOut.write(mem->getBufferStart(), mem->getBufferSize());
154  newOut.close();
155 
156  const FileEntry *newE = FileMgr->getFile(tempPath);
157  remap(origFE, newE);
158  infoOut << newE->getName() << '\n';
159  }
160  }
161 
162  infoOut.close();
163  return false;
164 }
165 
167  StringRef outputDir) {
168  using namespace llvm::sys;
169 
170  for (MappingsTy::iterator
171  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
172  const FileEntry *origFE = I->first;
173  assert(I->second.is<llvm::MemoryBuffer *>());
174  if (!fs::exists(origFE->getName()))
175  return report(StringRef("File does not exist: ") + origFE->getName(),
176  Diag);
177 
178  std::error_code EC;
179  llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None);
180  if (EC)
181  return report(EC.message(), Diag);
182 
183  llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
184  Out.write(mem->getBufferStart(), mem->getBufferSize());
185  Out.close();
186  }
187 
188  clear(outputDir);
189  return false;
190 }
191 
193  for (MappingsTy::const_iterator
194  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
195  if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
196  PPOpts.addRemappedFile(I->first->getName(), FE->getName());
197  } else {
198  llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
199  PPOpts.addRemappedFile(I->first->getName(), mem);
200  }
201  }
202 
203  PPOpts.RetainRemappedFileBuffers = true;
204 }
205 
206 void FileRemapper::remap(StringRef filePath,
207  std::unique_ptr<llvm::MemoryBuffer> memBuf) {
208  remap(getOriginalFile(filePath), std::move(memBuf));
209 }
210 
211 void FileRemapper::remap(const FileEntry *file,
212  std::unique_ptr<llvm::MemoryBuffer> memBuf) {
213  assert(file);
214  Target &targ = FromToMappings[file];
215  resetTarget(targ);
216  targ = memBuf.release();
217 }
218 
219 void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
220  assert(file && newfile);
221  Target &targ = FromToMappings[file];
222  resetTarget(targ);
223  targ = newfile;
224  ToFromMappings[newfile] = file;
225 }
226 
227 const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
228  const FileEntry *file = FileMgr->getFile(filePath);
229  // If we are updating a file that overriden an original file,
230  // actually update the original file.
231  llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
232  I = ToFromMappings.find(file);
233  if (I != ToFromMappings.end()) {
234  file = I->second;
235  assert(FromToMappings.find(file) != FromToMappings.end() &&
236  "Original file not in mappings!");
237  }
238  return file;
239 }
240 
241 void FileRemapper::resetTarget(Target &targ) {
242  if (!targ)
243  return;
244 
245  if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
246  delete oldmem;
247  } else {
248  const FileEntry *toFE = targ.get<const FileEntry *>();
249  ToFromMappings.erase(toFE);
250  }
251 }
252 
253 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
255  << err.str();
256  return true;
257 }
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Implements support for file system lookup, file system caching, and directory search management...
Definition: FileManager.h:116
Defines the clang::FileManager interface and associated types.
time_t getModificationTime() const
Definition: FileManager.h:91
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1207
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
void applyMappings(PreprocessorOptions &PPOpts) const
void addRemappedFile(StringRef From, StringRef To)
void remap(StringRef filePath, std::unique_ptr< llvm::MemoryBuffer > memBuf)
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:147
Defines the Diagnostic-related interfaces.
bool RetainRemappedFileBuffers
Whether the compiler instance should retain (i.e., not free) the buffers associated with remapped fil...
bool flushToFile(StringRef outputPath, DiagnosticsEngine &Diag)
bool initFromFile(StringRef filePath, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged)
bool overwriteOriginal(DiagnosticsEngine &Diag, StringRef outputDir=StringRef())
StringRef getName() const
Definition: FileManager.h:84
bool flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag)
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:59
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:691
Dataflow Directional Tag Classes.
Keeps track of options that affect how file operations are performed.
bool initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged)
void clear(StringRef outputDir=StringRef())