aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/qbs.qdoc4
-rw-r--r--doc/reference/cli/cli-options.qdocinc37
-rw-r--r--doc/reference/cli/tools/cli-config-ui.qdoc3
-rw-r--r--doc/reference/cli/tools/cli-config.qdoc24
-rw-r--r--doc/reference/cli/tools/cli-setup-qt.qdoc5
-rw-r--r--doc/reference/cli/tools/cli-setup-toolchains.qdoc5
-rw-r--r--qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs1
-rw-r--r--src/app/config-ui/commandlineparser.cpp9
-rw-r--r--src/app/config-ui/commandlineparser.h4
-rw-r--r--src/app/config-ui/main.cpp2
-rw-r--r--src/app/config-ui/mainwindow.cpp4
-rw-r--r--src/app/config-ui/mainwindow.h5
-rw-r--r--src/app/config/configcommandexecutor.cpp20
-rw-r--r--src/app/config/configcommandexecutor.h7
-rw-r--r--src/app/config/configcommandlineparser.cpp33
-rw-r--r--src/app/config/configcommandlineparser.h14
-rw-r--r--src/app/config/configmain.cpp7
-rw-r--r--src/app/qbs-setup-qt/commandlineparser.cpp11
-rw-r--r--src/app/qbs-setup-qt/commandlineparser.h4
-rw-r--r--src/app/qbs-setup-qt/main.cpp1
-rw-r--r--src/app/qbs-setup-toolchains/commandlineparser.cpp11
-rw-r--r--src/app/qbs-setup-toolchains/commandlineparser.h4
-rw-r--r--src/app/qbs-setup-toolchains/main.cpp1
-rw-r--r--src/lib/corelib/corelib.qbs3
-rw-r--r--src/lib/corelib/tools/preferences.cpp28
-rw-r--r--src/lib/corelib/tools/preferences.h4
-rw-r--r--src/lib/corelib/tools/profile.cpp6
-rw-r--r--src/lib/corelib/tools/settings.cpp149
-rw-r--r--src/lib/corelib/tools/settings.h24
-rw-r--r--src/lib/corelib/tools/settingsmodel.cpp13
-rw-r--r--src/lib/corelib/tools/settingsmodel.h3
-rw-r--r--src/lib/corelib/tools/tools.pri5
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.cpp3
-rw-r--r--tests/auto/blackbox/blackbox.pro1
-rw-r--r--tests/auto/blackbox/blackbox.qbs1
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp89
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
-rw-r--r--tests/auto/tools/tst_tools.cpp4
38 files changed, 460 insertions, 90 deletions
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc
index b37ef4b95..bfeda503a 100644
--- a/doc/qbs.qdoc
+++ b/doc/qbs.qdoc
@@ -509,6 +509,10 @@
\row \li qbs_use_bundled_qtscript \li Use the bundled QtScript library.
\endtable
+ In addition, you can set the \c QBS_SYSTEM_SETTINGS_DIR environment variable
+ before running qmake to specify a custom location for \QBS to look for its
+ system-level settings.
+
\section1 Using Docker
A set of Docker images for developing \QBS (which are maintained by the \QBS team) is available
diff --git a/doc/reference/cli/cli-options.qdocinc b/doc/reference/cli/cli-options.qdocinc
index 5ce7aab1c..189a3526a 100644
--- a/doc/reference/cli/cli-options.qdocinc
+++ b/doc/reference/cli/cli-options.qdocinc
@@ -287,6 +287,33 @@
//! [list-root]
+//! [config-user]
+
+ \section2 \c {--user}
+
+ Causes read operations to display only the user-level settings,
+ while the system-level settings are ignored.
+ Write operations will target the user-level settings, which is also the default.
+
+//! [config-user]
+
+//! [config-system]
+
+ \section2 \c {--system}
+
+ Read and write operations will consider only the system-level settings.
+
+//! [config-system]
+
+//! [config-ui-system]
+
+ \section2 \c {--system}
+
+ Instructs the tool to work on the system-level settings. Otherwise,
+ the user-level settings are presented.
+
+//! [config-ui-system]
+
//! [log-level]
\section2 \c {--log-level <level>}
@@ -418,6 +445,16 @@
//! [show-progress]
+//! [setup-tools-system]
+
+ \section2 \c {--system}
+
+ If this option is given, the profile(s) created by this tool will end up
+ in the system-level settings and thus be available to all users.
+ Otherwise, they go into the user-level settings.
+
+//! [setup-tools-system]
+
//! [type]
\section2 \c {--type <toolchain type>}
diff --git a/doc/reference/cli/tools/cli-config-ui.qdoc b/doc/reference/cli/tools/cli-config-ui.qdoc
index 29c0ed732..66f46e3e1 100644
--- a/doc/reference/cli/tools/cli-config-ui.qdoc
+++ b/doc/reference/cli/tools/cli-config-ui.qdoc
@@ -37,7 +37,7 @@
\section1 Synopsis
\code
- qbs config-ui [--settings-dir <directory>]
+ qbs config-ui [--settings-dir <directory>] [--system]
\endcode
\section1 Description
@@ -54,6 +54,7 @@
\section1 Options
\include cli-options.qdocinc settings-dir
+ \include cli-options.qdocinc config-ui-system
\include cli-options.qdocinc help
\section1 Examples
diff --git a/doc/reference/cli/tools/cli-config.qdoc b/doc/reference/cli/tools/cli-config.qdoc
index 0d2ba9989..e32ffee0e 100644
--- a/doc/reference/cli/tools/cli-config.qdoc
+++ b/doc/reference/cli/tools/cli-config.qdoc
@@ -36,10 +36,10 @@
\section1 Synopsis
\code
- qbs config [--settings-dir <directory>] [--list [<root> ...]]
+ qbs config [--settings-dir <directory>] [--user|--system] [--list [<root> ...]]
[--unset <key>] [--export <file>] [--import <file>]
- qbs config [--settings-dir <directory>] <key>
- qbs config [--settings-dir <directory>] <key> <value>
+ qbs config [--settings-dir <directory>] [--user|--system] <key>
+ qbs config [--settings-dir <directory>] [--user|--system] <key> <value>
\endcode
\section1 Description
@@ -52,6 +52,22 @@
The third form sets the specified \c <key> with the specified \c <value>.
+ There are two sets of settings: The system-level settings affect all users,
+ while the user-level settings are specific to the current user.
+ By default, the read operations consider both sources. If the same key is
+ present in both settings, then for list values, the system value is
+ appended to the user value, while for other types of values the user-level
+ one takes precedence. Write operations go to the user-level settings by default.
+ Use the \c {--user} and \c {--system} options to change this behavior.
+ \note It is conceivable that the default system value of the
+ \c {preferences.qbsSearchPaths} setting could pull in unwanted \QBS modules,
+ in particular when doing cross-builds. In such a case, you can set
+ \c {preferences.ignoreSystemSearchPaths} to exclude the search paths coming
+ from the system settings. You'll typically do this for a specific profile:
+ \code
+ $ qbs config profiles.myprofile.preferences.ignoreSystemSearchPaths true
+ \endcode
+
You can use the \l{config-ui} command to open the Qbs Settings tool for
managing settings in a hierarchical view.
@@ -65,6 +81,8 @@
\include cli-options.qdocinc unset
\include cli-options.qdocinc export
\include cli-options.qdocinc import
+ \include cli-options.qdocinc config-user
+ \include cli-options.qdocinc config-system
\include cli-options.qdocinc help
\section1 Parameters
diff --git a/doc/reference/cli/tools/cli-setup-qt.qdoc b/doc/reference/cli/tools/cli-setup-qt.qdoc
index 9673286b3..1cf961d0d 100644
--- a/doc/reference/cli/tools/cli-setup-qt.qdoc
+++ b/doc/reference/cli/tools/cli-setup-qt.qdoc
@@ -36,8 +36,8 @@
\section1 Synopsis
\code
- qbs setup-qt [--settings-dir <directory>] --detect
- qbs setup-qt [--settings-dir <directory>] <path to qmake> <profile name>
+ qbs setup-qt [--settings-dir <directory>] [--system] --detect
+ qbs setup-qt [--settings-dir <directory>] [--system] <path to qmake> <profile name>
\endcode
\section1 Description
@@ -56,6 +56,7 @@
\include cli-options.qdocinc detect-qt-versions
\include cli-options.qdocinc settings-dir
+ \include cli-options.qdocinc setup-tools-system
\include cli-options.qdocinc help
\section1 Examples
diff --git a/doc/reference/cli/tools/cli-setup-toolchains.qdoc b/doc/reference/cli/tools/cli-setup-toolchains.qdoc
index 15490cc2a..af3b25069 100644
--- a/doc/reference/cli/tools/cli-setup-toolchains.qdoc
+++ b/doc/reference/cli/tools/cli-setup-toolchains.qdoc
@@ -36,8 +36,8 @@
\section1 Synopsis
\code
- qbs setup-toolchains [--settings-dir <directory>] --detect
- qbs setup-toolchains [--settings-dir <directory>] [--type <toolchain type>]
+ qbs setup-toolchains [--settings-dir <directory>] [--system] --detect
+ qbs setup-toolchains [--settings-dir <directory>] [--system] [--type <toolchain type>]
<compiler path> <profile name>
\endcode
@@ -57,6 +57,7 @@
\section1 Options
\include cli-options.qdocinc settings-dir
+ \include cli-options.qdocinc setup-tools-system
\include cli-options.qdocinc detect-toolchains
\include cli-options.qdocinc type
\include cli-options.qdocinc help
diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
index 7f978d47b..50a5ea79e 100644
--- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
+++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
@@ -15,6 +15,7 @@ Module {
property string importLibInstallDir: libDirName
property string libexecInstallDir: qbs.targetOS.contains("windows") ? appInstallDir
: "libexec/qbs"
+ property string systemSettingsDir
property bool installManPage: qbs.targetOS.contains("unix")
property bool installHtml: true
property bool installQch: false
diff --git a/src/app/config-ui/commandlineparser.cpp b/src/app/config-ui/commandlineparser.cpp
index b05d84343..d38c34e55 100644
--- a/src/app/config-ui/commandlineparser.cpp
+++ b/src/app/config-ui/commandlineparser.cpp
@@ -48,6 +48,7 @@ using qbs::Internal::Tr;
static QString helpOptionShort() { return QLatin1String("-h"); }
static QString helpOptionLong() { return QLatin1String("--help"); }
static QString settingsDirOption() { return QLatin1String("--settings-dir"); }
+static QString systemOption() { return QLatin1String("--system"); }
void CommandLineParser::parse(const QStringList &commandLine)
{
@@ -63,6 +64,9 @@ void CommandLineParser::parse(const QStringList &commandLine)
if (arg == helpOptionShort() || arg == helpOptionLong()) {
m_commandLine.removeFirst();
m_helpRequested = true;
+ } else if (arg == systemOption()) {
+ m_commandLine.removeFirst();
+ m_settingsScope = qbs::Settings::SystemScope;
} else if (arg == settingsDirOption()) {
m_commandLine.removeFirst();
assignOptionArgument(settingsDirOption(), m_settingsDir);
@@ -85,8 +89,9 @@ QString CommandLineParser::usageString() const
"If you have more than a few settings, this might be preferable to "
"plain \"qbs config\", as it presents a hierarchical view.\n");
s += Tr::tr("Usage:\n");
- s += Tr::tr(" %1 [%2 <settings directory>] [%3|%4]\n")
- .arg(m_command, settingsDirOption(), helpOptionShort(), helpOptionLong());
+ s += Tr::tr(" %1 [%2 <settings directory>] [%5] [%3|%4]\n")
+ .arg(m_command, settingsDirOption(), helpOptionShort(), helpOptionLong(),
+ systemOption());
return s;
}
diff --git a/src/app/config-ui/commandlineparser.h b/src/app/config-ui/commandlineparser.h
index 1cb6bf2f7..dc43d33b3 100644
--- a/src/app/config-ui/commandlineparser.h
+++ b/src/app/config-ui/commandlineparser.h
@@ -39,6 +39,8 @@
#ifndef QBS_CONFIGUI_COMMANDLINEPARSER_H
#define QBS_CONFIGUI_COMMANDLINEPARSER_H
+#include <tools/settings.h>
+
#include <QtCore/qstringlist.h>
class CommandLineParser
@@ -48,6 +50,7 @@ public:
bool helpRequested() const { return m_helpRequested; }
QString settingsDir() const { return m_settingsDir; }
+ qbs::Settings::Scope settingsScope() const { return m_settingsScope; }
QString usageString() const;
@@ -57,6 +60,7 @@ private:
[[noreturn]] void complainAboutExtraArguments();
bool m_helpRequested;
+ qbs::Settings::Scope m_settingsScope = qbs::Settings::UserScope;
QString m_settingsDir;
QStringList m_commandLine;
QString m_command;
diff --git a/src/app/config-ui/main.cpp b/src/app/config-ui/main.cpp
index ea4c25760..a7c2662e6 100644
--- a/src/app/config-ui/main.cpp
+++ b/src/app/config-ui/main.cpp
@@ -65,7 +65,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
- MainWindow mw(clParser.settingsDir());
+ MainWindow mw(clParser.settingsDir(), clParser.settingsScope());
mw.show();
return app.exec();
}
diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp
index dda1ed523..13708366a 100644
--- a/src/app/config-ui/mainwindow.cpp
+++ b/src/app/config-ui/mainwindow.cpp
@@ -53,11 +53,11 @@
#include <QtWidgets/qmenubar.h>
#include <QtWidgets/qmessagebox.h>
-MainWindow::MainWindow(const QString &settingsDir, QWidget *parent)
+MainWindow::MainWindow(const QString &settingsDir, qbs::Settings::Scope scope, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
- m_model = new qbs::SettingsModel(settingsDir, this);
+ m_model = new qbs::SettingsModel(settingsDir, scope, this);
ui->treeView->setModel(m_model);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->treeView, &QTreeView::expanded, this, &MainWindow::adjustColumns);
diff --git a/src/app/config-ui/mainwindow.h b/src/app/config-ui/mainwindow.h
index 908e447ea..567a183f1 100644
--- a/src/app/config-ui/mainwindow.h
+++ b/src/app/config-ui/mainwindow.h
@@ -39,6 +39,8 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include <tools/settings.h>
+
#include <QtWidgets/qmainwindow.h>
namespace qbs { class SettingsModel; }
@@ -53,7 +55,8 @@ class MainWindow : public QMainWindow
Q_OBJECT
public:
- explicit MainWindow(const QString &settingsDir, QWidget *parent = nullptr);
+ explicit MainWindow(const QString &settingsDir, qbs::Settings::Scope scope,
+ QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event);
diff --git a/src/app/config/configcommandexecutor.cpp b/src/app/config/configcommandexecutor.cpp
index 365997043..1290ba2f0 100644
--- a/src/app/config/configcommandexecutor.cpp
+++ b/src/app/config/configcommandexecutor.cpp
@@ -52,8 +52,11 @@
using namespace qbs;
-ConfigCommandExecutor::ConfigCommandExecutor(Settings *settings) : m_settings(settings)
+ConfigCommandExecutor::ConfigCommandExecutor(Settings *settings, Settings::Scopes scope)
+ : m_settings(settings), m_scope(scope)
{
+ if (m_scope == qbs::Settings::SystemScope)
+ m_settings->setScopeForWriting(qbs::Settings::SystemScope);
}
void ConfigCommandExecutor::execute(const ConfigCommand &command)
@@ -94,15 +97,15 @@ void ConfigCommandExecutor::setValue(const QString &key, const QString &rawInput
void ConfigCommandExecutor::printSettings(const ConfigCommand &command)
{
if (command.varNames.empty()) {
- const auto keys = m_settings->allKeys();
+ const auto keys = m_settings->allKeys(m_scope);
for (const QString &key : keys)
printOneSetting(key);
} else {
for (const QString &parentKey : command.varNames) {
- if (m_settings->value(parentKey).isValid()) { // Key is a leaf.
+ if (m_settings->value(parentKey, m_scope).isValid()) { // Key is a leaf.
printOneSetting(parentKey);
} else { // Key is a node.
- const auto keys = m_settings->allKeysWithPrefix(parentKey);
+ const auto keys = m_settings->allKeysWithPrefix(parentKey, m_scope);
for (const QString &key : keys)
printOneSetting(parentKey + QLatin1Char('.') + key);
}
@@ -113,7 +116,7 @@ void ConfigCommandExecutor::printSettings(const ConfigCommand &command)
void ConfigCommandExecutor::printOneSetting(const QString &key)
{
printf("%s: %s\n", qPrintable(key),
- qPrintable(qbs::settingsValueToRepresentation(m_settings->value(key))));
+ qPrintable(qbs::settingsValueToRepresentation(m_settings->value(key, m_scope))));
}
void ConfigCommandExecutor::exportSettings(const QString &filename)
@@ -125,9 +128,10 @@ void ConfigCommandExecutor::exportSettings(const QString &filename)
}
QTextStream stream(&file);
stream.setCodec("UTF-8");
- const auto keys = m_settings->allKeys();
+ const auto keys = m_settings->allKeys(m_scope);
for (const QString &key : keys)
- stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key)) << endl;
+ stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope))
+ << endl;
}
void ConfigCommandExecutor::importSettings(const QString &filename)
@@ -138,7 +142,7 @@ void ConfigCommandExecutor::importSettings(const QString &filename)
.arg(QDir::toNativeSeparators(filename), file.errorString()));
}
// Remove all current settings
- const auto keys = m_settings->allKeys();
+ const auto keys = m_settings->allKeys(m_scope);
for (const QString &key : keys)
m_settings->remove(key);
diff --git a/src/app/config/configcommandexecutor.h b/src/app/config/configcommandexecutor.h
index c0383a436..463818a73 100644
--- a/src/app/config/configcommandexecutor.h
+++ b/src/app/config/configcommandexecutor.h
@@ -39,9 +39,9 @@
#ifndef CONFIGCOMMANDEXECUTOR_H
#define CONFIGCOMMANDEXECUTOR_H
-#include <QtCore/qcoreapplication.h>
+#include <tools/settings.h>
-namespace qbs { class Settings; }
+#include <QtCore/qcoreapplication.h>
class ConfigCommand;
@@ -49,7 +49,7 @@ class ConfigCommandExecutor
{
Q_DECLARE_TR_FUNCTIONS(ConfigCommandExecutor)
public:
- ConfigCommandExecutor(qbs::Settings *settings);
+ ConfigCommandExecutor(qbs::Settings *settings, qbs::Settings::Scopes scope);
void execute(const ConfigCommand &command);
@@ -61,6 +61,7 @@ private:
void importSettings(const QString &filename);
qbs::Settings *m_settings;
+ const qbs::Settings::Scopes m_scope;
};
#endif // CONFIGCOMMANDEXECUTOR_H
diff --git a/src/app/config/configcommandlineparser.cpp b/src/app/config/configcommandlineparser.cpp
index 46a354939..2f2d1610e 100644
--- a/src/app/config/configcommandlineparser.cpp
+++ b/src/app/config/configcommandlineparser.cpp
@@ -54,7 +54,7 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
m_commandLine = commandLine;
if (m_commandLine.empty())
- throw ErrorInfo(Tr::tr("No parameters supplied."));
+ throw Error(Tr::tr("No parameters supplied."));
if (m_commandLine.size() == 1 && (m_commandLine.front() == QLatin1String("--help")
|| m_commandLine.front() == QLatin1String("-h"))) {
m_helpRequested = true;
@@ -73,16 +73,20 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
setCommand(ConfigCommand::CfgImport);
else if (arg == QLatin1String("settings-dir"))
assignOptionArgument(arg, m_settingsDir);
+ else if (arg == QLatin1String("user"))
+ setScope(qbs::Settings::UserScope);
+ else if (arg == QLatin1String("system"))
+ setScope(qbs::Settings::SystemScope);
else
- throw ErrorInfo(Tr::tr("Unknown option for config command."));
+ throw Error(Tr::tr("Unknown option for config command."));
}
switch (command().command) {
case ConfigCommand::CfgNone:
if (m_commandLine.empty())
- throw ErrorInfo(Tr::tr("No parameters supplied."));
+ throw Error(Tr::tr("No parameters supplied."));
if (m_commandLine.size() > 2)
- throw ErrorInfo(Tr::tr("Too many arguments."));
+ throw Error(Tr::tr("Too many arguments."));
m_command.varNames << m_commandLine.front();
if (m_commandLine.size() == 1) {
setCommand(ConfigCommand::CfgList);
@@ -93,17 +97,17 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
break;
case ConfigCommand::CfgUnset:
if (m_commandLine.empty())
- throw ErrorInfo(Tr::tr("Need name of variable to unset."));
+ throw Error(Tr::tr("Need name of variable to unset."));
m_command.varNames = m_commandLine;
break;
case ConfigCommand::CfgExport:
if (m_commandLine.size() != 1)
- throw ErrorInfo(Tr::tr("Need name of file to which to export."));
+ throw Error(Tr::tr("Need name of file to which to export."));
m_command.fileName = m_commandLine.front();
break;
case ConfigCommand::CfgImport:
if (m_commandLine.size() != 1)
- throw ErrorInfo(Tr::tr("Need name of file from which to import."));
+ throw Error(Tr::tr("Need name of file from which to import."));
m_command.fileName = m_commandLine.front();
break;
case ConfigCommand::CfgList:
@@ -117,10 +121,17 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
void ConfigCommandLineParser::setCommand(ConfigCommand::Command command)
{
if (m_command.command != ConfigCommand::CfgNone)
- throw ErrorInfo(Tr::tr("You cannot specify more than one command."));
+ throw Error(Tr::tr("You cannot specify more than one command."));
m_command.command = command;
}
+void ConfigCommandLineParser::setScope(Settings::Scope scope)
+{
+ if (m_scope != qbs::Settings::allScopes())
+ throw Error(Tr::tr("The --user and --system options can only appear once."));
+ m_scope = scope;
+}
+
void ConfigCommandLineParser::printUsage() const
{
puts("Usage:\n"
@@ -130,6 +141,8 @@ void ConfigCommandLineParser::printUsage() const
"\n"
"Options:\n"
" --list [<root> ...] list keys under key <root> or all keys\n"
+ " --user consider only user-level settings\n"
+ " --system consider only system-level settings\n"
" --unset <name> remove key with given name\n"
" --import <file> import settings from given file\n"
" --export <file> export settings to given file\n");
@@ -138,8 +151,8 @@ void ConfigCommandLineParser::printUsage() const
void ConfigCommandLineParser::assignOptionArgument(const QString &option, QString &argument)
{
if (m_commandLine.empty())
- throw ErrorInfo(Tr::tr("Option '%1' needs an argument.").arg(option));
+ throw Error(Tr::tr("Option '%1' needs an argument.").arg(option));
argument = m_commandLine.takeFirst();
if (argument.isEmpty())
- throw ErrorInfo(Tr::tr("Argument for option '%1' must not be empty.").arg(option));
+ throw Error(Tr::tr("Argument for option '%1' must not be empty.").arg(option));
}
diff --git a/src/app/config/configcommandlineparser.h b/src/app/config/configcommandlineparser.h
index 5427c9187..22f6906f9 100644
--- a/src/app/config/configcommandlineparser.h
+++ b/src/app/config/configcommandlineparser.h
@@ -41,6 +41,8 @@
#include "configcommand.h"
+#include <tools/settings.h>
+
#include <QtCore/qstringlist.h>
class ConfigCommandLineParser
@@ -50,15 +52,27 @@ public:
ConfigCommand command() const { return m_command; }
+ qbs::Settings::Scopes scope() const { return m_scope; }
QString settingsDir() const { return m_settingsDir; }
bool helpRequested() const { return m_helpRequested; }
void printUsage() const;
+ class Error
+ {
+ public:
+ Error(const QString &message) : m_message(message) { }
+ QString message() const { return m_message; }
+ private:
+ QString m_message;
+ };
+
private:
void assignOptionArgument(const QString &option, QString &argument);
void setCommand(ConfigCommand::Command command);
+ void setScope(qbs::Settings::Scope scope);
ConfigCommand m_command;
+ qbs::Settings::Scopes m_scope = qbs::Settings::allScopes();
bool m_helpRequested;
QString m_settingsDir;
QStringList m_commandLine;
diff --git a/src/app/config/configmain.cpp b/src/app/config/configmain.cpp
index f97369ce5..29cfaf9c6 100644
--- a/src/app/config/configmain.cpp
+++ b/src/app/config/configmain.cpp
@@ -64,10 +64,13 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
Settings settings(parser.settingsDir());
- ConfigCommandExecutor(&settings).execute(parser.command());
+ ConfigCommandExecutor(&settings, parser.scope()).execute(parser.command());
+ } catch (const ConfigCommandLineParser::Error &e) {
+ std::cerr << qPrintable(e.message()) << std::endl;
+ parser.printUsage();
+ return EXIT_FAILURE;
} catch (const qbs::ErrorInfo &e) {
std::cerr << qPrintable(e.toString()) << std::endl;
- parser.printUsage();
return EXIT_FAILURE;
}
}
diff --git a/src/app/qbs-setup-qt/commandlineparser.cpp b/src/app/qbs-setup-qt/commandlineparser.cpp
index 2559e0c0b..8444faa77 100644
--- a/src/app/qbs-setup-qt/commandlineparser.cpp
+++ b/src/app/qbs-setup-qt/commandlineparser.cpp
@@ -49,6 +49,7 @@ static QString helpOptionShort() { return QLatin1String("-h"); }
static QString helpOptionLong() { return QLatin1String("--help"); }
static QString detectOption() { return QLatin1String("--detect"); }
static QString settingsDirOption() { return QLatin1String("--settings-dir"); }
+static QString systemOption() { return QLatin1String("--system"); }
void CommandLineParser::parse(const QStringList &commandLine)
{
@@ -73,6 +74,8 @@ void CommandLineParser::parse(const QStringList &commandLine)
m_helpRequested = true;
else if (arg == detectOption())
m_autoDetectionMode = true;
+ else if (arg == systemOption())
+ m_settingsScope = qbs::Settings::SystemScope;
else if (arg == settingsDirOption())
assignOptionArgument(settingsDirOption(), m_settingsDir);
}
@@ -107,10 +110,10 @@ QString CommandLineParser::usageString() const
{
QString s = Tr::tr("This tool creates qbs profiles from Qt versions.\n");
s += Tr::tr("Usage:\n");
- s += Tr::tr(" %1 [%2 <settings directory>] %3\n")
- .arg(m_command, settingsDirOption(), detectOption());
- s += Tr::tr(" %1 [%2 <settings directory>] <path to qmake> <profile name>\n")
- .arg(m_command, settingsDirOption());
+ s += Tr::tr(" %1 [%2 <settings directory>] [%4] %3\n")
+ .arg(m_command, settingsDirOption(), detectOption(), systemOption());
+ s += Tr::tr(" %1 [%2 <settings directory>] [%4] <path to qmake> <profile name>\n")
+ .arg(m_command, settingsDirOption(), systemOption());
s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong());
s += Tr::tr("The first form tries to auto-detect all known Qt versions, looking them up "
"via the PATH environment variable.\n");
diff --git a/src/app/qbs-setup-qt/commandlineparser.h b/src/app/qbs-setup-qt/commandlineparser.h
index 8ac1ba44d..a9ecb55c8 100644
--- a/src/app/qbs-setup-qt/commandlineparser.h
+++ b/src/app/qbs-setup-qt/commandlineparser.h
@@ -39,6 +39,8 @@
#ifndef QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
#define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
+#include <tools/settings.h>
+
#include <QtCore/qstringlist.h>
class CommandLineParser
@@ -52,6 +54,7 @@ public:
QString qmakePath() const { return m_qmakePath; }
QString profileName() const { return m_profileName; }
QString settingsDir() const { return m_settingsDir; }
+ qbs::Settings::Scope settingsScope() const { return m_settingsScope; }
QString usageString() const;
@@ -62,6 +65,7 @@ private:
bool m_helpRequested;
bool m_autoDetectionMode;
+ qbs::Settings::Scope m_settingsScope = qbs::Settings::UserScope;
QString m_qmakePath;
QString m_profileName;
QString m_settingsDir;
diff --git a/src/app/qbs-setup-qt/main.cpp b/src/app/qbs-setup-qt/main.cpp
index 55ec80582..d1bfd9614 100644
--- a/src/app/qbs-setup-qt/main.cpp
+++ b/src/app/qbs-setup-qt/main.cpp
@@ -68,6 +68,7 @@ int main(int argc, char *argv[])
}
Settings settings(clParser.settingsDir());
+ settings.setScopeForWriting(clParser.settingsScope());
if (clParser.autoDetectionMode()) {
// search all Qt's in path and dump their settings
diff --git a/src/app/qbs-setup-toolchains/commandlineparser.cpp b/src/app/qbs-setup-toolchains/commandlineparser.cpp
index 00e605f3f..fd2e0214f 100644
--- a/src/app/qbs-setup-toolchains/commandlineparser.cpp
+++ b/src/app/qbs-setup-toolchains/commandlineparser.cpp
@@ -50,6 +50,7 @@ static QString helpOptionLong() { return QLatin1String("--help"); }
static QString detectOption() { return QLatin1String("--detect"); }
static QString typeOption() { return QLatin1String("--type"); }
static QString settingsDirOption() { return QLatin1String("--settings-dir"); }
+static QString systemOption() { return QLatin1String("--system"); }
void CommandLineParser::parse(const QStringList &commandLine)
{
@@ -75,6 +76,8 @@ void CommandLineParser::parse(const QStringList &commandLine)
m_helpRequested = true;
else if (arg == detectOption())
m_autoDetectionMode = true;
+ else if (arg == systemOption())
+ m_settingsScope = qbs::Settings::SystemScope;
else if (arg == typeOption())
assignOptionArgument(typeOption(), m_toolchainType);
else if (arg == settingsDirOption())
@@ -111,11 +114,11 @@ QString CommandLineParser::usageString() const
{
QString s = Tr::tr("This tool creates qbs profiles from toolchains.\n");
s += Tr::tr("Usage:\n");
- s += Tr::tr(" %1 [%2 <settings directory>] %3\n")
- .arg(m_command, settingsDirOption(), detectOption());
- s += Tr::tr(" %1 [%3 <settings directory>] [%2 <toolchain type>] "
+ s += Tr::tr(" %1 [%2 <settings directory>] [%4] %3\n")
+ .arg(m_command, settingsDirOption(), detectOption(), systemOption());
+ s += Tr::tr(" %1 [%3 <settings directory>] [%4] [%2 <toolchain type>] "
"<compiler path> <profile name>\n")
- .arg(m_command, typeOption(), settingsDirOption());
+ .arg(m_command, typeOption(), settingsDirOption(), systemOption());
s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong());
s += Tr::tr("The first form tries to auto-detect all known toolchains, looking them up "
"via the PATH environment variable.\n");
diff --git a/src/app/qbs-setup-toolchains/commandlineparser.h b/src/app/qbs-setup-toolchains/commandlineparser.h
index 94a7a6283..cb0949bf1 100644
--- a/src/app/qbs-setup-toolchains/commandlineparser.h
+++ b/src/app/qbs-setup-toolchains/commandlineparser.h
@@ -39,6 +39,8 @@
#ifndef QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
#define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H
+#include <tools/settings.h>
+
#include <QtCore/qstringlist.h>
class CommandLineParser
@@ -53,6 +55,7 @@ public:
QString toolchainType() const { return m_toolchainType; }
QString profileName() const { return m_profileName; }
QString settingsDir() const { return m_settingsDir; }
+ qbs::Settings::Scope settingsScope() const { return m_settingsScope; }
QString usageString() const;
@@ -63,6 +66,7 @@ private:
bool m_helpRequested;
bool m_autoDetectionMode;
+ qbs::Settings::Scope m_settingsScope = qbs::Settings::UserScope;
QString m_compilerPath;
QString m_toolchainType;
QString m_profileName;
diff --git a/src/app/qbs-setup-toolchains/main.cpp b/src/app/qbs-setup-toolchains/main.cpp
index 546ae3adb..87a2a842a 100644
--- a/src/app/qbs-setup-toolchains/main.cpp
+++ b/src/app/qbs-setup-toolchains/main.cpp
@@ -69,6 +69,7 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
Settings settings(clParser.settingsDir());
+ settings.setScopeForWriting(clParser.settingsScope());
if (clParser.autoDetectionMode()) {
probe(&settings);
return EXIT_SUCCESS;
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 900fde759..fd25d30e0 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -25,10 +25,13 @@ QbsLibrary {
qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []
property stringList enableUnitTestsDefines:
qbsbuildconfig.enableUnitTests ? ["QBS_ENABLE_UNIT_TESTS"] : []
+ property stringList systemSettingsDirDefines: qbsbuildconfig.systemSettingsDir
+ ? ['QBS_SYSTEM_SETTINGS_DIR="' + qbsbuildconfig.systemSettingsDir + '"'] : []
cpp.defines: base.concat([
"QBS_RELATIVE_LIBEXEC_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeLibexecPath),
"QBS_VERSION=" + Utilities.cStringQuote(version),
]).concat(projectFileUpdateDefines).concat(enableUnitTestsDefines)
+ .concat(systemSettingsDirDefines)
Properties {
condition: qbs.targetOS.contains("windows")
diff --git a/src/lib/corelib/tools/preferences.cpp b/src/lib/corelib/tools/preferences.cpp
index da7639c40..66803a0f5 100644
--- a/src/lib/corelib/tools/preferences.cpp
+++ b/src/lib/corelib/tools/preferences.cpp
@@ -41,7 +41,6 @@
#include "buildoptions.h"
#include "hostosinfo.h"
#include "profile.h"
-#include "settings.h"
#include "stringconstants.h"
namespace qbs {
@@ -129,23 +128,30 @@ QVariant Preferences::getPreference(const QString &key, const QVariant &defaultV
{
static const QString keyPrefix = QLatin1String("preferences");
const QString fullKey = keyPrefix + QLatin1Char('.') + key;
+ const bool isSearchPaths = key == Internal::StringConstants::qbsSearchPathsProperty();
if (!m_profile.isEmpty()) {
QVariant value = Profile(m_profile, m_settings).value(fullKey);
if (value.isValid()) {
- if (key == Internal::StringConstants::qbsSearchPathsProperty()) // Merge with top-level value.
- value = value.toStringList() + m_settings->value(fullKey).toStringList();
+ if (isSearchPaths) { // Merge with top-level value.
+ value = value.toStringList() + m_settings->value(
+ fullKey, scopesForSearchPaths()).toStringList();
+ }
return value;
}
}
QVariant value = m_profileContents.value(keyPrefix).toMap().value(key);
if (value.isValid()) {
- if (key == Internal::StringConstants::qbsSearchPathsProperty()) // Merge with top-level value
- value = value.toStringList() + m_settings->value(fullKey).toStringList();
+ if (isSearchPaths) {// Merge with top-level value
+ value = value.toStringList() + m_settings->value(
+ fullKey, scopesForSearchPaths()).toStringList();
+ }
return value;
}
- return m_settings->value(fullKey, defaultValue);
+ return m_settings->value(fullKey,
+ isSearchPaths ? scopesForSearchPaths() : Settings::allScopes(),
+ defaultValue);
}
QStringList Preferences::pathList(const QString &key, const QString &defaultValue) const
@@ -155,4 +161,14 @@ QStringList Preferences::pathList(const QString &key, const QString &defaultValu
return paths;
}
+bool Preferences::ignoreSystemSearchPaths() const
+{
+ return getPreference(QStringLiteral("ignoreSystemSearchPaths")).toBool();
+}
+
+Settings::Scopes Preferences::scopesForSearchPaths() const
+{
+ return ignoreSystemSearchPaths() ? Settings::UserScope : Settings::allScopes();
+}
+
} // namespace qbs
diff --git a/src/lib/corelib/tools/preferences.h b/src/lib/corelib/tools/preferences.h
index 98468b78c..07f0edcd7 100644
--- a/src/lib/corelib/tools/preferences.h
+++ b/src/lib/corelib/tools/preferences.h
@@ -42,6 +42,7 @@
#include "qbs_export.h"
#include "commandechomode.h"
+#include "settings.h"
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
@@ -67,6 +68,9 @@ private:
QVariant getPreference(const QString &key, const QVariant &defaultValue = QVariant()) const;
QStringList pathList(const QString &key, const QString &defaultValue) const;
+ bool ignoreSystemSearchPaths() const;
+ Settings::Scopes scopesForSearchPaths() const;
+
Settings *m_settings;
QString m_profile;
QVariantMap m_profileContents;
diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp
index c6ec1b9e7..3ace77d4d 100644
--- a/src/lib/corelib/tools/profile.cpp
+++ b/src/lib/corelib/tools/profile.cpp
@@ -75,7 +75,7 @@ Profile::Profile(const QString &name, Settings *settings, const QVariantMap &pro
bool Profile::exists() const
{
return m_name == fallbackName() || !m_values.empty()
- || !m_settings->allKeysWithPrefix(profileKey()).empty();
+ || !m_settings->allKeysWithPrefix(profileKey(), Settings::allScopes()).empty();
}
/*!
@@ -200,7 +200,7 @@ QVariant Profile::localValue(const QString &key) const
{
QVariant val = m_values.value(key);
if (!val.isValid())
- val = m_settings->value(fullyQualifiedKey(key));
+ val = m_settings->value(fullyQualifiedKey(key), Settings::allScopes());
return val;
}
@@ -230,7 +230,7 @@ QStringList Profile::allKeysInternal(Profile::KeySelection selection,
extendAndCheckProfileChain(profileChain);
QStringList keys = m_values.keys();
if (keys.empty())
- keys = m_settings->allKeysWithPrefix(profileKey());
+ keys = m_settings->allKeysWithPrefix(profileKey(), Settings::allScopes());
if (selection == KeySelectionNonRecursive)
return keys;
const QString baseProfileName = baseProfile();
diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp
index 44f127993..189c96a6a 100644
--- a/src/lib/corelib/tools/settings.cpp
+++ b/src/lib/corelib/tools/settings.cpp
@@ -44,6 +44,7 @@
#include "settingscreator.h"
#include <logging/translator.h>
+#include <tools/hostosinfo.h>
#include <tools/stringconstants.h>
#include <QtCore/qsettings.h>
@@ -53,8 +54,46 @@
namespace qbs {
using namespace Internal;
-Settings::Settings(const QString &baseDir)
- : m_settings(SettingsCreator(baseDir).getQSettings()), m_baseDir(baseDir)
+static QString defaultSystemSettingsBaseDir()
+{
+ switch (HostOsInfo::hostOs()) {
+ case HostOsInfo::HostOsWindows: {
+ const char key[] = "ALLUSERSAPPDATA";
+ if (qEnvironmentVariableIsSet(key))
+ return QLatin1String(key);
+ return QStringLiteral("C:/ProgramData");
+ }
+ case HostOsInfo::HostOsMacos:
+ return QStringLiteral("/Library/Application Support");
+ case HostOsInfo::HostOsLinux:
+ case HostOsInfo::HostOsOtherUnix:
+ return QStringLiteral("/etc/xdg");
+ default:
+ return QString();
+ }
+}
+
+static QString systemSettingsBaseDir()
+{
+#ifdef QBS_ENABLE_UNIT_TESTS
+ const char key[] = "QBS_AUTOTEST_SYSTEM_SETTINGS_DIR";
+ if (qEnvironmentVariableIsSet(key))
+ return QLatin1String(qgetenv(key));
+#endif
+#ifdef QBS_SYSTEM_SETTINGS_DIR
+ return QLatin1String(QBS_SYSTEM_SETTINGS_DIR);
+#else
+ return defaultSystemSettingsBaseDir() + QStringLiteral("/qbs");
+#endif
+}
+
+Settings::Settings(const QString &baseDir) : Settings(baseDir, systemSettingsBaseDir()) { }
+
+Settings::Settings(const QString &baseDir, const QString &systemBaseDir)
+ : m_settings(SettingsCreator(baseDir).getQSettings()),
+ m_systemSettings(new QSettings(systemBaseDir + QStringLiteral("/qbs.conf"),
+ QSettings::IniFormat)),
+ m_baseDir(baseDir)
{
// Actual qbs settings are stored transparently within a group, because QSettings
// can see non-qbs fallback settings e.g. from QtProject that we're not interested in.
@@ -64,35 +103,66 @@ Settings::Settings(const QString &baseDir)
Settings::~Settings()
{
delete m_settings;
+ delete m_systemSettings;
}
-QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
+QVariant Settings::value(const QString &key, Scopes scopes, const QVariant &defaultValue) const
{
- return m_settings->value(internalRepresentation(key), defaultValue);
+ QVariant userValue;
+ if (scopes & UserScope)
+ userValue = m_settings->value(internalRepresentation(key));
+ QVariant systemValue;
+ if (scopes & SystemScope)
+ systemValue = m_systemSettings->value(internalRepresentation(key));
+ if (!userValue.isValid()) {
+ if (systemValue.isValid())
+ return systemValue;
+ return defaultValue;
+ }
+ if (!systemValue.isValid())
+ return userValue;
+ if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QStringList)
+ return userValue.toStringList() + systemValue.toStringList();
+ if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QVariantList)
+ return userValue.toList() + systemValue.toList();
+ return userValue;
}
-QStringList Settings::allKeys() const
+QStringList Settings::allKeys(Scopes scopes) const
{
- QStringList keys = m_settings->allKeys();
+ QStringList keys;
+ if (scopes & UserScope)
+ keys = m_settings->allKeys();
+ if (scopes & SystemScope)
+ keys += m_systemSettings->allKeys();
fixupKeys(keys);
return keys;
}
-QStringList Settings::directChildren(const QString &parentGroup)
+QStringList Settings::directChildren(const QString &parentGroup, Scope scope) const
{
- m_settings->beginGroup(internalRepresentation(parentGroup));
- QStringList children = m_settings->childGroups();
- children << m_settings->childKeys();
- m_settings->endGroup();
+ QSettings * const settings = settingsForScope(scope);
+ settings->beginGroup(internalRepresentation(parentGroup));
+ QStringList children = settings->childGroups();
+ children << settings->childKeys();
+ settings->endGroup();
fixupKeys(children);
return children;
}
-QStringList Settings::allKeysWithPrefix(const QString &group) const
+QStringList Settings::allKeysWithPrefix(const QString &group, Scopes scopes) const
{
- m_settings->beginGroup(internalRepresentation(group));
- QStringList keys = m_settings->allKeys();
- m_settings->endGroup();
+ QStringList keys;
+ if (scopes & UserScope) {
+ m_settings->beginGroup(internalRepresentation(group));
+ keys = m_settings->allKeys();
+ m_settings->endGroup();
+ }
+ if (scopes & SystemScope) {
+ m_systemSettings->beginGroup(internalRepresentation(group));
+ keys += m_systemSettings->allKeys();
+ m_systemSettings->endGroup();
+ }
fixupKeys(keys);
return keys;
}
@@ -103,40 +173,50 @@ void Settings::setValue(const QString &key, const QVariant &value)
throw ErrorInfo(Tr::tr("Invalid use of special profile name '%1'.")
.arg(Profile::fallbackName()));
}
- m_settings->setValue(internalRepresentation(key), value);
+ targetForWriting()->setValue(internalRepresentation(key), value);
+ checkForWriteError();
}
void Settings::remove(const QString &key)
{
- m_settings->remove(internalRepresentation(key));
+ targetForWriting()->remove(internalRepresentation(key));
+ checkForWriteError();
}
void Settings::clear()
{
- m_settings->clear();
+ targetForWriting()->clear();
+ checkForWriteError();
}
void Settings::sync()
{
- m_settings->sync();
+ targetForWriting()->sync();
}
QString Settings::defaultProfile() const
{
- return value(QLatin1String("defaultProfile")).toString();
+ return value(QLatin1String("defaultProfile"), allScopes()).toString();
}
QStringList Settings::profiles() const
{
- m_settings->beginGroup(StringConstants::profilesSettingsKey());
- QStringList result = m_settings->childGroups();
- m_settings->endGroup();
+ QStringList result;
+ if (m_scopeForWriting == UserScope) {
+ m_settings->beginGroup(StringConstants::profilesSettingsKey());
+ result = m_settings->childGroups();
+ m_settings->endGroup();
+ }
+ m_systemSettings->beginGroup(StringConstants::profilesSettingsKey());
+ result += m_systemSettings->childGroups();
+ m_systemSettings->endGroup();
+ result.removeDuplicates();
return result;
}
QString Settings::fileName() const
{
- return m_settings->fileName();
+ return targetForWriting()->fileName();
}
QString Settings::internalRepresentation(const QString &externalKey) const
@@ -159,4 +239,25 @@ void Settings::fixupKeys(QStringList &keys) const
key = externalRepresentation(key);
}
+QSettings *Settings::settingsForScope(Settings::Scope scope) const
+{
+ return scope == UserScope ? m_settings : m_systemSettings;
+}
+
+QSettings *Settings::targetForWriting() const
+{
+ return settingsForScope(m_scopeForWriting);
+}
+
+void Settings::checkForWriteError()
+{
+ if (m_scopeForWriting == SystemScope && m_systemSettings->status() == QSettings::NoError) {
+ sync();
+ if (m_systemSettings->status() == QSettings::AccessError)
+ throw ErrorInfo(Tr::tr("Failure writing system settings file '%1': "
+ "You do not have permission to write to that location.")
+ .arg(fileName()));
+ }
+}
+
} // namespace qbs
diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h
index cd6e664a3..d0676a072 100644
--- a/src/lib/corelib/tools/settings.h
+++ b/src/lib/corelib/tools/settings.h
@@ -57,18 +57,27 @@ class QBS_EXPORT Settings
public:
// The "pure" base directory without any version scope. Empty string means "system default".
Settings(const QString &baseDir);
+ Settings(const QString &baseDir, const QString &systemBaseDir);
~Settings();
- QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
- QStringList allKeys() const;
- QStringList directChildren(const QString &parentGroup); // Keys and groups.
- QStringList allKeysWithPrefix(const QString &group) const;
+ enum Scope { UserScope = 0x1, SystemScope = 0x2 };
+ Q_DECLARE_FLAGS(Scopes, Scope)
+ static Scopes allScopes() { return Scopes{UserScope, SystemScope}; }
+
+ QVariant value(const QString &key, Scopes scopes,
+ const QVariant &defaultValue = QVariant()) const;
+ QStringList allKeys(Scopes scopes) const;
+ QStringList directChildren(const QString &parentGroup, Scope scope) const; // Keys and groups.
+ QStringList allKeysWithPrefix(const QString &group, Scopes scopes) const;
void setValue(const QString &key, const QVariant &value);
void remove(const QString &key);
void clear();
void sync();
+ void setScopeForWriting(Scope scope) { m_scopeForWriting = scope; }
+ Scope scopeForWriting() const { return m_scopeForWriting; }
+
QString defaultProfile() const;
QStringList profiles() const;
@@ -79,11 +88,18 @@ private:
QString internalRepresentation(const QString &externalKey) const;
QString externalRepresentation(const QString &internalKey) const;
void fixupKeys(QStringList &keys) const;
+ QSettings *settingsForScope(Scope scope) const;
+ QSettings *targetForWriting() const;
+ void checkForWriteError();
QSettings * const m_settings;
+ QSettings * const m_systemSettings;
const QString m_baseDir;
+ Scope m_scopeForWriting = UserScope;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(Settings::Scopes)
+
} // namespace qbs
#endif // QBS_SETTINGS_H
diff --git a/src/lib/corelib/tools/settingsmodel.cpp b/src/lib/corelib/tools/settingsmodel.cpp
index 4defb46b9..e908bfe08 100644
--- a/src/lib/corelib/tools/settingsmodel.cpp
+++ b/src/lib/corelib/tools/settingsmodel.cpp
@@ -113,6 +113,8 @@ public:
void doSave(const Node *node, const QString &prefix);
Node *indexToNode(const QModelIndex &index);
+ Settings::Scope scope() const { return settings->scopeForWriting(); }
+
Node rootNode;
std::unique_ptr<qbs::Settings> settings;
QVariantMap additionalProperties;
@@ -120,10 +122,11 @@ public:
bool editable;
};
-SettingsModel::SettingsModel(const QString &settingsDir, QObject *parent)
+SettingsModel::SettingsModel(const QString &settingsDir, Settings::Scope scope, QObject *parent)
: QAbstractItemModel(parent), d(new SettingsModelPrivate)
{
d->settings.reset(new qbs::Settings(settingsDir));
+ d->settings->setScopeForWriting(scope);
d->readSettings();
}
@@ -150,8 +153,10 @@ void SettingsModel::save()
void SettingsModel::updateSettingsDir(const QString &settingsDir)
{
+ const Settings::Scope scope = d->scope();
beginResetModel();
d->settings.reset(new qbs::Settings(settingsDir));
+ d->settings->setScopeForWriting(scope);
d->readSettings();
endResetModel();
}
@@ -322,7 +327,7 @@ void SettingsModel::SettingsModelPrivate::readSettings()
{
qDeleteAll(rootNode.children);
rootNode.children.clear();
- for (const QString &topLevelKey : settings->directChildren(QString()))
+ for (const QString &topLevelKey : settings->directChildren(QString(), scope()))
addNodeFromSettings(&rootNode, topLevelKey);
for (QVariantMap::ConstIterator it = additionalProperties.constBegin();
it != additionalProperties.constEnd(); ++it) {
@@ -347,8 +352,8 @@ void SettingsModel::SettingsModelPrivate::addNodeFromSettings(Node *parentNode,
const QString &nodeName
= fullyQualifiedName.mid(fullyQualifiedName.lastIndexOf(QLatin1Char('.')) + 1);
Node * const node = createNode(parentNode, nodeName);
- node->value = settingsValueToRepresentation(settings->value(fullyQualifiedName));
- for (const QString &childKey : settings->directChildren(fullyQualifiedName))
+ node->value = settingsValueToRepresentation(settings->value(fullyQualifiedName, scope()));
+ for (const QString &childKey : settings->directChildren(fullyQualifiedName, scope()))
addNodeFromSettings(node, fullyQualifiedName + QLatin1Char('.') + childKey);
dirty = true;
}
diff --git a/src/lib/corelib/tools/settingsmodel.h b/src/lib/corelib/tools/settingsmodel.h
index adcc2a826..6f9631585 100644
--- a/src/lib/corelib/tools/settingsmodel.h
+++ b/src/lib/corelib/tools/settingsmodel.h
@@ -41,6 +41,7 @@
#define QBS_SETTINGSMODEL_H
#include <tools/qbs_export.h>
+#include <tools/settings.h>
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qvariant.h>
@@ -51,7 +52,7 @@ class QBS_EXPORT SettingsModel : public QAbstractItemModel
{
Q_OBJECT
public:
- SettingsModel(const QString &settingsDir, QObject *parent = nullptr);
+ SettingsModel(const QString &settingsDir, Settings::Scope scope, QObject *parent = nullptr);
~SettingsModel();
int keyColumn() const { return 0; }
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
index b6dbebc86..9db30664f 100644
--- a/src/lib/corelib/tools/tools.pri
+++ b/src/lib/corelib/tools/tools.pri
@@ -2,6 +2,11 @@ include(../../../install_prefix.pri)
INCLUDEPATH += $$PWD/../.. # for plugins
+QBS_SYSTEM_SETTINGS_DIR = $$(QBS_SYSTEM_SETTINGS_DIR)
+!isEmpty(QBS_SYSTEM_SETTINGS_DIR) {
+ DEFINES += QBS_SYSTEM_SETTINGS_DIR=\\\"$$QBS_SYSTEM_SETTINGS_DIR\\\"
+}
+
HEADERS += \
$$PWD/architectures.h \
$$PWD/buildgraphlocker.h \
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp
index 055e7c6b1..6d82d50ad 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.cpp
+++ b/src/lib/qtprofilesetup/qtprofilesetup.cpp
@@ -582,7 +582,8 @@ static void createModules(Profile &profile, Settings *settings,
? allQt4Modules(qtEnvironment)
: allQt5Modules(profile, qtEnvironment);
const QString profileBaseDir = QString::fromLatin1("%1/profiles/%2")
- .arg(QFileInfo(settings->fileName()).dir().absolutePath(), profile.name());
+ .arg(QFileInfo(settings->fileName()).dir().absolutePath(),
+ profile.name());
const QString qbsQtModuleBaseDir = profileBaseDir + QLatin1String("/modules/Qt");
QStringList allFiles;
copyTemplateFile(QLatin1String("QtModule.qbs"), qbsQtModuleBaseDir, profile, qtEnvironment,
diff --git a/tests/auto/blackbox/blackbox.pro b/tests/auto/blackbox/blackbox.pro
index 5e58e0e19..42848d077 100644
--- a/tests/auto/blackbox/blackbox.pro
+++ b/tests/auto/blackbox/blackbox.pro
@@ -4,6 +4,7 @@ HEADERS = tst_blackbox.h tst_blackboxbase.h
SOURCES = tst_blackbox.cpp tst_blackboxbase.cpp
OBJECTS_DIR = generic
MOC_DIR = $${OBJECTS_DIR}-moc
+qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS
include(../auto.pri)
diff --git a/tests/auto/blackbox/blackbox.qbs b/tests/auto/blackbox/blackbox.qbs
index 55c116480..917e8345b 100644
--- a/tests/auto/blackbox/blackbox.qbs
+++ b/tests/auto/blackbox/blackbox.qbs
@@ -25,4 +25,5 @@ QbsAutotest {
"tst_blackbox.h",
]
cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+ .concat(qbsbuildconfig.enableUnitTests ? ["QBS_ENABLE_UNIT_TESTS"] : [])
}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index 8ca35249a..844b421c4 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -33,6 +33,7 @@
#include <api/languageinfo.h>
#include <tools/hostosinfo.h>
#include <tools/installoptions.h>
+#include <tools/preferences.h>
#include <tools/profile.h>
#include <tools/qttools.h>
#include <tools/shellutils.h>
@@ -4600,6 +4601,94 @@ void TestBlackbox::pseudoMultiplexing()
QCOMPARE(runQbs(), 0);
}
+void TestBlackbox::qbsConfig()
+{
+ QbsRunParameters params("config");
+#ifdef QBS_ENABLE_UNIT_TESTS
+ QTemporaryDir tempSystemSettingsDir;
+ params.environment.insert("QBS_AUTOTEST_SYSTEM_SETTINGS_DIR", tempSystemSettingsDir.path());
+ QTemporaryDir tempUserSettingsDir;
+ QVERIFY(tempSystemSettingsDir.isValid());
+ QVERIFY(tempUserSettingsDir.isValid());
+ const QStringList settingsDirArgs = QStringList{"--settings-dir", tempUserSettingsDir.path()};
+
+ // Set values.
+ params.arguments = settingsDirArgs + QStringList{"--system", "key.subkey.scalar", "s"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs + QStringList{"--system", "key.subkey.list", "['sl']"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs + QStringList{"--user", "key.subkey.scalar", "u"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs + QStringList{"key.subkey.list", "[\"u1\",\"u2\"]"};
+ QCOMPARE(runQbs(params), 0);
+
+ // Check outputs.
+ const auto valueExtractor = [this] {
+ const QByteArray trimmed = m_qbsStdout.trimmed();
+ return trimmed.mid(trimmed.lastIndexOf(':') + 2);
+ };
+ params.arguments = settingsDirArgs + QStringList{"--list", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("\"u\""));
+ params.arguments = settingsDirArgs + QStringList{"--list", "--user", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("\"u\""));
+ params.arguments = settingsDirArgs + QStringList{"--list", "--system", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("\"s\""));
+ params.arguments = settingsDirArgs + QStringList{"--list", "key.subkey.list"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("[\"u1\", \"u2\", \"sl\"]"));
+ params.arguments = settingsDirArgs + QStringList{"--list", "--user", "key.subkey.list"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("[\"u1\", \"u2\"]"));
+ params.arguments = settingsDirArgs + QStringList{"--list", "--system", "key.subkey.list"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("[\"sl\"]"));
+
+ // Remove some values and re-check.
+ params.arguments = settingsDirArgs + QStringList{"--unset", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs + QStringList{"--system", "--unset", "key.subkey.list"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs + QStringList{"--list", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("\"s\""));
+ params.arguments = settingsDirArgs + QStringList{"--list", "key.subkey.list"};
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(valueExtractor(), QByteArray("[\"u1\", \"u2\"]"));
+
+ // Check preferences.ignoreSystemSearchPaths
+ params.arguments = settingsDirArgs
+ + QStringList{"--system", "preferences.qbsSearchPaths", "['/usr/lib/qbs']"};
+ QCOMPARE(runQbs(params), 0);
+ params.arguments = settingsDirArgs
+ + QStringList{"preferences.qbsSearchPaths", "['/home/user/qbs']"};
+ QCOMPARE(runQbs(params), 0);
+ qbs::Settings settings(tempUserSettingsDir.path(), tempSystemSettingsDir.path());
+ const qbs::Preferences prefs(&settings, "SomeProfile");
+ QVERIFY2(prefs.searchPaths().contains("/usr/lib/qbs")
+ && prefs.searchPaths().contains("/home/user/qbs"),
+ qPrintable(prefs.searchPaths().join(',')));
+ settings.setValue("profiles.SomeProfile.preferences.ignoreSystemSearchPaths", true);
+ QVERIFY2(!prefs.searchPaths().contains("/usr/lib/qbs")
+ && prefs.searchPaths().contains("/home/user/qbs"),
+ qPrintable(prefs.searchPaths().join(',')));
+
+#else
+ qDebug() << "ability to redirect the system settings dir not compiled in, skipping"
+ "most qbs-config tests";
+#endif // QBS_ENABLE_UNIT_TESTS
+
+ // Check that trying to write to actual system settings causes access failure.
+ params.expectFailure = true;
+ params.environment.clear();
+ params.arguments = QStringList{"--system", "key.subkey.scalar", "s"};
+ QVERIFY(runQbs(params) != 0);
+ QVERIFY2(m_qbsStderr.contains("You do not have permission to write to that location."),
+ m_qbsStderr.constData());
+}
+
void TestBlackbox::radAfterIncompleteBuild_data()
{
QTest::addColumn<QString>("projectFileName");
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index e5adc762f..6562e8cbc 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -205,6 +205,7 @@ private slots:
void properQuoting();
void propertiesInExportItems();
void pseudoMultiplexing();
+ void qbsConfig();
void qbsVersion();
void qtBug51237();
void radAfterIncompleteBuild();
diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp
index cf94553ba..0073f9e68 100644
--- a/tests/auto/tools/tst_tools.cpp
+++ b/tests/auto/tools/tst_tools.cpp
@@ -300,11 +300,11 @@ void TestTools::testSettingsMigration()
if (hasOldSettings) {
QVERIFY(QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt")
.exists());
- QCOMPARE(settings.value("key").toString(),
+ QCOMPARE(settings.value("key", Settings::UserScope).toString(),
settings.baseDirectory() + "/qbs/" QBS_VERSION "/profilesright");
} else {
QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles").exists());
- QVERIFY(settings.allKeys().empty());
+ QVERIFY(settings.allKeys(Settings::UserScope).empty());
}
}