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