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