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
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);
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 (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
223void 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
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
246OptionalFileEntryRef 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
260void 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
270bool 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:48
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: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())
The JSON file list parser is used to communicate input to InstallAPI.