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