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