forked from qt-creator/qt-creator
SdkTool: Fork utils/persistentsettings
And a few helper classes. This allows SdkTool being build without Creator's Utils, and does not impose restrictions on the development there. Change-Id: Id15db9293f343ad2aeee5c09cc819d112ec144e4 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -66,33 +66,7 @@ add_qtc_library(sdktoolLib
|
|||||||
rmqtoperation.cpp rmqtoperation.h
|
rmqtoperation.cpp rmqtoperation.h
|
||||||
rmtoolchainoperation.cpp rmtoolchainoperation.h
|
rmtoolchainoperation.cpp rmtoolchainoperation.h
|
||||||
settings.cpp settings.h
|
settings.cpp settings.h
|
||||||
)
|
sdkpersistentsettings.cpp sdkpersistentsettings.h
|
||||||
|
|
||||||
extend_qtc_library(sdktoolLib
|
|
||||||
SOURCES_PREFIX "${UtilsSourcesDir}"
|
|
||||||
PUBLIC_DEFINES UTILS_STATIC_LIBRARY
|
|
||||||
SOURCES
|
|
||||||
commandline.cpp commandline.h
|
|
||||||
devicefileaccess.cpp devicefileaccess.h
|
|
||||||
environment.cpp environment.h
|
|
||||||
filepath.cpp filepath.h
|
|
||||||
fileutils.cpp fileutils.h
|
|
||||||
hostosinfo.cpp hostosinfo.h
|
|
||||||
macroexpander.cpp macroexpander.h
|
|
||||||
namevaluedictionary.cpp namevaluedictionary.h
|
|
||||||
namevalueitem.cpp namevalueitem.h
|
|
||||||
persistentsettings.cpp persistentsettings.h
|
|
||||||
qtcassert.cpp qtcassert.h
|
|
||||||
savefile.cpp savefile.h
|
|
||||||
stringutils.cpp stringutils.h
|
|
||||||
)
|
|
||||||
|
|
||||||
extend_qtc_library(sdktoolLib CONDITION APPLE
|
|
||||||
SOURCES_PREFIX "${UtilsSourcesDir}"
|
|
||||||
SOURCES
|
|
||||||
fileutils_mac.mm fileutils_mac.h
|
|
||||||
PUBLIC_DEPENDS
|
|
||||||
${FWFoundation}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
@@ -4,13 +4,10 @@
|
|||||||
#include "addcmakeoperation.h"
|
#include "addcmakeoperation.h"
|
||||||
|
|
||||||
#include "addkeysoperation.h"
|
#include "addkeysoperation.h"
|
||||||
#include "findkeyoperation.h"
|
|
||||||
#include "findvalueoperation.h"
|
#include "findvalueoperation.h"
|
||||||
#include "getoperation.h"
|
#include "getoperation.h"
|
||||||
#include "rmkeysoperation.h"
|
#include "rmkeysoperation.h"
|
||||||
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
#endif
|
#endif
|
||||||
@@ -205,7 +202,7 @@ QVariantMap AddCMakeData::addCMake(const QVariantMap &map) const
|
|||||||
data << KeyValuePair({cm, ID_KEY}, QVariant(m_id));
|
data << KeyValuePair({cm, ID_KEY}, QVariant(m_id));
|
||||||
data << KeyValuePair({cm, DISPLAYNAME_KEY}, QVariant(m_displayName));
|
data << KeyValuePair({cm, DISPLAYNAME_KEY}, QVariant(m_displayName));
|
||||||
data << KeyValuePair({cm, AUTODETECTED_KEY}, QVariant(true));
|
data << KeyValuePair({cm, AUTODETECTED_KEY}, QVariant(true));
|
||||||
data << KeyValuePair({cm, PATH_KEY}, Utils::FilePath::fromUserInput(m_path).toVariant());
|
data << KeyValuePair({cm, PATH_KEY}, QVariant(m_path));
|
||||||
KeyValuePairList extraList;
|
KeyValuePairList extraList;
|
||||||
for (const KeyValuePair &pair : std::as_const(m_extra))
|
for (const KeyValuePair &pair : std::as_const(m_extra))
|
||||||
extraList << KeyValuePair(QStringList({cm}) << pair.key, pair.value);
|
extraList << KeyValuePair(QStringList({cm}) << pair.key, pair.value);
|
||||||
|
@@ -222,8 +222,7 @@ QVariantMap AddDebuggerData::addDebugger(const QVariantMap &map) const
|
|||||||
|
|
||||||
data << KeyValuePair(QStringList() << debugger << QLatin1String(ABIS), QVariant(m_abis));
|
data << KeyValuePair(QStringList() << debugger << QLatin1String(ABIS), QVariant(m_abis));
|
||||||
data << KeyValuePair(QStringList() << debugger << QLatin1String(ENGINE_TYPE), QVariant(m_engine));
|
data << KeyValuePair(QStringList() << debugger << QLatin1String(ENGINE_TYPE), QVariant(m_engine));
|
||||||
data << KeyValuePair(QStringList() << debugger << QLatin1String(BINARY),
|
data << KeyValuePair(QStringList() << debugger << QLatin1String(BINARY), QVariant(m_binary));
|
||||||
Utils::FilePath::fromUserInput(m_binary).toSettings());
|
|
||||||
|
|
||||||
data << KeyValuePair(QStringList() << QLatin1String(COUNT), QVariant(count + 1));
|
data << KeyValuePair(QStringList() << QLatin1String(COUNT), QVariant(count + 1));
|
||||||
|
|
||||||
|
@@ -685,8 +685,7 @@ QVariantMap AddKitData::addKit(const QVariantMap &map,
|
|||||||
if (!m_buildDevice.isNull())
|
if (!m_buildDevice.isNull())
|
||||||
data << KeyValuePair({kit, DATA, BUILDDEVICE_ID}, QVariant(m_buildDevice));
|
data << KeyValuePair({kit, DATA, BUILDDEVICE_ID}, QVariant(m_buildDevice));
|
||||||
if (!m_sysRoot.isNull())
|
if (!m_sysRoot.isNull())
|
||||||
data << KeyValuePair({kit, DATA, SYSROOT},
|
data << KeyValuePair({kit, DATA, SYSROOT}, QVariant(QDir::cleanPath(m_sysRoot)));
|
||||||
Utils::FilePath::fromUserInput(m_sysRoot).toSettings());
|
|
||||||
for (auto i = m_tcs.constBegin(); i != m_tcs.constEnd(); ++i)
|
for (auto i = m_tcs.constBegin(); i != m_tcs.constEnd(); ++i)
|
||||||
data << KeyValuePair({kit, DATA, TOOLCHAIN, i.key()}, QVariant(i.value()));
|
data << KeyValuePair({kit, DATA, TOOLCHAIN, i.key()}, QVariant(i.value()));
|
||||||
if (!qtId.isNull())
|
if (!qtId.isNull())
|
||||||
|
@@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
#endif
|
#endif
|
||||||
@@ -22,8 +20,6 @@
|
|||||||
|
|
||||||
Q_LOGGING_CATEGORY(log, "qtc.sdktool.operations.addqt", QtWarningMsg)
|
Q_LOGGING_CATEGORY(log, "qtc.sdktool.operations.addqt", QtWarningMsg)
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
// Qt version file stuff:
|
// Qt version file stuff:
|
||||||
const char PREFIX[] = "QtVersion.";
|
const char PREFIX[] = "QtVersion.";
|
||||||
const char VERSION[] = "Version";
|
const char VERSION[] = "Version";
|
||||||
@@ -282,7 +278,7 @@ QVariantMap AddQtData::addQt(const QVariantMap &map) const
|
|||||||
const QString qt = QString::fromLatin1(PREFIX) + QString::number(versionCount);
|
const QString qt = QString::fromLatin1(PREFIX) + QString::number(versionCount);
|
||||||
|
|
||||||
// Sanitize qmake path:
|
// Sanitize qmake path:
|
||||||
FilePath saneQmake = FilePath::fromUserInput(m_qmake).cleanPath();
|
QString saneQmake = QDir::cleanPath(m_qmake);
|
||||||
|
|
||||||
// insert data:
|
// insert data:
|
||||||
KeyValuePairList data;
|
KeyValuePairList data;
|
||||||
@@ -291,7 +287,7 @@ QVariantMap AddQtData::addQt(const QVariantMap &map) const
|
|||||||
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTED), QVariant(true));
|
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTED), QVariant(true));
|
||||||
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTION_SOURCE), QVariant(sdkId));
|
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTION_SOURCE), QVariant(sdkId));
|
||||||
|
|
||||||
data << KeyValuePair(QStringList() << qt << QLatin1String(QMAKE), saneQmake.toSettings());
|
data << KeyValuePair(QStringList() << qt << QLatin1String(QMAKE), QVariant(saneQmake));
|
||||||
data << KeyValuePair(QStringList() << qt << QLatin1String(TYPE), QVariant(m_type));
|
data << KeyValuePair(QStringList() << qt << QLatin1String(TYPE), QVariant(m_type));
|
||||||
data << KeyValuePair(QStringList() << qt << ABIS, QVariant(m_abis));
|
data << KeyValuePair(QStringList() << qt << ABIS, QVariant(m_abis));
|
||||||
|
|
||||||
|
@@ -4,13 +4,10 @@
|
|||||||
#include "addtoolchainoperation.h"
|
#include "addtoolchainoperation.h"
|
||||||
|
|
||||||
#include "addkeysoperation.h"
|
#include "addkeysoperation.h"
|
||||||
#include "findkeyoperation.h"
|
|
||||||
#include "findvalueoperation.h"
|
#include "findvalueoperation.h"
|
||||||
#include "getoperation.h"
|
#include "getoperation.h"
|
||||||
#include "rmkeysoperation.h"
|
#include "rmkeysoperation.h"
|
||||||
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
@@ -283,7 +280,7 @@ QVariantMap AddToolChainData::addToolChain(const QVariantMap &map) const
|
|||||||
data << KeyValuePair({tc, LANGUAGE_KEY_V2}, QVariant(newLang));
|
data << KeyValuePair({tc, LANGUAGE_KEY_V2}, QVariant(newLang));
|
||||||
data << KeyValuePair({tc, DISPLAYNAME}, QVariant(m_displayName));
|
data << KeyValuePair({tc, DISPLAYNAME}, QVariant(m_displayName));
|
||||||
data << KeyValuePair({tc, AUTODETECTED}, QVariant(true));
|
data << KeyValuePair({tc, AUTODETECTED}, QVariant(true));
|
||||||
data << KeyValuePair({tc, PATH}, Utils::FilePath::fromUserInput(m_path).toSettings());
|
data << KeyValuePair({tc, PATH}, QVariant(m_path));
|
||||||
data << KeyValuePair({tc, TARGET_ABI}, QVariant(m_targetAbi));
|
data << KeyValuePair({tc, TARGET_ABI}, QVariant(m_targetAbi));
|
||||||
QVariantList abis;
|
QVariantList abis;
|
||||||
const QStringList abiStrings = m_supportedAbis.split(',');
|
const QStringList abiStrings = m_supportedAbis.split(',');
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
@@ -60,10 +61,7 @@ void printHelp(const std::vector<std::unique_ptr<Operation>> &operations)
|
|||||||
std::cout << " --sdkpath=PATH|-s PATH Set the path to the SDK files" << std::endl << std::endl;
|
std::cout << " --sdkpath=PATH|-s PATH Set the path to the SDK files" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << "Default sdkpath is \""
|
std::cout << "Default sdkpath is \""
|
||||||
<< qPrintable(QDir::cleanPath(
|
<< qPrintable(QDir::cleanPath(QCoreApplication::applicationDirPath() + '/' + DATA_PATH))
|
||||||
Utils::FilePath::fromString(QCoreApplication::applicationDirPath())
|
|
||||||
.pathAppended(DATA_PATH)
|
|
||||||
.toUserOutput()))
|
|
||||||
<< "\"" << std::endl
|
<< "\"" << std::endl
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
@@ -105,7 +103,7 @@ int parseArguments(const QStringList &args, Settings *s,
|
|||||||
|
|
||||||
// sdkpath
|
// sdkpath
|
||||||
if (current.startsWith(QLatin1String("--sdkpath="))) {
|
if (current.startsWith(QLatin1String("--sdkpath="))) {
|
||||||
s->sdkPath = Utils::FilePath::fromString(current.mid(10));
|
s->sdkPath = current.mid(10);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (current == QLatin1String("-s")) {
|
if (current == QLatin1String("-s")) {
|
||||||
@@ -114,7 +112,7 @@ int parseArguments(const QStringList &args, Settings *s,
|
|||||||
printHelp(operations);
|
printHelp(operations);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
s->sdkPath = Utils::FilePath::fromString(next);
|
s->sdkPath = next;
|
||||||
++i; // skip next;
|
++i; // skip next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,7 @@
|
|||||||
#include "operation.h"
|
#include "operation.h"
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "sdkpersistentsettings.h"
|
||||||
#include <utils/persistentsettings.h>
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -65,9 +64,9 @@ QVariantMap Operation::load(const QString &file)
|
|||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
|
|
||||||
// Read values from original file:
|
// Read values from original file:
|
||||||
Utils::FilePath path = Settings::instance()->getPath(file);
|
QString path = Settings::instance()->getPath(file);
|
||||||
if (path.exists()) {
|
if (QFileInfo::exists(path)) {
|
||||||
Utils::PersistentSettingsReader reader;
|
SdkPersistentSettingsReader reader;
|
||||||
if (!reader.load(path))
|
if (!reader.load(path))
|
||||||
return QVariantMap();
|
return QVariantMap();
|
||||||
map = reader.restoreValues();
|
map = reader.restoreValues();
|
||||||
@@ -78,32 +77,32 @@ QVariantMap Operation::load(const QString &file)
|
|||||||
|
|
||||||
bool Operation::save(const QVariantMap &map, const QString &file) const
|
bool Operation::save(const QVariantMap &map, const QString &file) const
|
||||||
{
|
{
|
||||||
Utils::FilePath path = Settings::instance()->getPath(file);
|
QString path = Settings::instance()->getPath(file);
|
||||||
|
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
std::cerr << "Error: No path found for " << qPrintable(file) << "." << std::endl;
|
std::cerr << "Error: No path found for " << qPrintable(file) << "." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath dirName = path.parentDir();
|
QString dirName = QDir::cleanPath(path + "/..");
|
||||||
QDir dir(dirName.toString());
|
QDir dir(dirName);
|
||||||
if (!dir.exists() && !dir.mkpath(QLatin1String("."))) {
|
if (!dir.exists() && !dir.mkpath(QLatin1String("."))) {
|
||||||
std::cerr << "Error: Could not create directory " << qPrintable(dirName.toString())
|
std::cerr << "Error: Could not create directory " << qPrintable(dirName)
|
||||||
<< "." << std::endl;
|
<< "." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::PersistentSettingsWriter writer(path, QLatin1String("QtCreator")
|
SdkPersistentSettingsWriter writer(path, QLatin1String("QtCreator")
|
||||||
+ file[0].toUpper() + file.mid(1));
|
+ file[0].toUpper() + file.mid(1));
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!writer.save(map, &errorMessage)) {
|
if (!writer.save(map, &errorMessage)) {
|
||||||
std::cerr << "Error: Could not save settings " << qPrintable(path.toString())
|
std::cerr << "Error: Could not save settings " << qPrintable(path)
|
||||||
<< "." << std::endl;
|
<< "." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!path.setPermissions(QFile::ReadOwner | QFile::WriteOwner
|
if (!QFile(path).setPermissions(QFile::ReadOwner | QFile::WriteOwner
|
||||||
| QFile::ReadGroup | QFile::ReadOther)) {
|
| QFile::ReadGroup | QFile::ReadOther)) {
|
||||||
std::cerr << "Error: Could not set permissions for " << qPrintable(path.toString())
|
std::cerr << "Error: Could not set permissions for " << qPrintable(path)
|
||||||
<< "." << std::endl;
|
<< "." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
870
src/tools/sdktool/sdkpersistentsettings.cpp
Normal file
870
src/tools/sdktool/sdkpersistentsettings.cpp
Normal file
@@ -0,0 +1,870 @@
|
|||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "sdkpersistentsettings.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QXmlStreamAttributes>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
# include <windows.h>
|
||||||
|
# include <io.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
|
||||||
|
#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
|
||||||
|
#define QTC_ASSERT_STRING(cond) writeAssertLocation(\
|
||||||
|
"\"" cond"\" in " __FILE__ ":" QTC_ASSERT_STRINGIFY(__LINE__))
|
||||||
|
|
||||||
|
// The 'do {...} while (0)' idiom is not used for the main block here to be
|
||||||
|
// able to use 'break' and 'continue' as 'actions'.
|
||||||
|
|
||||||
|
#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
|
||||||
|
#define QTC_CHECK(cond) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); } do {} while (0)
|
||||||
|
#define QTC_GUARD(cond) ((Q_LIKELY(cond)) ? true : (QTC_ASSERT_STRING(#cond), false))
|
||||||
|
|
||||||
|
void writeAssertLocation(const char *msg)
|
||||||
|
{
|
||||||
|
const QByteArray time = QTime::currentTime().toString(Qt::ISODateWithMs).toLatin1();
|
||||||
|
static bool goBoom = qEnvironmentVariableIsSet("QTC_FATAL_ASSERTS");
|
||||||
|
if (goBoom)
|
||||||
|
qFatal("SOFT ASSERT [%s] made fatal: %s", time.data(), msg);
|
||||||
|
else
|
||||||
|
qDebug("SOFT ASSERT [%s]: %s", time.data(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QFile::Permissions m_umask;
|
||||||
|
|
||||||
|
class SdkSaveFile : public QFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SdkSaveFile(const QString &filePath) : m_finalFilePath(filePath) {}
|
||||||
|
~SdkSaveFile() override;
|
||||||
|
|
||||||
|
bool open(OpenMode flags = QIODevice::WriteOnly) override;
|
||||||
|
|
||||||
|
void rollback();
|
||||||
|
bool commit();
|
||||||
|
|
||||||
|
static void initializeUmask();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString m_finalFilePath;
|
||||||
|
std::unique_ptr<QTemporaryFile> m_tempFile;
|
||||||
|
bool m_finalized = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
SdkSaveFile::~SdkSaveFile()
|
||||||
|
{
|
||||||
|
if (!m_finalized)
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkSaveFile::open(OpenMode flags)
|
||||||
|
{
|
||||||
|
if (m_finalFilePath.isEmpty()) {
|
||||||
|
qWarning("Save file path empty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile ofi(m_finalFilePath);
|
||||||
|
// Check whether the existing file is writable
|
||||||
|
if (ofi.exists() && !ofi.open(QIODevice::ReadWrite)) {
|
||||||
|
setErrorString(ofi.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tempFile = std::make_unique<QTemporaryFile>(m_finalFilePath);
|
||||||
|
m_tempFile->setAutoRemove(false);
|
||||||
|
if (!m_tempFile->open())
|
||||||
|
return false;
|
||||||
|
setFileName(m_tempFile->fileName());
|
||||||
|
|
||||||
|
if (!QFile::open(flags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_finalized = false; // needs clean up in the end
|
||||||
|
if (ofi.exists()) {
|
||||||
|
setPermissions(ofi.permissions()); // Ignore errors
|
||||||
|
} else {
|
||||||
|
Permissions permAll = QFile::ReadOwner
|
||||||
|
| QFile::ReadGroup
|
||||||
|
| QFile::ReadOther
|
||||||
|
| QFile::WriteOwner
|
||||||
|
| QFile::WriteGroup
|
||||||
|
| QFile::WriteOther;
|
||||||
|
|
||||||
|
// set permissions with respect to the current umask
|
||||||
|
setPermissions(permAll & ~m_umask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkSaveFile::rollback()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
if (m_tempFile)
|
||||||
|
m_tempFile->remove();
|
||||||
|
m_finalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString resolveSymlinks(QString current)
|
||||||
|
{
|
||||||
|
int links = 16;
|
||||||
|
while (links--) {
|
||||||
|
const QFileInfo info(current);
|
||||||
|
if (!info.isSymLink())
|
||||||
|
return {};
|
||||||
|
current = info.symLinkTarget();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkSaveFile::commit()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!m_finalized && m_tempFile, return false;);
|
||||||
|
m_finalized = true;
|
||||||
|
|
||||||
|
if (!flush()) {
|
||||||
|
close();
|
||||||
|
m_tempFile->remove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(handle())));
|
||||||
|
#elif _POSIX_SYNCHRONIZED_IO > 0
|
||||||
|
fdatasync(handle());
|
||||||
|
#else
|
||||||
|
fsync(handle());
|
||||||
|
#endif
|
||||||
|
close();
|
||||||
|
m_tempFile->close();
|
||||||
|
if (error() != NoError) {
|
||||||
|
m_tempFile->remove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString finalFileName = resolveSymlinks(m_finalFilePath);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// Release the file lock
|
||||||
|
m_tempFile.reset();
|
||||||
|
bool result = ReplaceFile(finalFileName.toStdWString().data(),
|
||||||
|
fileName().toStdWString().data(),
|
||||||
|
nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr);
|
||||||
|
if (!result) {
|
||||||
|
DWORD replaceErrorCode = GetLastError();
|
||||||
|
QString errorStr;
|
||||||
|
if (!QFile::exists(finalFileName)) {
|
||||||
|
// Replace failed because finalFileName does not exist, try rename.
|
||||||
|
if (!(result = rename(finalFileName)))
|
||||||
|
errorStr = errorString();
|
||||||
|
} else {
|
||||||
|
if (replaceErrorCode == ERROR_UNABLE_TO_REMOVE_REPLACED) {
|
||||||
|
// If we do not get the rights to remove the original final file we still might try
|
||||||
|
// to replace the file contents
|
||||||
|
result = MoveFileEx(fileName().toStdWString().data(),
|
||||||
|
finalFileName.toStdWString().data(),
|
||||||
|
MOVEFILE_COPY_ALLOWED
|
||||||
|
| MOVEFILE_REPLACE_EXISTING
|
||||||
|
| MOVEFILE_WRITE_THROUGH);
|
||||||
|
if (!result)
|
||||||
|
replaceErrorCode = GetLastError();
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
wchar_t messageBuffer[256];
|
||||||
|
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, replaceErrorCode,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
messageBuffer, sizeof(messageBuffer), nullptr);
|
||||||
|
errorStr = QString::fromWCharArray(messageBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
remove();
|
||||||
|
setErrorString(errorStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
const QString backupName = finalFileName + '~';
|
||||||
|
|
||||||
|
// Back up current file.
|
||||||
|
// If it's opened by another application, the lock follows the move.
|
||||||
|
if (QFile::exists(finalFileName)) {
|
||||||
|
// Kill old backup. Might be useful if creator crashed before removing backup.
|
||||||
|
QFile::remove(backupName);
|
||||||
|
QFile finalFile(finalFileName);
|
||||||
|
if (!finalFile.rename(backupName)) {
|
||||||
|
m_tempFile->remove();
|
||||||
|
setErrorString(finalFile.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
if (!m_tempFile->rename(finalFileName)) {
|
||||||
|
// The case when someone else was able to create finalFileName after we've renamed it.
|
||||||
|
// Higher level call may try to save this file again but here we do nothing and
|
||||||
|
// return false while keeping the error string from last rename call.
|
||||||
|
const QString &renameError = m_tempFile->errorString();
|
||||||
|
m_tempFile->remove();
|
||||||
|
setErrorString(renameError);
|
||||||
|
QFile::rename(backupName, finalFileName); // rollback to backup if possible ...
|
||||||
|
return false; // ... or keep the backup copy at least
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile::remove(backupName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkSaveFile::initializeUmask()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
m_umask = QFile::WriteGroup | QFile::WriteOther;
|
||||||
|
#else
|
||||||
|
// Get the current process' file creation mask (umask)
|
||||||
|
// umask() is not thread safe so this has to be done by single threaded
|
||||||
|
// application initialization
|
||||||
|
mode_t mask = umask(0); // get current umask
|
||||||
|
umask(mask); // set it back
|
||||||
|
|
||||||
|
m_umask = ((mask & S_IRUSR) ? QFile::ReadOwner : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IWUSR) ? QFile::WriteOwner : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IXUSR) ? QFile::ExeOwner : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IRGRP) ? QFile::ReadGroup : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IWGRP) ? QFile::WriteGroup : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IXGRP) ? QFile::ExeGroup : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IROTH) ? QFile::ReadOther : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IWOTH) ? QFile::WriteOther : QFlags<QFile::Permission>())
|
||||||
|
| ((mask & S_IXOTH) ? QFile::ExeOther : QFlags<QFile::Permission>());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
class SdkFileSaverBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SdkFileSaverBase() = default;
|
||||||
|
virtual ~SdkFileSaverBase() = default;
|
||||||
|
|
||||||
|
QString filePath() const { return m_filePath; }
|
||||||
|
bool hasError() const { return m_hasError; }
|
||||||
|
QString errorString() const { return m_errorString; }
|
||||||
|
virtual bool finalize();
|
||||||
|
bool finalize(QString *errStr);
|
||||||
|
|
||||||
|
bool write(const char *data, int len);
|
||||||
|
bool write(const QByteArray &bytes);
|
||||||
|
bool setResult(QTextStream *stream);
|
||||||
|
bool setResult(QDataStream *stream);
|
||||||
|
bool setResult(QXmlStreamWriter *stream);
|
||||||
|
bool setResult(bool ok);
|
||||||
|
|
||||||
|
QFile *file() { return m_file.get(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<QFile> m_file;
|
||||||
|
QString m_filePath;
|
||||||
|
QString m_errorString;
|
||||||
|
bool m_hasError = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::finalize()
|
||||||
|
{
|
||||||
|
m_file->close();
|
||||||
|
setResult(m_file->error() == QFile::NoError);
|
||||||
|
m_file.reset();
|
||||||
|
return !m_hasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::finalize(QString *errStr)
|
||||||
|
{
|
||||||
|
if (finalize())
|
||||||
|
return true;
|
||||||
|
if (errStr)
|
||||||
|
*errStr = errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::write(const char *data, int len)
|
||||||
|
{
|
||||||
|
if (m_hasError)
|
||||||
|
return false;
|
||||||
|
return setResult(m_file->write(data, len) == len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::write(const QByteArray &bytes)
|
||||||
|
{
|
||||||
|
if (m_hasError)
|
||||||
|
return false;
|
||||||
|
return setResult(m_file->write(bytes) == bytes.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::setResult(bool ok)
|
||||||
|
{
|
||||||
|
if (!ok && !m_hasError) {
|
||||||
|
if (!m_file->errorString().isEmpty()) {
|
||||||
|
m_errorString = QString("Cannot write file %1: %2")
|
||||||
|
.arg(m_filePath, m_file->errorString());
|
||||||
|
} else {
|
||||||
|
m_errorString = QString("Cannot write file %1. Disk full?")
|
||||||
|
.arg(m_filePath);
|
||||||
|
}
|
||||||
|
m_hasError = true;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::setResult(QTextStream *stream)
|
||||||
|
{
|
||||||
|
stream->flush();
|
||||||
|
return setResult(stream->status() == QTextStream::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::setResult(QDataStream *stream)
|
||||||
|
{
|
||||||
|
return setResult(stream->status() == QDataStream::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaverBase::setResult(QXmlStreamWriter *stream)
|
||||||
|
{
|
||||||
|
return setResult(!stream->hasError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// SdkFileSaver
|
||||||
|
|
||||||
|
class SdkFileSaver : public SdkFileSaverBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// QIODevice::WriteOnly is implicit
|
||||||
|
explicit SdkFileSaver(const QString &filePath, QIODevice::OpenMode mode = QIODevice::NotOpen);
|
||||||
|
|
||||||
|
bool finalize() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_isSafe = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
SdkFileSaver::SdkFileSaver(const QString &filePath, QIODevice::OpenMode mode)
|
||||||
|
{
|
||||||
|
m_filePath = filePath;
|
||||||
|
// Workaround an assert in Qt -- and provide a useful error message, too:
|
||||||
|
#if Q_OS_WIN
|
||||||
|
// Taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||||
|
static const QStringList reservedNames
|
||||||
|
= {"CON", "PRN", "AUX", "NUL",
|
||||||
|
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||||
|
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
|
||||||
|
const QString fn = QFileInfo(filePath).baseName().toUpper();
|
||||||
|
if (reservedNames.contains(fn)) {
|
||||||
|
m_errorString = QString("%1: Is a reserved filename on Windows. Cannot save.").arg(filePath);
|
||||||
|
m_hasError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
|
||||||
|
m_file.reset(new QFile{filePath});
|
||||||
|
m_isSafe = false;
|
||||||
|
} else {
|
||||||
|
m_file.reset(new SdkSaveFile(filePath));
|
||||||
|
m_isSafe = true;
|
||||||
|
}
|
||||||
|
if (!m_file->open(QIODevice::WriteOnly | mode)) {
|
||||||
|
QString err = QFileInfo::exists(filePath) ?
|
||||||
|
QString("Cannot overwrite file %1: %2") : QString("Cannot create file %1: %2");
|
||||||
|
m_errorString = err.arg(filePath, m_file->errorString());
|
||||||
|
m_hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkFileSaver::finalize()
|
||||||
|
{
|
||||||
|
if (!m_isSafe)
|
||||||
|
return SdkFileSaverBase::finalize();
|
||||||
|
|
||||||
|
auto sf = static_cast<SdkSaveFile *>(m_file.get());
|
||||||
|
if (m_hasError) {
|
||||||
|
if (sf->isOpen())
|
||||||
|
sf->rollback();
|
||||||
|
} else {
|
||||||
|
setResult(sf->commit());
|
||||||
|
}
|
||||||
|
m_file.reset();
|
||||||
|
return !m_hasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read and write rectangle in X11 resource syntax "12x12+4+3"
|
||||||
|
static QString rectangleToString(const QRect &r)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
QTextStream str(&result);
|
||||||
|
str << r.width() << 'x' << r.height();
|
||||||
|
if (r.x() >= 0)
|
||||||
|
str << '+';
|
||||||
|
str << r.x();
|
||||||
|
if (r.y() >= 0)
|
||||||
|
str << '+';
|
||||||
|
str << r.y();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QRect stringToRectangle(const QString &v)
|
||||||
|
{
|
||||||
|
static QRegularExpression pattern("^(\\d+)x(\\d+)([-+]\\d+)([-+]\\d+)$");
|
||||||
|
Q_ASSERT(pattern.isValid());
|
||||||
|
const QRegularExpressionMatch match = pattern.match(v);
|
||||||
|
return match.hasMatch() ?
|
||||||
|
QRect(QPoint(match.captured(3).toInt(), match.captured(4).toInt()),
|
||||||
|
QSize(match.captured(1).toInt(), match.captured(2).toInt())) :
|
||||||
|
QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class SdkPersistentSettingsReader
|
||||||
|
|
||||||
|
\note This is aQString based fork of Utils::PersistentSettigsReader
|
||||||
|
|
||||||
|
\brief The SdkPersistentSettingsReader class reads a QVariantMap of arbitrary,
|
||||||
|
nested data structures from an XML file.
|
||||||
|
|
||||||
|
Handles all string-serializable simple types and QVariantList and QVariantMap. Example:
|
||||||
|
\code
|
||||||
|
<qtcreator>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||||
|
<value type="int">0</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
When parsing the structure, a parse stack of ParseValueStackEntry is used for each
|
||||||
|
<data> element. ParseValueStackEntry is a variant/union of:
|
||||||
|
\list
|
||||||
|
\li simple value
|
||||||
|
\li map
|
||||||
|
\li list
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
You can register string-serialize functions for custom types by registering them in the Qt Meta
|
||||||
|
type system. Example:
|
||||||
|
\code
|
||||||
|
QMetaType::registerConverter(&MyCustomType::toString);
|
||||||
|
QMetaType::registerConverter<QString, MyCustomType>(&myCustomTypeFromString);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
When entering a value element ( \c <value> / \c <valuelist> , \c <valuemap> ), entry is pushed
|
||||||
|
accordingly. When leaving the element, the QVariant-value of the entry is taken off the stack
|
||||||
|
and added to the stack entry below (added to list or inserted into map). The first element
|
||||||
|
of the stack is the value of the <data> element.
|
||||||
|
|
||||||
|
\sa SdkPersistentSettingsWriter
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Context // Basic context containing element name string constants.
|
||||||
|
{
|
||||||
|
Context() {}
|
||||||
|
const QString qtCreatorElement = QString("qtcreator");
|
||||||
|
const QString dataElement = QString("data");
|
||||||
|
const QString variableElement = QString("variable");
|
||||||
|
const QString typeAttribute = QString("type");
|
||||||
|
const QString valueElement = QString("value");
|
||||||
|
const QString valueListElement = QString("valuelist");
|
||||||
|
const QString valueMapElement = QString("valuemap");
|
||||||
|
const QString keyAttribute = QString("key");
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParseValueStackEntry
|
||||||
|
{
|
||||||
|
explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = QString()) : type(t), key(k) {}
|
||||||
|
explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k);
|
||||||
|
|
||||||
|
QVariant value() const;
|
||||||
|
void addChild(const QString &key, const QVariant &v);
|
||||||
|
|
||||||
|
QVariant::Type type;
|
||||||
|
QString key;
|
||||||
|
QVariant simpleValue;
|
||||||
|
QVariantList listValue;
|
||||||
|
QVariantMap mapValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k) :
|
||||||
|
type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(simpleValue.isValid(), return);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ParseValueStackEntry::value() const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QVariant::Invalid:
|
||||||
|
return QVariant();
|
||||||
|
case QVariant::Map:
|
||||||
|
return QVariant(mapValue);
|
||||||
|
case QVariant::List:
|
||||||
|
return QVariant(listValue);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return simpleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseValueStackEntry::addChild(const QString &key, const QVariant &v)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QVariant::Map:
|
||||||
|
mapValue.insert(key, v);
|
||||||
|
break;
|
||||||
|
case QVariant::List:
|
||||||
|
listValue.push_back(v);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qWarning() << "ParseValueStackEntry::Internal error adding " << key << v << " to "
|
||||||
|
<< QVariant::typeToName(type) << value();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParseContext : public Context
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QVariantMap parse(const QString &file);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Element { QtCreatorElement, DataElement, VariableElement,
|
||||||
|
SimpleValueElement, ListValueElement, MapValueElement, UnknownElement };
|
||||||
|
|
||||||
|
Element element(const QStringView &r) const;
|
||||||
|
static inline bool isValueElement(Element e)
|
||||||
|
{ return e == SimpleValueElement || e == ListValueElement || e == MapValueElement; }
|
||||||
|
QVariant readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const;
|
||||||
|
|
||||||
|
bool handleStartElement(QXmlStreamReader &r);
|
||||||
|
bool handleEndElement(const QStringView &name);
|
||||||
|
|
||||||
|
static QString formatWarning(const QXmlStreamReader &r, const QString &message);
|
||||||
|
|
||||||
|
QStack<ParseValueStackEntry> m_valueStack;
|
||||||
|
QVariantMap m_result;
|
||||||
|
QString m_currentVariableName;
|
||||||
|
};
|
||||||
|
|
||||||
|
static QByteArray fileContents(const QString &path)
|
||||||
|
{
|
||||||
|
QFile f(path);
|
||||||
|
if (!f.exists())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!f.open(QFile::ReadOnly))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return f.readAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap ParseContext::parse(const QString &file)
|
||||||
|
{
|
||||||
|
QXmlStreamReader r(fileContents(file));
|
||||||
|
|
||||||
|
m_result.clear();
|
||||||
|
m_currentVariableName.clear();
|
||||||
|
|
||||||
|
while (!r.atEnd()) {
|
||||||
|
switch (r.readNext()) {
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
if (handleStartElement(r))
|
||||||
|
return m_result;
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
if (handleEndElement(r.name()))
|
||||||
|
return m_result;
|
||||||
|
break;
|
||||||
|
case QXmlStreamReader::Invalid:
|
||||||
|
qWarning("Error reading %s:%d: %s", qPrintable(file),
|
||||||
|
int(r.lineNumber()), qPrintable(r.errorString()));
|
||||||
|
return QVariantMap();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} // switch token
|
||||||
|
} // while (!r.atEnd())
|
||||||
|
return m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseContext::handleStartElement(QXmlStreamReader &r)
|
||||||
|
{
|
||||||
|
const QStringView name = r.name();
|
||||||
|
const Element e = element(name);
|
||||||
|
if (e == VariableElement) {
|
||||||
|
m_currentVariableName = r.readElementText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ParseContext::isValueElement(e))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QXmlStreamAttributes attributes = r.attributes();
|
||||||
|
const QString key = attributes.hasAttribute(keyAttribute) ?
|
||||||
|
attributes.value(keyAttribute).toString() : QString();
|
||||||
|
switch (e) {
|
||||||
|
case SimpleValueElement: {
|
||||||
|
// This reads away the end element, so, handle end element right here.
|
||||||
|
const QVariant v = readSimpleValue(r, attributes);
|
||||||
|
if (!v.isValid()) {
|
||||||
|
qWarning() << ParseContext::formatWarning(r, QString::fromLatin1("Failed to read element \"%1\".").arg(name.toString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_valueStack.push_back(ParseValueStackEntry(v, key));
|
||||||
|
return handleEndElement(name);
|
||||||
|
}
|
||||||
|
case ListValueElement:
|
||||||
|
m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key));
|
||||||
|
break;
|
||||||
|
case MapValueElement:
|
||||||
|
m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseContext::handleEndElement(const QStringView &name)
|
||||||
|
{
|
||||||
|
const Element e = element(name);
|
||||||
|
if (ParseContext::isValueElement(e)) {
|
||||||
|
QTC_ASSERT(!m_valueStack.isEmpty(), return true);
|
||||||
|
const ParseValueStackEntry top = m_valueStack.pop();
|
||||||
|
if (m_valueStack.isEmpty()) { // Last element? -> Done with that variable.
|
||||||
|
QTC_ASSERT(!m_currentVariableName.isEmpty(), return true);
|
||||||
|
m_result.insert(m_currentVariableName, top.value());
|
||||||
|
m_currentVariableName.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_valueStack.top().addChild(top.key, top.value());
|
||||||
|
}
|
||||||
|
return e == QtCreatorElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ParseContext::formatWarning(const QXmlStreamReader &r, const QString &message)
|
||||||
|
{
|
||||||
|
QString result = QLatin1String("Warning reading ");
|
||||||
|
if (const QIODevice *device = r.device())
|
||||||
|
if (const auto file = qobject_cast<const QFile *>(device))
|
||||||
|
result += QDir::toNativeSeparators(file->fileName()) + QLatin1Char(':');
|
||||||
|
result += QString::number(r.lineNumber());
|
||||||
|
result += QLatin1String(": ");
|
||||||
|
result += message;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseContext::Element ParseContext::element(const QStringView &r) const
|
||||||
|
{
|
||||||
|
if (r == valueElement)
|
||||||
|
return SimpleValueElement;
|
||||||
|
if (r == valueListElement)
|
||||||
|
return ListValueElement;
|
||||||
|
if (r == valueMapElement)
|
||||||
|
return MapValueElement;
|
||||||
|
if (r == qtCreatorElement)
|
||||||
|
return QtCreatorElement;
|
||||||
|
if (r == dataElement)
|
||||||
|
return DataElement;
|
||||||
|
if (r == variableElement)
|
||||||
|
return VariableElement;
|
||||||
|
return UnknownElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ParseContext::readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const
|
||||||
|
{
|
||||||
|
// Simple value
|
||||||
|
const QStringView type = attributes.value(typeAttribute);
|
||||||
|
const QString text = r.readElementText();
|
||||||
|
if (type == QLatin1String("QChar")) { // Workaround: QTBUG-12345
|
||||||
|
QTC_ASSERT(text.size() == 1, return QVariant());
|
||||||
|
return QVariant(QChar(text.at(0)));
|
||||||
|
}
|
||||||
|
if (type == QLatin1String("QRect")) {
|
||||||
|
const QRect rectangle = stringToRectangle(text);
|
||||||
|
return rectangle.isValid() ? QVariant(rectangle) : QVariant();
|
||||||
|
}
|
||||||
|
QVariant value;
|
||||||
|
value.setValue(text);
|
||||||
|
value.convert(QMetaType::type(type.toLatin1().constData()));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================== SdkPersistentSettingsReader
|
||||||
|
|
||||||
|
SdkPersistentSettingsReader::SdkPersistentSettingsReader() = default;
|
||||||
|
|
||||||
|
QVariant SdkPersistentSettingsReader::restoreValue(const QString &variable, const QVariant &defaultValue) const
|
||||||
|
{
|
||||||
|
if (m_valueMap.contains(variable))
|
||||||
|
return m_valueMap.value(variable);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap SdkPersistentSettingsReader::restoreValues() const
|
||||||
|
{
|
||||||
|
return m_valueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkPersistentSettingsReader::load(const QString &fileName)
|
||||||
|
{
|
||||||
|
m_valueMap.clear();
|
||||||
|
|
||||||
|
if (QFileInfo(fileName).size() == 0) // skip empty files
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ParseContext ctx;
|
||||||
|
m_valueMap = ctx.parse(fileName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class SdkPersistentSettingsWriter
|
||||||
|
|
||||||
|
\note This is a fork of Utils::PersistentSettingsWriter
|
||||||
|
|
||||||
|
\brief The SdkPersistentSettingsWriter class serializes a QVariantMap of
|
||||||
|
arbitrary, nested data structures to an XML file.
|
||||||
|
\sa SdkPersistentSettingsReader
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void writeVariantValue(QXmlStreamWriter &w, const Context &ctx,
|
||||||
|
const QVariant &variant, const QString &key = QString())
|
||||||
|
{
|
||||||
|
switch (static_cast<int>(variant.type())) {
|
||||||
|
case static_cast<int>(QVariant::StringList):
|
||||||
|
case static_cast<int>(QVariant::List): {
|
||||||
|
w.writeStartElement(ctx.valueListElement);
|
||||||
|
w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::List)));
|
||||||
|
if (!key.isEmpty())
|
||||||
|
w.writeAttribute(ctx.keyAttribute, key);
|
||||||
|
const QList<QVariant> list = variant.toList();
|
||||||
|
for (const QVariant &var : list)
|
||||||
|
writeVariantValue(w, ctx, var);
|
||||||
|
w.writeEndElement();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case static_cast<int>(QVariant::Map): {
|
||||||
|
w.writeStartElement(ctx.valueMapElement);
|
||||||
|
w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::Map)));
|
||||||
|
if (!key.isEmpty())
|
||||||
|
w.writeAttribute(ctx.keyAttribute, key);
|
||||||
|
const QVariantMap varMap = variant.toMap();
|
||||||
|
const QVariantMap::const_iterator cend = varMap.constEnd();
|
||||||
|
for (QVariantMap::const_iterator i = varMap.constBegin(); i != cend; ++i)
|
||||||
|
writeVariantValue(w, ctx, i.value(), i.key());
|
||||||
|
w.writeEndElement();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case static_cast<int>(QMetaType::QObjectStar): // ignore QObjects!
|
||||||
|
case static_cast<int>(QMetaType::VoidStar): // ignore void pointers!
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
w.writeStartElement(ctx.valueElement);
|
||||||
|
w.writeAttribute(ctx.typeAttribute, QLatin1String(variant.typeName()));
|
||||||
|
if (!key.isEmpty())
|
||||||
|
w.writeAttribute(ctx.keyAttribute, key);
|
||||||
|
switch (variant.type()) {
|
||||||
|
case QVariant::Rect:
|
||||||
|
w.writeCharacters(rectangleToString(variant.toRect()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
w.writeCharacters(variant.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
w.writeEndElement();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkPersistentSettingsWriter::SdkPersistentSettingsWriter(const QString &fileName, const QString &docType) :
|
||||||
|
m_fileName(fileName), m_docType(docType)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool SdkPersistentSettingsWriter::save(const QVariantMap &data, QString *errorString) const
|
||||||
|
{
|
||||||
|
if (data == m_savedData)
|
||||||
|
return true;
|
||||||
|
return write(data, errorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SdkPersistentSettingsWriter::fileName() const
|
||||||
|
{ return m_fileName; }
|
||||||
|
|
||||||
|
//** * @brief Set contents of file (e.g. from data read from it). */
|
||||||
|
void SdkPersistentSettingsWriter::setContents(const QVariantMap &data)
|
||||||
|
{
|
||||||
|
m_savedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkPersistentSettingsWriter::write(const QVariantMap &data, QString *errorString) const
|
||||||
|
{
|
||||||
|
const QString parentDir = QDir::cleanPath(m_fileName + "/..");
|
||||||
|
|
||||||
|
const QFileInfo fi(parentDir);
|
||||||
|
if (!(fi.exists() && fi.isDir() && fi.isWritable())) {
|
||||||
|
bool res = QDir().mkpath(parentDir);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkFileSaver saver(m_fileName, QIODevice::Text);
|
||||||
|
if (!saver.hasError()) {
|
||||||
|
const Context ctx;
|
||||||
|
QXmlStreamWriter w(saver.file());
|
||||||
|
w.setAutoFormatting(true);
|
||||||
|
w.setAutoFormattingIndent(1); // Historical, used to be QDom.
|
||||||
|
w.writeStartDocument();
|
||||||
|
w.writeDTD(QLatin1String("<!DOCTYPE ") + m_docType + QLatin1Char('>'));
|
||||||
|
w.writeComment(QString::fromLatin1(" Written by %1 %2, %3. ").
|
||||||
|
arg(QCoreApplication::applicationName(),
|
||||||
|
QCoreApplication::applicationVersion(),
|
||||||
|
QDateTime::currentDateTime().toString(Qt::ISODate)));
|
||||||
|
w.writeStartElement(ctx.qtCreatorElement);
|
||||||
|
const QVariantMap::const_iterator cend = data.constEnd();
|
||||||
|
for (QVariantMap::const_iterator it = data.constBegin(); it != cend; ++it) {
|
||||||
|
w.writeStartElement(ctx.dataElement);
|
||||||
|
w.writeTextElement(ctx.variableElement, it.key());
|
||||||
|
writeVariantValue(w, ctx, it.value());
|
||||||
|
w.writeEndElement();
|
||||||
|
}
|
||||||
|
w.writeEndDocument();
|
||||||
|
|
||||||
|
saver.setResult(&w);
|
||||||
|
}
|
||||||
|
bool ok = saver.finalize();
|
||||||
|
if (ok) {
|
||||||
|
m_savedData = data;
|
||||||
|
} else if (errorString) {
|
||||||
|
m_savedData.clear();
|
||||||
|
*errorString = saver.errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
37
src/tools/sdktool/sdkpersistentsettings.h
Normal file
37
src/tools/sdktool/sdkpersistentsettings.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class SdkPersistentSettingsReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SdkPersistentSettingsReader();
|
||||||
|
QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const;
|
||||||
|
QVariantMap restoreValues() const;
|
||||||
|
bool load(const QString &fileName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, QVariant> m_valueMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SdkPersistentSettingsWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SdkPersistentSettingsWriter(const QString &fileName, const QString &docType);
|
||||||
|
|
||||||
|
bool save(const QVariantMap &data, QString *errorString) const;
|
||||||
|
|
||||||
|
QString fileName() const;
|
||||||
|
|
||||||
|
void setContents(const QVariantMap &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool write(const QVariantMap &data, QString *errorString) const;
|
||||||
|
|
||||||
|
const QString m_fileName;
|
||||||
|
const QString m_docType;
|
||||||
|
mutable QMap<QString, QVariant> m_savedData;
|
||||||
|
};
|
@@ -85,34 +85,7 @@ QtcLibrary {
|
|||||||
"rmtoolchainoperation.h",
|
"rmtoolchainoperation.h",
|
||||||
"settings.cpp",
|
"settings.cpp",
|
||||||
"settings.h",
|
"settings.h",
|
||||||
]
|
"sdkpersistentsettings.cpp",
|
||||||
|
"sdkpersistentsettings.h",
|
||||||
Group {
|
|
||||||
name: "Utils"
|
|
||||||
prefix: libsDir + "/utils/"
|
|
||||||
files: [
|
|
||||||
"commandline.cpp", "commandline.h",
|
|
||||||
"devicefileaccess.cpp", "devicefileaccess.h",
|
|
||||||
"environment.cpp", "environment.h",
|
|
||||||
"filepath.cpp", "filepath.h",
|
|
||||||
"fileutils.cpp", "fileutils.h",
|
|
||||||
"hostosinfo.cpp", "hostosinfo.h",
|
|
||||||
"macroexpander.cpp", "macroexpander.h",
|
|
||||||
"namevaluedictionary.cpp", "namevaluedictionary.h",
|
|
||||||
"namevalueitem.cpp", "namevalueitem.h",
|
|
||||||
"persistentsettings.cpp", "persistentsettings.h",
|
|
||||||
"qtcassert.cpp", "qtcassert.h",
|
|
||||||
"savefile.cpp", "savefile.h",
|
|
||||||
"stringutils.cpp"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Group {
|
|
||||||
name: "Utils/macOS"
|
|
||||||
condition: qbs.targetOS.contains("macos")
|
|
||||||
prefix: libsDir + "/utils/"
|
|
||||||
files: [
|
|
||||||
"fileutils_mac.h",
|
|
||||||
"fileutils_mac.mm",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -2,11 +2,11 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "operation.h"
|
|
||||||
|
|
||||||
#include <app/app_version.h>
|
#include <app/app_version.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
static Settings *m_instance = nullptr;
|
static Settings *m_instance = nullptr;
|
||||||
|
|
||||||
@@ -21,28 +21,28 @@ Settings::Settings()
|
|||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
|
||||||
// autodetect sdk dir:
|
// autodetect sdk dir:
|
||||||
sdkPath = Utils::FilePath::fromUserInput(QCoreApplication::applicationDirPath())
|
sdkPath = QDir::cleanPath(QCoreApplication::applicationDirPath()
|
||||||
.pathAppended(DATA_PATH).cleanPath()
|
+ '/' + DATA_PATH
|
||||||
.pathAppended(Core::Constants::IDE_SETTINGSVARIANT_STR)
|
+ '/' + Core::Constants::IDE_SETTINGSVARIANT_STR
|
||||||
.pathAppended(Core::Constants::IDE_ID);
|
+ '/' + Core::Constants::IDE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath Settings::getPath(const QString &file)
|
QString Settings::getPath(const QString &file)
|
||||||
{
|
{
|
||||||
Utils::FilePath result = sdkPath;
|
QString result = sdkPath;
|
||||||
const QString lowerFile = file.toLower();
|
const QString lowerFile = file.toLower();
|
||||||
const QStringList identical = {
|
const QStringList identical = {
|
||||||
"android", "cmaketools", "debuggers", "devices", "profiles", "qtversions", "toolchains", "abi"
|
"android", "cmaketools", "debuggers", "devices", "profiles", "qtversions", "toolchains", "abi"
|
||||||
};
|
};
|
||||||
if (lowerFile == "cmake")
|
if (lowerFile == "cmake")
|
||||||
result = result.pathAppended("cmaketools");
|
result += "/cmaketools";
|
||||||
else if (lowerFile == "kits")
|
else if (lowerFile == "kits")
|
||||||
result = result.pathAppended("profiles");
|
result += "/profiles";
|
||||||
else if (lowerFile == "qtversions")
|
else if (lowerFile == "qtversions")
|
||||||
result = result.pathAppended("qtversion");
|
result += "/qtversion";
|
||||||
else if (identical.contains(lowerFile))
|
else if (identical.contains(lowerFile))
|
||||||
result = result.pathAppended(lowerFile);
|
result += '/' + lowerFile;
|
||||||
else
|
else
|
||||||
result = result.pathAppended(file); // handle arbitrary file names not known yet
|
result += '/' + file; // handle arbitrary file names not known yet
|
||||||
return result.stringAppended(".xml");
|
return result += ".xml";
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
#include <QString>
|
||||||
|
|
||||||
class Operation;
|
class Operation;
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ public:
|
|||||||
Settings();
|
Settings();
|
||||||
static Settings *instance();
|
static Settings *instance();
|
||||||
|
|
||||||
Utils::FilePath getPath(const QString &file);
|
QString getPath(const QString &file);
|
||||||
|
|
||||||
Utils::FilePath sdkPath;
|
QString sdkPath;
|
||||||
Operation *operation = nullptr;
|
Operation *operation = nullptr;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user