diff options
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()); } } |