diff options
Diffstat (limited to 'src/lib')
52 files changed, 2072 insertions, 61 deletions
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index 3ffd6b2e9..d0fe7296e 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -726,7 +726,7 @@ void ProjectPrivate::updateExternalCodeLocations(const ProjectData &project, void ProjectPrivate::prepareChangeToProject() { if (internalProject->locked) - throw ErrorInfo(Tr::tr("A job is currently in process.")); + throw ErrorInfo(Tr::tr("A job is currently in progress.")); if (!m_projectData.isValid()) retrieveProjectData(m_projectData, internalProject); } @@ -766,7 +766,7 @@ RuleCommandList ProjectPrivate::ruleCommands(const ProductData &product, const QString &inputFilePath, const QString &outputFileTag) { if (internalProject->locked) - throw ErrorInfo(Tr::tr("A job is currently in process.")); + throw ErrorInfo(Tr::tr("A job is currently in progress.")); const ResolvedProductConstPtr resolvedProduct = internalProduct(product); if (!resolvedProduct) throw ErrorInfo(Tr::tr("No such product '%1'.").arg(product.name())); @@ -896,7 +896,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData, } for (const ResolvedProductPtr &resolvedDependentProduct : qAsConst(resolvedProduct->dependencies)) { - product.d->dependencies << resolvedDependentProduct->name; + product.d->dependencies << resolvedDependentProduct->name; // FIXME: Shouldn't this be a unique name? } std::sort(product.d->type.begin(), product.d->type.end()); std::sort(product.d->groups.begin(), product.d->groups.end()); @@ -1252,6 +1252,22 @@ Project::BuildGraphInfo Project::getBuildGraphInfo(const QString &bgFilePath, return info; } +Project::BuildGraphInfo Project::getBuildGraphInfo() const +{ + QBS_ASSERT(isValid(), return {}); + BuildGraphInfo info; + try { + if (d->internalProject->locked) + throw ErrorInfo(Tr::tr("A job is currently in progress.")); + info.bgFilePath = d->internalProject->buildGraphFilePath(); + info.overriddenProperties = d->internalProject->overriddenValues; + info.profileData = d->internalProject->profileConfigs; + } catch (const ErrorInfo &e) { + info.error = e; + } + return info; +} + #ifdef QBS_ENABLE_PROJECT_FILE_UPDATES /*! * \brief Adds a new empty group to the given product. diff --git a/src/lib/corelib/api/project.h b/src/lib/corelib/api/project.h index 05f08deee..9000d6548 100644 --- a/src/lib/corelib/api/project.h +++ b/src/lib/corelib/api/project.h @@ -155,6 +155,9 @@ public: static BuildGraphInfo getBuildGraphInfo(const QString &bgFilePath, const QStringList &requestedProperties); + // Use with loaded project. Does not set requestedProperties. + BuildGraphInfo getBuildGraphInfo() const; + #ifdef QBS_ENABLE_PROJECT_FILE_UPDATES ErrorInfo addGroup(const ProductData &product, const QString &groupName); diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp index 56700b8be..7c64bf6ff 100644 --- a/src/lib/corelib/api/projectdata.cpp +++ b/src/lib/corelib/api/projectdata.cpp @@ -45,21 +45,57 @@ #include <tools/fileinfo.h> #include <tools/jsliterals.h> #include <tools/qbsassert.h> +#include <tools/stringconstants.h> #include <tools/qttools.h> #include <tools/stringconstants.h> #include <QtCore/qdir.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> #include <algorithm> namespace qbs { +using namespace Internal; + +template<typename T> static QJsonArray toJsonArray(const QList<T> &list, + const QStringList &moduleProperties) +{ + QJsonArray jsonArray; + std::transform(list.begin(), list.end(), std::back_inserter(jsonArray), + [&moduleProperties](const T &v) { return v.toJson(moduleProperties);}); + return jsonArray; +} + +static QVariant getModuleProperty(const PropertyMap &properties, const QString &fullPropertyName) +{ + const int lastDotIndex = fullPropertyName.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex == -1) + return QVariant(); + return properties.getModuleProperty(fullPropertyName.left(lastDotIndex), + fullPropertyName.mid(lastDotIndex + 1)); +} + +static void addModuleProperties(QJsonObject &obj, const PropertyMap &properties, + const QStringList &propertyNames) +{ + QJsonObject propertyValues; + for (const QString &prop : propertyNames) { + const QVariant v = getModuleProperty(properties, prop); + if (v.isValid()) + propertyValues.insert(prop, QJsonValue::fromVariant(v)); + } + if (!propertyValues.isEmpty()) + obj.insert(StringConstants::modulePropertiesKey(), propertyValues); +} + /*! * \class GroupData * \brief The \c GroupData class corresponds to the Group item in a qbs source file. */ -GroupData::GroupData() : d(new Internal::GroupDataPrivate) +GroupData::GroupData() : d(new GroupDataPrivate) { } @@ -81,6 +117,22 @@ bool GroupData::isValid() const return d->isValid; } +QJsonObject GroupData::toJson(const QStringList &moduleProperties) const +{ + QJsonObject obj; + if (isValid()) { + obj.insert(StringConstants::locationKey(), location().toJson()); + obj.insert(StringConstants::nameProperty(), name()); + obj.insert(StringConstants::prefixProperty(), prefix()); + obj.insert(StringConstants::isEnabledKey(), isEnabled()); + obj.insert(QStringLiteral("source-artifacts"), toJsonArray(sourceArtifacts(), {})); + obj.insert(QStringLiteral("source-artifacts-from-wildcards"), + toJsonArray(sourceArtifactsFromWildcards(), {})); + addModuleProperties(obj, properties(), moduleProperties); + } + return obj; +} + /*! * \brief The location at which the group is defined in the respective source file. */ @@ -204,7 +256,7 @@ bool operator<(const GroupData &lhs, const GroupData &rhs) * or it gets generated during the build process. */ -ArtifactData::ArtifactData() : d(new Internal::ArtifactDataPrivate) +ArtifactData::ArtifactData() : d(new ArtifactDataPrivate) { } @@ -226,6 +278,21 @@ bool ArtifactData::isValid() const return d->isValid; } +QJsonObject ArtifactData::toJson(const QStringList &moduleProperties) const +{ + QJsonObject obj; + if (isValid()) { + obj.insert(StringConstants::filePathKey(), filePath()); + obj.insert(QStringLiteral("file-tags"), QJsonArray::fromStringList(fileTags())); + obj.insert(QStringLiteral("is-generated"), isGenerated()); + obj.insert(QStringLiteral("is-executable"), isExecutable()); + obj.insert(QStringLiteral("is-target"), isTargetArtifact()); + obj.insert(QStringLiteral("install-data"), installData().toJson()); + addModuleProperties(obj, properties(), moduleProperties); + } + return obj; +} + /*! * \brief The full path of this file. */ @@ -256,8 +323,8 @@ bool ArtifactData::isExecutable() const { const bool isBundle = d->properties.getModuleProperty( QStringLiteral("bundle"), QStringLiteral("isBundle")).toBool(); - return Internal::isRunnableArtifact( - Internal::FileTags::fromStringList(d->fileTags), isBundle); + return isRunnableArtifact( + FileTags::fromStringList(d->fileTags), isBundle); } /*! @@ -309,7 +376,7 @@ bool operator<(const ArtifactData &ta1, const ArtifactData &ta2) * \brief The \c InstallData class provides the installation-related data of an artifact. */ -InstallData::InstallData() : d(new Internal::InstallDataPrivate) +InstallData::InstallData() : d(new InstallDataPrivate) { } @@ -331,6 +398,19 @@ bool InstallData::isValid() const return d->isValid; } +QJsonObject InstallData::toJson() const +{ + QJsonObject obj; + if (isValid()) { + obj.insert(QStringLiteral("is-installable"), isInstallable()); + if (isInstallable()) { + obj.insert(QStringLiteral("install-file-path"), installFilePath()); + obj.insert(QStringLiteral("install-root"), installRoot()); + } + } + return obj; +} + /*! \brief Returns true if and only if \c{qbs.install} is \c true for the artifact. */ @@ -348,7 +428,7 @@ bool InstallData::isInstallable() const QString InstallData::installDir() const { QBS_ASSERT(isValid(), return {}); - return Internal::FileInfo::path(installFilePath()); + return FileInfo::path(installFilePath()); } /*! @@ -392,7 +472,7 @@ QString InstallData::localInstallFilePath() const * \brief The \c ProductData class corresponds to the Product item in a qbs source file. */ -ProductData::ProductData() : d(new Internal::ProductDataPrivate) +ProductData::ProductData() : d(new ProductDataPrivate) { } @@ -414,6 +494,39 @@ bool ProductData::isValid() const return d->isValid; } +QJsonObject ProductData::toJson(const QStringList &propertyNames) const +{ + QJsonObject obj; + if (!isValid()) + return obj; + obj.insert(StringConstants::typeProperty(), QJsonArray::fromStringList(type())); + obj.insert(StringConstants::dependenciesProperty(), + QJsonArray::fromStringList(dependencies())); + obj.insert(StringConstants::nameProperty(), name()); + obj.insert(StringConstants::fullDisplayNameKey(), fullDisplayName()); + obj.insert(QStringLiteral("target-name"), targetName()); + obj.insert(StringConstants::versionProperty(), version()); + obj.insert(QStringLiteral("multiplex-configuration-id"), multiplexConfigurationId()); + obj.insert(StringConstants::locationKey(), location().toJson()); + obj.insert(StringConstants::buildDirectoryKey(), buildDirectory()); + obj.insert(QStringLiteral("generated-artifacts"), toJsonArray(generatedArtifacts(), + propertyNames)); + obj.insert(QStringLiteral("target-executable"), targetExecutable()); + QJsonArray groupArray; + for (const GroupData &g : groups()) { + const QStringList groupPropNames = g.properties() == moduleProperties() + ? QStringList() : propertyNames; + groupArray << g.toJson(groupPropNames); + } + obj.insert(QStringLiteral("groups"), groupArray); + obj.insert(QStringLiteral("properties"), QJsonObject::fromVariantMap(properties())); + obj.insert(StringConstants::isEnabledKey(), isEnabled()); + obj.insert(QStringLiteral("is-runnable"), isRunnable()); + obj.insert(QStringLiteral("is-multiplexed"), isMultiplexed()); + addModuleProperties(obj, moduleProperties(), propertyNames); + return obj; +} + /*! * \brief The product type, which is the list of file tags matching the product's target artifacts. */ @@ -445,7 +558,7 @@ QString ProductData::name() const */ QString ProductData::fullDisplayName() const { - return Internal::ResolvedProduct::fullDisplayName(name(), multiplexConfigurationId()); + return ResolvedProduct::fullDisplayName(name(), multiplexConfigurationId()); } /*! @@ -470,8 +583,8 @@ QString ProductData::version() const QString ProductData::profile() const { return d->moduleProperties.getModuleProperty( - Internal::StringConstants::qbsModule(), - Internal::StringConstants::profileProperty()).toString(); + StringConstants::qbsModule(), + StringConstants::profileProperty()).toString(); } QString ProductData::multiplexConfigurationId() const @@ -661,7 +774,7 @@ bool operator<(const ProductData &lhs, const ProductData &rhs) * \brief The products in this project. */ -ProjectData::ProjectData() : d(new Internal::ProjectDataPrivate) +ProjectData::ProjectData() : d(new ProjectDataPrivate) { } @@ -683,6 +796,19 @@ bool ProjectData::isValid() const return d->isValid; } +QJsonObject ProjectData::toJson(const QStringList &moduleProperties) const +{ + QJsonObject obj; + if (!isValid()) + return obj; + obj.insert(StringConstants::nameProperty(), name()); + obj.insert(StringConstants::locationKey(), location().toJson()); + obj.insert(StringConstants::isEnabledKey(), isEnabled()); + obj.insert(StringConstants::productsKey(), toJsonArray(products(), moduleProperties)); + obj.insert(QStringLiteral("sub-projects"), toJsonArray(subProjects(), moduleProperties)); + return obj; +} + /*! * \brief The name of this project. */ @@ -788,14 +914,14 @@ bool operator<(const ProjectData &lhs, const ProjectData &rhs) */ PropertyMap::PropertyMap() - : d(std::make_unique<Internal::PropertyMapPrivate>()) + : d(std::make_unique<PropertyMapPrivate>()) { - static Internal::PropertyMapPtr defaultInternalMap = Internal::PropertyMapInternal::create(); + static PropertyMapPtr defaultInternalMap = PropertyMapInternal::create(); d->m_map = defaultInternalMap; } PropertyMap::PropertyMap(const PropertyMap &other) - : d(std::make_unique<Internal::PropertyMapPrivate>(*other.d)) + : d(std::make_unique<PropertyMapPrivate>(*other.d)) { } @@ -806,7 +932,7 @@ PropertyMap::~PropertyMap() = default; PropertyMap &PropertyMap::operator =(const PropertyMap &other) { if (this != &other) - d = std::make_unique<Internal::PropertyMapPrivate>(*other.d); + d = std::make_unique<PropertyMapPrivate>(*other.d); return *this; } diff --git a/src/lib/corelib/api/projectdata.h b/src/lib/corelib/api/projectdata.h index 3bd1c4540..a285f8570 100644 --- a/src/lib/corelib/api/projectdata.h +++ b/src/lib/corelib/api/projectdata.h @@ -110,6 +110,7 @@ public: ~ArtifactData(); bool isValid() const; + QJsonObject toJson(const QStringList &moduleProperties = {}) const; QString filePath() const; QStringList fileTags() const; @@ -135,6 +136,7 @@ public: ~InstallData(); bool isValid() const; + QJsonObject toJson() const; bool isInstallable() const; QString installDir() const; @@ -162,6 +164,7 @@ public: ~GroupData(); bool isValid() const; + QJsonObject toJson(const QStringList &moduleProperties = {}) const; CodeLocation location() const; QString name() const; @@ -193,6 +196,7 @@ public: ~ProductData(); bool isValid() const; + QJsonObject toJson(const QStringList &propertyNames = {}) const; QStringList type() const; QStringList dependencies() const; @@ -235,6 +239,7 @@ public: ~ProjectData(); bool isValid() const; + QJsonObject toJson(const QStringList &moduleProperties = {}) const; QString name() const; CodeLocation location() const; diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp index 1a1d51f11..16c3621b6 100644 --- a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -58,6 +59,14 @@ AbstractCommandExecutor::AbstractCommandExecutor(Logger logger, QObject *parent) , m_dryRun(false) , m_logger(std::move(logger)) { + m_watchdog.setSingleShot(true); + connect(&m_watchdog, &QTimer::timeout, + this, [this]() { + cancel(ErrorInfo{Tr::tr("Command cancelled because it exceeded the timeout.")}); + }); + connect(this, &AbstractCommandExecutor::finished, + &m_watchdog, &QTimer::stop); + } void AbstractCommandExecutor::start(Transformer *transformer, AbstractCommand *cmd) @@ -66,7 +75,8 @@ void AbstractCommandExecutor::start(Transformer *transformer, AbstractCommand *c m_command = cmd; doSetup(); doReportCommandDescription(transformer->product()->fullDisplayName()); - doStart(); + if (doStart()) + startTimeout(); } void AbstractCommandExecutor::doReportCommandDescription(const QString &productName) @@ -84,5 +94,14 @@ void AbstractCommandExecutor::doReportCommandDescription(const QString &productN } } +void AbstractCommandExecutor::startTimeout() +{ + if (!m_dryRun || m_command->ignoreDryRun()) { + const auto timeout = m_command->timeout(); + if (timeout > 0) + m_watchdog.start(timeout * 1000); + } +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.h b/src/lib/corelib/buildgraph/abstractcommandexecutor.h index 60b2b40b2..c0f149622 100644 --- a/src/lib/corelib/buildgraph/abstractcommandexecutor.h +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -45,6 +46,7 @@ #include <tools/error.h> #include <QtCore/qobject.h> +#include <QtCore/qtimer.h> namespace qbs { class ErrorInfo; @@ -64,7 +66,7 @@ public: void setDryRunEnabled(bool enabled) { m_dryRun = enabled; } void setEchoMode(CommandEchoMode echoMode) { m_echoMode = echoMode; } - virtual void cancel() = 0; + virtual void cancel(const qbs::ErrorInfo &reason = {}) = 0; void start(Transformer *transformer, AbstractCommand *cmd); @@ -83,7 +85,9 @@ protected: private: virtual void doSetup() { }; - virtual void doStart() = 0; + virtual bool doStart() = 0; + + void startTimeout(); private: AbstractCommand *m_command; @@ -91,6 +95,7 @@ private: ScriptEngine *m_mainThreadScriptEngine; bool m_dryRun; Internal::Logger m_logger; + QTimer m_watchdog; }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp index 30970779c..5c83b2056 100644 --- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -82,9 +83,11 @@ public: return m_result; } - void cancel() + void cancel(const qbs::ErrorInfo &reason) { QBS_ASSERT(m_scriptEngine, return); + m_result.success = !reason.hasError(); + m_result.errorMessage = reason.toString(); m_scriptEngine->abortEvaluation(); } @@ -226,24 +229,25 @@ void JsCommandExecutor::waitForFinished() loop.exec(); } -void JsCommandExecutor::doStart() +bool JsCommandExecutor::doStart() { - QBS_ASSERT(!m_running, return); + QBS_ASSERT(!m_running, return false); m_thread->start(); if (dryRun() && !command()->ignoreDryRun()) { QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. - return; + return false; } m_running = true; emit startRequested(jsCommand(), transformer()); + return true; } -void JsCommandExecutor::cancel() +void JsCommandExecutor::cancel(const qbs::ErrorInfo &reason) { if (m_running && !dryRun()) - QTimer::singleShot(0, m_objectInThread, [this] { m_objectInThread->cancel(); }); + QTimer::singleShot(0, m_objectInThread, [objectInThread = m_objectInThread, reason] { objectInThread->cancel(reason); }); } void JsCommandExecutor::onJavaScriptCommandFinished() diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.h b/src/lib/corelib/buildgraph/jscommandexecutor.h index 0170c5231..0725f0d24 100644 --- a/src/lib/corelib/buildgraph/jscommandexecutor.h +++ b/src/lib/corelib/buildgraph/jscommandexecutor.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -65,8 +66,8 @@ private: void onJavaScriptCommandFinished(); void doReportCommandDescription(const QString &productName) override; - void doStart() override; - void cancel() override; + bool doStart() override; + void cancel(const qbs::ErrorInfo &reason) override; void waitForFinished(); diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp index c4e4a2be6..79edda320 100644 --- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -119,9 +120,9 @@ void ProcessCommandExecutor::doSetup() m_shellInvocation = shellQuote(QDir::toNativeSeparators(m_program), m_arguments); } -void ProcessCommandExecutor::doStart() +bool ProcessCommandExecutor::doStart() { - QBS_ASSERT(m_process.state() == QProcess::NotRunning, return); + QBS_ASSERT(m_process.state() == QProcess::NotRunning, return false); const ProcessCommand * const cmd = processCommand(); @@ -131,7 +132,7 @@ void ProcessCommandExecutor::doStart() if (dryRun() && !cmd->ignoreDryRun()) { QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. - return; + return false; } const QString workingDir = QDir::fromNativeSeparators(cmd->workingDir()); @@ -142,7 +143,7 @@ void ProcessCommandExecutor::doStart() "is invalid.").arg(QDir::toNativeSeparators(workingDir), QDir::toNativeSeparators(m_program)), cmd->codeLocation())); - return; + return false; } } @@ -163,7 +164,7 @@ void ProcessCommandExecutor::doStart() if (!responseFile.open()) { emit finished(ErrorInfo(Tr::tr("Cannot create response file '%1'.") .arg(responseFile.fileName()))); - return; + return false; } for (int i = cmd->responseFileArgumentIndex(); i < cmd->arguments().size(); ++i) { const QString arg = cmd->arguments().at(i); @@ -172,7 +173,7 @@ void ProcessCommandExecutor::doStart() if (!f.open(QIODevice::ReadOnly)) { emit finished(ErrorInfo(Tr::tr("Cannot open command file '%1'.") .arg(QDir::toNativeSeparators(f.fileName())))); - return; + return false; } responseFile.write(f.readAll()); } else { @@ -194,13 +195,15 @@ void ProcessCommandExecutor::doStart() qCDebug(lcExec) << "Additional environment:" << additionalVariables.toStringList(); m_process.setWorkingDirectory(workingDir); m_process.start(m_program, arguments); + return true; } -void ProcessCommandExecutor::cancel() +void ProcessCommandExecutor::cancel(const qbs::ErrorInfo &reason) { // We don't want this command to be reported as failing, since we explicitly terminated it. disconnect(this, &ProcessCommandExecutor::reportProcessResult, nullptr, nullptr); + m_cancelReason = reason; m_process.cancel(); } @@ -304,10 +307,13 @@ void ProcessCommandExecutor::sendProcessOutput() const bool processError = result.error() != QProcess::UnknownError; const bool failureExit = quint32(m_process.exitCode()) > quint32(processCommand()->maxExitCode()); - result.d->success = !processError && !failureExit; + const bool cancelledWithError = m_cancelReason.hasError(); + result.d->success = !processError && !failureExit && !cancelledWithError; emit reportProcessResult(result); - if (Q_UNLIKELY(processError)) { + if (Q_UNLIKELY(cancelledWithError)) { + emit finished(m_cancelReason); + } else if (Q_UNLIKELY(processError)) { emit finished(ErrorInfo(errorString)); } else if (Q_UNLIKELY(failureExit)) { emit finished(ErrorInfo(Tr::tr("Process failed with exit code %1.") @@ -325,6 +331,8 @@ void ProcessCommandExecutor::onProcessError() QTimer::singleShot(0, this, &ProcessCommandExecutor::onProcessError); return; } + if (m_cancelReason.hasError()) + return; // Ignore. Cancel reasons will be handled by on ProcessFinished(). switch (m_process.error()) { case QProcess::FailedToStart: { removeResponseFile(); diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.h b/src/lib/corelib/buildgraph/processcommandexecutor.h index 67eb9f746..b0f955882 100644 --- a/src/lib/corelib/buildgraph/processcommandexecutor.h +++ b/src/lib/corelib/buildgraph/processcommandexecutor.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -71,8 +72,8 @@ private: void doSetup() override; void doReportCommandDescription(const QString &productName) override; - void doStart() override; - void cancel() override; + bool doStart() override; + void cancel(const qbs::ErrorInfo &reason) override; void startProcessCommand(); QString filterProcessOutput(const QByteArray &output, const QString &filterFunctionSource); @@ -91,6 +92,7 @@ private: QProcessEnvironment m_buildEnvironment; QProcessEnvironment m_commandEnvironment; QString m_responseFileName; + qbs::ErrorInfo m_cancelReason; }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp index ecbc54292..31ff6be4b 100644 --- a/src/lib/corelib/buildgraph/rulecommands.cpp +++ b/src/lib/corelib/buildgraph/rulecommands.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -74,6 +75,7 @@ static QString stderrFilePathProperty() { return QStringLiteral("stderrFilePath" static QString stderrFilterFunctionProperty() { return QStringLiteral("stderrFilterFunction"); } static QString stdoutFilePathProperty() { return QStringLiteral("stdoutFilePath"); } static QString stdoutFilterFunctionProperty() { return QStringLiteral("stdoutFilterFunction"); } +static QString timeoutProperty() { return QStringLiteral("timeout"); } static QString workingDirProperty() { return QStringLiteral("workingDirectory"); } static QString invokedSourceCode(const QScriptValue codeOrFunction) @@ -87,7 +89,8 @@ AbstractCommand::AbstractCommand() m_extendedDescription(defaultExtendedDescription()), m_highlight(defaultHighLight()), m_ignoreDryRun(defaultIgnoreDryRun()), - m_silent(defaultIsSilent()) + m_silent(defaultIsSilent()), + m_timeout(defaultTimeout()) { } @@ -104,6 +107,7 @@ bool AbstractCommand::equals(const AbstractCommand *other) const && m_ignoreDryRun == other->m_ignoreDryRun && m_silent == other->m_silent && m_jobPool == other->m_jobPool + && m_timeout == other->m_timeout && m_properties == other->m_properties; } @@ -115,6 +119,9 @@ void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const m_ignoreDryRun = scriptValue->property(ignoreDryRunProperty()).toBool(); m_silent = scriptValue->property(silentProperty()).toBool(); m_jobPool = scriptValue->property(StringConstants::jobPoolProperty()).toString(); + const auto timeoutScriptValue = scriptValue->property(timeoutProperty()); + if (!timeoutScriptValue.isUndefined() && !timeoutScriptValue.isNull()) + m_timeout = timeoutScriptValue.toInt32(); m_codeLocation = codeLocation; m_predefinedProperties @@ -123,7 +130,8 @@ void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const << highlightProperty() << ignoreDryRunProperty() << StringConstants::jobPoolProperty() - << silentProperty(); + << silentProperty() + << timeoutProperty(); } QString AbstractCommand::fullDescription(const QString &productName) const @@ -173,6 +181,8 @@ static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engin engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun())); cmd.setProperty(silentProperty(), engine->toScriptValue(AbstractCommand::defaultIsSilent())); + cmd.setProperty(timeoutProperty(), + engine->toScriptValue(AbstractCommand::defaultTimeout())); return cmd; } diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h index d9d561454..d4d70d591 100644 --- a/src/lib/corelib/buildgraph/rulecommands.h +++ b/src/lib/corelib/buildgraph/rulecommands.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Jochen Ulrich <jochenulrich@t-online.de> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. @@ -70,6 +71,7 @@ public: static QString defaultHighLight() { return {}; } static bool defaultIgnoreDryRun() { return false; } static bool defaultIsSilent() { return false; } + static int defaultTimeout() { return -1; } virtual CommandType type() const = 0; virtual bool equals(const AbstractCommand *other) const; @@ -83,6 +85,7 @@ public: bool isSilent() const { return m_silent; } QString jobPool() const { return m_jobPool; } CodeLocation codeLocation() const { return m_codeLocation; } + int timeout() const { return m_timeout; } const QVariantMap &properties() const { return m_properties; } @@ -100,7 +103,7 @@ private: { pool.serializationOp<opType>(m_description, m_extendedDescription, m_highlight, m_ignoreDryRun, m_silent, m_codeLocation, m_jobPool, - m_properties); + m_timeout, m_properties); } QString m_description; @@ -110,6 +113,7 @@ private: bool m_silent; CodeLocation m_codeLocation; QString m_jobPool; + int m_timeout; QVariantMap m_properties; }; diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index a9ca5131a..2f0ced926 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -198,7 +198,24 @@ QbsLibrary { "generatableprojectiterator.h", "generator.cpp", "generatordata.cpp", + "generatorutils.cpp", + "generatorutils.h", + "generatorversioninfo.cpp", + "generatorversioninfo.h", "igeneratableprojectvisitor.h", + "ixmlnodevisitor.h", + "xmlproject.cpp", + "xmlproject.h", + "xmlprojectwriter.cpp", + "xmlprojectwriter.h", + "xmlproperty.cpp", + "xmlproperty.h", + "xmlpropertygroup.cpp", + "xmlpropertygroup.h", + "xmlworkspace.cpp", + "xmlworkspace.h", + "xmlworkspacewriter.cpp", + "xmlworkspacewriter.h", ] } Group { @@ -401,6 +418,7 @@ QbsLibrary { "joblimits.cpp", "jsliterals.cpp", "jsliterals.h", + "jsonhelper.h", "installoptions.cpp", "launcherinterface.cpp", "launcherinterface.h", diff --git a/src/lib/corelib/generators/generators.pri b/src/lib/corelib/generators/generators.pri index 093e45f40..e9730d895 100644 --- a/src/lib/corelib/generators/generators.pri +++ b/src/lib/corelib/generators/generators.pri @@ -3,13 +3,31 @@ include(../../../install_prefix.pri) SOURCES += \ $$PWD/generatableprojectiterator.cpp \ $$PWD/generator.cpp \ - $$PWD/generatordata.cpp + $$PWD/generatordata.cpp \ + $$PWD/generatorutils.cpp \ + $$PWD/generatorversioninfo.cpp \ + $$PWD/xmlproject.cpp \ + $$PWD/xmlprojectwriter.cpp\ + $$PWD/xmlproperty.cpp \ + $$PWD/xmlpropertygroup.cpp \ + $$PWD/xmlworkspace.cpp \ + $$PWD/xmlworkspacewriter.cpp HEADERS += \ $$PWD/generatableprojectiterator.h \ $$PWD/generator.h \ $$PWD/generatordata.h \ - $$PWD/igeneratableprojectvisitor.h + $$PWD/generatorutils.h \ + $$PWD/generatorversioninfo.h \ + $$PWD/igeneratableprojectvisitor.h \ + $$PWD/ixmlnodevisitor.h \ + $$PWD/ixmlnodevisitor.h \ + $$PWD/xmlproject.h \ + $$PWD/xmlprojectwriter.h \ + $$PWD/xmlproperty.h \ + $$PWD/xmlpropertygroup.h \ + $$PWD/xmlworkspace.h \ + $$PWD/xmlworkspacewriter.h !qbs_no_dev_install { generators_headers.files = \ diff --git a/src/lib/corelib/generators/generatorutils.cpp b/src/lib/corelib/generators/generatorutils.cpp new file mode 100644 index 000000000..9c00eef05 --- /dev/null +++ b/src/lib/corelib/generators/generatorutils.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "generatorutils.h" + +namespace qbs { +namespace gen { +namespace utils { + +QString architectureName(Architecture arch) +{ + switch (arch) { + case Architecture::Arm: + return QStringLiteral("arm"); + case Architecture::Avr: + return QStringLiteral("avr"); + case Architecture::Mcs51: + return QStringLiteral("mcs51"); + default: + return QStringLiteral("unknown"); + } +} + +Architecture architecture(const Project &qbsProject) +{ + const auto qbsArch = qbsProject.projectConfiguration() + .value(Internal::StringConstants::qbsModule()).toMap() + .value(QStringLiteral("architecture")).toString(); + + if (qbsArch == QLatin1String("arm")) + return Architecture::Arm; + if (qbsArch == QLatin1String("avr")) + return Architecture::Avr; + if (qbsArch == QLatin1String("mcs51")) + return Architecture::Mcs51; + if (qbsArch == QLatin1String("stm8")) + return Architecture::Stm8; + if (qbsArch == QLatin1String("msp430")) + return Architecture::Msp430; + return Architecture::Unknown; +} + +QString buildConfigurationName(const Project &qbsProject) +{ + return qbsProject.projectConfiguration() + .value(Internal::StringConstants::qbsModule()).toMap() + .value(QStringLiteral("configurationName")).toString(); +} + +int debugInformation(const ProductData &qbsProduct) +{ + return qbsProduct.moduleProperties().getModuleProperty( + Internal::StringConstants::qbsModule(), + QStringLiteral("debugInformation")) + .toInt(); +} + +QString buildRootPath(const Project &qbsProject) +{ + QDir dir(qbsProject.projectData().buildDirectory()); + dir.cdUp(); + return dir.absolutePath(); +} + +QString relativeFilePath(const QString &baseDirectory, + const QString &fullFilePath) +{ + return QDir(baseDirectory).relativeFilePath(fullFilePath); +} + +QString binaryOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct) +{ + return QDir(baseDirectory).relativeFilePath( + qbsProduct.buildDirectory()) + + QLatin1String("/bin"); +} + +QString objectsOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct) +{ + return QDir(baseDirectory).relativeFilePath( + qbsProduct.buildDirectory()) + + QLatin1String("/obj"); +} + +QString listingOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct) +{ + return QDir(baseDirectory).relativeFilePath( + qbsProduct.buildDirectory()) + + QLatin1String("/lst"); +} + +std::vector<ProductData> dependenciesOf(const ProductData &qbsProduct, + const GeneratableProject &genProject, + const QString &configurationName) +{ + std::vector<ProductData> result; + const auto depsNames = qbsProduct.dependencies(); + for (const auto &product : qAsConst(genProject.products)) { + const auto pt = product.type(); + if (!pt.contains(QLatin1String("staticlibrary"))) + continue; + const auto pn = product.name(); + if (!depsNames.contains(pn)) + continue; + result.push_back(product.data.value(configurationName)); + } + return result; +} + +QString targetBinary(const ProductData &qbsProduct) +{ + const auto type = qbsProduct.type(); + if (type.contains(QLatin1String("application"))) { + return QFileInfo(qbsProduct.targetExecutable()).fileName(); + } else if (type.contains(QLatin1String("staticlibrary"))) { + const auto artifacts = qbsProduct.targetArtifacts(); + for (const auto &artifact : artifacts) { + if (artifact.fileTags().contains(QLatin1String("staticlibrary"))) + return QFileInfo(artifact.filePath()).fileName(); + } + } + + return {}; +} + +QString targetBinaryPath(const QString &baseDirectory, + const ProductData &qbsProduct) +{ + return binaryOutputDirectory(baseDirectory, qbsProduct) + + QLatin1Char('/') + targetBinary(qbsProduct); +} + +QString cppStringModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName) +{ + return qbsProps.getModuleProperty( + Internal::StringConstants::cppModule(), + propertyName).toString().trimmed(); +} + +bool cppBooleanModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName) +{ + return qbsProps.getModuleProperty( + Internal::StringConstants::cppModule(), + propertyName).toBool(); +} + +int cppIntegerModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName) +{ + return qbsProps.getModuleProperty( + Internal::StringConstants::cppModule(), + propertyName).toInt(); +} + +QStringList cppStringModuleProperties(const PropertyMap &qbsProps, + const QStringList &propertyNames) +{ + QStringList properties; + for (const auto &propertyName : propertyNames) { + const auto entries = qbsProps.getModuleProperty( + Internal::StringConstants::cppModule(), + propertyName).toStringList(); + for (const auto &entry : entries) + properties.push_back(entry.trimmed()); + } + return properties; +} + +QVariantList cppVariantModuleProperties(const PropertyMap &qbsProps, + const QStringList &propertyNames) +{ + QVariantList properties; + for (const auto &propertyName : propertyNames) { + properties << qbsProps.getModuleProperty( + Internal::StringConstants::cppModule(), + propertyName).toList(); + } + return properties; +} + +static QString parseFlagValue(const QString &flagKey, + QStringList::const_iterator &flagIt, + const QStringList::const_iterator &flagEnd) +{ + if (flagIt->contains(QLatin1Char('='))) { + // In this case an option is in form of 'flagKey=<flagValue>'. + const auto parts = flagIt->split(QLatin1Char('=')); + if (parts.count() == 2) + return parts.at(1).trimmed(); + } else if (flagKey < *flagIt) { + // In this case an option is in form of 'flagKey<flagValue>'. + return flagIt->mid(flagKey.count()).trimmed(); + } else { + // In this case an option is in form of 'flagKey <flagValue>'. + ++flagIt; + if (flagIt < flagEnd && !flagIt->startsWith(QLatin1Char('-'))) + return (*flagIt).trimmed(); + } + return {}; +} + +QString firstFlagValue(const QStringList &flags, const QString &flagKey) +{ + const auto flagBegin = flags.cbegin(); + const auto flagEnd = flags.cend(); + auto flagIt = std::find_if(flagBegin, flagEnd, [flagKey](const QString &flag) { + return flag == flagKey || flag.startsWith(flagKey); + }); + if (flagIt == flagEnd) + return {}; + return parseFlagValue(flagKey, flagIt, flagEnd); +} + +QStringList allFlagValues(const QStringList &flags, const QString &flagKey) +{ + QStringList values; + const auto flagEnd = flags.cend(); + for (auto flagIt = flags.cbegin(); flagIt < flagEnd; ++flagIt) { + if (*flagIt == flagKey || flagIt->startsWith(flagKey)) { + const QString value = parseFlagValue(flagKey, flagIt, flagEnd); + if (!value.isEmpty()) + values.push_back(value); + } + } + return values; +} + +} // namespace utils +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/generatorutils.h b/src/lib/corelib/generators/generatorutils.h new file mode 100644 index 000000000..9348ab18c --- /dev/null +++ b/src/lib/corelib/generators/generatorutils.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_UTILS_H +#define GENERATORS_UTILS_H + +#include <qbs.h> + +#include <tools/qbs_export.h> +#include <tools/stringconstants.h> + +namespace qbs { +namespace gen { +namespace utils { + +enum class Architecture { + Arm, + Avr, + Mcs51, + Stm8, + Msp430, + Unknown +}; + +QBS_EXPORT QString architectureName(Architecture arch); +QBS_EXPORT Architecture architecture(const Project &qbsProject); +QBS_EXPORT QString buildConfigurationName(const Project &qbsProject); +QBS_EXPORT int debugInformation(const ProductData &qbsProduct); +QBS_EXPORT QString buildRootPath(const Project &qbsProject); +QBS_EXPORT QString relativeFilePath(const QString &baseDirectory, + const QString &fullFilePath); +QBS_EXPORT QString binaryOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct); +QBS_EXPORT QString objectsOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct); +QBS_EXPORT QString listingOutputDirectory(const QString &baseDirectory, + const ProductData &qbsProduct); +QBS_EXPORT std::vector<ProductData> dependenciesOf(const ProductData &qbsProduct, + const GeneratableProject &genProject, + const QString &configurationName); +QBS_EXPORT QString targetBinary(const ProductData &qbsProduct); +QBS_EXPORT QString targetBinaryPath(const QString &baseDirectory, + const ProductData &qbsProduct); +QBS_EXPORT QString cppStringModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName); +QBS_EXPORT bool cppBooleanModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName); +QBS_EXPORT int cppIntegerModuleProperty(const PropertyMap &qbsProps, + const QString &propertyName); +QBS_EXPORT QStringList cppStringModuleProperties(const PropertyMap &qbsProps, + const QStringList &propertyNames); +QBS_EXPORT QVariantList cppVariantModuleProperties(const PropertyMap &qbsProps, + const QStringList &propertyNames); +QBS_EXPORT QString firstFlagValue(const QStringList &flags, + const QString &flagKey); +QBS_EXPORT QStringList allFlagValues(const QStringList &flags, + const QString &flagKey); + +template <typename T> +bool inBounds(const T &value, const T &low, const T &high) +{ + return !(value < low) && !(high < value); +} + +} // namespace utils +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_UTILS_H diff --git a/src/lib/corelib/generators/generatorversioninfo.cpp b/src/lib/corelib/generators/generatorversioninfo.cpp new file mode 100644 index 000000000..3e2106b57 --- /dev/null +++ b/src/lib/corelib/generators/generatorversioninfo.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generatorversioninfo.h" + +namespace qbs { +namespace gen { + +VersionInfo::VersionInfo(const Version &version, + const std::set<utils::Architecture> &archs) + : m_version(version), m_archs(archs) +{ +} + +bool VersionInfo::operator<(const VersionInfo &other) const +{ + return m_version < other.m_version; +} + +bool VersionInfo::operator==(const VersionInfo &other) const +{ + return m_version == other.m_version + && m_archs == other.m_archs; +} + +Version VersionInfo::version() const +{ + return m_version; +} + +bool VersionInfo::containsArchitecture(utils::Architecture arch) const +{ + return m_archs.find(arch) != m_archs.cend(); +} + +int VersionInfo::marketingVersion() const +{ + return m_version.majorVersion(); +} + +quint32 qHash(const VersionInfo &info) +{ + return qHash(info.version().toString()); +} + +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/generatorversioninfo.h b/src/lib/corelib/generators/generatorversioninfo.h new file mode 100644 index 000000000..65bfcf685 --- /dev/null +++ b/src/lib/corelib/generators/generatorversioninfo.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATORS_VERSION_INFO_H +#define GENERATORS_VERSION_INFO_H + +#include "generatorutils.h" + +#include <tools/qbs_export.h> +#include <tools/version.h> + +#include <set> + +namespace qbs { +namespace gen { + +class QBS_EXPORT VersionInfo +{ +public: + VersionInfo(const Version &version, + const std::set<utils::Architecture> &archs); + virtual ~VersionInfo() = default; + + bool operator<(const VersionInfo &other) const; + bool operator==(const VersionInfo &other) const; + + Version version() const; + bool containsArchitecture(utils::Architecture arch) const; + + virtual int marketingVersion() const; + +private: + Version m_version; + std::set<utils::Architecture> m_archs; +}; + +quint32 qHash(const VersionInfo &info); + +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_VERSION_INFO_H diff --git a/src/lib/corelib/generators/ixmlnodevisitor.h b/src/lib/corelib/generators/ixmlnodevisitor.h new file mode 100644 index 000000000..d3d118975 --- /dev/null +++ b/src/lib/corelib/generators/ixmlnodevisitor.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_INODE_VISITOR_H +#define GENERATORS_XML_INODE_VISITOR_H + +#include <tools/qbs_export.h> + +#include <QtCore/qxmlstream.h> + +namespace qbs { +namespace gen { +namespace xml { + +class Project; +class Property; +class PropertyGroup; +class Workspace; + +class QBS_EXPORT INodeVisitor +{ +public: + virtual ~INodeVisitor() {} + + virtual void visitWorkspaceStart(const Workspace *workspace) { Q_UNUSED(workspace) } + virtual void visitWorkspaceEnd(const Workspace *workspace) { Q_UNUSED(workspace) } + + virtual void visitProjectStart(const Project *project) { Q_UNUSED(project) } + virtual void visitProjectEnd(const Project *project) { Q_UNUSED(project) } + + virtual void visitPropertyStart(const Property *property) = 0; + virtual void visitPropertyEnd(const Property *property) = 0; + + virtual void visitPropertyGroupStart(const PropertyGroup *propertyGroup) = 0; + virtual void visitPropertyGroupEnd(const PropertyGroup *propertyGroup) = 0; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_INODE_VISITOR_H diff --git a/src/lib/corelib/generators/xmlproject.cpp b/src/lib/corelib/generators/xmlproject.cpp new file mode 100644 index 000000000..e2ac951aa --- /dev/null +++ b/src/lib/corelib/generators/xmlproject.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "xmlproject.h" +#include "ixmlnodevisitor.h" + +namespace qbs { +namespace gen { +namespace xml { + +void Project::accept(INodeVisitor *visitor) const +{ + visitor->visitProjectStart(this); + + for (const auto &child : children()) + child->accept(visitor); + + visitor->visitProjectEnd(this); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlproject.h b/src/lib/corelib/generators/xmlproject.h new file mode 100644 index 000000000..a7f5b2b65 --- /dev/null +++ b/src/lib/corelib/generators/xmlproject.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_PROJECT_H +#define GENERATORS_XML_PROJECT_H + +#include "xmlproperty.h" + +#include <tools/qbs_export.h> + +#include <memory> + +namespace qbs { +namespace gen { +namespace xml { + +class QBS_EXPORT Project : public Property +{ +public: + void accept(INodeVisitor *visitor) const final; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_PROJECT_H diff --git a/src/lib/corelib/generators/xmlprojectwriter.cpp b/src/lib/corelib/generators/xmlprojectwriter.cpp new file mode 100644 index 000000000..5554e5935 --- /dev/null +++ b/src/lib/corelib/generators/xmlprojectwriter.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "xmlproject.h" +#include "xmlprojectwriter.h" +#include "xmlproperty.h" +#include "xmlpropertygroup.h" + +#include <ostream> + +namespace qbs { +namespace gen { +namespace xml { + +ProjectWriter::ProjectWriter(std::ostream *device) + : m_device(device) +{ + m_writer.reset(new QXmlStreamWriter(&m_buffer)); + m_writer->setAutoFormatting(true); +} + +bool ProjectWriter::write(const Project *project) +{ + m_buffer.clear(); + m_writer->writeStartDocument(); + project->accept(this); + m_writer->writeEndDocument(); + if (m_writer->hasError()) + return false; + m_device->write(&*std::begin(m_buffer), m_buffer.size()); + return m_device->good(); +} + +void ProjectWriter::visitPropertyStart(const Property *property) +{ + const auto value = property->value().toString(); + const auto name = QString::fromUtf8(property->name()); + m_writer->writeTextElement(name, value); +} + +void ProjectWriter::visitPropertyEnd(const Property *property) +{ + Q_UNUSED(property) +} + +void ProjectWriter::visitPropertyGroupStart(const PropertyGroup *propertyGroup) +{ + const auto name = QString::fromUtf8(propertyGroup->name()); + m_writer->writeStartElement(name); +} + +void ProjectWriter::visitPropertyGroupEnd(const PropertyGroup *propertyGroup) +{ + Q_UNUSED(propertyGroup) + m_writer->writeEndElement(); +} + +QXmlStreamWriter *ProjectWriter::writer() const +{ + return m_writer.get(); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlprojectwriter.h b/src/lib/corelib/generators/xmlprojectwriter.h new file mode 100644 index 000000000..8198de61c --- /dev/null +++ b/src/lib/corelib/generators/xmlprojectwriter.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_PROJECT_WRITER_H +#define GENERATORS_XML_PROJECT_WRITER_H + +#include "ixmlnodevisitor.h" + +#include <tools/qbs_export.h> + +#include <memory> + +namespace qbs { +namespace gen { +namespace xml { + +class QBS_EXPORT ProjectWriter : public INodeVisitor +{ + Q_DISABLE_COPY(ProjectWriter) +public: + explicit ProjectWriter(std::ostream *device); + bool write(const Project *project); + +protected: + QXmlStreamWriter *writer() const; + +private: + void visitPropertyStart(const Property *property) final; + void visitPropertyEnd(const Property *property) final; + + void visitPropertyGroupStart(const PropertyGroup *propertyGroup) final; + void visitPropertyGroupEnd(const PropertyGroup *propertyGroup) final; + + std::ostream *m_device = nullptr; + QByteArray m_buffer; + std::unique_ptr<QXmlStreamWriter> m_writer; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_PROJECT_WRITER_H diff --git a/src/lib/corelib/generators/xmlproperty.cpp b/src/lib/corelib/generators/xmlproperty.cpp new file mode 100644 index 000000000..2fe5a0147 --- /dev/null +++ b/src/lib/corelib/generators/xmlproperty.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "ixmlnodevisitor.h" +#include "xmlproperty.h" + +namespace qbs { +namespace gen { +namespace xml { + +Property::Property(QByteArray name, QVariant value) +{ + setName(std::move(name)); + setValue(std::move(value)); +} + +void Property::accept(INodeVisitor *visitor) const +{ + visitor->visitPropertyStart(this); + + for (const auto &child : children()) + child->accept(visitor); + + visitor->visitPropertyEnd(this); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlproperty.h b/src/lib/corelib/generators/xmlproperty.h new file mode 100644 index 000000000..795735881 --- /dev/null +++ b/src/lib/corelib/generators/xmlproperty.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_PROPERTY_H +#define GENERATORS_XML_PROPERTY_H + +#include <tools/qbs_export.h> + +#include <QtCore/qvariant.h> + +#include <memory> + +namespace qbs { +namespace gen { +namespace xml { + +class INodeVisitor; + +class QBS_EXPORT Property +{ + Q_DISABLE_COPY(Property) +public: + Property() = default; + explicit Property(QByteArray name, QVariant value); + virtual ~Property() = default; + + QByteArray name() const { return m_name; } + void setName(QByteArray name) { m_name = std::move(name); } + + QVariant value() const { return m_value; } + void setValue(QVariant value) { m_value = std::move(value); } + + template<class T> + T *appendChild(std::unique_ptr<T> child) { + const auto p = child.get(); + m_children.push_back(std::move(child)); + return p; + } + + template<class T, class... Args> + T *appendChild(Args&&... args) { + return appendChild(std::make_unique<T>(std::forward<Args>(args)...)); + } + + virtual void accept(INodeVisitor *visitor) const; + +protected: + const std::vector<std::unique_ptr<Property>> &children() const + { return m_children; } + +private: + QByteArray m_name; + QVariant m_value; + std::vector<std::unique_ptr<Property>> m_children; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_PROPERTY_H diff --git a/src/lib/corelib/generators/xmlpropertygroup.cpp b/src/lib/corelib/generators/xmlpropertygroup.cpp new file mode 100644 index 000000000..398d68e77 --- /dev/null +++ b/src/lib/corelib/generators/xmlpropertygroup.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "ixmlnodevisitor.h" +#include "xmlpropertygroup.h" + +namespace qbs { +namespace gen { +namespace xml { + +PropertyGroup::PropertyGroup(QByteArray name) +{ + setName(std::move(name)); +} + +void PropertyGroup::appendProperty(QByteArray name, QVariant value) +{ + appendChild<Property>(std::move(name), std::move(value)); +} + +void PropertyGroup::appendMultiLineProperty( + QByteArray key, QStringList values, QChar sep) +{ + const auto line = values.join(std::move(sep)); + appendProperty(std::move(key), QVariant::fromValue(line)); +} + +void PropertyGroup::accept(INodeVisitor *visitor) const +{ + visitor->visitPropertyGroupStart(this); + + for (const auto &child : children()) + child->accept(visitor); + + visitor->visitPropertyGroupEnd(this); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlpropertygroup.h b/src/lib/corelib/generators/xmlpropertygroup.h new file mode 100644 index 000000000..e63b515fc --- /dev/null +++ b/src/lib/corelib/generators/xmlpropertygroup.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_PROPERTY_GROUP_H +#define GENERATORS_XML_PROPERTY_GROUP_H + +#include "generatorversioninfo.h" +#include "xmlproperty.h" + +#include <tools/qbs_export.h> + +#include <memory> + +namespace qbs { + +class ProductData; +class Project; + +namespace gen { +namespace xml { + +class QBS_EXPORT PropertyGroup : public Property +{ +public: + explicit PropertyGroup(QByteArray name); + + void appendProperty(QByteArray name, QVariant value); + void appendMultiLineProperty(QByteArray key, QStringList values, + QChar sep = QLatin1Char(',')); + + void accept(INodeVisitor *visitor) const final; +}; + +class PropertyGroupFactory +{ +public: + virtual ~PropertyGroupFactory() = default; + virtual bool canCreate(utils::Architecture arch, + const Version &version) const = 0; + + virtual std::unique_ptr<PropertyGroup> create( + const qbs::Project &qbsProject, + const qbs::ProductData &qbsProduct, + const std::vector<ProductData> &qbsProductDeps) const = 0; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_PROPERTY_GROUP_H diff --git a/src/lib/corelib/generators/xmlworkspace.cpp b/src/lib/corelib/generators/xmlworkspace.cpp new file mode 100644 index 000000000..7ce3f5164 --- /dev/null +++ b/src/lib/corelib/generators/xmlworkspace.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ixmlnodevisitor.h" +#include "xmlproperty.h" +#include "xmlpropertygroup.h" +#include "xmlworkspace.h" + +namespace qbs { +namespace gen { +namespace xml { + +Workspace::Workspace(const QString &workspacePath) + : m_baseDirectory(QFileInfo(workspacePath).absoluteDir()) +{ +} + +void Workspace::accept(INodeVisitor *visitor) const +{ + visitor->visitWorkspaceStart(this); + + for (const auto &child : children()) + child->accept(visitor); + + visitor->visitWorkspaceEnd(this); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlworkspace.h b/src/lib/corelib/generators/xmlworkspace.h new file mode 100644 index 000000000..beab22c4a --- /dev/null +++ b/src/lib/corelib/generators/xmlworkspace.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATORS_XML_WWORKSPACE_H +#define GENERATORS_XML_WWORKSPACE_H + +#include "xmlproperty.h" + +#include <tools/qbs_export.h> + +#include <QtCore/qdir.h> + +namespace qbs { +namespace gen { +namespace xml { + +class QBS_EXPORT Workspace : public Property +{ +public: + explicit Workspace(const QString &workspacePath); + void accept(INodeVisitor *visitor) const final; + + virtual void addProject(const QString &projectPath) = 0; + +protected: + const QDir m_baseDirectory; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_WWORKSPACE_H diff --git a/src/lib/corelib/generators/xmlworkspacewriter.cpp b/src/lib/corelib/generators/xmlworkspacewriter.cpp new file mode 100644 index 000000000..c88cb06d0 --- /dev/null +++ b/src/lib/corelib/generators/xmlworkspacewriter.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "xmlproperty.h" +#include "xmlpropertygroup.h" +#include "xmlworkspace.h" +#include "xmlworkspacewriter.h" + +#include <ostream> + +namespace qbs { +namespace gen { +namespace xml { + +WorkspaceWriter::WorkspaceWriter(std::ostream *device) + : m_device(device) +{ + m_writer.reset(new QXmlStreamWriter(&m_buffer)); + m_writer->setAutoFormatting(true); +} + +bool WorkspaceWriter::write(const Workspace *workspace) +{ + m_buffer.clear(); + m_writer->writeStartDocument(); + workspace->accept(this); + m_writer->writeEndDocument(); + if (m_writer->hasError()) + return false; + m_device->write(&*std::begin(m_buffer), m_buffer.size()); + return m_device->good(); +} + +void WorkspaceWriter::visitPropertyStart(const Property *property) +{ + const auto value = property->value().toString(); + const auto name = QString::fromUtf8(property->name()); + m_writer->writeTextElement(name, value); +} + +void WorkspaceWriter::visitPropertyEnd(const Property *property) +{ + Q_UNUSED(property) +} + +void WorkspaceWriter::visitPropertyGroupStart(const PropertyGroup *propertyGroup) +{ + const auto name = QString::fromUtf8(propertyGroup->name()); + m_writer->writeStartElement(name); +} + +void WorkspaceWriter::visitPropertyGroupEnd(const PropertyGroup *propertyGroup) +{ + Q_UNUSED(propertyGroup) + m_writer->writeEndElement(); +} + +QXmlStreamWriter *WorkspaceWriter::writer() const +{ + return m_writer.get(); +} + +} // namespace xml +} // namespace gen +} // namespace qbs diff --git a/src/lib/corelib/generators/xmlworkspacewriter.h b/src/lib/corelib/generators/xmlworkspacewriter.h new file mode 100644 index 000000000..343face5d --- /dev/null +++ b/src/lib/corelib/generators/xmlworkspacewriter.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GENERATORS_XML_WORKSPACE_WRITER_H +#define GENERATORS_XML_WORKSPACE_WRITER_H + +#include "ixmlnodevisitor.h" + +#include <tools/qbs_export.h> + +#include <memory> + +namespace qbs { +namespace gen { +namespace xml { + +class QBS_EXPORT WorkspaceWriter : public INodeVisitor +{ + Q_DISABLE_COPY(WorkspaceWriter) +public: + explicit WorkspaceWriter(std::ostream *device); + bool write(const Workspace *workspace); + +protected: + QXmlStreamWriter *writer() const; + +private: + void visitPropertyStart(const Property *property) final; + void visitPropertyEnd(const Property *property) final; + + void visitPropertyGroupStart(const PropertyGroup *propertyGroup) final; + void visitPropertyGroupEnd(const PropertyGroup *propertyGroup) final; + + std::ostream *m_device = nullptr; + QByteArray m_buffer; + std::unique_ptr<QXmlStreamWriter> m_writer; +}; + +} // namespace xml +} // namespace gen +} // namespace qbs + +#endif // GENERATORS_XML_WORKSPACE_WRITER_H diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp index 55a9e1aac..375954133 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.cpp +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -205,7 +205,18 @@ private: result.second = false; return result; } - SVConverter converter(scriptClass, object, item->property(*propertyName), item, + const ValuePtr v = item->property(*propertyName); + + // This can happen when resolving shadow products. The error will be ignored + // in that case. + if (!v) { + const QString errorMessage = Tr::tr("Error setting up 'original'."); + extraScope = engine->currentContext()->throwError(errorMessage); + result.second = false; + return result; + } + + SVConverter converter(scriptClass, object, v, item, propertyName, data, &originalValue); converter.start(); } else { diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 67d60e05d..9c8f9da1d 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -688,6 +688,9 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, m_qbsVersion.toString())); } + resolveProbes(&dummyProductContext, projectItem); + projectContext.topLevelProject->probes << dummyProductContext.info.probes; + handleProfileItems(projectItem, &projectContext); QList<Item *> multiplexedProducts; @@ -699,9 +702,6 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, for (Item * const additionalProductItem : qAsConst(multiplexedProducts)) Item::addChild(projectItem, additionalProductItem); - resolveProbes(&dummyProductContext, projectItem); - projectContext.topLevelProject->probes << dummyProductContext.info.probes; - const QList<Item *> originalChildren = projectItem->children(); for (Item * const child : originalChildren) { switch (child->type()) { diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp index 5507e0842..75417ab0b 100644 --- a/src/lib/corelib/tools/buildoptions.cpp +++ b/src/lib/corelib/tools/buildoptions.cpp @@ -38,6 +38,9 @@ ****************************************************************************/ #include "buildoptions.h" +#include "jsonhelper.h" + +#include <QtCore/qjsonobject.h> #include <QtCore/qshareddata.h> #include <QtCore/qthread.h> @@ -413,4 +416,56 @@ bool operator==(const BuildOptions &bo1, const BuildOptions &bo2) && bo1.removeExistingInstallation() == bo2.removeExistingInstallation(); } +namespace Internal { +template<> JobLimits fromJson(const QJsonValue &limitsData) +{ + JobLimits limits; + const QJsonArray &limitsArray = limitsData.toArray(); + for (const QJsonValue &v : limitsArray) { + const QJsonObject limitData = v.toObject(); + QString pool; + int limit = 0; + setValueFromJson(pool, limitData, "pool"); + setValueFromJson(limit, limitData, "limit"); + if (!pool.isEmpty() && limit > 0) + limits.setJobLimit(pool, limit); + } + return limits; +} + +template<> CommandEchoMode fromJson(const QJsonValue &modeData) +{ + const QString modeString = modeData.toString(); + if (modeString == QLatin1String("silent")) + return CommandEchoModeSilent; + if (modeString == QLatin1String("command-line")) + return CommandEchoModeCommandLine; + if (modeString == QLatin1String("command-line-with-environment")) + return CommandEchoModeCommandLineWithEnvironment; + return CommandEchoModeSummary; +} +} // namespace Internal + +qbs::BuildOptions qbs::BuildOptions::fromJson(const QJsonObject &data) +{ + using namespace Internal; + BuildOptions opt; + setValueFromJson(opt.d->changedFiles, data, "changed-files"); + setValueFromJson(opt.d->filesToConsider, data, "files-to-consider"); + setValueFromJson(opt.d->activeFileTags, data, "active-file-tags"); + setValueFromJson(opt.d->jobLimits, data, "job-limits"); + setValueFromJson(opt.d->maxJobCount, data, "max-job-count"); + setValueFromJson(opt.d->dryRun, data, "dry-run"); + setValueFromJson(opt.d->keepGoing, data, "keep-going"); + setValueFromJson(opt.d->forceTimestampCheck, data, "check-timestamps"); + setValueFromJson(opt.d->forceOutputCheck, data, "check-outputs"); + setValueFromJson(opt.d->logElapsedTime, data, "log-time"); + setValueFromJson(opt.d->echoMode, data, "command-echo-mode"); + setValueFromJson(opt.d->install, data, "install"); + setValueFromJson(opt.d->removeExistingInstallation, data, "clean-install-root"); + setValueFromJson(opt.d->onlyExecuteRules, data, "only-execute-rules"); + setValueFromJson(opt.d->jobLimitsFromProjectTakePrecedence, data, "enforce-project-job-limits"); + return opt; +} + } // namespace qbs diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h index cea89d0ea..bd0fb22cb 100644 --- a/src/lib/corelib/tools/buildoptions.h +++ b/src/lib/corelib/tools/buildoptions.h @@ -47,6 +47,7 @@ #include <QtCore/qshareddata.h> QT_BEGIN_NAMESPACE +class QJsonObject; class QStringList; QT_END_NAMESPACE @@ -61,6 +62,8 @@ public: BuildOptions &operator=(const BuildOptions &other); ~BuildOptions(); + static BuildOptions fromJson(const QJsonObject &data); + QStringList filesToConsider() const; void setFilesToConsider(const QStringList &files); diff --git a/src/lib/corelib/tools/cleanoptions.cpp b/src/lib/corelib/tools/cleanoptions.cpp index 4fbe77b5d..b888fb1e8 100644 --- a/src/lib/corelib/tools/cleanoptions.cpp +++ b/src/lib/corelib/tools/cleanoptions.cpp @@ -38,6 +38,8 @@ ****************************************************************************/ #include "cleanoptions.h" +#include "jsonhelper.h" + #include <QtCore/qshareddata.h> namespace qbs { @@ -151,4 +153,14 @@ void CleanOptions::setLogElapsedTime(bool log) d->logElapsedTime = log; } +qbs::CleanOptions qbs::CleanOptions::fromJson(const QJsonObject &data) +{ + CleanOptions opt; + using namespace Internal; + setValueFromJson(opt.d->dryRun, data, "dry-run"); + setValueFromJson(opt.d->keepGoing, data, "keep-going"); + setValueFromJson(opt.d->logElapsedTime, data, "log-time"); + return opt; +} + } // namespace qbs diff --git a/src/lib/corelib/tools/cleanoptions.h b/src/lib/corelib/tools/cleanoptions.h index 3f67cf5a5..7827697bb 100644 --- a/src/lib/corelib/tools/cleanoptions.h +++ b/src/lib/corelib/tools/cleanoptions.h @@ -43,6 +43,10 @@ #include <QtCore/qshareddata.h> +QT_BEGIN_NAMESPACE +class QJsonObject; +QT_END_NAMESPACE + namespace qbs { namespace Internal { class CleanOptionsPrivate; } @@ -56,6 +60,8 @@ public: CleanOptions &operator=(CleanOptions &&other) Q_DECL_NOEXCEPT; ~CleanOptions(); + static CleanOptions fromJson(const QJsonObject &data); + bool dryRun() const; void setDryRun(bool dryRun); diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp index 2c6ade3b0..5eff378e1 100644 --- a/src/lib/corelib/tools/codelocation.cpp +++ b/src/lib/corelib/tools/codelocation.cpp @@ -41,9 +41,12 @@ #include <tools/fileinfo.h> #include <tools/persistence.h> #include <tools/qbsassert.h> +#include <tools/stringconstants.h> #include <QtCore/qdatastream.h> #include <QtCore/qdir.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> #include <QtCore/qregexp.h> #include <QtCore/qshareddata.h> #include <QtCore/qstring.h> @@ -134,6 +137,18 @@ QString CodeLocation::toString() const return str; } +QJsonObject CodeLocation::toJson() const +{ + QJsonObject obj; + if (!filePath().isEmpty()) + obj.insert(Internal::StringConstants::filePathKey(), filePath()); + if (line() != -1) + obj.insert(QStringLiteral("line"), line()); + if (column() != -1) + obj.insert(QStringLiteral("column"), column()); + return obj; +} + void CodeLocation::load(Internal::PersistentPool &pool) { const bool isValid = pool.load<bool>(); diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h index 3dc8f26b1..3e84ce2d1 100644 --- a/src/lib/corelib/tools/codelocation.h +++ b/src/lib/corelib/tools/codelocation.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE class QDataStream; +class QJsonObject; class QString; QT_END_NAMESPACE @@ -70,6 +71,7 @@ public: bool isValid() const; QString toString() const; + QJsonObject toJson() const; void load(Internal::PersistentPool &pool); void store(Internal::PersistentPool &pool) const; diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp index 185dc0531..fc0b9377e 100644 --- a/src/lib/corelib/tools/error.cpp +++ b/src/lib/corelib/tools/error.cpp @@ -41,7 +41,10 @@ #include "persistence.h" #include "qttools.h" +#include "stringconstants.h" +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> #include <QtCore/qshareddata.h> #include <QtCore/qstringlist.h> @@ -156,6 +159,14 @@ QString ErrorItem::toString() const return str += description(); } +QJsonObject ErrorItem::toJson() const +{ + QJsonObject data; + data.insert(Internal::StringConstants::descriptionProperty(), description()); + data.insert(Internal::StringConstants::locationKey(), codeLocation().toJson()); + return data; +} + class ErrorInfo::ErrorInfoPrivate : public QSharedData { @@ -248,7 +259,7 @@ void ErrorInfo::prepend(const QString &description, const CodeLocation &location * Most often, there will be one element in this list, but there can be more e.g. to illustrate * how an error condition propagates through several source files. */ -QList<ErrorItem> ErrorInfo::items() const +const QList<ErrorItem> ErrorInfo::items() const { return d->items; } @@ -282,6 +293,17 @@ QString ErrorInfo::toString() const return lines.join(QLatin1Char('\n')); } +QJsonObject ErrorInfo::toJson() const +{ + QJsonObject data; + data.insert(QLatin1String("is-internal"), isInternalError()); + QJsonArray itemsArray; + for (const ErrorItem &item : items()) + itemsArray.append(item.toJson()); + data.insert(QLatin1String("items"), itemsArray); + return data; +} + /*! * \brief Returns true if this error represents a bug in qbs, false otherwise. */ diff --git a/src/lib/corelib/tools/error.h b/src/lib/corelib/tools/error.h index 4832499af..abad85bad 100644 --- a/src/lib/corelib/tools/error.h +++ b/src/lib/corelib/tools/error.h @@ -47,6 +47,7 @@ #include <QtCore/qshareddata.h> QT_BEGIN_NAMESPACE +class QJsonObject; template <class T> class QList; class QString; class QStringList; @@ -68,6 +69,7 @@ public: QString description() const; CodeLocation codeLocation() const; QString toString() const; + QJsonObject toJson() const; bool isBacktraceItem() const; @@ -97,10 +99,11 @@ public: void append(const ErrorItem &item); void append(const QString &description, const CodeLocation &location = CodeLocation()); void prepend(const QString &description, const CodeLocation &location = CodeLocation()); - QList<ErrorItem> items() const; + const QList<ErrorItem> items() const; bool hasError() const { return !items().empty(); } void clear(); QString toString() const; + QJsonObject toJson() const; bool isInternalError() const; bool hasLocation() const; diff --git a/src/lib/corelib/tools/installoptions.cpp b/src/lib/corelib/tools/installoptions.cpp index 5cddae4ad..93fd54efe 100644 --- a/src/lib/corelib/tools/installoptions.cpp +++ b/src/lib/corelib/tools/installoptions.cpp @@ -36,9 +36,13 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include "installoptions.h" -#include "language/language.h" -#include <tools/stringconstants.h> + +#include "jsonhelper.h" +#include "stringconstants.h" + +#include <language/language.h> #include <QtCore/qdir.h> #include <QtCore/qshareddata.h> @@ -230,4 +234,17 @@ void InstallOptions::setLogElapsedTime(bool logElapsedTime) d->logElapsedTime = logElapsedTime; } +qbs::InstallOptions qbs::InstallOptions::fromJson(const QJsonObject &data) +{ + using namespace Internal; + InstallOptions opt; + setValueFromJson(opt.d->installRoot, data, "install-root"); + setValueFromJson(opt.d->useSysroot, data, "use-sysroot"); + setValueFromJson(opt.d->removeExisting, data, "clean-install-root"); + setValueFromJson(opt.d->dryRun, data, "dry-run"); + setValueFromJson(opt.d->keepGoing, data, "keep-going"); + setValueFromJson(opt.d->logElapsedTime, data, "log-time"); + return opt; +} + } // namespace qbs diff --git a/src/lib/corelib/tools/installoptions.h b/src/lib/corelib/tools/installoptions.h index 69e00aae5..16511aa3d 100644 --- a/src/lib/corelib/tools/installoptions.h +++ b/src/lib/corelib/tools/installoptions.h @@ -44,6 +44,7 @@ #include <QtCore/qshareddata.h> QT_BEGIN_NAMESPACE +class QJsonObject; class QString; QT_END_NAMESPACE @@ -65,6 +66,8 @@ public: InstallOptions &operator=(InstallOptions &&other) Q_DECL_NOEXCEPT; ~InstallOptions(); + static InstallOptions fromJson(const QJsonObject &data); + static QString defaultInstallRoot(); QString installRoot() const; void setInstallRoot(const QString &installRoot); diff --git a/src/lib/corelib/tools/jsonhelper.h b/src/lib/corelib/tools/jsonhelper.h new file mode 100644 index 000000000..d87802c0a --- /dev/null +++ b/src/lib/corelib/tools/jsonhelper.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_JSON_HELPER_H +#define QBS_JSON_HELPER_H + +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> +#include <QtCore/qprocess.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvariant.h> + +#include <algorithm> +#include <iterator> + +namespace qbs { +namespace Internal { + +template<typename T> inline T fromJson(const QJsonValue &v); +template<> inline bool fromJson(const QJsonValue &v) { return v.toBool(); } +template<> inline int fromJson(const QJsonValue &v) { return v.toInt(); } +template<> inline QString fromJson(const QJsonValue &v) { return v.toString(); } +template<> inline QStringList fromJson(const QJsonValue &v) +{ + const QJsonArray &jsonList = v.toArray(); + QStringList stringList; + std::transform(jsonList.begin(), jsonList.end(), std::back_inserter(stringList), + [](const QVariant &v) { return v.toString(); }); + return stringList; +} +template<> inline QVariantMap fromJson(const QJsonValue &v) { return v.toObject().toVariantMap(); } +template<> inline QProcessEnvironment fromJson(const QJsonValue &v) +{ + const QJsonObject obj = v.toObject(); + QProcessEnvironment env; + for (auto it = obj.begin(); it != obj.end(); ++it) + env.insert(it.key(), it.value().toString()); + return env; +} + +template<typename T> inline void setValueFromJson(T &targetValue, const QJsonObject &data, + const char *jsonProperty) +{ + const QJsonValue v = data.value(QLatin1String(jsonProperty)); + if (!v.isNull()) + targetValue = fromJson<T>(v); +} + +} // namespace Internal +} // namespace qbs + +#endif // Include guard diff --git a/src/lib/corelib/tools/processresult.cpp b/src/lib/corelib/tools/processresult.cpp index 12e45b251..3fb2f8dbc 100644 --- a/src/lib/corelib/tools/processresult.cpp +++ b/src/lib/corelib/tools/processresult.cpp @@ -39,6 +39,9 @@ #include "processresult.h" #include "processresult_p.h" +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> + /*! * \class SetupProjectParameters * \brief The \c ProcessResult class describes a finished qbs process command. @@ -129,4 +132,31 @@ QStringList ProcessResult::stdErr() const return d->stdErr; } +static QJsonValue processErrorToJson(QProcess::ProcessError error) +{ + switch (error) { + case QProcess::FailedToStart: return QLatin1String("failed-to-start"); + case QProcess::Crashed: return QLatin1String("crashed"); + case QProcess::Timedout: return QLatin1String("timed-out"); + case QProcess::WriteError: return QLatin1String("write-error"); + case QProcess::ReadError: return QLatin1String("read-error"); + case QProcess::UnknownError: return QStringLiteral("unknown-error"); + } + return {}; // For dumb compilers. +} + +QJsonObject qbs::ProcessResult::toJson() const +{ + return QJsonObject{ + {QStringLiteral("success"), success()}, + {QStringLiteral("executable-file-path"), executableFilePath()}, + {QStringLiteral("arguments"), QJsonArray::fromStringList(arguments())}, + {QStringLiteral("working-directory"), workingDirectory()}, + {QStringLiteral("error"), processErrorToJson(error())}, + {QStringLiteral("exit-code"), exitCode()}, + {QStringLiteral("stdout"), QJsonArray::fromStringList(stdOut())}, + {QStringLiteral("stderr"), QJsonArray::fromStringList(stdErr())} + }; +} + } // namespace qbs diff --git a/src/lib/corelib/tools/processresult.h b/src/lib/corelib/tools/processresult.h index 2d2ebbfb4..92408aa31 100644 --- a/src/lib/corelib/tools/processresult.h +++ b/src/lib/corelib/tools/processresult.h @@ -46,6 +46,7 @@ #include <QtCore/qprocess.h> QT_BEGIN_NAMESPACE +class QJsonObject; class QString; class QStringList; QT_END_NAMESPACE @@ -65,6 +66,8 @@ public: ProcessResult &operator=(const ProcessResult &other); ~ProcessResult(); + QJsonObject toJson() const; + bool success() const; QString executableFilePath() const; QStringList arguments() const; diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp index 6d817c8f3..41af7b926 100644 --- a/src/lib/corelib/tools/setupprojectparameters.cpp +++ b/src/lib/corelib/tools/setupprojectparameters.cpp @@ -42,6 +42,7 @@ #include <logging/translator.h> #include <tools/buildgraphlocker.h> #include <tools/installoptions.h> +#include <tools/jsonhelper.h> #include <tools/profile.h> #include <tools/qbsassert.h> #include <tools/scripttools.h> @@ -50,6 +51,7 @@ #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> #include <QtCore/qprocess.h> +#include <QtCore/qjsonobject.h> namespace qbs { namespace Internal { @@ -69,14 +71,14 @@ public: , forceProbeExecution(false) , waitLockBuildGraph(false) , restoreBehavior(SetupProjectParameters::RestoreAndTrackChanges) - , propertyCheckingMode(ErrorHandlingMode::Relaxed) + , propertyCheckingMode(ErrorHandlingMode::Strict) , productErrorMode(ErrorHandlingMode::Strict) { } QString projectFilePath; QString topLevelProfile; - QString configurationName; + QString configurationName = QLatin1String("default"); QString buildRoot; QStringList searchPaths; QStringList pluginPaths; @@ -121,6 +123,47 @@ SetupProjectParameters &SetupProjectParameters::operator=(const SetupProjectPara return *this; } +namespace Internal { +template<> ErrorHandlingMode fromJson(const QJsonValue &v) +{ + if (v.toString() == QLatin1String("relaxed")) + return ErrorHandlingMode::Relaxed; + return ErrorHandlingMode::Strict; +} + +template<> SetupProjectParameters::RestoreBehavior fromJson(const QJsonValue &v) +{ + const QString value = v.toString(); + if (value == QLatin1String("restore-only")) + return SetupProjectParameters::RestoreOnly; + if (value == QLatin1String("resolve-only")) + return SetupProjectParameters::ResolveOnly; + return SetupProjectParameters::RestoreAndTrackChanges; +} +} // namespace Internal + +SetupProjectParameters SetupProjectParameters::fromJson(const QJsonObject &data) +{ + using namespace Internal; + SetupProjectParameters params; + setValueFromJson(params.d->topLevelProfile, data, "top-level-profile"); + setValueFromJson(params.d->configurationName, data, "configuration-name"); + setValueFromJson(params.d->projectFilePath, data, "project-file-path"); + setValueFromJson(params.d->buildRoot, data, "build-root"); + setValueFromJson(params.d->settingsBaseDir, data, "settings-directory"); + setValueFromJson(params.d->overriddenValues, data, "overridden-properties"); + setValueFromJson(params.d->dryRun, data, "dry-run"); + setValueFromJson(params.d->logElapsedTime, data, "log-time"); + setValueFromJson(params.d->forceProbeExecution, data, "force-probe-execution"); + setValueFromJson(params.d->waitLockBuildGraph, data, "wait-lock-build-graph"); + setValueFromJson(params.d->fallbackProviderEnabled, data, "fallback-provider-enabled"); + setValueFromJson(params.d->environment, data, "environment"); + setValueFromJson(params.d->restoreBehavior, data, "restore-behavior"); + setValueFromJson(params.d->propertyCheckingMode, data, "error-handling-mode"); + params.d->productErrorMode = params.d->propertyCheckingMode; + return params; +} + SetupProjectParameters &SetupProjectParameters::operator=(SetupProjectParameters &&other) Q_DECL_NOEXCEPT = default; /*! diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h index cf3b200cb..a4d090ec5 100644 --- a/src/lib/corelib/tools/setupprojectparameters.h +++ b/src/lib/corelib/tools/setupprojectparameters.h @@ -71,6 +71,8 @@ public: SetupProjectParameters &operator=(const SetupProjectParameters &other); SetupProjectParameters &operator=(SetupProjectParameters &&other) Q_DECL_NOEXCEPT; + static SetupProjectParameters fromJson(const QJsonObject &data); + QString topLevelProfile() const; void setTopLevelProfile(const QString &profile); diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h index cd41f3768..79cbcd125 100644 --- a/src/lib/corelib/tools/stringconstants.h +++ b/src/lib/corelib/tools/stringconstants.h @@ -69,6 +69,7 @@ public: QBS_STRING_CONSTANT(baseNameProperty, "baseName") QBS_STRING_CONSTANT(baseProfileProperty, "baseProfile") QBS_STRING_CONSTANT(buildDirectoryProperty, "buildDirectory") + QBS_STRING_CONSTANT(buildDirectoryKey, "build-directory") QBS_STRING_CONSTANT(builtByDefaultProperty, "builtByDefault") QBS_STRING_CONSTANT(classNameProperty, "className") QBS_STRING_CONSTANT(completeBaseNameProperty, "completeBaseName") @@ -90,11 +91,13 @@ public: static const QString &fileNameProperty() { return fileName(); } static const QString &filePathProperty() { return filePath(); } static const QString &filePathVar() { return filePath(); } + QBS_STRING_CONSTANT(filePathKey, "file-path") QBS_STRING_CONSTANT(fileTagsFilterProperty, "fileTagsFilter") QBS_STRING_CONSTANT(fileTagsProperty, "fileTags") QBS_STRING_CONSTANT(filesProperty, "files") QBS_STRING_CONSTANT(filesAreTargetsProperty, "filesAreTargets") QBS_STRING_CONSTANT(foundProperty, "found") + QBS_STRING_CONSTANT(fullDisplayNameKey, "full-display-name") QBS_STRING_CONSTANT(imports, "imports") static const QString &importsDir() { return imports(); } static const QString &importsProperty() { return imports(); } @@ -106,12 +109,16 @@ public: QBS_STRING_CONSTANT(installPrefixProperty, "installPrefix") QBS_STRING_CONSTANT(installDirProperty, "installDir") QBS_STRING_CONSTANT(installSourceBaseProperty, "installSourceBase") + QBS_STRING_CONSTANT(isEnabledKey, "is-enabled") QBS_STRING_CONSTANT(jobCountProperty, "jobCount") QBS_STRING_CONSTANT(jobPoolProperty, "jobPool") QBS_STRING_CONSTANT(lengthProperty, "length") QBS_STRING_CONSTANT(limitToSubProjectProperty, "limitToSubProject") + QBS_STRING_CONSTANT(locationKey, "location") + QBS_STRING_CONSTANT(messageKey, "message") QBS_STRING_CONSTANT(minimumQbsVersionProperty, "minimumQbsVersion") QBS_STRING_CONSTANT(moduleNameProperty, "moduleName") + QBS_STRING_CONSTANT(modulePropertiesKey, "module-properties") QBS_STRING_CONSTANT(moduleProviders, "moduleProviders") QBS_STRING_CONSTANT(multiplexByQbsPropertiesProperty, "multiplexByQbsProperties") QBS_STRING_CONSTANT(multiplexConfigurationIdProperty, "multiplexConfigurationId") @@ -135,6 +142,7 @@ public: QBS_STRING_CONSTANT(profileProperty, "profile") static const QString &profilesProperty() { return profiles(); } QBS_STRING_CONSTANT(productTypesProperty, "productTypes") + QBS_STRING_CONSTANT(productsKey, "products") QBS_STRING_CONSTANT(qbsSearchPathsProperty, "qbsSearchPaths") QBS_STRING_CONSTANT(referencesProperty, "references") QBS_STRING_CONSTANT(recursiveProperty, "recursive") @@ -149,7 +157,8 @@ public: QBS_STRING_CONSTANT(sourceDirectoryProperty, "sourceDirectory") QBS_STRING_CONSTANT(submodulesProperty, "submodules") QBS_STRING_CONSTANT(targetNameProperty, "targetName") - QBS_STRING_CONSTANT(typeProperty, "type") + static const QString &typeProperty() { return type(); } + QBS_STRING_CONSTANT(type, "type") QBS_STRING_CONSTANT(validateProperty, "validate") QBS_STRING_CONSTANT(versionProperty, "version") QBS_STRING_CONSTANT(versionAtLeastProperty, "versionAtLeast") diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri index f9c6be9a5..89d752671 100644 --- a/src/lib/corelib/tools/tools.pri +++ b/src/lib/corelib/tools/tools.pri @@ -23,6 +23,7 @@ HEADERS += \ $$PWD/iosutils.h \ $$PWD/joblimits.h \ $$PWD/jsliterals.h \ + $$PWD/jsonhelper.h \ $$PWD/launcherinterface.h \ $$PWD/launcherpackets.h \ $$PWD/launchersocket.h \ diff --git a/src/lib/corelib/tools/version.cpp b/src/lib/corelib/tools/version.cpp index d2b337d3a..dfb7f49b7 100644 --- a/src/lib/corelib/tools/version.cpp +++ b/src/lib/corelib/tools/version.cpp @@ -105,13 +105,17 @@ Version Version::fromString(const QString &versionString, bool buildNumberAllowe return Version{majorNr, minorNr, patchNr, buildNr}; } -QString Version::toString() const +QString Version::toString(const QChar &separator, const QChar &buildSeparator) const { if (m_build) { - return QString(QStringLiteral("%1.%2.%3-%4")) - .arg(m_major).arg(m_minor).arg(m_patch).arg(m_build); + return QStringLiteral("%1%5%2%5%3%6%4") + .arg(QString::number(m_major), QString::number(m_minor), + QString::number(m_patch), QString::number(m_build), + separator, buildSeparator); } - return QString(QStringLiteral("%1.%2.%3")).arg(m_major).arg(m_minor).arg(m_patch); + return QStringLiteral("%1%4%2%4%3") + .arg(QString::number(m_major), QString::number(m_minor), + QString::number(m_patch), separator); } int compare(const Version &lhs, const Version &rhs) diff --git a/src/lib/corelib/tools/version.h b/src/lib/corelib/tools/version.h index 4f5e46500..a0239a6e4 100644 --- a/src/lib/corelib/tools/version.h +++ b/src/lib/corelib/tools/version.h @@ -42,6 +42,7 @@ #include "qbs_export.h" +#include <QtCore/qchar.h> #include <QtCore/qglobal.h> QT_BEGIN_NAMESPACE @@ -71,7 +72,8 @@ public: void setBuildNumber(int nr); static Version fromString(const QString &versionString, bool buildNumberAllowed = false); - QString toString() const; + QString toString(const QChar &separator = QLatin1Char('.'), + const QChar &buildSeparator = QLatin1Char('-')) const; private: int m_major; |