1d70fb981SJohn McCall //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
2d70fb981SJohn McCall //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d70fb981SJohn McCall //
7d70fb981SJohn McCall //===----------------------------------------------------------------------===//
8d70fb981SJohn McCall 
9d70fb981SJohn McCall #include "clang/ARCMigrate/FileRemapper.h"
10f7639e1bSTed Kremenek #include "clang/Basic/Diagnostic.h"
113a02247dSChandler Carruth #include "clang/Basic/FileManager.h"
123a02247dSChandler Carruth #include "clang/Lex/PreprocessorOptions.h"
133a02247dSChandler Carruth #include "llvm/Support/FileSystem.h"
14d70fb981SJohn McCall #include "llvm/Support/MemoryBuffer.h"
15d70fb981SJohn McCall #include "llvm/Support/Path.h"
16d70fb981SJohn McCall #include "llvm/Support/raw_ostream.h"
17d70fb981SJohn McCall #include <fstream>
18d70fb981SJohn McCall 
19d70fb981SJohn McCall using namespace clang;
20d70fb981SJohn McCall using namespace arcmt;
21d70fb981SJohn McCall 
FileRemapper()22d70fb981SJohn McCall FileRemapper::FileRemapper() {
23d70fb981SJohn McCall   FileMgr.reset(new FileManager(FileSystemOptions()));
24d70fb981SJohn McCall }
25d70fb981SJohn McCall 
~FileRemapper()26d70fb981SJohn McCall FileRemapper::~FileRemapper() {
27d70fb981SJohn McCall   clear();
28d70fb981SJohn McCall }
29d70fb981SJohn McCall 
clear(StringRef outputDir)300e62c1ccSChris Lattner void FileRemapper::clear(StringRef outputDir) {
31d70fb981SJohn McCall   for (MappingsTy::iterator
32d70fb981SJohn McCall          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
33d70fb981SJohn McCall     resetTarget(I->second);
34d70fb981SJohn McCall   FromToMappings.clear();
35d70fb981SJohn McCall   assert(ToFromMappings.empty());
36d70fb981SJohn McCall   if (!outputDir.empty()) {
37d70fb981SJohn McCall     std::string infoFile = getRemapInfoFile(outputDir);
382a008784SRafael Espindola     llvm::sys::fs::remove(infoFile);
39d70fb981SJohn McCall   }
40d70fb981SJohn McCall }
41d70fb981SJohn McCall 
getRemapInfoFile(StringRef outputDir)420e62c1ccSChris Lattner std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
43d70fb981SJohn McCall   assert(!outputDir.empty());
4433d43303SBenjamin Kramer   SmallString<128> InfoFile = outputDir;
4533d43303SBenjamin Kramer   llvm::sys::path::append(InfoFile, "remap");
46adcd0268SBenjamin Kramer   return std::string(InfoFile.str());
47d70fb981SJohn McCall }
48d70fb981SJohn McCall 
initFromDisk(StringRef outputDir,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)499c902b55SDavid Blaikie bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
50d70fb981SJohn McCall                                 bool ignoreIfFilesChanged) {
51f7639e1bSTed Kremenek   std::string infoFile = getRemapInfoFile(outputDir);
52f7639e1bSTed Kremenek   return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
53f7639e1bSTed Kremenek }
54f7639e1bSTed Kremenek 
initFromFile(StringRef filePath,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)55f7639e1bSTed Kremenek bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
56f7639e1bSTed Kremenek                                 bool ignoreIfFilesChanged) {
57d70fb981SJohn McCall   assert(FromToMappings.empty() &&
58d70fb981SJohn McCall          "initFromDisk should be called before any remap calls");
59adcd0268SBenjamin Kramer   std::string infoFile = std::string(filePath);
60611505f7SRafael Espindola   if (!llvm::sys::fs::exists(infoFile))
61d70fb981SJohn McCall     return false;
62d70fb981SJohn McCall 
63d70fb981SJohn McCall   std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
64d70fb981SJohn McCall 
652d2b420aSRafael Espindola   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
66*fbc32593SAbhina Sreeskantharajan       llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);
672d2b420aSRafael Espindola   if (!fileBuf)
6879cfba18SBenjamin Kramer     return report("Error opening file: " + infoFile, Diag);
69d70fb981SJohn McCall 
703da6bc16SArgyrios Kyrtzidis   SmallVector<StringRef, 64> lines;
712d2b420aSRafael Espindola   fileBuf.get()->getBuffer().split(lines, "\n");
72d70fb981SJohn McCall 
7310454305SArgyrios Kyrtzidis   for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
7479cfba18SBenjamin Kramer     StringRef fromFilename = lines[idx];
755be7a1c8SDouglas Gregor     unsigned long long timeModified;
76dabed222SDavid Blaikie     if (lines[idx+1].getAsInteger(10, timeModified))
77dabed222SDavid Blaikie       return report("Invalid file data: '" + lines[idx+1] + "' not a number",
78dabed222SDavid Blaikie                     Diag);
7979cfba18SBenjamin Kramer     StringRef toFilename = lines[idx+2];
80d70fb981SJohn McCall 
818d323d15SHarlan Haskins     llvm::ErrorOr<const FileEntry *> origFE = FileMgr->getFile(fromFilename);
82d70fb981SJohn McCall     if (!origFE) {
83d70fb981SJohn McCall       if (ignoreIfFilesChanged)
84d70fb981SJohn McCall         continue;
8579cfba18SBenjamin Kramer       return report("File does not exist: " + fromFilename, Diag);
86d70fb981SJohn McCall     }
878d323d15SHarlan Haskins     llvm::ErrorOr<const FileEntry *> newFE = FileMgr->getFile(toFilename);
88d70fb981SJohn McCall     if (!newFE) {
89d70fb981SJohn McCall       if (ignoreIfFilesChanged)
90d70fb981SJohn McCall         continue;
9179cfba18SBenjamin Kramer       return report("File does not exist: " + toFilename, Diag);
92d70fb981SJohn McCall     }
93d70fb981SJohn McCall 
948d323d15SHarlan Haskins     if ((uint64_t)(*origFE)->getModificationTime() != timeModified) {
95d70fb981SJohn McCall       if (ignoreIfFilesChanged)
96d70fb981SJohn McCall         continue;
9779cfba18SBenjamin Kramer       return report("File was modified: " + fromFilename, Diag);
98d70fb981SJohn McCall     }
99d70fb981SJohn McCall 
1008d323d15SHarlan Haskins     pairs.push_back(std::make_pair(*origFE, *newFE));
101d70fb981SJohn McCall   }
102d70fb981SJohn McCall 
103d70fb981SJohn McCall   for (unsigned i = 0, e = pairs.size(); i != e; ++i)
104d70fb981SJohn McCall     remap(pairs[i].first, pairs[i].second);
105d70fb981SJohn McCall 
106d70fb981SJohn McCall   return false;
107d70fb981SJohn McCall }
108d70fb981SJohn McCall 
flushToDisk(StringRef outputDir,DiagnosticsEngine & Diag)1099c902b55SDavid Blaikie bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
110d70fb981SJohn McCall   using namespace llvm::sys;
111d70fb981SJohn McCall 
1123ae0620aSRafael Espindola   if (fs::create_directory(outputDir))
11379cfba18SBenjamin Kramer     return report("Could not create directory: " + outputDir, Diag);
114d70fb981SJohn McCall 
115d70fb981SJohn McCall   std::string infoFile = getRemapInfoFile(outputDir);
116f7639e1bSTed Kremenek   return flushToFile(infoFile, Diag);
117f7639e1bSTed Kremenek }
118f7639e1bSTed Kremenek 
flushToFile(StringRef outputPath,DiagnosticsEngine & Diag)119f7639e1bSTed Kremenek bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
120f7639e1bSTed Kremenek   using namespace llvm::sys;
121f7639e1bSTed Kremenek 
122dae941a6SRafael Espindola   std::error_code EC;
123adcd0268SBenjamin Kramer   std::string infoFile = std::string(outputPath);
124*fbc32593SAbhina Sreeskantharajan   llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);
125dae941a6SRafael Espindola   if (EC)
126dae941a6SRafael Espindola     return report(EC.message(), Diag);
127d70fb981SJohn McCall 
128d70fb981SJohn McCall   for (MappingsTy::iterator
129d70fb981SJohn McCall          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
130d70fb981SJohn McCall 
131d70fb981SJohn McCall     const FileEntry *origFE = I->first;
1322c1dd271SDylan Noblesmith     SmallString<200> origPath = StringRef(origFE->getName());
1337fbd97f6SArgyrios Kyrtzidis     fs::make_absolute(origPath);
1347fbd97f6SArgyrios Kyrtzidis     infoOut << origPath << '\n';
135d70fb981SJohn McCall     infoOut << (uint64_t)origFE->getModificationTime() << '\n';
136d70fb981SJohn McCall 
137d70fb981SJohn McCall     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
1382c1dd271SDylan Noblesmith       SmallString<200> newPath = StringRef(FE->getName());
1397fbd97f6SArgyrios Kyrtzidis       fs::make_absolute(newPath);
1407fbd97f6SArgyrios Kyrtzidis       infoOut << newPath << '\n';
141d70fb981SJohn McCall     } else {
142d70fb981SJohn McCall 
1432c1dd271SDylan Noblesmith       SmallString<64> tempPath;
144d70fb981SJohn McCall       int fd;
145*fbc32593SAbhina Sreeskantharajan       if (fs::createTemporaryFile(
146*fbc32593SAbhina Sreeskantharajan               path::filename(origFE->getName()),
147*fbc32593SAbhina Sreeskantharajan               path::extension(origFE->getName()).drop_front(), fd, tempPath,
148*fbc32593SAbhina Sreeskantharajan               llvm::sys::fs::OF_Text))
14979cfba18SBenjamin Kramer         return report("Could not create file: " + tempPath.str(), Diag);
150d70fb981SJohn McCall 
151d70fb981SJohn McCall       llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
152d70fb981SJohn McCall       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
153d70fb981SJohn McCall       newOut.write(mem->getBufferStart(), mem->getBufferSize());
154d70fb981SJohn McCall       newOut.close();
155d70fb981SJohn McCall 
1568d323d15SHarlan Haskins       auto newE = FileMgr->getFile(tempPath);
1578d323d15SHarlan Haskins       if (newE) {
1588d323d15SHarlan Haskins         remap(origFE, *newE);
1598d323d15SHarlan Haskins         infoOut << (*newE)->getName() << '\n';
1608d323d15SHarlan Haskins       }
161d70fb981SJohn McCall     }
162d70fb981SJohn McCall   }
163d70fb981SJohn McCall 
164d70fb981SJohn McCall   infoOut.close();
165d70fb981SJohn McCall   return false;
166d70fb981SJohn McCall }
167d70fb981SJohn McCall 
overwriteOriginal(DiagnosticsEngine & Diag,StringRef outputDir)1689c902b55SDavid Blaikie bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
1690e62c1ccSChris Lattner                                      StringRef outputDir) {
170d70fb981SJohn McCall   using namespace llvm::sys;
171d70fb981SJohn McCall 
172d70fb981SJohn McCall   for (MappingsTy::iterator
173d70fb981SJohn McCall          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
174d70fb981SJohn McCall     const FileEntry *origFE = I->first;
175884bee86SArgyrios Kyrtzidis     assert(I->second.is<llvm::MemoryBuffer *>());
176611505f7SRafael Espindola     if (!fs::exists(origFE->getName()))
17779cfba18SBenjamin Kramer       return report(StringRef("File does not exist: ") + origFE->getName(),
178d70fb981SJohn McCall                     Diag);
179d70fb981SJohn McCall 
180dae941a6SRafael Espindola     std::error_code EC;
181d9b948b6SFangrui Song     llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None);
182dae941a6SRafael Espindola     if (EC)
183dae941a6SRafael Espindola       return report(EC.message(), Diag);
184d70fb981SJohn McCall 
185d70fb981SJohn McCall     llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
186d70fb981SJohn McCall     Out.write(mem->getBufferStart(), mem->getBufferSize());
187d70fb981SJohn McCall     Out.close();
188d70fb981SJohn McCall   }
189d70fb981SJohn McCall 
190d70fb981SJohn McCall   clear(outputDir);
191d70fb981SJohn McCall   return false;
192d70fb981SJohn McCall }
193d70fb981SJohn McCall 
forEachMapping(llvm::function_ref<void (StringRef,StringRef)> CaptureFile,llvm::function_ref<void (StringRef,const llvm::MemoryBufferRef &)> CaptureBuffer) const194dcc4f7f3SDuncan P. N. Exon Smith void FileRemapper::forEachMapping(
195dcc4f7f3SDuncan P. N. Exon Smith     llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
196dcc4f7f3SDuncan P. N. Exon Smith     llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
197dcc4f7f3SDuncan P. N. Exon Smith         CaptureBuffer) const {
198dcc4f7f3SDuncan P. N. Exon Smith   for (auto &Mapping : FromToMappings) {
199dcc4f7f3SDuncan P. N. Exon Smith     if (const FileEntry *FE = Mapping.second.dyn_cast<const FileEntry *>()) {
200dcc4f7f3SDuncan P. N. Exon Smith       CaptureFile(Mapping.first->getName(), FE->getName());
201dcc4f7f3SDuncan P. N. Exon Smith       continue;
202dcc4f7f3SDuncan P. N. Exon Smith     }
203dcc4f7f3SDuncan P. N. Exon Smith     CaptureBuffer(
204dcc4f7f3SDuncan P. N. Exon Smith         Mapping.first->getName(),
205dcc4f7f3SDuncan P. N. Exon Smith         Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef());
206dcc4f7f3SDuncan P. N. Exon Smith   }
207dcc4f7f3SDuncan P. N. Exon Smith }
208dcc4f7f3SDuncan P. N. Exon Smith 
applyMappings(PreprocessorOptions & PPOpts) const209f7639e1bSTed Kremenek void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
210d70fb981SJohn McCall   for (MappingsTy::const_iterator
211d70fb981SJohn McCall          I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
212d70fb981SJohn McCall     if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
213d70fb981SJohn McCall       PPOpts.addRemappedFile(I->first->getName(), FE->getName());
214d70fb981SJohn McCall     } else {
215d70fb981SJohn McCall       llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
216d70fb981SJohn McCall       PPOpts.addRemappedFile(I->first->getName(), mem);
217d70fb981SJohn McCall     }
218d70fb981SJohn McCall   }
219d70fb981SJohn McCall 
220d70fb981SJohn McCall   PPOpts.RetainRemappedFileBuffers = true;
221d70fb981SJohn McCall }
222d70fb981SJohn McCall 
remap(StringRef filePath,std::unique_ptr<llvm::MemoryBuffer> memBuf)2231a1b1562SRafael Espindola void FileRemapper::remap(StringRef filePath,
2241a1b1562SRafael Espindola                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
2251a1b1562SRafael Espindola   remap(getOriginalFile(filePath), std::move(memBuf));
226d70fb981SJohn McCall }
227d70fb981SJohn McCall 
remap(const FileEntry * file,std::unique_ptr<llvm::MemoryBuffer> memBuf)2281a1b1562SRafael Espindola void FileRemapper::remap(const FileEntry *file,
2291a1b1562SRafael Espindola                          std::unique_ptr<llvm::MemoryBuffer> memBuf) {
230d70fb981SJohn McCall   assert(file);
231d70fb981SJohn McCall   Target &targ = FromToMappings[file];
232d70fb981SJohn McCall   resetTarget(targ);
2331a1b1562SRafael Espindola   targ = memBuf.release();
234d70fb981SJohn McCall }
235d70fb981SJohn McCall 
remap(const FileEntry * file,const FileEntry * newfile)236d70fb981SJohn McCall void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
237d70fb981SJohn McCall   assert(file && newfile);
238d70fb981SJohn McCall   Target &targ = FromToMappings[file];
239d70fb981SJohn McCall   resetTarget(targ);
240d70fb981SJohn McCall   targ = newfile;
241d70fb981SJohn McCall   ToFromMappings[newfile] = file;
242d70fb981SJohn McCall }
243d70fb981SJohn McCall 
getOriginalFile(StringRef filePath)2440e62c1ccSChris Lattner const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
2458d323d15SHarlan Haskins   const FileEntry *file = nullptr;
2468d323d15SHarlan Haskins   if (auto fileOrErr = FileMgr->getFile(filePath))
2478d323d15SHarlan Haskins     file = *fileOrErr;
248b23ccecbSRaphael Isemann   // If we are updating a file that overridden an original file,
249d70fb981SJohn McCall   // actually update the original file.
250d70fb981SJohn McCall   llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
251d70fb981SJohn McCall     I = ToFromMappings.find(file);
252d70fb981SJohn McCall   if (I != ToFromMappings.end()) {
253d70fb981SJohn McCall     file = I->second;
254d70fb981SJohn McCall     assert(FromToMappings.find(file) != FromToMappings.end() &&
255d70fb981SJohn McCall            "Original file not in mappings!");
256d70fb981SJohn McCall   }
257d70fb981SJohn McCall   return file;
258d70fb981SJohn McCall }
259d70fb981SJohn McCall 
resetTarget(Target & targ)260d70fb981SJohn McCall void FileRemapper::resetTarget(Target &targ) {
261d70fb981SJohn McCall   if (!targ)
262d70fb981SJohn McCall     return;
263d70fb981SJohn McCall 
264d70fb981SJohn McCall   if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
265d70fb981SJohn McCall     delete oldmem;
266d70fb981SJohn McCall   } else {
267d70fb981SJohn McCall     const FileEntry *toFE = targ.get<const FileEntry *>();
268e894e09eSBenjamin Kramer     ToFromMappings.erase(toFE);
269d70fb981SJohn McCall   }
270d70fb981SJohn McCall }
271d70fb981SJohn McCall 
report(const Twine & err,DiagnosticsEngine & Diag)2729c902b55SDavid Blaikie bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
27329cb66baSAlp Toker   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
27429cb66baSAlp Toker       << err.str();
275d70fb981SJohn McCall   return true;
276d70fb981SJohn McCall }
277