1 //===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/ARCMigrate/FileRemapper.h"
10 #include "clang/Basic/Diagnostic.h"
11 #include "clang/Basic/FileManager.h"
12 #include "clang/Lex/PreprocessorOptions.h"
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/raw_ostream.h"
19 using namespace clang
;
20 using namespace arcmt
;
22 FileRemapper::FileRemapper() {
23 FileMgr
.reset(new FileManager(FileSystemOptions()));
26 FileRemapper::~FileRemapper() {
30 void FileRemapper::clear(StringRef outputDir
) {
31 for (MappingsTy::iterator
32 I
= FromToMappings
.begin(), E
= FromToMappings
.end(); I
!= E
; ++I
)
33 resetTarget(I
->second
);
34 FromToMappings
.clear();
35 assert(ToFromMappings
.empty());
36 if (!outputDir
.empty()) {
37 std::string infoFile
= getRemapInfoFile(outputDir
);
38 llvm::sys::fs::remove(infoFile
);
42 std::string
FileRemapper::getRemapInfoFile(StringRef outputDir
) {
43 assert(!outputDir
.empty());
44 SmallString
<128> InfoFile
= outputDir
;
45 llvm::sys::path::append(InfoFile
, "remap");
46 return std::string(InfoFile
.str());
49 bool FileRemapper::initFromDisk(StringRef outputDir
, DiagnosticsEngine
&Diag
,
50 bool ignoreIfFilesChanged
) {
51 std::string infoFile
= getRemapInfoFile(outputDir
);
52 return initFromFile(infoFile
, Diag
, ignoreIfFilesChanged
);
55 bool FileRemapper::initFromFile(StringRef filePath
, DiagnosticsEngine
&Diag
,
56 bool ignoreIfFilesChanged
) {
57 assert(FromToMappings
.empty() &&
58 "initFromDisk should be called before any remap calls");
59 std::string infoFile
= std::string(filePath
);
60 if (!llvm::sys::fs::exists(infoFile
))
63 std::vector
<std::pair
<const FileEntry
*, const FileEntry
*> > pairs
;
65 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> fileBuf
=
66 llvm::MemoryBuffer::getFile(infoFile
, /*IsText=*/true);
68 return report("Error opening file: " + infoFile
, Diag
);
70 SmallVector
<StringRef
, 64> lines
;
71 fileBuf
.get()->getBuffer().split(lines
, "\n");
73 for (unsigned idx
= 0; idx
+3 <= lines
.size(); idx
+= 3) {
74 StringRef fromFilename
= lines
[idx
];
75 unsigned long long timeModified
;
76 if (lines
[idx
+1].getAsInteger(10, timeModified
))
77 return report("Invalid file data: '" + lines
[idx
+1] + "' not a number",
79 StringRef toFilename
= lines
[idx
+2];
81 llvm::ErrorOr
<const FileEntry
*> origFE
= FileMgr
->getFile(fromFilename
);
83 if (ignoreIfFilesChanged
)
85 return report("File does not exist: " + fromFilename
, Diag
);
87 llvm::ErrorOr
<const FileEntry
*> newFE
= FileMgr
->getFile(toFilename
);
89 if (ignoreIfFilesChanged
)
91 return report("File does not exist: " + toFilename
, Diag
);
94 if ((uint64_t)(*origFE
)->getModificationTime() != timeModified
) {
95 if (ignoreIfFilesChanged
)
97 return report("File was modified: " + fromFilename
, Diag
);
100 pairs
.push_back(std::make_pair(*origFE
, *newFE
));
103 for (unsigned i
= 0, e
= pairs
.size(); i
!= e
; ++i
)
104 remap(pairs
[i
].first
, pairs
[i
].second
);
109 bool FileRemapper::flushToDisk(StringRef outputDir
, DiagnosticsEngine
&Diag
) {
110 using namespace llvm::sys
;
112 if (fs::create_directory(outputDir
))
113 return report("Could not create directory: " + outputDir
, Diag
);
115 std::string infoFile
= getRemapInfoFile(outputDir
);
116 return flushToFile(infoFile
, Diag
);
119 bool FileRemapper::flushToFile(StringRef outputPath
, DiagnosticsEngine
&Diag
) {
120 using namespace llvm::sys
;
123 std::string infoFile
= std::string(outputPath
);
124 llvm::raw_fd_ostream
infoOut(infoFile
, EC
, llvm::sys::fs::OF_Text
);
126 return report(EC
.message(), Diag
);
128 for (MappingsTy::iterator
129 I
= FromToMappings
.begin(), E
= FromToMappings
.end(); I
!= E
; ++I
) {
131 const FileEntry
*origFE
= I
->first
;
132 SmallString
<200> origPath
= StringRef(origFE
->getName());
133 fs::make_absolute(origPath
);
134 infoOut
<< origPath
<< '\n';
135 infoOut
<< (uint64_t)origFE
->getModificationTime() << '\n';
137 if (const FileEntry
*FE
= I
->second
.dyn_cast
<const FileEntry
*>()) {
138 SmallString
<200> newPath
= StringRef(FE
->getName());
139 fs::make_absolute(newPath
);
140 infoOut
<< newPath
<< '\n';
143 SmallString
<64> tempPath
;
145 if (fs::createTemporaryFile(
146 path::filename(origFE
->getName()),
147 path::extension(origFE
->getName()).drop_front(), fd
, tempPath
,
148 llvm::sys::fs::OF_Text
))
149 return report("Could not create file: " + tempPath
.str(), Diag
);
151 llvm::raw_fd_ostream
newOut(fd
, /*shouldClose=*/true);
152 llvm::MemoryBuffer
*mem
= I
->second
.get
<llvm::MemoryBuffer
*>();
153 newOut
.write(mem
->getBufferStart(), mem
->getBufferSize());
156 auto newE
= FileMgr
->getFile(tempPath
);
158 remap(origFE
, *newE
);
159 infoOut
<< (*newE
)->getName() << '\n';
168 bool FileRemapper::overwriteOriginal(DiagnosticsEngine
&Diag
,
169 StringRef outputDir
) {
170 using namespace llvm::sys
;
172 for (MappingsTy::iterator
173 I
= FromToMappings
.begin(), E
= FromToMappings
.end(); I
!= E
; ++I
) {
174 const FileEntry
*origFE
= I
->first
;
175 assert(I
->second
.is
<llvm::MemoryBuffer
*>());
176 if (!fs::exists(origFE
->getName()))
177 return report(StringRef("File does not exist: ") + origFE
->getName(),
181 llvm::raw_fd_ostream
Out(origFE
->getName(), EC
, llvm::sys::fs::OF_None
);
183 return report(EC
.message(), Diag
);
185 llvm::MemoryBuffer
*mem
= I
->second
.get
<llvm::MemoryBuffer
*>();
186 Out
.write(mem
->getBufferStart(), mem
->getBufferSize());
194 void FileRemapper::forEachMapping(
195 llvm::function_ref
<void(StringRef
, StringRef
)> CaptureFile
,
196 llvm::function_ref
<void(StringRef
, const llvm::MemoryBufferRef
&)>
197 CaptureBuffer
) const {
198 for (auto &Mapping
: FromToMappings
) {
199 if (const FileEntry
*FE
= Mapping
.second
.dyn_cast
<const FileEntry
*>()) {
200 CaptureFile(Mapping
.first
->getName(), FE
->getName());
204 Mapping
.first
->getName(),
205 Mapping
.second
.get
<llvm::MemoryBuffer
*>()->getMemBufferRef());
209 void FileRemapper::applyMappings(PreprocessorOptions
&PPOpts
) const {
210 for (MappingsTy::const_iterator
211 I
= FromToMappings
.begin(), E
= FromToMappings
.end(); I
!= E
; ++I
) {
212 if (const FileEntry
*FE
= I
->second
.dyn_cast
<const FileEntry
*>()) {
213 PPOpts
.addRemappedFile(I
->first
->getName(), FE
->getName());
215 llvm::MemoryBuffer
*mem
= I
->second
.get
<llvm::MemoryBuffer
*>();
216 PPOpts
.addRemappedFile(I
->first
->getName(), mem
);
220 PPOpts
.RetainRemappedFileBuffers
= true;
223 void FileRemapper::remap(StringRef filePath
,
224 std::unique_ptr
<llvm::MemoryBuffer
> memBuf
) {
225 remap(getOriginalFile(filePath
), std::move(memBuf
));
228 void FileRemapper::remap(const FileEntry
*file
,
229 std::unique_ptr
<llvm::MemoryBuffer
> memBuf
) {
231 Target
&targ
= FromToMappings
[file
];
233 targ
= memBuf
.release();
236 void FileRemapper::remap(const FileEntry
*file
, const FileEntry
*newfile
) {
237 assert(file
&& newfile
);
238 Target
&targ
= FromToMappings
[file
];
241 ToFromMappings
[newfile
] = file
;
244 const FileEntry
*FileRemapper::getOriginalFile(StringRef filePath
) {
245 const FileEntry
*file
= nullptr;
246 if (auto fileOrErr
= FileMgr
->getFile(filePath
))
248 // If we are updating a file that overridden an original file,
249 // actually update the original file.
250 llvm::DenseMap
<const FileEntry
*, const FileEntry
*>::iterator
251 I
= ToFromMappings
.find(file
);
252 if (I
!= ToFromMappings
.end()) {
254 assert(FromToMappings
.find(file
) != FromToMappings
.end() &&
255 "Original file not in mappings!");
260 void FileRemapper::resetTarget(Target
&targ
) {
264 if (llvm::MemoryBuffer
*oldmem
= targ
.dyn_cast
<llvm::MemoryBuffer
*>()) {
267 const FileEntry
*toFE
= targ
.get
<const FileEntry
*>();
268 ToFromMappings
.erase(toFE
);
272 bool FileRemapper::report(const Twine
&err
, DiagnosticsEngine
&Diag
) {
273 Diag
.Report(Diag
.getCustomDiagID(DiagnosticsEngine::Error
, "%0"))