1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the qmake application of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "qmakevfs.h" |
30 | |
31 | #include "ioutils.h" |
32 | using namespace QMakeInternal; |
33 | |
34 | #include <qdir.h> |
35 | #include <qfile.h> |
36 | #include <qfileinfo.h> |
37 | |
38 | #if QT_CONFIG(textcodec) |
39 | #include <qtextcodec.h> |
40 | #endif |
41 | |
42 | #define fL1S(s) QString::fromLatin1(s) |
43 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | QMakeVfs::QMakeVfs() |
47 | #ifndef PROEVALUATOR_FULL |
48 | : m_magicMissing(fL1S("missing" )) |
49 | , m_magicExisting(fL1S("existing" )) |
50 | #endif |
51 | { |
52 | #if QT_CONFIG(textcodec) |
53 | m_textCodec = 0; |
54 | #endif |
55 | ref(); |
56 | } |
57 | |
58 | QMakeVfs::~QMakeVfs() |
59 | { |
60 | deref(); |
61 | } |
62 | |
63 | void QMakeVfs::ref() |
64 | { |
65 | #ifdef PROEVALUATOR_THREAD_SAFE |
66 | QMutexLocker locker(&s_mutex); |
67 | #endif |
68 | ++s_refCount; |
69 | } |
70 | |
71 | void QMakeVfs::deref() |
72 | { |
73 | #ifdef PROEVALUATOR_THREAD_SAFE |
74 | QMutexLocker locker(&s_mutex); |
75 | #endif |
76 | if (!--s_refCount) { |
77 | s_fileIdCounter = 0; |
78 | s_fileIdMap.clear(); |
79 | s_idFileMap.clear(); |
80 | } |
81 | } |
82 | |
83 | #ifdef PROPARSER_THREAD_SAFE |
84 | QMutex QMakeVfs::s_mutex; |
85 | #endif |
86 | int QMakeVfs::s_refCount; |
87 | QAtomicInt QMakeVfs::s_fileIdCounter; |
88 | QHash<QString, int> QMakeVfs::s_fileIdMap; |
89 | QHash<int, QString> QMakeVfs::s_idFileMap; |
90 | |
91 | int QMakeVfs::idForFileName(const QString &fn, VfsFlags flags) |
92 | { |
93 | #ifdef PROEVALUATOR_DUAL_VFS |
94 | { |
95 | # ifdef PROPARSER_THREAD_SAFE |
96 | QMutexLocker locker(&m_vmutex); |
97 | # endif |
98 | int idx = (flags & VfsCumulative) ? 1 : 0; |
99 | if (flags & VfsCreate) { |
100 | int &id = m_virtualFileIdMap[idx][fn]; |
101 | if (!id) { |
102 | id = ++s_fileIdCounter; |
103 | m_virtualIdFileMap[id] = fn; |
104 | } |
105 | return id; |
106 | } |
107 | int id = m_virtualFileIdMap[idx].value(fn); |
108 | if (id || (flags & VfsCreatedOnly)) |
109 | return id; |
110 | } |
111 | #endif |
112 | #ifdef PROPARSER_THREAD_SAFE |
113 | QMutexLocker locker(&s_mutex); |
114 | #endif |
115 | if (!(flags & VfsAccessedOnly)) { |
116 | int &id = s_fileIdMap[fn]; |
117 | if (!id) { |
118 | id = ++s_fileIdCounter; |
119 | s_idFileMap[id] = fn; |
120 | } |
121 | return id; |
122 | } |
123 | return s_fileIdMap.value(akey: fn); |
124 | } |
125 | |
126 | QString QMakeVfs::fileNameForId(int id) |
127 | { |
128 | #ifdef PROEVALUATOR_DUAL_VFS |
129 | { |
130 | # ifdef PROPARSER_THREAD_SAFE |
131 | QMutexLocker locker(&m_vmutex); |
132 | # endif |
133 | const QString &fn = m_virtualIdFileMap.value(id); |
134 | if (!fn.isEmpty()) |
135 | return fn; |
136 | } |
137 | #endif |
138 | #ifdef PROPARSER_THREAD_SAFE |
139 | QMutexLocker locker(&s_mutex); |
140 | #endif |
141 | return s_idFileMap.value(akey: id); |
142 | } |
143 | |
144 | bool QMakeVfs::writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, |
145 | const QString &contents, QString *errStr) |
146 | { |
147 | #ifndef PROEVALUATOR_FULL |
148 | # ifdef PROEVALUATOR_THREAD_SAFE |
149 | QMutexLocker locker(&m_mutex); |
150 | # endif |
151 | QString *cont = &m_files[id]; |
152 | Q_UNUSED(flags) |
153 | if (mode & QIODevice::Append) |
154 | *cont += contents; |
155 | else |
156 | *cont = contents; |
157 | Q_UNUSED(errStr) |
158 | return true; |
159 | #else |
160 | QFileInfo qfi(fileNameForId(id)); |
161 | if (!QDir::current().mkpath(dirPath: qfi.path())) { |
162 | *errStr = fL1S("Cannot create parent directory" ); |
163 | return false; |
164 | } |
165 | QByteArray bytes = contents.toLocal8Bit(); |
166 | QFile cfile(qfi.filePath()); |
167 | if (!(mode & QIODevice::Append) && cfile.open(flags: QIODevice::ReadOnly | QIODevice::Text)) { |
168 | if (cfile.readAll() == bytes) { |
169 | if (flags & VfsExecutable) { |
170 | cfile.setPermissions(cfile.permissions() |
171 | | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); |
172 | } else { |
173 | cfile.setPermissions(cfile.permissions() |
174 | & ~(QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther)); |
175 | } |
176 | return true; |
177 | } |
178 | cfile.close(); |
179 | } |
180 | if (!cfile.open(flags: mode | QIODevice::WriteOnly | QIODevice::Text)) { |
181 | *errStr = cfile.errorString(); |
182 | return false; |
183 | } |
184 | cfile.write(data: bytes); |
185 | cfile.close(); |
186 | if (cfile.error() != QFile::NoError) { |
187 | *errStr = cfile.errorString(); |
188 | return false; |
189 | } |
190 | if (flags & VfsExecutable) |
191 | cfile.setPermissions(cfile.permissions() |
192 | | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); |
193 | return true; |
194 | #endif |
195 | } |
196 | |
197 | QMakeVfs::ReadResult QMakeVfs::readFile(int id, QString *contents, QString *errStr) |
198 | { |
199 | #ifndef PROEVALUATOR_FULL |
200 | # ifdef PROEVALUATOR_THREAD_SAFE |
201 | QMutexLocker locker(&m_mutex); |
202 | # endif |
203 | auto it = m_files.constFind(id); |
204 | if (it != m_files.constEnd()) { |
205 | if (it->constData() == m_magicMissing.constData()) { |
206 | *errStr = fL1S("No such file or directory" ); |
207 | return ReadNotFound; |
208 | } |
209 | if (it->constData() != m_magicExisting.constData()) { |
210 | *contents = *it; |
211 | return ReadOk; |
212 | } |
213 | } |
214 | #endif |
215 | |
216 | QFile file(fileNameForId(id)); |
217 | if (!file.open(flags: QIODevice::ReadOnly)) { |
218 | if (!file.exists()) { |
219 | #ifndef PROEVALUATOR_FULL |
220 | m_files[id] = m_magicMissing; |
221 | #endif |
222 | *errStr = fL1S("No such file or directory" ); |
223 | return ReadNotFound; |
224 | } |
225 | *errStr = file.errorString(); |
226 | return ReadOtherError; |
227 | } |
228 | #ifndef PROEVALUATOR_FULL |
229 | m_files[id] = m_magicExisting; |
230 | #endif |
231 | |
232 | QByteArray bcont = file.readAll(); |
233 | if (bcont.startsWith(c: "\xef\xbb\xbf" )) { |
234 | // UTF-8 BOM will cause subtle errors |
235 | *errStr = fL1S("Unexpected UTF-8 BOM" ); |
236 | return ReadOtherError; |
237 | } |
238 | *contents = |
239 | #if QT_CONFIG(textcodec) |
240 | m_textCodec ? m_textCodec->toUnicode(bcont) : |
241 | #endif |
242 | QString::fromLocal8Bit(str: bcont); |
243 | return ReadOk; |
244 | } |
245 | |
246 | bool QMakeVfs::exists(const QString &fn, VfsFlags flags) |
247 | { |
248 | #ifndef PROEVALUATOR_FULL |
249 | # ifdef PROEVALUATOR_THREAD_SAFE |
250 | QMutexLocker locker(&m_mutex); |
251 | # endif |
252 | int id = idForFileName(fn, flags); |
253 | auto it = m_files.constFind(id); |
254 | if (it != m_files.constEnd()) |
255 | return it->constData() != m_magicMissing.constData(); |
256 | #else |
257 | Q_UNUSED(flags) |
258 | #endif |
259 | bool ex = IoUtils::fileType(fileName: fn) == IoUtils::FileIsRegular; |
260 | #ifndef PROEVALUATOR_FULL |
261 | m_files[id] = ex ? m_magicExisting : m_magicMissing; |
262 | #endif |
263 | return ex; |
264 | } |
265 | |
266 | #ifndef PROEVALUATOR_FULL |
267 | // This should be called when the sources may have changed (e.g., VCS update). |
268 | void QMakeVfs::invalidateCache() |
269 | { |
270 | # ifdef PROEVALUATOR_THREAD_SAFE |
271 | QMutexLocker locker(&m_mutex); |
272 | # endif |
273 | auto it = m_files.begin(), eit = m_files.end(); |
274 | while (it != eit) { |
275 | if (it->constData() == m_magicMissing.constData() |
276 | ||it->constData() == m_magicExisting.constData()) |
277 | it = m_files.erase(it); |
278 | else |
279 | ++it; |
280 | } |
281 | } |
282 | |
283 | // This should be called when generated files may have changed (e.g., actual build). |
284 | void QMakeVfs::invalidateContents() |
285 | { |
286 | # ifdef PROEVALUATOR_THREAD_SAFE |
287 | QMutexLocker locker(&m_mutex); |
288 | # endif |
289 | m_files.clear(); |
290 | } |
291 | #endif |
292 | |
293 | #if QT_CONFIG(textcodec) |
294 | void QMakeVfs::setTextCodec(const QTextCodec *textCodec) |
295 | { |
296 | m_textCodec = textCodec; |
297 | } |
298 | #endif |
299 | |
300 | QT_END_NAMESPACE |
301 | |