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:
Jarek Kobus
2024-04-30 14:39:40 +02:00
parent ba4d9b8134
commit 15e3d26c86

View File

@@ -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.