1 | |
2 | |
3 | |
4 | |
5 | |
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 | FileRemapper::FileRemapper() { |
23 | FileMgr.reset(new FileManager(FileSystemOptions())); |
24 | } |
25 | |
26 | FileRemapper::~FileRemapper() { |
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 InfoFile.str(); |
47 | } |
48 | |
49 | bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag, |
50 | bool ignoreIfFilesChanged) { |
51 | std::string infoFile = getRemapInfoFile(outputDir); |
52 | return initFromFile(infoFile, Diag, ignoreIfFilesChanged); |
53 | } |
54 | |
55 | bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, |
56 | bool ignoreIfFilesChanged) { |
57 | (0) . __assert_fail ("FromToMappings.empty() && \"initFromDisk should be called before any remap calls\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 58, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FromToMappings.empty() && |
58 | (0) . __assert_fail ("FromToMappings.empty() && \"initFromDisk should be called before any remap calls\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 58, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "initFromDisk should be called before any remap calls"); |
59 | std::string infoFile = filePath; |
60 | if (!llvm::sys::fs::exists(infoFile)) |
61 | return false; |
62 | |
63 | std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; |
64 | |
65 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf = |
66 | llvm::MemoryBuffer::getFile(infoFile); |
67 | if (!fileBuf) |
68 | return report("Error opening file: " + infoFile, Diag); |
69 | |
70 | SmallVector<StringRef, 64> lines; |
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 | const FileEntry *origFE = FileMgr->getFile(fromFilename); |
82 | if (!origFE) { |
83 | if (ignoreIfFilesChanged) |
84 | continue; |
85 | return report("File does not exist: " + fromFilename, Diag); |
86 | } |
87 | const FileEntry *newFE = FileMgr->getFile(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 = outputPath; |
124 | llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None); |
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 | const FileEntry *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 FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
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(path::filename(origFE->getName()), |
146 | path::extension(origFE->getName()).drop_front(), fd, |
147 | tempPath)) |
148 | return report("Could not create file: " + tempPath.str(), Diag); |
149 | |
150 | llvm::raw_fd_ostream newOut(fd, ); |
151 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
152 | newOut.write(mem->getBufferStart(), mem->getBufferSize()); |
153 | newOut.close(); |
154 | |
155 | const FileEntry *newE = FileMgr->getFile(tempPath); |
156 | remap(origFE, newE); |
157 | infoOut << newE->getName() << '\n'; |
158 | } |
159 | } |
160 | |
161 | infoOut.close(); |
162 | return false; |
163 | } |
164 | |
165 | bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, |
166 | StringRef outputDir) { |
167 | using namespace llvm::sys; |
168 | |
169 | for (MappingsTy::iterator |
170 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { |
171 | const FileEntry *origFE = I->first; |
172 | second.is()", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 172, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(I->second.is<llvm::MemoryBuffer *>()); |
173 | if (!fs::exists(origFE->getName())) |
174 | return report(StringRef("File does not exist: ") + origFE->getName(), |
175 | Diag); |
176 | |
177 | std::error_code EC; |
178 | llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None); |
179 | if (EC) |
180 | return report(EC.message(), Diag); |
181 | |
182 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
183 | Out.write(mem->getBufferStart(), mem->getBufferSize()); |
184 | Out.close(); |
185 | } |
186 | |
187 | clear(outputDir); |
188 | return false; |
189 | } |
190 | |
191 | void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const { |
192 | for (MappingsTy::const_iterator |
193 | I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { |
194 | if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { |
195 | PPOpts.addRemappedFile(I->first->getName(), FE->getName()); |
196 | } else { |
197 | llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); |
198 | PPOpts.addRemappedFile(I->first->getName(), mem); |
199 | } |
200 | } |
201 | |
202 | PPOpts.RetainRemappedFileBuffers = true; |
203 | } |
204 | |
205 | void FileRemapper::remap(StringRef filePath, |
206 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
207 | remap(getOriginalFile(filePath), std::move(memBuf)); |
208 | } |
209 | |
210 | void FileRemapper::remap(const FileEntry *file, |
211 | std::unique_ptr<llvm::MemoryBuffer> memBuf) { |
212 | assert(file); |
213 | Target &targ = FromToMappings[file]; |
214 | resetTarget(targ); |
215 | targ = memBuf.release(); |
216 | } |
217 | |
218 | void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { |
219 | assert(file && newfile); |
220 | Target &targ = FromToMappings[file]; |
221 | resetTarget(targ); |
222 | targ = newfile; |
223 | ToFromMappings[newfile] = file; |
224 | } |
225 | |
226 | const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { |
227 | const FileEntry *file = FileMgr->getFile(filePath); |
228 | |
229 | |
230 | llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator |
231 | I = ToFromMappings.find(file); |
232 | if (I != ToFromMappings.end()) { |
233 | file = I->second; |
234 | (0) . __assert_fail ("FromToMappings.find(file) != FromToMappings.end() && \"Original file not in mappings!\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 235, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FromToMappings.find(file) != FromToMappings.end() && |
235 | (0) . __assert_fail ("FromToMappings.find(file) != FromToMappings.end() && \"Original file not in mappings!\"", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/FileRemapper.cpp", 235, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "Original file not in mappings!"); |
236 | } |
237 | return file; |
238 | } |
239 | |
240 | void FileRemapper::resetTarget(Target &targ) { |
241 | if (!targ) |
242 | return; |
243 | |
244 | if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { |
245 | delete oldmem; |
246 | } else { |
247 | const FileEntry *toFE = targ.get<const FileEntry *>(); |
248 | ToFromMappings.erase(toFE); |
249 | } |
250 | } |
251 | |
252 | bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) { |
253 | Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) |
254 | << err.str(); |
255 | return true; |
256 | } |
257 | |