clang  19.0.0git
FileRemapper.cpp
Go to the documentation of this file.
1 //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/Basic/Diagnostic.h"
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include <fstream>
18 
19 using namespace clang;
20 using namespace arcmt;
21 
23  FileMgr.reset(new FileManager(FileSystemOptions()));
24 }
25 
27  clear();
28 }
29 
30 void FileRemapper::clear(StringRef outputDir) {
31  for (MappingsTy::iterator
32  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
33  resetTarget(I->second);
34  FromToMappings.clear();
35  assert(ToFromMappings.empty());
36  if (!outputDir.empty()) {
37  std::string infoFile = getRemapInfoFile(outputDir);
38  llvm::sys::fs::remove(infoFile);
39  }
40 }
41 
42 std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
43  assert(!outputDir.empty());
44  SmallString<128> InfoFile = outputDir;
45  llvm::sys::path::append(InfoFile, "remap");
46  return std::string(InfoFile);
47 }
48 
50  bool ignoreIfFilesChanged) {
51  std::string infoFile = getRemapInfoFile(outputDir);
52  return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
53 }
54 
56  bool ignoreIfFilesChanged) {
57  assert(FromToMappings.empty() &&
58  "initFromDisk should be called before any remap calls");
59  std::string infoFile = std::string(filePath);
60  if (!llvm::sys::fs::exists(infoFile))
61  return false;
62 
63  std::vector<std::pair<FileEntryRef, FileEntryRef>> pairs;
64 
65  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
66  llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);
67  if (!fileBuf)
68  return report("Error opening file: " + infoFile, Diag);
69 
71  fileBuf.get()->getBuffer().split(lines, "\n");
72 
73  for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
74  StringRef fromFilename = lines[idx];
75  unsigned long long timeModified;
76  if (lines[idx+1].getAsInteger(10, timeModified))
77  return report("Invalid file data: '" + lines[idx+1] + "' not a number",
78  Diag);
79  StringRef toFilename = lines[idx+2];
80 
81  auto origFE = FileMgr->getOptionalFileRef(fromFilename);
82  if (!origFE) {
83  if (ignoreIfFilesChanged)
84  continue;
85  return report("File does not exist: " + fromFilename, Diag);
86  }
87  auto newFE = FileMgr->getOptionalFileRef(toFilename);
88  if (!newFE) {
89  if (ignoreIfFilesChanged)
90  continue;
91  return report("File does not exist: " + toFilename, Diag);
92  }
93 
94  if ((uint64_t)origFE->getModificationTime() != timeModified) {
95  if (ignoreIfFilesChanged)
96  continue;
97  return report("File was modified: " + fromFilename, Diag);
98  }
99 
100  pairs.push_back(std::make_pair(*origFE, *newFE));
101  }
102 
103  for (unsigned i = 0, e = pairs.size(); i != e; ++i)
104  remap(pairs[i].first, pairs[i].second);
105 
106  return false;
107 }
108 
109 bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
110  using namespace llvm::sys;
111 
112  if (fs::create_directory(outputDir))
113  return report("Could not create directory: " + outputDir, Diag);
114 
115  std::string infoFile = getRemapInfoFile(outputDir);
116  return flushToFile(infoFile, Diag);
117 }
118 
119 bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
120  using namespace llvm::sys;
121 
122  std::error_code EC;
123  std::string infoFile = std::string(outputPath);
124  llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);
125  if (EC)
126  return report(EC.message(), Diag);
127 
128  for (MappingsTy::iterator
129  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
130 
131  FileEntryRef origFE = I->first;
132  SmallString<200> origPath = StringRef(origFE.getName());
133  fs::make_absolute(origPath);
134  infoOut << origPath << '\n';
135  infoOut << (uint64_t)origFE.getModificationTime() << '\n';
136 
137  if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {
138  SmallString<200> newPath = StringRef(FE->getName());
139  fs::make_absolute(newPath);
140  infoOut << newPath << '\n';
141  } else {
142 
143  SmallString<64> tempPath;
144  int fd;
145  if (fs::createTemporaryFile(
146  path::filename(origFE.getName()),
147  path::extension(origFE.getName()).drop_front(), fd, tempPath,
148  llvm::sys::fs::OF_Text))
149  return report("Could not create file: " + tempPath.str(), Diag);
150 
151  llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
152  llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
153  newOut.write(mem->getBufferStart(), mem->getBufferSize());
154  newOut.close();
155 
156  auto newE = FileMgr->getOptionalFileRef(tempPath);
157  if (newE) {
158  remap(origFE, *newE);
159  infoOut << newE->getName() << '\n';
160  }
161  }
162  }
163 
164  infoOut.close();
165  return false;
166 }
167 
169  StringRef outputDir) {
170  using namespace llvm::sys;
171 
172  for (MappingsTy::iterator
173  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
174  FileEntryRef origFE = I->first;
175  assert(std::holds_alternative<llvm::MemoryBuffer *>(I->second));
176  if (!fs::exists(origFE.getName()))
177  return report(StringRef("File does not exist: ") + origFE.getName(),
178  Diag);
179 
180  std::error_code EC;
181  llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None);
182  if (EC)
183  return report(EC.message(), Diag);
184 
185  llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
186  Out.write(mem->getBufferStart(), mem->getBufferSize());
187  Out.close();
188  }
189 
190  clear(outputDir);
191  return false;
192 }
193 
195  llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
196  llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
197  CaptureBuffer) const {
198  for (auto &Mapping : FromToMappings) {
199  if (const auto *FE = std::get_if<FileEntryRef>(&Mapping.second)) {
200  CaptureFile(Mapping.first.getName(), FE->getName());
201  continue;
202  }
203  CaptureBuffer(
204  Mapping.first.getName(),
205  std::get<llvm::MemoryBuffer *>(Mapping.second)->getMemBufferRef());
206  }
207 }
208 
210  for (MappingsTy::const_iterator
211  I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
212  if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {
213  PPOpts.addRemappedFile(I->first.getName(), FE->getName());
214  } else {
215  llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
216  PPOpts.addRemappedFile(I->first.getName(), mem);
217  }
218  }
219 
220  PPOpts.RetainRemappedFileBuffers = true;
221 }
222 
223 void FileRemapper::remap(StringRef filePath,
224  std::unique_ptr<llvm::MemoryBuffer> memBuf) {
225  OptionalFileEntryRef File = getOriginalFile(filePath);
226  assert(File);
227  remap(*File, std::move(memBuf));
228 }
229 
231  std::unique_ptr<llvm::MemoryBuffer> MemBuf) {
232  auto [It, New] = FromToMappings.insert({File, nullptr});
233  if (!New)
234  resetTarget(It->second);
235  It->second = MemBuf.release();
236 }
237 
238 void FileRemapper::remap(FileEntryRef File, FileEntryRef NewFile) {
239  auto [It, New] = FromToMappings.insert({File, nullptr});
240  if (!New)
241  resetTarget(It->second);
242  It->second = NewFile;
243  ToFromMappings.insert({NewFile, File});
244 }
245 
246 OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) {
247  OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath);
248  if (!File)
249  return std::nullopt;
250  // If we are updating a file that overridden an original file,
251  // actually update the original file.
252  auto I = ToFromMappings.find(*File);
253  if (I != ToFromMappings.end()) {
254  *File = I->second;
255  assert(FromToMappings.contains(*File) && "Original file not in mappings!");
256  }
257  return File;
258 }
259 
260 void FileRemapper::resetTarget(Target &targ) {
261  if (std::holds_alternative<llvm::MemoryBuffer *>(targ)) {
262  llvm::MemoryBuffer *oldmem = std::get<llvm::MemoryBuffer *>(targ);
263  delete oldmem;
264  } else {
265  FileEntryRef toFE = std::get<FileEntryRef>(targ);
266  ToFromMappings.erase(toFE);
267  }
268 }
269 
270 bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
271  Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
272  << err.str();
273  return true;
274 }
Defines the Diagnostic-related interfaces.
Defines the clang::FileManager interface and associated types.
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.
llvm::MachO::Target Target
Definition: MachO.h:50
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:193
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Definition: FileEntry.h:57
time_t getModificationTime() const
Definition: FileEntry.h:348
StringRef getName() const
The name of this FileEntry.
Definition: FileEntry.h:61
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
Keeps track of options that affect how file operations are performed.
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
bool RetainRemappedFileBuffers
Whether the compiler instance should retain (i.e., not free) the buffers associated with remapped fil...
void addRemappedFile(StringRef From, StringRef To)
bool initFromFile(StringRef filePath, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged)
void forEachMapping(llvm::function_ref< void(StringRef, StringRef)> CaptureFile, llvm::function_ref< void(StringRef, const llvm::MemoryBufferRef &)> CaptureBuffer) const
Iterate through all the mappings.
bool flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag)
bool flushToFile(StringRef outputPath, DiagnosticsEngine &Diag)
void remap(StringRef filePath, std::unique_ptr< llvm::MemoryBuffer > memBuf)
void clear(StringRef outputDir=StringRef())
void applyMappings(PreprocessorOptions &PPOpts) const
bool initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, bool ignoreIfFilesChanged)
bool overwriteOriginal(DiagnosticsEngine &Diag, StringRef outputDir=StringRef())
ASTEdit remove(RangeSelector S)
Removes the source selected by S.
The JSON file list parser is used to communicate input to InstallAPI.
unsigned long uint64_t