Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/FileRemapper.cpp
35236 views
//===--- FileRemapper.cpp - File Remapping Helper -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/ARCMigrate/FileRemapper.h"9#include "clang/Basic/Diagnostic.h"10#include "clang/Basic/FileManager.h"11#include "clang/Lex/PreprocessorOptions.h"12#include "llvm/Support/FileSystem.h"13#include "llvm/Support/MemoryBuffer.h"14#include "llvm/Support/Path.h"15#include "llvm/Support/raw_ostream.h"16#include <fstream>1718using namespace clang;19using namespace arcmt;2021FileRemapper::FileRemapper() {22FileMgr.reset(new FileManager(FileSystemOptions()));23}2425FileRemapper::~FileRemapper() {26clear();27}2829void FileRemapper::clear(StringRef outputDir) {30for (MappingsTy::iterator31I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)32resetTarget(I->second);33FromToMappings.clear();34assert(ToFromMappings.empty());35if (!outputDir.empty()) {36std::string infoFile = getRemapInfoFile(outputDir);37llvm::sys::fs::remove(infoFile);38}39}4041std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {42assert(!outputDir.empty());43SmallString<128> InfoFile = outputDir;44llvm::sys::path::append(InfoFile, "remap");45return std::string(InfoFile);46}4748bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,49bool ignoreIfFilesChanged) {50std::string infoFile = getRemapInfoFile(outputDir);51return initFromFile(infoFile, Diag, ignoreIfFilesChanged);52}5354bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,55bool ignoreIfFilesChanged) {56assert(FromToMappings.empty() &&57"initFromDisk should be called before any remap calls");58std::string infoFile = std::string(filePath);59if (!llvm::sys::fs::exists(infoFile))60return false;6162std::vector<std::pair<FileEntryRef, FileEntryRef>> pairs;6364llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =65llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);66if (!fileBuf)67return report("Error opening file: " + infoFile, Diag);6869SmallVector<StringRef, 64> lines;70fileBuf.get()->getBuffer().split(lines, "\n");7172for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {73StringRef fromFilename = lines[idx];74unsigned long long timeModified;75if (lines[idx+1].getAsInteger(10, timeModified))76return report("Invalid file data: '" + lines[idx+1] + "' not a number",77Diag);78StringRef toFilename = lines[idx+2];7980auto origFE = FileMgr->getOptionalFileRef(fromFilename);81if (!origFE) {82if (ignoreIfFilesChanged)83continue;84return report("File does not exist: " + fromFilename, Diag);85}86auto newFE = FileMgr->getOptionalFileRef(toFilename);87if (!newFE) {88if (ignoreIfFilesChanged)89continue;90return report("File does not exist: " + toFilename, Diag);91}9293if ((uint64_t)origFE->getModificationTime() != timeModified) {94if (ignoreIfFilesChanged)95continue;96return report("File was modified: " + fromFilename, Diag);97}9899pairs.push_back(std::make_pair(*origFE, *newFE));100}101102for (unsigned i = 0, e = pairs.size(); i != e; ++i)103remap(pairs[i].first, pairs[i].second);104105return false;106}107108bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {109using namespace llvm::sys;110111if (fs::create_directory(outputDir))112return report("Could not create directory: " + outputDir, Diag);113114std::string infoFile = getRemapInfoFile(outputDir);115return flushToFile(infoFile, Diag);116}117118bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {119using namespace llvm::sys;120121std::error_code EC;122std::string infoFile = std::string(outputPath);123llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);124if (EC)125return report(EC.message(), Diag);126127for (MappingsTy::iterator128I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {129130FileEntryRef origFE = I->first;131SmallString<200> origPath = StringRef(origFE.getName());132fs::make_absolute(origPath);133infoOut << origPath << '\n';134infoOut << (uint64_t)origFE.getModificationTime() << '\n';135136if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {137SmallString<200> newPath = StringRef(FE->getName());138fs::make_absolute(newPath);139infoOut << newPath << '\n';140} else {141142SmallString<64> tempPath;143int fd;144if (fs::createTemporaryFile(145path::filename(origFE.getName()),146path::extension(origFE.getName()).drop_front(), fd, tempPath,147llvm::sys::fs::OF_Text))148return report("Could not create file: " + tempPath.str(), Diag);149150llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);151llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);152newOut.write(mem->getBufferStart(), mem->getBufferSize());153newOut.close();154155auto newE = FileMgr->getOptionalFileRef(tempPath);156if (newE) {157remap(origFE, *newE);158infoOut << newE->getName() << '\n';159}160}161}162163infoOut.close();164return false;165}166167bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,168StringRef outputDir) {169using namespace llvm::sys;170171for (MappingsTy::iterator172I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {173FileEntryRef origFE = I->first;174assert(std::holds_alternative<llvm::MemoryBuffer *>(I->second));175if (!fs::exists(origFE.getName()))176return report(StringRef("File does not exist: ") + origFE.getName(),177Diag);178179std::error_code EC;180llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None);181if (EC)182return report(EC.message(), Diag);183184llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);185Out.write(mem->getBufferStart(), mem->getBufferSize());186Out.close();187}188189clear(outputDir);190return false;191}192193void FileRemapper::forEachMapping(194llvm::function_ref<void(StringRef, StringRef)> CaptureFile,195llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>196CaptureBuffer) const {197for (auto &Mapping : FromToMappings) {198if (const auto *FE = std::get_if<FileEntryRef>(&Mapping.second)) {199CaptureFile(Mapping.first.getName(), FE->getName());200continue;201}202CaptureBuffer(203Mapping.first.getName(),204std::get<llvm::MemoryBuffer *>(Mapping.second)->getMemBufferRef());205}206}207208void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {209for (MappingsTy::const_iterator210I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {211if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {212PPOpts.addRemappedFile(I->first.getName(), FE->getName());213} else {214llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);215PPOpts.addRemappedFile(I->first.getName(), mem);216}217}218219PPOpts.RetainRemappedFileBuffers = true;220}221222void FileRemapper::remap(StringRef filePath,223std::unique_ptr<llvm::MemoryBuffer> memBuf) {224OptionalFileEntryRef File = getOriginalFile(filePath);225assert(File);226remap(*File, std::move(memBuf));227}228229void FileRemapper::remap(FileEntryRef File,230std::unique_ptr<llvm::MemoryBuffer> MemBuf) {231auto [It, New] = FromToMappings.insert({File, nullptr});232if (!New)233resetTarget(It->second);234It->second = MemBuf.release();235}236237void FileRemapper::remap(FileEntryRef File, FileEntryRef NewFile) {238auto [It, New] = FromToMappings.insert({File, nullptr});239if (!New)240resetTarget(It->second);241It->second = NewFile;242ToFromMappings.insert({NewFile, File});243}244245OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) {246OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath);247if (!File)248return std::nullopt;249// If we are updating a file that overridden an original file,250// actually update the original file.251auto I = ToFromMappings.find(*File);252if (I != ToFromMappings.end()) {253*File = I->second;254assert(FromToMappings.contains(*File) && "Original file not in mappings!");255}256return File;257}258259void FileRemapper::resetTarget(Target &targ) {260if (std::holds_alternative<llvm::MemoryBuffer *>(targ)) {261llvm::MemoryBuffer *oldmem = std::get<llvm::MemoryBuffer *>(targ);262delete oldmem;263} else {264FileEntryRef toFE = std::get<FileEntryRef>(targ);265ToFromMappings.erase(toFE);266}267}268269bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {270Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))271<< err.str();272return true;273}274275276