1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "persistence.h"
41
42 #include "fileinfo.h"
43 #include <logging/translator.h>
44 #include <tools/error.h>
45
46 #include <QtCore/qdir.h>
47
48 namespace qbs {
49 namespace Internal {
50
51 static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-130";
52
NoBuildGraphError(const QString & filePath)53 NoBuildGraphError::NoBuildGraphError(const QString &filePath)
54 : ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.")
55 .arg(FileInfo::completeBaseName(filePath), QDir::toNativeSeparators(filePath)))
56 {
57 }
58
PersistentPool(Logger & logger)59 PersistentPool::PersistentPool(Logger &logger) : m_logger(logger)
60 {
61 Q_UNUSED(m_logger);
62 m_stream.setVersion(QDataStream::Qt_4_8);
63 }
64
65 PersistentPool::~PersistentPool() = default;
66
load(const QString & filePath)67 void PersistentPool::load(const QString &filePath)
68 {
69 std::unique_ptr<QFile> file(new QFile(filePath));
70 if (!file->exists())
71 throw NoBuildGraphError(filePath);
72 if (!file->open(QFile::ReadOnly)) {
73 throw ErrorInfo(Tr::tr("Could not open open build graph file '%1': %2")
74 .arg(filePath, file->errorString()));
75 }
76
77 m_stream.setDevice(file.get());
78 QByteArray magic;
79 m_stream >> magic;
80 if (magic != QBS_PERSISTENCE_MAGIC) {
81 m_stream.setDevice(nullptr);
82 throw ErrorInfo(Tr::tr("Cannot use stored build graph at '%1': Incompatible file format. "
83 "Expected magic token '%2', got '%3'.")
84 .arg(filePath, QLatin1String(QBS_PERSISTENCE_MAGIC),
85 QString::fromLatin1(magic)));
86 }
87
88 m_stream >> m_headData.projectConfig;
89 m_file = std::move(file);
90 m_loadedRaw.clear();
91 m_loaded.clear();
92 m_storageIndices.clear();
93 m_stringStorage.clear();
94 m_inverseStringStorage.clear();
95 }
96
setupWriteStream(const QString & filePath)97 void PersistentPool::setupWriteStream(const QString &filePath)
98 {
99 QString dirPath = FileInfo::path(filePath);
100 if (!FileInfo::exists(dirPath) && !QDir().mkpath(dirPath)) {
101 throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot create directory '%1'.")
102 .arg(dirPath));
103 }
104
105 if (QFile::exists(filePath) && !QFile::remove(filePath)) {
106 throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot remove old file '%1'")
107 .arg(filePath));
108 }
109 QBS_CHECK(!QFile::exists(filePath));
110 std::unique_ptr<QFile> file(new QFile(filePath));
111 if (!file->open(QFile::WriteOnly)) {
112 throw ErrorInfo(Tr::tr("Failure storing build graph: "
113 "Cannot open file '%1' for writing: %2").arg(filePath, file->errorString()));
114 }
115
116 m_stream.setDevice(file.get());
117 m_file = std::move(file);
118 m_stream << QByteArray(qstrlen(QBS_PERSISTENCE_MAGIC), 0) << m_headData.projectConfig;
119 m_lastStoredObjectId = 0;
120 m_lastStoredStringId = 0;
121 m_lastStoredEnvId = 0;
122 m_lastStoredStringListId = 0;
123 }
124
finalizeWriteStream()125 void PersistentPool::finalizeWriteStream()
126 {
127 if (m_stream.status() != QDataStream::Ok)
128 throw ErrorInfo(Tr::tr("Failure serializing build graph."));
129 m_stream.device()->seek(0);
130 m_stream << QByteArray(QBS_PERSISTENCE_MAGIC);
131 if (m_stream.status() != QDataStream::Ok)
132 throw ErrorInfo(Tr::tr("Failure serializing build graph."));
133 const auto file = static_cast<QFile *>(m_stream.device());
134 if (!file->flush()) {
135 file->close();
136 file->remove();
137 throw ErrorInfo(Tr::tr("Failure serializing build graph: %1").arg(file->errorString()));
138 }
139 }
140
storeVariant(const QVariant & variant)141 void PersistentPool::storeVariant(const QVariant &variant)
142 {
143 const auto type = static_cast<quint32>(variant.userType());
144 m_stream << type;
145 switch (type) {
146 case QMetaType::QString:
147 store(variant.toString());
148 break;
149 case QMetaType::QStringList:
150 store(variant.toStringList());
151 break;
152 case QMetaType::QVariantList:
153 store(variant.toList());
154 break;
155 case QMetaType::QVariantMap:
156 store(variant.toMap());
157 break;
158 default:
159 m_stream << variant;
160 }
161 }
162
loadVariant()163 QVariant PersistentPool::loadVariant()
164 {
165 const auto type = load<quint32>();
166 QVariant value;
167 switch (type) {
168 case QMetaType::QString:
169 value = load<QString>();
170 break;
171 case QMetaType::QStringList:
172 value = load<QStringList>();
173 break;
174 case QMetaType::QVariantList:
175 value = load<QVariantList>();
176 break;
177 case QMetaType::QVariantMap:
178 value = load<QVariantMap>();
179 break;
180 default:
181 m_stream >> value;
182 }
183 return value;
184 }
185
clear()186 void PersistentPool::clear()
187 {
188 m_loaded.clear();
189 m_storageIndices.clear();
190 m_stringStorage.clear();
191 m_inverseStringStorage.clear();
192 }
193
doLoadValue(QString & s)194 void PersistentPool::doLoadValue(QString &s)
195 {
196 m_stream >> s;
197 }
198
doLoadValue(QStringList & l)199 void PersistentPool::doLoadValue(QStringList &l)
200 {
201 int size;
202 m_stream >> size;
203 for (int i = 0; i < size; ++i)
204 l << load<QString>();
205 }
206
doLoadValue(QProcessEnvironment & env)207 void PersistentPool::doLoadValue(QProcessEnvironment &env)
208 {
209 const auto keys = load<QStringList>();
210 for (const QString &key : keys)
211 env.insert(key, load<QString>());
212 }
213
doStoreValue(const QString & s)214 void PersistentPool::doStoreValue(const QString &s)
215 {
216 m_stream << s;
217 }
218
doStoreValue(const QStringList & l)219 void PersistentPool::doStoreValue(const QStringList &l)
220 {
221 m_stream << int(l.size());
222 for (const QString &s : l)
223 store(s);
224 }
225
doStoreValue(const QProcessEnvironment & env)226 void PersistentPool::doStoreValue(const QProcessEnvironment &env)
227 {
228 const QStringList &keys = env.keys();
229 store(keys);
230 for (const QString &key : keys)
231 store(env.value(key));
232 }
233
234 const PersistentPool::PersistentObjectId PersistentPool::ValueNotFoundId;
235 const PersistentPool::PersistentObjectId PersistentPool::EmptyValueId;
236
237 } // namespace Internal
238 } // namespace qbs
239