/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ARCMigrate/FileRemapper.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
9 | | #include "clang/ARCMigrate/FileRemapper.h" |
10 | | #include "clang/Basic/Diagnostic.h" |
11 | | #include "clang/Basic/FileManager.h" |
12 | | #include "clang/Lex/PreprocessorOptions.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 | | |
22 | 88 | FileRemapper::FileRemapper() { |
23 | 88 | FileMgr.reset(new FileManager(FileSystemOptions())); |
24 | 88 | } |
25 | | |
26 | 88 | FileRemapper::~FileRemapper() { |
27 | 88 | clear(); |
28 | 88 | } |
29 | | |
30 | 90 | void FileRemapper::clear(StringRef outputDir) { |
31 | 90 | for (MappingsTy::iterator |
32 | 185 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I95 ) |
33 | 95 | resetTarget(I->second); |
34 | 90 | FromToMappings.clear(); |
35 | 90 | assert(ToFromMappings.empty()); |
36 | 90 | if (!outputDir.empty()) { |
37 | 0 | std::string infoFile = getRemapInfoFile(outputDir); |
38 | 0 | llvm::sys::fs::remove(infoFile); |
39 | 0 | } |
40 | 90 | } |
41 | | |
42 | 82 | std::string FileRemapper::getRemapInfoFile(StringRef outputDir) { |
43 | 82 | assert(!outputDir.empty()); |
44 | 82 | SmallString<128> InfoFile = outputDir; |
45 | 82 | llvm::sys::path::append(InfoFile, "remap"); |
46 | 82 | return std::string(InfoFile.str()); |
47 | 82 | } |
48 | | |
49 | | bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, |
50 | 53 | bool ignoreIfFilesChanged) { |
51 | 53 | std::string infoFile = getRemapInfoFile(outputDir); |
52 | 53 | return initFromFile(infoFile, Diag, ignoreIfFilesChanged); |
53 | 53 | } |
54 | | |
55 | | bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, |
56 | 53 | bool ignoreIfFilesChanged) { |
57 | 53 | assert(FromToMappings.empty() && |
58 | 53 | "initFromDisk should be called before any remap calls"); |
59 | 53 | std::string infoFile = std::string(filePath); |
60 | 53 | if (!llvm::sys::fs::exists(infoFile)) |
61 | 26 | return false; |
62 | | |
63 | 27 | std::vector<std::pair<FileEntryRef, FileEntryRef>> pairs; |
64 | | |
65 | 27 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = |
66 | 27 | llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true); |
67 | 27 | if (!fileBuf) |
68 | 0 | return report("Error opening file: " + infoFile, Diag); |
69 | | |
70 | 27 | SmallVector<StringRef, 64> lines; |
71 | 27 | fileBuf.get()->getBuffer().split(lines, "\n"); |
72 | | |
73 | 63 | for (unsigned idx = 0; idx+3 <= lines.size(); idx += 336 ) { |
74 | 36 | StringRef fromFilename = lines[idx]; |
75 | 36 | unsigned long long timeModified; |
76 | 36 | if (lines[idx+1].getAsInteger(10, timeModified)) |
77 | 0 | return report("Invalid file data: '" + lines[idx+1] + "' not a number", |
78 | 0 | Diag); |
79 | 36 | StringRef toFilename = lines[idx+2]; |
80 | | |
81 | 36 | auto origFE = FileMgr->getOptionalFileRef(fromFilename); |
82 | 36 | if (!origFE) { |
83 | 0 | if (ignoreIfFilesChanged) |
84 | 0 | continue; |
85 | 0 | return report("File does not exist: " + fromFilename, Diag); |
86 | 0 | } |
87 | 36 | auto newFE = FileMgr->getOptionalFileRef(toFilename); |
88 | 36 | if (!newFE) { |
89 | 0 | if (ignoreIfFilesChanged) |
90 | 0 | continue; |
91 | 0 | return report("File does not exist: " + toFilename, Diag); |
92 | 0 | } |
93 | | |
94 | 36 | if ((uint64_t)origFE->getModificationTime() != timeModified) { |
95 | 0 | if (ignoreIfFilesChanged) |
96 | 0 | continue; |
97 | 0 | return report("File was modified: " + fromFilename, Diag); |
98 | 0 | } |
99 | | |
100 | 36 | pairs.push_back(std::make_pair(*origFE, *newFE)); |
101 | 36 | } |
102 | | |
103 | 63 | for (unsigned i = 0, e = pairs.size(); 27 i != e; ++i36 ) |
104 | 36 | remap(pairs[i].first, pairs[i].second); |
105 | | |
106 | 27 | return false; |
107 | 27 | } |
108 | | |
109 | 29 | bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) { |
110 | 29 | using namespace llvm::sys; |
111 | | |
112 | 29 | if (fs::create_directory(outputDir)) |
113 | 0 | return report("Could not create directory: " + outputDir, Diag); |
114 | | |
115 | 29 | std::string infoFile = getRemapInfoFile(outputDir); |
116 | 29 | return flushToFile(infoFile, Diag); |
117 | 29 | } |
118 | | |
119 | 29 | bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { |
120 | 29 | using namespace llvm::sys; |
121 | | |
122 | 29 | std::error_code EC; |
123 | 29 | std::string infoFile = std::string(outputPath); |
124 | 29 | llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text); |
125 | 29 | if (EC) |
126 | 0 | return report(EC.message(), Diag); |
127 | | |
128 | 29 | for (MappingsTy::iterator |
129 | 67 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I38 ) { |
130 | | |
131 | 38 | FileEntryRef origFE = I->first; |
132 | 38 | SmallString<200> origPath = StringRef(origFE.getName()); |
133 | 38 | fs::make_absolute(origPath); |
134 | 38 | infoOut << origPath << '\n'; |
135 | 38 | infoOut << (uint64_t)origFE.getModificationTime() << '\n'; |
136 | | |
137 | 38 | if (I->second.is<FileEntryRef>()) { |
138 | 3 | auto FE = I->second.get<FileEntryRef>(); |
139 | 3 | SmallString<200> newPath = StringRef(FE.getName()); |
140 | 3 | fs::make_absolute(newPath); |
141 | 3 | infoOut << newPath << '\n'; |
142 | 35 | } else { |
143 | | |
144 | 35 | SmallString<64> tempPath; |
145 | 35 | int fd; |
146 | 35 | if (fs::createTemporaryFile( |
147 | 35 | path::filename(origFE.getName()), |
148 | 35 | path::extension(origFE.getName()).drop_front(), fd, tempPath, |
149 | 35 | llvm::sys::fs::OF_Text)) |
150 | 0 | return report("Could not create file: " + tempPath.str(), Diag); |
151 | | |
152 | 35 | llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); |
153 | 35 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
154 | 35 | newOut.write(mem->getBufferStart(), mem->getBufferSize()); |
155 | 35 | newOut.close(); |
156 | | |
157 | 35 | auto newE = FileMgr->getOptionalFileRef(tempPath); |
158 | 35 | if (newE) { |
159 | 35 | remap(origFE, *newE); |
160 | 35 | infoOut << newE->getName() << '\n'; |
161 | 35 | } |
162 | 35 | } |
163 | 38 | } |
164 | | |
165 | 29 | infoOut.close(); |
166 | 29 | return false; |
167 | 29 | } |
168 | | |
169 | | bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, |
170 | 2 | StringRef outputDir) { |
171 | 2 | using namespace llvm::sys; |
172 | | |
173 | 2 | for (MappingsTy::iterator |
174 | 4 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I2 ) { |
175 | 2 | FileEntryRef origFE = I->first; |
176 | 2 | assert(I->second.is<llvm::MemoryBuffer *>()); |
177 | 2 | if (!fs::exists(origFE.getName())) |
178 | 0 | return report(StringRef("File does not exist: ") + origFE.getName(), |
179 | 0 | Diag); |
180 | | |
181 | 2 | std::error_code EC; |
182 | 2 | llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None); |
183 | 2 | if (EC) |
184 | 0 | return report(EC.message(), Diag); |
185 | | |
186 | 2 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
187 | 2 | Out.write(mem->getBufferStart(), mem->getBufferSize()); |
188 | 2 | Out.close(); |
189 | 2 | } |
190 | | |
191 | 2 | clear(outputDir); |
192 | 2 | return false; |
193 | 2 | } |
194 | | |
195 | | void FileRemapper::forEachMapping( |
196 | | llvm::function_ref<void(StringRef, StringRef)> CaptureFile, |
197 | | llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)> |
198 | 49 | CaptureBuffer) const { |
199 | 55 | for (auto &Mapping : FromToMappings) { |
200 | 55 | if (Mapping.second.is<FileEntryRef>()) { |
201 | 30 | auto FE = Mapping.second.get<FileEntryRef>(); |
202 | 30 | CaptureFile(Mapping.first.getName(), FE.getName()); |
203 | 30 | continue; |
204 | 30 | } |
205 | 25 | CaptureBuffer( |
206 | 25 | Mapping.first.getName(), |
207 | 25 | Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef()); |
208 | 25 | } |
209 | 49 | } |
210 | | |
211 | 72 | void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { |
212 | 72 | for (MappingsTy::const_iterator |
213 | 124 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I52 ) { |
214 | 52 | if (I->second.is<FileEntryRef>()) { |
215 | 9 | auto FE = I->second.get<FileEntryRef>(); |
216 | 9 | PPOpts.addRemappedFile(I->first.getName(), FE.getName()); |
217 | 43 | } else { |
218 | 43 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
219 | 43 | PPOpts.addRemappedFile(I->first.getName(), mem); |
220 | 43 | } |
221 | 52 | } |
222 | | |
223 | 72 | PPOpts.RetainRemappedFileBuffers = true; |
224 | 72 | } |
225 | | |
226 | | void FileRemapper::remap(StringRef filePath, |
227 | 98 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
228 | 98 | OptionalFileEntryRef File = getOriginalFile(filePath); |
229 | 98 | assert(File); |
230 | 98 | remap(*File, std::move(memBuf)); |
231 | 98 | } |
232 | | |
233 | | void FileRemapper::remap(FileEntryRef file, |
234 | 98 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
235 | 98 | Target &targ = FromToMappings[file]; |
236 | 98 | resetTarget(targ); |
237 | 98 | targ = memBuf.release(); |
238 | 98 | } |
239 | | |
240 | 71 | void FileRemapper::remap(FileEntryRef file, FileEntryRef newfile) { |
241 | 71 | Target &targ = FromToMappings[file]; |
242 | 71 | resetTarget(targ); |
243 | 71 | targ = newfile; |
244 | 71 | ToFromMappings.insert({newfile, file}); |
245 | 71 | } |
246 | | |
247 | 98 | OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) { |
248 | 98 | OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath); |
249 | 98 | if (!File) |
250 | 0 | return std::nullopt; |
251 | | // If we are updating a file that overridden an original file, |
252 | | // actually update the original file. |
253 | 98 | auto I = ToFromMappings.find(*File); |
254 | 98 | if (I != ToFromMappings.end()) { |
255 | 0 | *File = I->second; |
256 | 0 | assert(FromToMappings.contains(*File) && "Original file not in mappings!"); |
257 | 0 | } |
258 | 98 | return File; |
259 | 98 | } |
260 | | |
261 | 264 | void FileRemapper::resetTarget(Target &targ) { |
262 | 264 | if (!targ) |
263 | 95 | return; |
264 | | |
265 | 169 | if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { |
266 | 98 | delete oldmem; |
267 | 98 | } else { |
268 | 71 | FileEntryRef toFE = targ.get<FileEntryRef>(); |
269 | 71 | ToFromMappings.erase(toFE); |
270 | 71 | } |
271 | 169 | } |
272 | | |
273 | 0 | bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { |
274 | 0 | Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) |
275 | 0 | << err.str(); |
276 | 0 | return true; |
277 | 0 | } |