diff options
author | Maurice Kalinowski <maurice.kalinowski@qt.io> | 2018-12-14 13:19:06 +0100 |
---|---|---|
committer | Maurice Kalinowski <maurice.kalinowski@qt.io> | 2018-12-20 08:51:17 +0000 |
commit | f5d0032d763435c6ab2a9d4015882878acff1589 (patch) | |
tree | 5bf104157883697737c59444b40427a4cad7dd83 | |
parent | 306058b96ab8219a5176118e7cbe25d12b2e6aa6 (diff) |
Add console examples
Other solutions provide minimal console applications for publishing and
subscribing, which can then be used as tools.
Task-number: QTBUG-69995
Task-number: QTBUG-71957
Change-Id: Ica4a856e25b540d089e1aa20f853b780c18d59dd
Reviewed-by: hjk <hjk@qt.io>
-rw-r--r-- | examples/mqtt/consolepubsub/configuration.h | 307 | ||||
-rw-r--r-- | examples/mqtt/consolepubsub/consolepubsub.pro | 6 | ||||
-rw-r--r-- | examples/mqtt/consolepubsub/main_pub.cpp | 112 | ||||
-rw-r--r-- | examples/mqtt/consolepubsub/main_sub.cpp | 112 | ||||
-rw-r--r-- | examples/mqtt/consolepubsub/qtmqtt_pub.pro | 25 | ||||
-rw-r--r-- | examples/mqtt/consolepubsub/qtmqtt_sub.pro | 25 | ||||
-rw-r--r-- | examples/mqtt/mqtt.pro | 1 |
7 files changed, 588 insertions, 0 deletions
diff --git a/examples/mqtt/consolepubsub/configuration.h b/examples/mqtt/consolepubsub/configuration.h new file mode 100644 index 0000000..b25920f --- /dev/null +++ b/examples/mqtt/consolepubsub/configuration.h @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QByteArray> +#include <QCommandLineParser> +#include <QCommandLineOption> +#include <QDir> +#include <QFile> +#include <QFileInfo> +#include <QLoggingCategory> +#include <QString> +#include <QMqttClient> +#include <QSslSocket> + +struct Configuration +{ + QString topic; + QByteArray content; + quint8 qos; + bool retain{false}; + bool useEncryption{false}; +}; + +QMqttClient *createClientWithConfiguration(QCoreApplication *app, + Configuration *msg, + bool publish) +{ + QCommandLineParser parser; + if (publish) + parser.setApplicationDescription("Qt MQTT publish tool"); + else + parser.setApplicationDescription("Qt MQTT subscription tool"); + + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption optionDebug("d", + QLatin1String("Enable debug messages / logging categories")); + parser.addOption(optionDebug); + + QCommandLineOption optionFile("f", + QLatin1String("Specify the content of a file as message."), + QLatin1String("filename")); + if (publish) + parser.addOption(optionFile); + + QCommandLineOption optionClientId("i", + QLatin1String("Specify a client ID. Defaults to random value."), + QLatin1String("clientid")); + parser.addOption(optionClientId); + + QCommandLineOption optionKeepAlive("k", + QLatin1String("Specify the keep-alive value in seconds."), + QLatin1String("keepAlive")); + parser.addOption(optionKeepAlive); + + QCommandLineOption optionMessageContent("m", + QLatin1String("Specify the message content. Defaults to" + " a null message."), + QLatin1String("messageContent")); + if (publish) + parser.addOption(optionMessageContent); + + QCommandLineOption optionPassword("P", + QLatin1String("Provide a password."), + QLatin1String("password")); + parser.addOption(optionPassword); + + QCommandLineOption optionPort("p", + QLatin1String("Network port to connect to. Defaults to 1883."), + QLatin1String("hostPort"), + QLatin1String("1883")); + parser.addOption(optionPort); + + QCommandLineOption optionQos("q", + QLatin1String("Quality of service level to use for all messages." + "Defaults to 0."), + QLatin1String("qos"), + QLatin1String("0")); + parser.addOption(optionQos); + + QCommandLineOption optionRetain("r", + QLatin1String("Specify the retain flag for a message.")); + if (publish) + parser.addOption(optionRetain); + + QCommandLineOption optionHost("s", + QLatin1String("MQTT server to connect to. Defaults to localhost."), + QLatin1String("hostName"), + QLatin1String("localhost")); + parser.addOption(optionHost); + + QCommandLineOption optionMessageTopic("t", + QLatin1String("Specify the message topic."), + QLatin1String("messageTopic")); + parser.addOption(optionMessageTopic); + + QCommandLineOption optionUser("u", + QLatin1String("Provide a username."), + QLatin1String("username")); + parser.addOption(optionUser); + + QCommandLineOption optionVersion("V", + QLatin1String("Specify the protocol version. Options are " + "mqtt31, mqtt311, mqtt5. Defaults to mqtt311."), + QLatin1String("protocolVersion"), + QLatin1String("mqtt311")); + parser.addOption(optionVersion); + + QCommandLineOption optionCaFile("cafile", + QLatin1String("Specify a file containing trusted CA " + "certificates to enable encrypted communication."), + QLatin1String("cafile")); + parser.addOption(optionCaFile); + + QCommandLineOption optionCaPath("capath", + QLatin1String("Specify a directory containing trusted CA " + "certificates to enable encrypted communication."), + QLatin1String("capath")); + parser.addOption(optionCaPath); + + parser.process(*app); + + auto *client = new QMqttClient(app); + + client->setHostname(parser.value(optionHost)); + bool ok = true; + quint16 port = static_cast<quint16>(parser.value(optionPort).toInt(&ok)); + if (!ok) { + qWarning() << "Invalid port specified:" << parser.value(optionPort); + return nullptr; + } + client->setPort(port); + + if (parser.isSet(optionUser)) + client->setUsername(parser.value(optionUser)); + if (parser.isSet(optionPassword)) + client->setPassword(parser.value(optionPassword)); + if (parser.isSet(optionClientId)) + client->setClientId(parser.value(optionClientId)); + if (parser.isSet(optionVersion)) { + const QString version = parser.value(optionVersion); + if (version == QLatin1String("mqtt31")) + client->setProtocolVersion(QMqttClient::MQTT_3_1); + else if (version == QLatin1String("mqtt311")) + client->setProtocolVersion(QMqttClient::MQTT_3_1_1); + else if (version == QLatin1String("mqtt5")) + client->setProtocolVersion(QMqttClient::MQTT_5_0); + else { + qWarning() << "Invalid protocol version specified:" << version; + return nullptr; + } + } + + if (parser.isSet(optionCaFile) || parser.isSet(optionCaPath)) { +#ifdef QT_NO_SSL + qWarning() << "Qt has not been compiled with SSL support."; + return nullptr; +#else + QList<QString> fileNames; + if (parser.isSet(optionCaFile)) + fileNames.append(parser.value(optionCaFile)); + + if (parser.isSet(optionCaPath)) { + QFileInfo path(parser.value(optionCaPath)); + if (!path.isDir()) { + qWarning() << "Specified capath is not a directory"; + return nullptr; + } + auto entries = QDir(parser.value(optionCaPath)).entryInfoList(QDir::Files | QDir::NoDotAndDotDot); + for (auto entry : entries) + fileNames.append(entry.absoluteFilePath()); + } + if (fileNames.isEmpty()) { + qWarning() << "No certificate file found."; + return nullptr; + } + + QList<QSslCertificate> defaultCerts; + for (auto it : fileNames) { + auto certificates = QSslCertificate::fromPath(it); + if (certificates.isEmpty() && parser.isSet(optionDebug)) + qWarning() << "File " << it << " does not contain any certificates"; + defaultCerts.append(certificates); + } + if (defaultCerts.isEmpty()) { + qWarning() << "No certificate could be loaded."; + return nullptr; + } + + QSslSocket::addDefaultCaCertificates(defaultCerts); + msg->useEncryption = true; +#endif + } + + if (parser.isSet(optionDebug)) + QLoggingCategory::setFilterRules(QLatin1String("qt.mqtt.*=true")); + + msg->qos = static_cast<quint8>(parser.value(optionQos).toInt(&ok)); + if (!ok || msg->qos > 2) { + qWarning() << "Invalid quality of service for message specified:" << msg->qos; + return nullptr; + } + + if (parser.isSet(optionKeepAlive)) { + const quint16 keep = static_cast<quint16>(parser.value(optionKeepAlive).toUInt(&ok)); + if (!ok) { + qWarning() << "Invalid keep alive value specified"; + return nullptr; + } + client->setKeepAlive(keep); + } + + if (publish) { + if (parser.isSet(optionFile) && parser.isSet(optionMessageContent)) { + qWarning() << "You cannot specify a file and a text as message."; + return nullptr; + } + if (parser.isSet(optionFile)) { + QFile file(parser.value(optionFile)); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open specified file for reading."; + return nullptr; + } + msg->content = file.readAll(); + file.close(); + } + if (parser.isSet(optionMessageContent)) + msg->content = parser.value(optionMessageContent).toUtf8(); + } + + if (!parser.isSet(optionMessageTopic)) { + qWarning() << "You must specify a topic to publish a message."; + return nullptr; + } + msg->topic = parser.value(optionMessageTopic); + + if (publish && !QMqttTopicName(msg->topic).isValid()) { + qWarning() << "The specified message topic is invalid."; + return nullptr; + } + + if (!publish && !QMqttTopicFilter(msg->topic).isValid()) { + qWarning() << "The specified subscription topic is invalid."; + return nullptr; + } + + if (publish && parser.isSet(optionRetain)) + msg->retain = true; + + // Output: + qInfo() << "Client configuration:"; + qInfo() << " Host:" << client->hostname() << " Port:" << client->port() + << " Protocol:" << client->protocolVersion(); + qInfo() << " Username:" << client->username() << " Password:" << !client->password().isEmpty(); + qInfo() << " Client ID:" << client->clientId() << "Keep Alive:" << client->keepAlive(); + + return client; +} + diff --git a/examples/mqtt/consolepubsub/consolepubsub.pro b/examples/mqtt/consolepubsub/consolepubsub.pro new file mode 100644 index 0000000..6aee476 --- /dev/null +++ b/examples/mqtt/consolepubsub/consolepubsub.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + qtmqtt_pub.pro \ + qtmqtt_sub.pro + diff --git a/examples/mqtt/consolepubsub/main_pub.cpp b/examples/mqtt/consolepubsub/main_pub.cpp new file mode 100644 index 0000000..b47fb8e --- /dev/null +++ b/examples/mqtt/consolepubsub/main_pub.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "configuration.h" + +#include <QCoreApplication> +#include <QMqttClient> +#include <QSslSocket> +#include <QTimer> + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral("qtmqtt_pub")); + QCoreApplication::setApplicationVersion(QStringLiteral("1.0")); + + // Create the client + Configuration description; + auto *client = createClientWithConfiguration(&a, &description, true); + + if (!client) + return -1; + + a.connect(client, &QMqttClient::errorChanged, [&client](const QMqttClient::ClientError e) { + if (e == QMqttClient::NoError) + return; + + qWarning() << "Error Occurred:" << e << " Client state:" << client->state(); + client->disconnectFromHost(); + }); + + a.connect(client, &QMqttClient::messageSent, [&client] (quint32 id) { + qInfo() << "Message with ID:" << id << " sent"; + client->disconnectFromHost(); + }); + + a.connect(client, &QMqttClient::stateChanged, [&client] (QMqttClient::ClientState s) { + if (s == QMqttClient::Disconnected) { + client->deleteLater(); + qApp->quit(); + } + }); + + a.connect(client, &QMqttClient::connected, [&client, description]() { + qInfo() << "Message:"; + qInfo() << " Topic:" << description.topic << " QoS:" << description.qos + << " Retain:" << description.retain; + qInfo() << " Content: " << description.content.left(50); + client->publish(description.topic, + description.content, + description.qos, + description.retain); + if (description.qos == 0)// 0 has no acknowledgment + QTimer::singleShot(500, client, &QMqttClient::disconnectFromHost); + }); + +#ifndef QT_NO_SSL + if (description.useEncryption) + client->connectToHostEncrypted(); + else +#endif + client->connectToHost(); + + return a.exec(); +} diff --git a/examples/mqtt/consolepubsub/main_sub.cpp b/examples/mqtt/consolepubsub/main_sub.cpp new file mode 100644 index 0000000..9e94037 --- /dev/null +++ b/examples/mqtt/consolepubsub/main_sub.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "configuration.h" + +#include <QCoreApplication> +#include <QMqttClient> +#include <QSslSocket> +#include <QTimer> + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral("qtmqtt_sub")); + QCoreApplication::setApplicationVersion(QStringLiteral("1.0")); + + // Create the client + Configuration description; + auto *client = createClientWithConfiguration(&a, &description, false); + + if (!client) + return -1; + + a.connect(client, &QMqttClient::errorChanged, [&client](const QMqttClient::ClientError e) { + if (e == QMqttClient::NoError) + return; + + qWarning() << "Error Occurred:" << e << " Client state:" << client->state(); + client->disconnectFromHost(); + }); + + a.connect(client, &QMqttClient::stateChanged, [&client] (QMqttClient::ClientState s) { + if (s == QMqttClient::Disconnected) { + client->deleteLater(); + qApp->quit(); + } + }); + + a.connect(client, &QMqttClient::connected, [&client, description]() { + auto sub = client->subscribe(description.topic, description.qos); + client->connect(sub, &QMqttSubscription::stateChanged, [&client](QMqttSubscription::SubscriptionState s) { + qInfo() << "Subscription state:" << s; + if (s == QMqttSubscription::Unsubscribed) + client->disconnectFromHost(); + }); + + client->connect(sub, &QMqttSubscription::messageReceived, [](const QMqttMessage &msg) { + qInfo() << "ID:" << msg.id() + << "Topic:" << msg.topic().name() + << "QoS:" << msg.qos() + << "Retain:" << msg.retain() + << "Duplicate:" << msg.duplicate() + << "Payload:" << msg.payload().left(50) << (msg.payload().size() > 50 ? "..." : ""); + }); + }); + +#ifndef QT_NO_SSL + if (description.useEncryption) + client->connectToHostEncrypted(); + else +#endif + client->connectToHost(); + + return a.exec(); +} diff --git a/examples/mqtt/consolepubsub/qtmqtt_pub.pro b/examples/mqtt/consolepubsub/qtmqtt_pub.pro new file mode 100644 index 0000000..bfa84b7 --- /dev/null +++ b/examples/mqtt/consolepubsub/qtmqtt_pub.pro @@ -0,0 +1,25 @@ +QT -= gui +QT += mqtt + +CONFIG += c++11 console +CONFIG -= app_bundle + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main_pub.cpp + +HEADERS += \ + configuration.h + +target.path = $$[QT_INSTALL_EXAMPLES]/mqtt/consolepubsub +INSTALLS += target diff --git a/examples/mqtt/consolepubsub/qtmqtt_sub.pro b/examples/mqtt/consolepubsub/qtmqtt_sub.pro new file mode 100644 index 0000000..c022650 --- /dev/null +++ b/examples/mqtt/consolepubsub/qtmqtt_sub.pro @@ -0,0 +1,25 @@ +QT -= gui +QT += mqtt + +CONFIG += c++11 console +CONFIG -= app_bundle + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main_sub.cpp + +HEADERS += \ + configuration.h + +target.path = $$[QT_INSTALL_EXAMPLES]/mqtt/consolepubsub +INSTALLS += target diff --git a/examples/mqtt/mqtt.pro b/examples/mqtt/mqtt.pro index c734248..db620d0 100644 --- a/examples/mqtt/mqtt.pro +++ b/examples/mqtt/mqtt.pro @@ -1,5 +1,6 @@ TEMPLATE = subdirs SUBDIRS += \ + consolepubsub \ simpleclient \ subscriptions |