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