clang 18.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
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
19using namespace clang;
20using namespace arcmt;
21
23 FileMgr.reset(new FileManager(FileSystemOptions()));
24}
25
27 clear();
28}
29
30void 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
42std::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.str());
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
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
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 (I->second.is<FileEntryRef>()) {
138 auto FE = I->second.get<FileEntryRef>();
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(
147 path::filename(origFE.getName()),
148 path::extension(origFE.getName()).drop_front(), fd, tempPath,
149 llvm::sys::fs::OF_Text))
150 return report("Could not create file: " + tempPath.str(), Diag);
151
152 llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
153 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
154 newOut.write(mem->getBufferStart(), mem->getBufferSize());
155 newOut.close();
156
157 auto newE = FileMgr->getOptionalFileRef(tempPath);
158 if (newE) {
159 remap(origFE, *newE);
160 infoOut << newE->getName() << '\n';
161 }
162 }
163 }
164
165 infoOut.close();
166 return false;
167}
168
170 StringRef outputDir) {
171 using namespace llvm::sys;
172
173 for (MappingsTy::iterator
174 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
175 FileEntryRef origFE = I->first;
176 assert(I->second.is<llvm::MemoryBuffer *>());
177 if (!fs::exists(origFE.getName()))
178 return report(StringRef("File does not exist: ") + origFE.getName(),
179 Diag);
180
181 std::error_code EC;
182 llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None);
183 if (EC)
184 return report(EC.message(), Diag);
185
186 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
187 Out.write(mem->getBufferStart(), mem->getBufferSize());
188 Out.close();
189 }
190
191 clear(outputDir);
192 return false;
193}
194
196 llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
197 llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
198 CaptureBuffer) const {
199 for (auto &Mapping : FromToMappings) {
200 if (Mapping.second.is<FileEntryRef>()) {
201 auto FE = Mapping.second.get<FileEntryRef>();
202 CaptureFile(Mapping.first.getName(), FE.getName());
203 continue;
204 }
205 CaptureBuffer(
206 Mapping.first.getName(),
207 Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef());
208 }
209}
210
212 for (MappingsTy::const_iterator
213 I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
214 if (I->second.is<FileEntryRef>()) {
215 auto FE = I->second.get<FileEntryRef>();
216 PPOpts.addRemappedFile(I->first.getName(), FE.getName());
217 } else {
218 llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
219 PPOpts.addRemappedFile(I->first.getName(), mem);
220 }
221 }
222
223 PPOpts.RetainRemappedFileBuffers = true;
224}
225
226void FileRemapper::remap(StringRef filePath,
227 std::unique_ptr<llvm::MemoryBuffer> memBuf) {
228 OptionalFileEntryRef File = getOriginalFile(filePath);
229 assert(File);
230 remap(*File, std::move(memBuf));
231}
232
234 std::unique_ptr<llvm::MemoryBuffer> memBuf) {
235 Target &targ = FromToMappings[file];
236 resetTarget(targ);
237 targ = memBuf.release();
238}
239
241 Target &targ = FromToMappings[file];
242 resetTarget(targ);
243 targ = newfile;
244 ToFromMappings.insert({newfile, file});
245}
246
247OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) {
248 OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath);
249 if (!File)
250 return std::nullopt;
251 // If we are updating a file that overridden an original file,
252 // actually update the original file.
253 auto I = ToFromMappings.find(*File);
254 if (I != ToFromMappings.end()) {
255 *File = I->second;
256 assert(FromToMappings.contains(*File) && "Original file not in mappings!");
257 }
258 return File;
259}
260
261void FileRemapper::resetTarget(Target &targ) {
262 if (!targ)
263 return;
264
265 if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
266 delete oldmem;
267 } else {
268 FileEntryRef toFE = targ.get<FileEntryRef>();
269 ToFromMappings.erase(toFE);
270 }
271}
272
273bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
274 Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
275 << err.str();
276 return true;
277}
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.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
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:443
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())