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