forked from qt-creator/qt-creator
Android: Add licensesRecipe
Change-Id: I19fcd9ac354d1ea0100126c8c3640a19256b2a9e Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <solutions/tasking/tasktreerunner.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/async.h>
|
#include <utils/async.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
@@ -30,6 +32,7 @@ Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
|||||||
const char commonArgsKey[] = "Common Arguments:";
|
const char commonArgsKey[] = "Common Arguments:";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace Tasking;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
@@ -115,10 +118,10 @@ private:
|
|||||||
OutputFormatter *m_formatter = nullptr;
|
OutputFormatter *m_formatter = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
const int sdkManagerCmdTimeoutS = 60;
|
static QString sdkRootArg(const AndroidConfig &config)
|
||||||
const int sdkManagerOperationTimeoutS = 600;
|
{
|
||||||
|
return "--sdk_root=" + config.sdkLocation().toString();
|
||||||
using SdkCmdPromise = QPromise<AndroidSdkManager::OperationOutput>;
|
}
|
||||||
|
|
||||||
static const QRegularExpression &assertionRegExp()
|
static const QRegularExpression &assertionRegExp()
|
||||||
{
|
{
|
||||||
@@ -129,6 +132,108 @@ static const QRegularExpression &assertionRegExp()
|
|||||||
return theRegExp;
|
return theRegExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::optional<int> parseProgress(const QString &out)
|
||||||
|
{
|
||||||
|
if (out.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
static const QRegularExpression reg("(?<progress>\\d*)%");
|
||||||
|
static const QRegularExpression regEndOfLine("[\\n\\r]");
|
||||||
|
const QStringList lines = out.split(regEndOfLine, Qt::SkipEmptyParts);
|
||||||
|
std::optional<int> progress;
|
||||||
|
for (const QString &line : lines) {
|
||||||
|
QRegularExpressionMatch match = reg.match(line);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
const int parsedProgress = match.captured("progress").toInt();
|
||||||
|
if (parsedProgress >= 0 && parsedProgress <= 100)
|
||||||
|
progress = parsedProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DialogStorage
|
||||||
|
{
|
||||||
|
DialogStorage() { m_dialog.reset(new QuestionProgressDialog(Core::ICore::dialogParent())); };
|
||||||
|
std::unique_ptr<QuestionProgressDialog> m_dialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GroupItem licensesRecipe(const Storage<DialogStorage> &dialogStorage)
|
||||||
|
{
|
||||||
|
struct OutputData
|
||||||
|
{
|
||||||
|
QString buffer;
|
||||||
|
int current = 0;
|
||||||
|
int total = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Storage<OutputData> outputStorage;
|
||||||
|
|
||||||
|
const auto onLicenseSetup = [dialogStorage, outputStorage](Process &process) {
|
||||||
|
QuestionProgressDialog *dialog = dialogStorage->m_dialog.get();
|
||||||
|
dialog->setProgress(0);
|
||||||
|
dialog->appendMessage(Tr::tr("Checking pending licenses...") + "\n", NormalMessageFormat);
|
||||||
|
dialog->appendMessage(Tr::tr("The installation of Android SDK packages may fail if the "
|
||||||
|
"respective licenses are not accepted.") + "\n\n",
|
||||||
|
LogMessageFormat);
|
||||||
|
process.setProcessMode(ProcessMode::Writer);
|
||||||
|
process.setEnvironment(androidConfig().toolsEnvironment());
|
||||||
|
process.setCommand(CommandLine(androidConfig().sdkManagerToolPath(),
|
||||||
|
{"--licenses", sdkRootArg(androidConfig())}));
|
||||||
|
process.setUseCtrlCStub(true);
|
||||||
|
|
||||||
|
Process *processPtr = &process;
|
||||||
|
OutputData *outputPtr = outputStorage.activeStorage();
|
||||||
|
QObject::connect(processPtr, &Process::readyReadStandardOutput, dialog,
|
||||||
|
[processPtr, outputPtr, dialog] {
|
||||||
|
QTextCodec *codec = QTextCodec::codecForLocale();
|
||||||
|
const QString stdOut = codec->toUnicode(processPtr->readAllRawStandardOutput());
|
||||||
|
outputPtr->buffer += stdOut;
|
||||||
|
dialog->appendMessage(stdOut, StdOutFormat);
|
||||||
|
const auto progress = parseProgress(stdOut);
|
||||||
|
if (progress)
|
||||||
|
dialog->setProgress(*progress);
|
||||||
|
if (assertionRegExp().match(outputPtr->buffer).hasMatch()) {
|
||||||
|
dialog->setQuestionVisible(true);
|
||||||
|
dialog->setQuestionEnabled(true);
|
||||||
|
if (outputPtr->total == 0) {
|
||||||
|
// Example output to match:
|
||||||
|
// 5 of 6 SDK package licenses not accepted.
|
||||||
|
// Review licenses that have not been accepted (y/N)?
|
||||||
|
static const QRegularExpression reg(R"(((?<steps>\d+)\sof\s)\d+)");
|
||||||
|
const QRegularExpressionMatch match = reg.match(outputPtr->buffer);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
outputPtr->total = match.captured("steps").toInt();
|
||||||
|
const QByteArray reply = "y\n";
|
||||||
|
dialog->appendMessage(QString::fromUtf8(reply), NormalMessageFormat);
|
||||||
|
processPtr->writeRaw(reply);
|
||||||
|
dialog->setProgress(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputPtr->buffer.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(dialog, &QuestionProgressDialog::answerClicked, processPtr,
|
||||||
|
[processPtr, outputPtr, dialog](bool accepted) {
|
||||||
|
dialog->setQuestionEnabled(false);
|
||||||
|
const QByteArray reply = accepted ? "y\n" : "n\n";
|
||||||
|
dialog->appendMessage(QString::fromUtf8(reply), NormalMessageFormat);
|
||||||
|
processPtr->writeRaw(reply);
|
||||||
|
++outputPtr->current;
|
||||||
|
if (outputPtr->total != 0)
|
||||||
|
dialog->setProgress(outputPtr->current * 100.0 / outputPtr->total);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Group { outputStorage, ProcessTask(onLicenseSetup) };
|
||||||
|
}
|
||||||
|
|
||||||
|
const int sdkManagerCmdTimeoutS = 60;
|
||||||
|
const int sdkManagerOperationTimeoutS = 600;
|
||||||
|
|
||||||
|
using SdkCmdPromise = QPromise<AndroidSdkManager::OperationOutput>;
|
||||||
|
|
||||||
int parseProgress(const QString &out, bool &foundAssertion)
|
int parseProgress(const QString &out, bool &foundAssertion)
|
||||||
{
|
{
|
||||||
int progress = -1;
|
int progress = -1;
|
||||||
@@ -161,10 +266,6 @@ void watcherDeleter(QFutureWatcher<void> *watcher)
|
|||||||
delete watcher;
|
delete watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString sdkRootArg(const AndroidConfig &config)
|
|
||||||
{
|
|
||||||
return "--sdk_root=" + config.sdkLocation().toString();
|
|
||||||
}
|
|
||||||
/*!
|
/*!
|
||||||
Runs the \c sdkmanger tool with arguments \a args. Returns \c true if the command is
|
Runs the \c sdkmanger tool with arguments \a args. Returns \c true if the command is
|
||||||
successfully executed. Output is copied into \a output. The function blocks the calling thread.
|
successfully executed. Output is copied into \a output. The function blocks the calling thread.
|
||||||
|
|||||||
Reference in New Issue
Block a user