1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "qmakevfs.h" |
5 | |
6 | #include "ioutils.h" |
7 | using namespace QMakeInternal; |
8 | |
9 | #include <qdir.h> |
10 | #include <qfile.h> |
11 | #include <qfileinfo.h> |
12 | |
13 | #define fL1S(s) QString::fromLatin1(s) |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | QMakeVfs::QMakeVfs() |
18 | #ifndef PROEVALUATOR_FULL |
19 | : m_magicMissing(fL1S("missing" )) |
20 | , m_magicExisting(fL1S("existing" )) |
21 | #endif |
22 | { |
23 | ref(); |
24 | } |
25 | |
26 | QMakeVfs::~QMakeVfs() |
27 | { |
28 | deref(); |
29 | } |
30 | |
31 | void QMakeVfs::ref() |
32 | { |
33 | #ifdef PROEVALUATOR_THREAD_SAFE |
34 | QMutexLocker locker(&s_mutex); |
35 | #endif |
36 | ++s_refCount; |
37 | } |
38 | |
39 | void QMakeVfs::deref() |
40 | { |
41 | #ifdef PROEVALUATOR_THREAD_SAFE |
42 | QMutexLocker locker(&s_mutex); |
43 | #endif |
44 | if (!--s_refCount) { |
45 | s_fileIdCounter = 0; |
46 | s_fileIdMap.clear(); |
47 | s_idFileMap.clear(); |
48 | } |
49 | } |
50 | |
51 | #ifdef PROPARSER_THREAD_SAFE |
52 | QMutex QMakeVfs::s_mutex; |
53 | #endif |
54 | int QMakeVfs::s_refCount; |
55 | QAtomicInt QMakeVfs::s_fileIdCounter; |
56 | QHash<QString, int> QMakeVfs::s_fileIdMap; |
57 | QHash<int, QString> QMakeVfs::s_idFileMap; |
58 | |
59 | int QMakeVfs::idForFileName(const QString &fn, VfsFlags flags) |
60 | { |
61 | #ifdef PROEVALUATOR_DUAL_VFS |
62 | { |
63 | # ifdef PROPARSER_THREAD_SAFE |
64 | QMutexLocker locker(&m_vmutex); |
65 | # endif |
66 | int idx = (flags & VfsCumulative) ? 1 : 0; |
67 | if (flags & VfsCreate) { |
68 | int &id = m_virtualFileIdMap[idx][fn]; |
69 | if (!id) { |
70 | id = ++s_fileIdCounter; |
71 | m_virtualIdFileMap[id] = fn; |
72 | } |
73 | return id; |
74 | } |
75 | int id = m_virtualFileIdMap[idx].value(fn); |
76 | if (id || (flags & VfsCreatedOnly)) |
77 | return id; |
78 | } |
79 | #endif |
80 | #ifdef PROPARSER_THREAD_SAFE |
81 | QMutexLocker locker(&s_mutex); |
82 | #endif |
83 | if (!(flags & VfsAccessedOnly)) { |
84 | int &id = s_fileIdMap[fn]; |
85 | if (!id) { |
86 | id = ++s_fileIdCounter; |
87 | s_idFileMap[id] = fn; |
88 | } |
89 | return id; |
90 | } |
91 | return s_fileIdMap.value(key: fn); |
92 | } |
93 | |
94 | QString QMakeVfs::fileNameForId(int id) |
95 | { |
96 | #ifdef PROEVALUATOR_DUAL_VFS |
97 | { |
98 | # ifdef PROPARSER_THREAD_SAFE |
99 | QMutexLocker locker(&m_vmutex); |
100 | # endif |
101 | const QString &fn = m_virtualIdFileMap.value(id); |
102 | if (!fn.isEmpty()) |
103 | return fn; |
104 | } |
105 | #endif |
106 | #ifdef PROPARSER_THREAD_SAFE |
107 | QMutexLocker locker(&s_mutex); |
108 | #endif |
109 | return s_idFileMap.value(key: id); |
110 | } |
111 | |
112 | bool QMakeVfs::writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, |
113 | const QString &contents, QString *errStr) |
114 | { |
115 | #ifndef PROEVALUATOR_FULL |
116 | # ifdef PROEVALUATOR_THREAD_SAFE |
117 | QMutexLocker locker(&m_mutex); |
118 | # endif |
119 | QString *cont = &m_files[id]; |
120 | Q_UNUSED(flags); |
121 | if (mode & QIODevice::Append) |
122 | *cont += contents; |
123 | else |
124 | *cont = contents; |
125 | Q_UNUSED(errStr); |
126 | return true; |
127 | #else |
128 | QFileInfo qfi(fileNameForId(id)); |
129 | if (!QDir::current().mkpath(qfi.path())) { |
130 | *errStr = fL1S("Cannot create parent directory" ); |
131 | return false; |
132 | } |
133 | QByteArray bytes = contents.toLocal8Bit(); |
134 | QFile cfile(qfi.filePath()); |
135 | if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) { |
136 | if (cfile.readAll() == bytes) { |
137 | if (flags & VfsExecutable) { |
138 | cfile.setPermissions(cfile.permissions() |
139 | | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); |
140 | } else { |
141 | cfile.setPermissions(cfile.permissions() |
142 | & ~(QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther)); |
143 | } |
144 | return true; |
145 | } |
146 | cfile.close(); |
147 | } |
148 | if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) { |
149 | *errStr = cfile.errorString(); |
150 | return false; |
151 | } |
152 | cfile.write(bytes); |
153 | cfile.close(); |
154 | if (cfile.error() != QFile::NoError) { |
155 | *errStr = cfile.errorString(); |
156 | return false; |
157 | } |
158 | if (flags & VfsExecutable) |
159 | cfile.setPermissions(cfile.permissions() |
160 | | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); |
161 | return true; |
162 | #endif |
163 | } |
164 | |
165 | QMakeVfs::ReadResult QMakeVfs::readFile(int id, QString *contents, QString *errStr) |
166 | { |
167 | #ifndef PROEVALUATOR_FULL |
168 | # ifdef PROEVALUATOR_THREAD_SAFE |
169 | QMutexLocker locker(&m_mutex); |
170 | # endif |
171 | auto it = m_files.constFind(key: id); |
172 | if (it != m_files.constEnd()) { |
173 | if (it->constData() == m_magicMissing.constData()) { |
174 | *errStr = fL1S("No such file or directory" ); |
175 | return ReadNotFound; |
176 | } |
177 | if (it->constData() != m_magicExisting.constData()) { |
178 | *contents = *it; |
179 | return ReadOk; |
180 | } |
181 | } |
182 | #endif |
183 | |
184 | QFile file(fileNameForId(id)); |
185 | if (!file.open(flags: QIODevice::ReadOnly)) { |
186 | if (!file.exists()) { |
187 | #ifndef PROEVALUATOR_FULL |
188 | m_files[id] = m_magicMissing; |
189 | #endif |
190 | *errStr = fL1S("No such file or directory" ); |
191 | return ReadNotFound; |
192 | } |
193 | *errStr = file.errorString(); |
194 | return ReadOtherError; |
195 | } |
196 | #ifndef PROEVALUATOR_FULL |
197 | m_files[id] = m_magicExisting; |
198 | #endif |
199 | |
200 | QByteArray bcont = file.readAll(); |
201 | if (bcont.startsWith(bv: "\xef\xbb\xbf" )) { |
202 | // UTF-8 BOM will cause subtle errors |
203 | *errStr = fL1S("Unexpected UTF-8 BOM" ); |
204 | return ReadOtherError; |
205 | } |
206 | *contents = QString::fromLocal8Bit(ba: bcont); |
207 | return ReadOk; |
208 | } |
209 | |
210 | bool QMakeVfs::exists(const QString &fn, VfsFlags flags) |
211 | { |
212 | #ifndef PROEVALUATOR_FULL |
213 | # ifdef PROEVALUATOR_THREAD_SAFE |
214 | QMutexLocker locker(&m_mutex); |
215 | # endif |
216 | int id = idForFileName(fn, flags); |
217 | auto it = m_files.constFind(key: id); |
218 | if (it != m_files.constEnd()) |
219 | return it->constData() != m_magicMissing.constData(); |
220 | #else |
221 | Q_UNUSED(flags); |
222 | #endif |
223 | bool ex = IoUtils::fileType(fileName: fn) == IoUtils::FileIsRegular; |
224 | #ifndef PROEVALUATOR_FULL |
225 | m_files[id] = ex ? m_magicExisting : m_magicMissing; |
226 | #endif |
227 | return ex; |
228 | } |
229 | |
230 | #ifndef PROEVALUATOR_FULL |
231 | // This should be called when the sources may have changed (e.g., VCS update). |
232 | void QMakeVfs::invalidateCache() |
233 | { |
234 | # ifdef PROEVALUATOR_THREAD_SAFE |
235 | QMutexLocker locker(&m_mutex); |
236 | # endif |
237 | auto it = m_files.begin(), eit = m_files.end(); |
238 | while (it != eit) { |
239 | if (it->constData() == m_magicMissing.constData() |
240 | ||it->constData() == m_magicExisting.constData()) |
241 | it = m_files.erase(it); |
242 | else |
243 | ++it; |
244 | } |
245 | } |
246 | |
247 | // This should be called when generated files may have changed (e.g., actual build). |
248 | void QMakeVfs::invalidateContents() |
249 | { |
250 | # ifdef PROEVALUATOR_THREAD_SAFE |
251 | QMutexLocker locker(&m_mutex); |
252 | # endif |
253 | m_files.clear(); |
254 | } |
255 | #endif |
256 | |
257 | QT_END_NAMESPACE |
258 | |