/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<const FileEntry *, const FileEntry *> > pairs; |
64 | | |
65 | 27 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = |
66 | 27 | llvm::MemoryBuffer::getFile(infoFile); |
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 | llvm::ErrorOr<const FileEntry *> origFE = FileMgr->getFile(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 | llvm::ErrorOr<const FileEntry *> newFE = FileMgr->getFile(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_None); |
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 | const FileEntry *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 (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
138 | 3 | SmallString<200> newPath = StringRef(FE->getName()); |
139 | 3 | fs::make_absolute(newPath); |
140 | 3 | infoOut << newPath << '\n'; |
141 | 35 | } else { |
142 | | |
143 | 35 | SmallString<64> tempPath; |
144 | 35 | int fd; |
145 | 35 | if (fs::createTemporaryFile(path::filename(origFE->getName()), |
146 | 35 | path::extension(origFE->getName()).drop_front(), fd, |
147 | 35 | tempPath)) |
148 | 0 | return report("Could not create file: " + tempPath.str(), Diag); |
149 | | |
150 | 35 | llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); |
151 | 35 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
152 | 35 | newOut.write(mem->getBufferStart(), mem->getBufferSize()); |
153 | 35 | newOut.close(); |
154 | | |
155 | 35 | auto newE = FileMgr->getFile(tempPath); |
156 | 35 | if (newE) { |
157 | 35 | remap(origFE, *newE); |
158 | 35 | infoOut << (*newE)->getName() << '\n'; |
159 | 35 | } |
160 | 35 | } |
161 | 38 | } |
162 | | |
163 | 29 | infoOut.close(); |
164 | 29 | return false; |
165 | 29 | } |
166 | | |
167 | | bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, |
168 | 2 | StringRef outputDir) { |
169 | 2 | using namespace llvm::sys; |
170 | | |
171 | 2 | for (MappingsTy::iterator |
172 | 4 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I2 ) { |
173 | 2 | const FileEntry *origFE = I->first; |
174 | 2 | assert(I->second.is<llvm::MemoryBuffer *>()); |
175 | 2 | if (!fs::exists(origFE->getName())) |
176 | 0 | return report(StringRef("File does not exist: ") + origFE->getName(), |
177 | 0 | Diag); |
178 | | |
179 | 2 | std::error_code EC; |
180 | 2 | llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None); |
181 | 2 | if (EC) |
182 | 0 | return report(EC.message(), Diag); |
183 | | |
184 | 2 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
185 | 2 | Out.write(mem->getBufferStart(), mem->getBufferSize()); |
186 | 2 | Out.close(); |
187 | 2 | } |
188 | | |
189 | 2 | clear(outputDir); |
190 | 2 | return false; |
191 | 2 | } |
192 | | |
193 | | void FileRemapper::forEachMapping( |
194 | | llvm::function_ref<void(StringRef, StringRef)> CaptureFile, |
195 | | llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)> |
196 | 49 | CaptureBuffer) const { |
197 | 55 | for (auto &Mapping : FromToMappings) { |
198 | 55 | if (const FileEntry *FE = Mapping.second.dyn_cast<const FileEntry *>()) { |
199 | 30 | CaptureFile(Mapping.first->getName(), FE->getName()); |
200 | 30 | continue; |
201 | 30 | } |
202 | 25 | CaptureBuffer( |
203 | 25 | Mapping.first->getName(), |
204 | 25 | Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef()); |
205 | 25 | } |
206 | 49 | } |
207 | | |
208 | 72 | void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { |
209 | 72 | for (MappingsTy::const_iterator |
210 | 124 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I52 ) { |
211 | 52 | if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
212 | 9 | PPOpts.addRemappedFile(I->first->getName(), FE->getName()); |
213 | 43 | } else { |
214 | 43 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
215 | 43 | PPOpts.addRemappedFile(I->first->getName(), mem); |
216 | 43 | } |
217 | 52 | } |
218 | | |
219 | 72 | PPOpts.RetainRemappedFileBuffers = true; |
220 | 72 | } |
221 | | |
222 | | void FileRemapper::remap(StringRef filePath, |
223 | 98 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
224 | 98 | remap(getOriginalFile(filePath), std::move(memBuf)); |
225 | 98 | } |
226 | | |
227 | | void FileRemapper::remap(const FileEntry *file, |
228 | 98 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
229 | 98 | assert(file); |
230 | 98 | Target &targ = FromToMappings[file]; |
231 | 98 | resetTarget(targ); |
232 | 98 | targ = memBuf.release(); |
233 | 98 | } |
234 | | |
235 | 71 | void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { |
236 | 71 | assert(file && newfile); |
237 | 71 | Target &targ = FromToMappings[file]; |
238 | 71 | resetTarget(targ); |
239 | 71 | targ = newfile; |
240 | 71 | ToFromMappings[newfile] = file; |
241 | 71 | } |
242 | | |
243 | 98 | const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { |
244 | 98 | const FileEntry *file = nullptr; |
245 | 98 | if (auto fileOrErr = FileMgr->getFile(filePath)) |
246 | 98 | file = *fileOrErr; |
247 | | // If we are updating a file that overridden an original file, |
248 | | // actually update the original file. |
249 | 98 | llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator |
250 | 98 | I = ToFromMappings.find(file); |
251 | 98 | if (I != ToFromMappings.end()) { |
252 | 0 | file = I->second; |
253 | 0 | assert(FromToMappings.find(file) != FromToMappings.end() && |
254 | 0 | "Original file not in mappings!"); |
255 | 0 | } |
256 | 98 | return file; |
257 | 98 | } |
258 | | |
259 | 264 | void FileRemapper::resetTarget(Target &targ) { |
260 | 264 | if (!targ) |
261 | 95 | return; |
262 | | |
263 | 169 | if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { |
264 | 98 | delete oldmem; |
265 | 71 | } else { |
266 | 71 | const FileEntry *toFE = targ.get<const FileEntry *>(); |
267 | 71 | ToFromMappings.erase(toFE); |
268 | 71 | } |
269 | 169 | } |
270 | | |
271 | 0 | bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { |
272 | 0 | Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) |
273 | 0 | << err.str(); |
274 | 0 | return true; |
275 | 0 | } |