17330f729Sjoerg //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg
97330f729Sjoerg #include "clang/ARCMigrate/FileRemapper.h"
107330f729Sjoerg #include "clang/Basic/Diagnostic.h"
117330f729Sjoerg #include "clang/Basic/FileManager.h"
127330f729Sjoerg #include "clang/Lex/PreprocessorOptions.h"
137330f729Sjoerg #include "llvm/Support/FileSystem.h"
147330f729Sjoerg #include "llvm/Support/MemoryBuffer.h"
157330f729Sjoerg #include "llvm/Support/Path.h"
167330f729Sjoerg #include "llvm/Support/raw_ostream.h"
177330f729Sjoerg #include <fstream>
187330f729Sjoerg
197330f729Sjoerg using namespace clang;
207330f729Sjoerg using namespace arcmt;
217330f729Sjoerg
FileRemapper()227330f729Sjoerg FileRemapper::FileRemapper() {
237330f729Sjoerg FileMgr.reset(new FileManager(FileSystemOptions()));
247330f729Sjoerg }
257330f729Sjoerg
~FileRemapper()267330f729Sjoerg FileRemapper::~FileRemapper() {
277330f729Sjoerg clear();
287330f729Sjoerg }
297330f729Sjoerg
clear(StringRef outputDir)307330f729Sjoerg void FileRemapper::clear(StringRef outputDir) {
317330f729Sjoerg for (MappingsTy::iterator
327330f729Sjoerg I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
337330f729Sjoerg resetTarget(I->second);
347330f729Sjoerg FromToMappings.clear();
357330f729Sjoerg assert(ToFromMappings.empty());
367330f729Sjoerg if (!outputDir.empty()) {
377330f729Sjoerg std::string infoFile = getRemapInfoFile(outputDir);
387330f729Sjoerg llvm::sys::fs::remove(infoFile);
397330f729Sjoerg }
407330f729Sjoerg }
417330f729Sjoerg
getRemapInfoFile(StringRef outputDir)427330f729Sjoerg std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
437330f729Sjoerg assert(!outputDir.empty());
447330f729Sjoerg SmallString<128> InfoFile = outputDir;
457330f729Sjoerg llvm::sys::path::append(InfoFile, "remap");
46*e038c9c4Sjoerg return std::string(InfoFile.str());
477330f729Sjoerg }
487330f729Sjoerg
initFromDisk(StringRef outputDir,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)497330f729Sjoerg bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
507330f729Sjoerg bool ignoreIfFilesChanged) {
517330f729Sjoerg std::string infoFile = getRemapInfoFile(outputDir);
527330f729Sjoerg return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
537330f729Sjoerg }
547330f729Sjoerg
initFromFile(StringRef filePath,DiagnosticsEngine & Diag,bool ignoreIfFilesChanged)557330f729Sjoerg bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
567330f729Sjoerg bool ignoreIfFilesChanged) {
577330f729Sjoerg assert(FromToMappings.empty() &&
587330f729Sjoerg "initFromDisk should be called before any remap calls");
59*e038c9c4Sjoerg std::string infoFile = std::string(filePath);
607330f729Sjoerg if (!llvm::sys::fs::exists(infoFile))
617330f729Sjoerg return false;
627330f729Sjoerg
637330f729Sjoerg std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs;
647330f729Sjoerg
657330f729Sjoerg llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
66*e038c9c4Sjoerg llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);
677330f729Sjoerg if (!fileBuf)
687330f729Sjoerg return report("Error opening file: " + infoFile, Diag);
697330f729Sjoerg
707330f729Sjoerg SmallVector<StringRef, 64> lines;
717330f729Sjoerg fileBuf.get()->getBuffer().split(lines, "\n");
727330f729Sjoerg
737330f729Sjoerg for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
747330f729Sjoerg StringRef fromFilename = lines[idx];
757330f729Sjoerg unsigned long long timeModified;
767330f729Sjoerg if (lines[idx+1].getAsInteger(10, timeModified))
777330f729Sjoerg return report("Invalid file data: '" + lines[idx+1] + "' not a number",
787330f729Sjoerg Diag);
797330f729Sjoerg StringRef toFilename = lines[idx+2];
807330f729Sjoerg
817330f729Sjoerg llvm::ErrorOr<const FileEntry *> origFE = FileMgr->getFile(fromFilename);
827330f729Sjoerg if (!origFE) {
837330f729Sjoerg if (ignoreIfFilesChanged)
847330f729Sjoerg continue;
857330f729Sjoerg return report("File does not exist: " + fromFilename, Diag);
867330f729Sjoerg }
877330f729Sjoerg llvm::ErrorOr<const FileEntry *> newFE = FileMgr->getFile(toFilename);
887330f729Sjoerg if (!newFE) {
897330f729Sjoerg if (ignoreIfFilesChanged)
907330f729Sjoerg continue;
917330f729Sjoerg return report("File does not exist: " + toFilename, Diag);
927330f729Sjoerg }
937330f729Sjoerg
947330f729Sjoerg if ((uint64_t)(*origFE)->getModificationTime() != timeModified) {
957330f729Sjoerg if (ignoreIfFilesChanged)
967330f729Sjoerg continue;
977330f729Sjoerg return report("File was modified: " + fromFilename, Diag);
987330f729Sjoerg }
997330f729Sjoerg
1007330f729Sjoerg pairs.push_back(std::make_pair(*origFE, *newFE));
1017330f729Sjoerg }
1027330f729Sjoerg
1037330f729Sjoerg for (unsigned i = 0, e = pairs.size(); i != e; ++i)
1047330f729Sjoerg remap(pairs[i].first, pairs[i].second);
1057330f729Sjoerg
1067330f729Sjoerg return false;
1077330f729Sjoerg }
1087330f729Sjoerg
flushToDisk(StringRef outputDir,DiagnosticsEngine & Diag)1097330f729Sjoerg bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
1107330f729Sjoerg using namespace llvm::sys;
1117330f729Sjoerg
1127330f729Sjoerg if (fs::create_directory(outputDir))
1137330f729Sjoerg return report("Could not create directory: " + outputDir, Diag);
1147330f729Sjoerg
1157330f729Sjoerg std::string infoFile = getRemapInfoFile(outputDir);
1167330f729Sjoerg return flushToFile(infoFile, Diag);
1177330f729Sjoerg }
1187330f729Sjoerg
flushToFile(StringRef outputPath,DiagnosticsEngine & Diag)1197330f729Sjoerg bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
1207330f729Sjoerg using namespace llvm::sys;
1217330f729Sjoerg
1227330f729Sjoerg std::error_code EC;
123*e038c9c4Sjoerg std::string infoFile = std::string(outputPath);
124*e038c9c4Sjoerg llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);
1257330f729Sjoerg if (EC)
1267330f729Sjoerg return report(EC.message(), Diag);
1277330f729Sjoerg
1287330f729Sjoerg for (MappingsTy::iterator
1297330f729Sjoerg I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
1307330f729Sjoerg
1317330f729Sjoerg const FileEntry *origFE = I->first;
1327330f729Sjoerg SmallString<200> origPath = StringRef(origFE->getName());
1337330f729Sjoerg fs::make_absolute(origPath);
1347330f729Sjoerg infoOut << origPath << '\n';
1357330f729Sjoerg infoOut << (uint64_t)origFE->getModificationTime() << '\n';
1367330f729Sjoerg
1377330f729Sjoerg if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
1387330f729Sjoerg SmallString<200> newPath = StringRef(FE->getName());
1397330f729Sjoerg fs::make_absolute(newPath);
1407330f729Sjoerg infoOut << newPath << '\n';
1417330f729Sjoerg } else {
1427330f729Sjoerg
1437330f729Sjoerg SmallString<64> tempPath;
1447330f729Sjoerg int fd;
145*e038c9c4Sjoerg if (fs::createTemporaryFile(
146*e038c9c4Sjoerg path::filename(origFE->getName()),
147*e038c9c4Sjoerg path::extension(origFE->getName()).drop_front(), fd, tempPath,
148*e038c9c4Sjoerg llvm::sys::fs::OF_Text))
1497330f729Sjoerg return report("Could not create file: " + tempPath.str(), Diag);
1507330f729Sjoerg
1517330f729Sjoerg llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
1527330f729Sjoerg llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
1537330f729Sjoerg newOut.write(mem->getBufferStart(), mem->getBufferSize());
1547330f729Sjoerg newOut.close();
1557330f729Sjoerg
1567330f729Sjoerg auto newE = FileMgr->getFile(tempPath);
1577330f729Sjoerg if (newE) {
1587330f729Sjoerg remap(origFE, *newE);
1597330f729Sjoerg infoOut << (*newE)->getName() << '\n';
1607330f729Sjoerg }
1617330f729Sjoerg }
1627330f729Sjoerg }
1637330f729Sjoerg
1647330f729Sjoerg infoOut.close();
1657330f729Sjoerg return false;
1667330f729Sjoerg }
1677330f729Sjoerg
overwriteOriginal(DiagnosticsEngine & Diag,StringRef outputDir)1687330f729Sjoerg bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
1697330f729Sjoerg StringRef outputDir) {
1707330f729Sjoerg using namespace llvm::sys;
1717330f729Sjoerg
1727330f729Sjoerg for (MappingsTy::iterator
1737330f729Sjoerg I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
1747330f729Sjoerg const FileEntry *origFE = I->first;
1757330f729Sjoerg assert(I->second.is<llvm::MemoryBuffer *>());
1767330f729Sjoerg if (!fs::exists(origFE->getName()))
1777330f729Sjoerg return report(StringRef("File does not exist: ") + origFE->getName(),
1787330f729Sjoerg Diag);
1797330f729Sjoerg
1807330f729Sjoerg std::error_code EC;
1817330f729Sjoerg llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None);
1827330f729Sjoerg if (EC)
1837330f729Sjoerg return report(EC.message(), Diag);
1847330f729Sjoerg
1857330f729Sjoerg llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
1867330f729Sjoerg Out.write(mem->getBufferStart(), mem->getBufferSize());
1877330f729Sjoerg Out.close();
1887330f729Sjoerg }
1897330f729Sjoerg
1907330f729Sjoerg clear(outputDir);
1917330f729Sjoerg return false;
1927330f729Sjoerg }
1937330f729Sjoerg
forEachMapping(llvm::function_ref<void (StringRef,StringRef)> CaptureFile,llvm::function_ref<void (StringRef,const llvm::MemoryBufferRef &)> CaptureBuffer) const194*e038c9c4Sjoerg void FileRemapper::forEachMapping(
195*e038c9c4Sjoerg llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
196*e038c9c4Sjoerg llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
197*e038c9c4Sjoerg CaptureBuffer) const {
198*e038c9c4Sjoerg for (auto &Mapping : FromToMappings) {
199*e038c9c4Sjoerg if (const FileEntry *FE = Mapping.second.dyn_cast<const FileEntry *>()) {
200*e038c9c4Sjoerg CaptureFile(Mapping.first->getName(), FE->getName());
201*e038c9c4Sjoerg continue;
202*e038c9c4Sjoerg }
203*e038c9c4Sjoerg CaptureBuffer(
204*e038c9c4Sjoerg Mapping.first->getName(),
205*e038c9c4Sjoerg Mapping.second.get<llvm::MemoryBuffer *>()->getMemBufferRef());
206*e038c9c4Sjoerg }
207*e038c9c4Sjoerg }
208*e038c9c4Sjoerg
applyMappings(PreprocessorOptions & PPOpts) const2097330f729Sjoerg void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
2107330f729Sjoerg for (MappingsTy::const_iterator
2117330f729Sjoerg I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
2127330f729Sjoerg if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) {
2137330f729Sjoerg PPOpts.addRemappedFile(I->first->getName(), FE->getName());
2147330f729Sjoerg } else {
2157330f729Sjoerg llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>();
2167330f729Sjoerg PPOpts.addRemappedFile(I->first->getName(), mem);
2177330f729Sjoerg }
2187330f729Sjoerg }
2197330f729Sjoerg
2207330f729Sjoerg PPOpts.RetainRemappedFileBuffers = true;
2217330f729Sjoerg }
2227330f729Sjoerg
remap(StringRef filePath,std::unique_ptr<llvm::MemoryBuffer> memBuf)2237330f729Sjoerg void FileRemapper::remap(StringRef filePath,
2247330f729Sjoerg std::unique_ptr<llvm::MemoryBuffer> memBuf) {
2257330f729Sjoerg remap(getOriginalFile(filePath), std::move(memBuf));
2267330f729Sjoerg }
2277330f729Sjoerg
remap(const FileEntry * file,std::unique_ptr<llvm::MemoryBuffer> memBuf)2287330f729Sjoerg void FileRemapper::remap(const FileEntry *file,
2297330f729Sjoerg std::unique_ptr<llvm::MemoryBuffer> memBuf) {
2307330f729Sjoerg assert(file);
2317330f729Sjoerg Target &targ = FromToMappings[file];
2327330f729Sjoerg resetTarget(targ);
2337330f729Sjoerg targ = memBuf.release();
2347330f729Sjoerg }
2357330f729Sjoerg
remap(const FileEntry * file,const FileEntry * newfile)2367330f729Sjoerg void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) {
2377330f729Sjoerg assert(file && newfile);
2387330f729Sjoerg Target &targ = FromToMappings[file];
2397330f729Sjoerg resetTarget(targ);
2407330f729Sjoerg targ = newfile;
2417330f729Sjoerg ToFromMappings[newfile] = file;
2427330f729Sjoerg }
2437330f729Sjoerg
getOriginalFile(StringRef filePath)2447330f729Sjoerg const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) {
2457330f729Sjoerg const FileEntry *file = nullptr;
2467330f729Sjoerg if (auto fileOrErr = FileMgr->getFile(filePath))
2477330f729Sjoerg file = *fileOrErr;
2487330f729Sjoerg // If we are updating a file that overridden an original file,
2497330f729Sjoerg // actually update the original file.
2507330f729Sjoerg llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator
2517330f729Sjoerg I = ToFromMappings.find(file);
2527330f729Sjoerg if (I != ToFromMappings.end()) {
2537330f729Sjoerg file = I->second;
2547330f729Sjoerg assert(FromToMappings.find(file) != FromToMappings.end() &&
2557330f729Sjoerg "Original file not in mappings!");
2567330f729Sjoerg }
2577330f729Sjoerg return file;
2587330f729Sjoerg }
2597330f729Sjoerg
resetTarget(Target & targ)2607330f729Sjoerg void FileRemapper::resetTarget(Target &targ) {
2617330f729Sjoerg if (!targ)
2627330f729Sjoerg return;
2637330f729Sjoerg
2647330f729Sjoerg if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) {
2657330f729Sjoerg delete oldmem;
2667330f729Sjoerg } else {
2677330f729Sjoerg const FileEntry *toFE = targ.get<const FileEntry *>();
2687330f729Sjoerg ToFromMappings.erase(toFE);
2697330f729Sjoerg }
2707330f729Sjoerg }
2717330f729Sjoerg
report(const Twine & err,DiagnosticsEngine & Diag)2727330f729Sjoerg bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
2737330f729Sjoerg Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
2747330f729Sjoerg << err.str();
2757330f729Sjoerg return true;
2767330f729Sjoerg }
277