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