forked from qt-creator/qt-creator
Android: Extract sdkmanageroutputparser to separate file
- Needed for testing and for improving readability Change-Id: Ie7a716b204ae0a216d72fa0191d4d05b7708c16c Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -52,6 +52,7 @@ add_qtc_plugin(Android
|
|||||||
javaparser.cpp javaparser.h
|
javaparser.cpp javaparser.h
|
||||||
splashscreencontainerwidget.cpp splashscreencontainerwidget.h
|
splashscreencontainerwidget.cpp splashscreencontainerwidget.h
|
||||||
splashscreenwidget.cpp splashscreenwidget.h
|
splashscreenwidget.cpp splashscreenwidget.h
|
||||||
|
sdkmanageroutputparser.cpp sdkmanageroutputparser.h
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(Android
|
extend_qtc_plugin(Android
|
||||||
|
|||||||
@@ -111,7 +111,9 @@ Project {
|
|||||||
"splashscreencontainerwidget.cpp",
|
"splashscreencontainerwidget.cpp",
|
||||||
"splashscreencontainerwidget.h",
|
"splashscreencontainerwidget.h",
|
||||||
"splashscreenwidget.cpp",
|
"splashscreenwidget.cpp",
|
||||||
"splashscreenwidget.h"
|
"splashscreenwidget.h",
|
||||||
|
"sdkmanageroutputparser.cpp",
|
||||||
|
"sdkmanageroutputparser.h"
|
||||||
]
|
]
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
|
||||||
#include "androidmanager.h"
|
|
||||||
#include "androidsdkmanager.h"
|
#include "androidsdkmanager.h"
|
||||||
#include "androidtr.h"
|
#include "androidtr.h"
|
||||||
#include "avdmanageroutputparser.h"
|
#include "avdmanageroutputparser.h"
|
||||||
|
#include "sdkmanageroutputparser.h"
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -29,16 +28,13 @@
|
|||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
||||||
|
const char commonArgsKey[] = "Common Arguments:";
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
const char installLocationKey[] = "Installed Location:";
|
|
||||||
const char revisionKey[] = "Version:";
|
|
||||||
const char descriptionKey[] = "Description:";
|
|
||||||
const char commonArgsKey[] = "Common Arguments:";
|
|
||||||
|
|
||||||
const int sdkManagerCmdTimeoutS = 60;
|
const int sdkManagerCmdTimeoutS = 60;
|
||||||
const int sdkManagerOperationTimeoutS = 600;
|
const int sdkManagerOperationTimeoutS = 600;
|
||||||
@@ -54,28 +50,14 @@ static const QRegularExpression &assertionRegExp()
|
|||||||
return theRegExp;
|
return theRegExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
|
|
||||||
\c true if \a key is found, false otherwise. Result is copied into \a value.
|
|
||||||
*/
|
|
||||||
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
|
|
||||||
{
|
|
||||||
auto trimmedInput = line.trimmed();
|
|
||||||
if (trimmedInput.startsWith(key)) {
|
|
||||||
if (value)
|
|
||||||
*value = trimmedInput.section(key, 1, 1).trimmed();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int parseProgress(const QString &out, bool &foundAssertion)
|
int parseProgress(const QString &out, bool &foundAssertion)
|
||||||
{
|
{
|
||||||
int progress = -1;
|
int progress = -1;
|
||||||
if (out.isEmpty())
|
if (out.isEmpty())
|
||||||
return progress;
|
return progress;
|
||||||
QRegularExpression reg("(?<progress>\\d*)%");
|
static const QRegularExpression reg("(?<progress>\\d*)%");
|
||||||
QStringList lines = out.split(QRegularExpression("[\\n\\r]"), Qt::SkipEmptyParts);
|
static const QRegularExpression regEndOfLine("[\\n\\r]");
|
||||||
|
const QStringList lines = out.split(regEndOfLine, Qt::SkipEmptyParts);
|
||||||
for (const QString &line : lines) {
|
for (const QString &line : lines) {
|
||||||
QRegularExpressionMatch match = reg.match(line);
|
QRegularExpressionMatch match = reg.match(line);
|
||||||
if (match.hasMatch()) {
|
if (match.hasMatch()) {
|
||||||
@@ -223,87 +205,6 @@ public:
|
|||||||
bool m_packageListingSuccessful = false;
|
bool m_packageListingSuccessful = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
\class SdkManagerOutputParser
|
|
||||||
\brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager
|
|
||||||
commands.
|
|
||||||
*/
|
|
||||||
class SdkManagerOutputParser
|
|
||||||
{
|
|
||||||
class GenericPackageData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool isValid() const { return !revision.isNull() && !description.isNull(); }
|
|
||||||
QStringList headerParts;
|
|
||||||
QVersionNumber revision;
|
|
||||||
QString description;
|
|
||||||
Utils::FilePath installedLocation;
|
|
||||||
QMap<QString, QString> extraData;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum MarkerTag
|
|
||||||
{
|
|
||||||
None = 0x001,
|
|
||||||
InstalledPackagesMarker = 0x002,
|
|
||||||
AvailablePackagesMarkers = 0x004,
|
|
||||||
AvailableUpdatesMarker = 0x008,
|
|
||||||
EmptyMarker = 0x010,
|
|
||||||
PlatformMarker = 0x020,
|
|
||||||
SystemImageMarker = 0x040,
|
|
||||||
BuildToolsMarker = 0x080,
|
|
||||||
SdkToolsMarker = 0x100,
|
|
||||||
PlatformToolsMarker = 0x200,
|
|
||||||
EmulatorToolsMarker = 0x400,
|
|
||||||
NdkMarker = 0x800,
|
|
||||||
ExtrasMarker = 0x1000,
|
|
||||||
CmdlineSdkToolsMarker = 0x2000,
|
|
||||||
GenericToolMarker = 0x4000,
|
|
||||||
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
|
||||||
};
|
|
||||||
|
|
||||||
SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {}
|
|
||||||
void parsePackageListing(const QString &output);
|
|
||||||
|
|
||||||
AndroidSdkPackageList &m_packages;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void compilePackageAssociations();
|
|
||||||
void parsePackageData(MarkerTag packageMarker, const QStringList &data);
|
|
||||||
bool parseAbstractData(GenericPackageData &output, const QStringList &input, int minParts,
|
|
||||||
const QString &logStrTag,
|
|
||||||
const QStringList &extraKeys = QStringList()) const;
|
|
||||||
AndroidSdkPackage *parsePlatform(const QStringList &data) const;
|
|
||||||
QPair<SystemImage *, int> parseSystemImage(const QStringList &data) const;
|
|
||||||
BuildTools *parseBuildToolsPackage(const QStringList &data) const;
|
|
||||||
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
|
||||||
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
|
||||||
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
|
||||||
Ndk *parseNdkPackage(const QStringList &data) const;
|
|
||||||
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
|
||||||
GenericSdkPackage *parseGenericTools(const QStringList &data) const;
|
|
||||||
MarkerTag parseMarkers(const QString &line);
|
|
||||||
|
|
||||||
MarkerTag m_currentSection = MarkerTag::None;
|
|
||||||
QHash<AndroidSdkPackage *, int> m_systemImages;
|
|
||||||
};
|
|
||||||
|
|
||||||
using MarkerTagsType = std::map<SdkManagerOutputParser::MarkerTag, const char *>;
|
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags, ({
|
|
||||||
{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::NdkMarker, Constants::ndkPackageName},
|
|
||||||
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config):
|
AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config):
|
||||||
m_d(new AndroidSdkManagerPrivate(*this, config))
|
m_d(new AndroidSdkManagerPrivate(*this, config))
|
||||||
{
|
{
|
||||||
@@ -464,403 +365,6 @@ void AndroidSdkManager::acceptSdkLicense(bool accept)
|
|||||||
m_d->setLicenseInput(accept);
|
m_d->setLicenseInput(accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdkManagerOutputParser::parsePackageListing(const QString &output)
|
|
||||||
{
|
|
||||||
QStringList packageData;
|
|
||||||
bool collectingPackageData = false;
|
|
||||||
MarkerTag currentPackageMarker = MarkerTag::None;
|
|
||||||
|
|
||||||
auto processCurrentPackage = [&] {
|
|
||||||
if (collectingPackageData) {
|
|
||||||
collectingPackageData = false;
|
|
||||||
parsePackageData(currentPackageMarker, packageData);
|
|
||||||
packageData.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QRegularExpression delimiters("[\\n\\r]");
|
|
||||||
const auto lines = output.split(delimiters);
|
|
||||||
for (const QString &outputLine : lines) {
|
|
||||||
|
|
||||||
// NOTE: we don't want to parse Dependencies part as it does not add value
|
|
||||||
if (outputLine.startsWith(" "))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We don't need to parse this because they would still be listed on available packages
|
|
||||||
if (m_currentSection == AvailableUpdatesMarker)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
MarkerTag marker = parseMarkers(outputLine.trimmed());
|
|
||||||
if (marker & SectionMarkers) {
|
|
||||||
// Section marker found. Update the current section being parsed.
|
|
||||||
m_currentSection = marker;
|
|
||||||
processCurrentPackage();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_currentSection == None)
|
|
||||||
continue; // Continue with the verbose output until a valid section starts.
|
|
||||||
|
|
||||||
if (marker == EmptyMarker) {
|
|
||||||
// Empty marker. Occurs at the end of a package details.
|
|
||||||
// Process the collected package data, if any.
|
|
||||||
processCurrentPackage();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (marker == None) {
|
|
||||||
if (collectingPackageData)
|
|
||||||
packageData << outputLine; // Collect data until next marker.
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// Package marker found.
|
|
||||||
processCurrentPackage(); // New package starts. Process the collected package data, if any.
|
|
||||||
currentPackageMarker = marker;
|
|
||||||
collectingPackageData = true;
|
|
||||||
packageData << outputLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compilePackageAssociations();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SdkManagerOutputParser::compilePackageAssociations()
|
|
||||||
{
|
|
||||||
// Return true if package p is already installed i.e. there exists a installed package having
|
|
||||||
// same sdk style path and same revision as of p.
|
|
||||||
auto isInstalled = [](const AndroidSdkPackageList &container, AndroidSdkPackage *p) {
|
|
||||||
return Utils::anyOf(container, [p](AndroidSdkPackage *other) {
|
|
||||||
return other->state() == AndroidSdkPackage::Installed &&
|
|
||||||
other->sdkStylePath() == p->sdkStylePath() &&
|
|
||||||
other->revision() == p->revision();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
auto deleteAlreadyInstalled = [isInstalled](AndroidSdkPackageList &packages) {
|
|
||||||
for (auto p = packages.begin(); p != packages.end();) {
|
|
||||||
if ((*p)->state() == AndroidSdkPackage::Available && isInstalled(packages, *p)) {
|
|
||||||
delete *p;
|
|
||||||
p = packages.erase(p);
|
|
||||||
} else {
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove already installed packages.
|
|
||||||
deleteAlreadyInstalled(m_packages);
|
|
||||||
|
|
||||||
// Filter out available images that are already installed.
|
|
||||||
AndroidSdkPackageList images = m_systemImages.keys();
|
|
||||||
deleteAlreadyInstalled(images);
|
|
||||||
|
|
||||||
// Associate the system images with sdk platforms.
|
|
||||||
for (AndroidSdkPackage *image : std::as_const(images)) {
|
|
||||||
int imageApi = m_systemImages[image];
|
|
||||||
auto itr = std::find_if(m_packages.begin(), m_packages.end(),
|
|
||||||
[imageApi](const AndroidSdkPackage *p) {
|
|
||||||
const SdkPlatform *platform = nullptr;
|
|
||||||
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
|
|
||||||
platform = static_cast<const SdkPlatform*>(p);
|
|
||||||
return platform && platform->apiLevel() == imageApi;
|
|
||||||
});
|
|
||||||
if (itr != m_packages.end()) {
|
|
||||||
auto platform = static_cast<SdkPlatform*>(*itr);
|
|
||||||
platform->addSystemImage(static_cast<SystemImage *>(image));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!data.isEmpty() && packageMarker != None, return);
|
|
||||||
|
|
||||||
AndroidSdkPackage *package = nullptr;
|
|
||||||
auto createPackage = [&](std::function<AndroidSdkPackage *(SdkManagerOutputParser *,
|
|
||||||
const QStringList &)> creator) {
|
|
||||||
if ((package = creator(this, data)))
|
|
||||||
m_packages.append(package);
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (packageMarker) {
|
|
||||||
case MarkerTag::BuildToolsMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseBuildToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::SdkToolsMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::CmdlineSdkToolsMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::PlatformToolsMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parsePlatformToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::EmulatorToolsMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseEmulatorToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::PlatformMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parsePlatform);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::SystemImageMarker:
|
|
||||||
{
|
|
||||||
QPair<SystemImage *, int> result = parseSystemImage(data);
|
|
||||||
if (result.first) {
|
|
||||||
m_systemImages[result.first] = result.second;
|
|
||||||
package = result.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::NdkMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseNdkPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::ExtrasMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MarkerTag::GenericToolMarker:
|
|
||||||
createPackage(&SdkManagerOutputParser::parseGenericTools);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags->at(packageMarker);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (package) {
|
|
||||||
switch (m_currentSection) {
|
|
||||||
case MarkerTag::InstalledPackagesMarker:
|
|
||||||
package->setState(AndroidSdkPackage::Installed);
|
|
||||||
break;
|
|
||||||
case MarkerTag::AvailablePackagesMarkers:
|
|
||||||
case MarkerTag::AvailableUpdatesMarker:
|
|
||||||
package->setState(AndroidSdkPackage::Available);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags->at(m_currentSection);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPackageData &output,
|
|
||||||
const QStringList &input, int minParts,
|
|
||||||
const QString &logStrTag,
|
|
||||||
const QStringList &extraKeys) const
|
|
||||||
{
|
|
||||||
if (input.isEmpty()) {
|
|
||||||
qCDebug(sdkManagerLog) << logStrTag + ": Empty input";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.headerParts = input.at(0).split(';');
|
|
||||||
if (output.headerParts.count() < minParts) {
|
|
||||||
qCDebug(sdkManagerLog) << logStrTag + "%1: Unexpected header:" << input;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList keys = extraKeys;
|
|
||||||
keys << installLocationKey << revisionKey << descriptionKey;
|
|
||||||
for (const QString &line : input) {
|
|
||||||
QString value;
|
|
||||||
for (const auto &key: std::as_const(keys)) {
|
|
||||||
if (valueForKey(key, line, &value)) {
|
|
||||||
if (key == installLocationKey)
|
|
||||||
output.installedLocation = Utils::FilePath::fromUserInput(value);
|
|
||||||
else if (key == revisionKey)
|
|
||||||
output.revision = QVersionNumber::fromString(value);
|
|
||||||
else if (key == descriptionKey)
|
|
||||||
output.description = value;
|
|
||||||
else
|
|
||||||
output.extraData[key] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const
|
|
||||||
{
|
|
||||||
SdkPlatform *platform = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 2, "Platform")) {
|
|
||||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
|
||||||
if (apiLevel == -1) {
|
|
||||||
qCDebug(sdkManagerLog) << "Platform: Cannot parse api level:"<< data;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
platform = new SdkPlatform(packageData.revision, data.at(0), apiLevel);
|
|
||||||
platform->setExtension(convertNameToExtension(packageData.headerParts.at(1)));
|
|
||||||
platform->setInstalledLocation(packageData.installedLocation);
|
|
||||||
platform->setDescriptionText(packageData.description);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:"
|
|
||||||
<< data;
|
|
||||||
}
|
|
||||||
return platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPair<SystemImage *, int> SdkManagerOutputParser::parseSystemImage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
QPair <SystemImage *, int> result(nullptr, -1);
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 4, "System-image")) {
|
|
||||||
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
|
||||||
if (apiLevel == -1) {
|
|
||||||
qCDebug(sdkManagerLog) << "System-image: Cannot parse api level:"<< data;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
auto image = new SystemImage(packageData.revision, data.at(0),
|
|
||||||
packageData.headerParts.at(3));
|
|
||||||
image->setInstalledLocation(packageData.installedLocation);
|
|
||||||
image->setDisplayText(packageData.description);
|
|
||||||
image->setDescriptionText(packageData.description);
|
|
||||||
image->setApiLevel(apiLevel);
|
|
||||||
result = {image, apiLevel};
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildTools *SdkManagerOutputParser::parseBuildToolsPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
BuildTools *buildTools = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 2, "Build-tools")) {
|
|
||||||
buildTools = new BuildTools(packageData.revision, data.at(0));
|
|
||||||
buildTools->setDescriptionText(packageData.description);
|
|
||||||
buildTools->setDisplayText(packageData.description);
|
|
||||||
buildTools->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Build-tools: Parsing failed. Minimum required data unavailable:"
|
|
||||||
<< data;
|
|
||||||
}
|
|
||||||
return buildTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
SdkTools *SdkManagerOutputParser::parseSdkToolsPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
SdkTools *sdkTools = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "SDK-tools")) {
|
|
||||||
sdkTools = new SdkTools(packageData.revision, data.at(0));
|
|
||||||
sdkTools->setDescriptionText(packageData.description);
|
|
||||||
sdkTools->setDisplayText(packageData.description);
|
|
||||||
sdkTools->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "SDK-tools: Parsing failed. Minimum required data unavailable:"
|
|
||||||
<< data;
|
|
||||||
}
|
|
||||||
return sdkTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformTools *SdkManagerOutputParser::parsePlatformToolsPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
PlatformTools *platformTools = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "Platform-tools")) {
|
|
||||||
platformTools = new PlatformTools(packageData.revision, data.at(0));
|
|
||||||
platformTools->setDescriptionText(packageData.description);
|
|
||||||
platformTools->setDisplayText(packageData.description);
|
|
||||||
platformTools->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Platform-tools: Parsing failed. Minimum required data "
|
|
||||||
"unavailable:" << data;
|
|
||||||
}
|
|
||||||
return platformTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmulatorTools *SdkManagerOutputParser::parseEmulatorToolsPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
EmulatorTools *emulatorTools = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "Emulator-tools")) {
|
|
||||||
emulatorTools = new EmulatorTools(packageData.revision, data.at(0));
|
|
||||||
emulatorTools->setDescriptionText(packageData.description);
|
|
||||||
emulatorTools->setDisplayText(packageData.description);
|
|
||||||
emulatorTools->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Emulator-tools: Parsing failed. Minimum required data "
|
|
||||||
"unavailable:" << data;
|
|
||||||
}
|
|
||||||
return emulatorTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ndk *SdkManagerOutputParser::parseNdkPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
Ndk *ndk = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "NDK")) {
|
|
||||||
ndk = new Ndk(packageData.revision, data.at(0));
|
|
||||||
ndk->setDescriptionText(packageData.description);
|
|
||||||
ndk->setDisplayText(packageData.description);
|
|
||||||
ndk->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "NDK: Parsing failed. Minimum required data unavailable:"
|
|
||||||
<< data;
|
|
||||||
}
|
|
||||||
return ndk;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
|
||||||
{
|
|
||||||
ExtraTools *extraTools = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "Extras")) {
|
|
||||||
extraTools = new ExtraTools(packageData.revision, data.at(0));
|
|
||||||
extraTools->setDescriptionText(packageData.description);
|
|
||||||
extraTools->setDisplayText(packageData.description);
|
|
||||||
extraTools->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Extra-tools: Parsing failed. Minimum required data "
|
|
||||||
"unavailable:" << data;
|
|
||||||
}
|
|
||||||
return extraTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
GenericSdkPackage *SdkManagerOutputParser::parseGenericTools(const QStringList &data) const
|
|
||||||
{
|
|
||||||
GenericSdkPackage *sdkPackage = nullptr;
|
|
||||||
GenericPackageData packageData;
|
|
||||||
if (parseAbstractData(packageData, data, 1, "Generic")) {
|
|
||||||
sdkPackage = new GenericSdkPackage(packageData.revision, data.at(0));
|
|
||||||
sdkPackage->setDescriptionText(packageData.description);
|
|
||||||
sdkPackage->setDisplayText(packageData.description);
|
|
||||||
sdkPackage->setInstalledLocation(packageData.installedLocation);
|
|
||||||
} else {
|
|
||||||
qCDebug(sdkManagerLog) << "Generic: Parsing failed. Minimum required data "
|
|
||||||
"unavailable:" << data;
|
|
||||||
}
|
|
||||||
return sdkPackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line)
|
|
||||||
{
|
|
||||||
if (line.isEmpty())
|
|
||||||
return EmptyMarker;
|
|
||||||
|
|
||||||
for (auto pair : *markerTags) {
|
|
||||||
if (line.startsWith(QLatin1String(pair.second)))
|
|
||||||
return pair.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRegularExpressionMatch match = QRegularExpression("^[a-zA-Z]+[A-Za-z0-9;._-]+").match(line);
|
|
||||||
if (match.hasMatch() && match.captured(0) == line)
|
|
||||||
return GenericToolMarker;
|
|
||||||
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager,
|
AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager,
|
||||||
const AndroidConfig &config):
|
const AndroidConfig &config):
|
||||||
m_activeOperation(nullptr, watcherDeleter),
|
m_activeOperation(nullptr, watcherDeleter),
|
||||||
|
|||||||
449
src/plugins/android/sdkmanageroutputparser.cpp
Normal file
449
src/plugins/android/sdkmanageroutputparser.cpp
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "sdkmanageroutputparser.h"
|
||||||
|
|
||||||
|
#include "avdmanageroutputparser.h"
|
||||||
|
#include "androidsdkpackage.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg)
|
||||||
|
const char installLocationKey[] = "Installed Location:";
|
||||||
|
const char revisionKey[] = "Version:";
|
||||||
|
const char descriptionKey[] = "Description:";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
using namespace Android;
|
||||||
|
using namespace Android::Internal;
|
||||||
|
|
||||||
|
using MarkerTagsType = std::map<SdkManagerOutputParser::MarkerTag, const char *>;
|
||||||
|
Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags,
|
||||||
|
({{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::NdkMarker, Constants::ndkPackageName},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}}));
|
||||||
|
|
||||||
|
void SdkManagerOutputParser::parsePackageListing(const QString &output)
|
||||||
|
{
|
||||||
|
QStringList packageData;
|
||||||
|
bool collectingPackageData = false;
|
||||||
|
MarkerTag currentPackageMarker = MarkerTag::None;
|
||||||
|
|
||||||
|
auto processCurrentPackage = [&] {
|
||||||
|
if (collectingPackageData) {
|
||||||
|
collectingPackageData = false;
|
||||||
|
parsePackageData(currentPackageMarker, packageData);
|
||||||
|
packageData.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QRegularExpression delimiters("[\\n\\r]");
|
||||||
|
const auto lines = output.split(delimiters);
|
||||||
|
for (const QString &outputLine : lines) {
|
||||||
|
|
||||||
|
// NOTE: we don't want to parse Dependencies part as it does not add value
|
||||||
|
if (outputLine.startsWith(" "))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We don't need to parse this because they would still be listed on available packages
|
||||||
|
if (m_currentSection == AvailableUpdatesMarker)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MarkerTag marker = parseMarkers(outputLine.trimmed());
|
||||||
|
if (marker & SectionMarkers) {
|
||||||
|
// Section marker found. Update the current section being parsed.
|
||||||
|
m_currentSection = marker;
|
||||||
|
processCurrentPackage();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentSection == None)
|
||||||
|
continue; // Continue with the verbose output until a valid section starts.
|
||||||
|
|
||||||
|
if (marker == EmptyMarker) {
|
||||||
|
// Empty marker. Occurs at the end of a package details.
|
||||||
|
// Process the collected package data, if any.
|
||||||
|
processCurrentPackage();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (marker == None) {
|
||||||
|
if (collectingPackageData)
|
||||||
|
packageData << outputLine; // Collect data until next marker.
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Package marker found.
|
||||||
|
processCurrentPackage(); // New package starts. Process the collected package data, if any.
|
||||||
|
currentPackageMarker = marker;
|
||||||
|
collectingPackageData = true;
|
||||||
|
packageData << outputLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compilePackageAssociations();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkManagerOutputParser::compilePackageAssociations()
|
||||||
|
{
|
||||||
|
// Return true if package p is already installed i.e. there exists a installed package having
|
||||||
|
// same sdk style path and same revision as of p.
|
||||||
|
auto isInstalled = [](const AndroidSdkPackageList &container, AndroidSdkPackage *p) {
|
||||||
|
return Utils::anyOf(container, [p](AndroidSdkPackage *other) {
|
||||||
|
return other->state() == AndroidSdkPackage::Installed &&
|
||||||
|
other->sdkStylePath() == p->sdkStylePath() &&
|
||||||
|
other->revision() == p->revision();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto deleteAlreadyInstalled = [isInstalled](AndroidSdkPackageList &packages) {
|
||||||
|
for (auto p = packages.begin(); p != packages.end();) {
|
||||||
|
if ((*p)->state() == AndroidSdkPackage::Available && isInstalled(packages, *p)) {
|
||||||
|
delete *p;
|
||||||
|
p = packages.erase(p);
|
||||||
|
} else {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove already installed packages.
|
||||||
|
deleteAlreadyInstalled(m_packages);
|
||||||
|
|
||||||
|
// Filter out available images that are already installed.
|
||||||
|
AndroidSdkPackageList images = m_systemImages.keys();
|
||||||
|
deleteAlreadyInstalled(images);
|
||||||
|
|
||||||
|
// Associate the system images with sdk platforms.
|
||||||
|
for (AndroidSdkPackage *image : std::as_const(images)) {
|
||||||
|
int imageApi = m_systemImages[image];
|
||||||
|
auto itr = std::find_if(m_packages.begin(), m_packages.end(),
|
||||||
|
[imageApi](const AndroidSdkPackage *p) {
|
||||||
|
const SdkPlatform *platform = nullptr;
|
||||||
|
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
|
||||||
|
platform = static_cast<const SdkPlatform*>(p);
|
||||||
|
return platform && platform->apiLevel() == imageApi;
|
||||||
|
});
|
||||||
|
if (itr != m_packages.end()) {
|
||||||
|
auto platform = static_cast<SdkPlatform*>(*itr);
|
||||||
|
platform->addSystemImage(static_cast<SystemImage *>(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!data.isEmpty() && packageMarker != None, return);
|
||||||
|
|
||||||
|
AndroidSdkPackage *package = nullptr;
|
||||||
|
auto createPackage = [&](std::function<AndroidSdkPackage *(SdkManagerOutputParser *,
|
||||||
|
const QStringList &)> creator) {
|
||||||
|
if ((package = creator(this, data)))
|
||||||
|
m_packages.append(package);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (packageMarker) {
|
||||||
|
case MarkerTag::BuildToolsMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseBuildToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::SdkToolsMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::CmdlineSdkToolsMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseSdkToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::PlatformToolsMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parsePlatformToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::EmulatorToolsMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseEmulatorToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::PlatformMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parsePlatform);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::SystemImageMarker:
|
||||||
|
{
|
||||||
|
QPair<SystemImage *, int> result = parseSystemImage(data);
|
||||||
|
if (result.first) {
|
||||||
|
m_systemImages[result.first] = result.second;
|
||||||
|
package = result.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::NdkMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseNdkPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::ExtrasMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::GenericToolMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseGenericTools);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags->at(packageMarker);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (package) {
|
||||||
|
switch (m_currentSection) {
|
||||||
|
case MarkerTag::InstalledPackagesMarker:
|
||||||
|
package->setState(AndroidSdkPackage::Installed);
|
||||||
|
break;
|
||||||
|
case MarkerTag::AvailablePackagesMarkers:
|
||||||
|
case MarkerTag::AvailableUpdatesMarker:
|
||||||
|
package->setState(AndroidSdkPackage::Available);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags->at(m_currentSection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
|
||||||
|
\c true if \a key is found, false otherwise. Result is copied into \a value.
|
||||||
|
*/
|
||||||
|
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
|
||||||
|
{
|
||||||
|
auto trimmedInput = line.trimmed();
|
||||||
|
if (trimmedInput.startsWith(key)) {
|
||||||
|
if (value)
|
||||||
|
*value = trimmedInput.section(key, 1, 1).trimmed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPackageData &output,
|
||||||
|
const QStringList &input, int minParts,
|
||||||
|
const QString &logStrTag,
|
||||||
|
const QStringList &extraKeys) const
|
||||||
|
{
|
||||||
|
if (input.isEmpty()) {
|
||||||
|
qCDebug(sdkManagerLog) << logStrTag + ": Empty input";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.headerParts = input.at(0).split(';');
|
||||||
|
if (output.headerParts.count() < minParts) {
|
||||||
|
qCDebug(sdkManagerLog) << logStrTag + "%1: Unexpected header:" << input;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList keys = extraKeys;
|
||||||
|
keys << installLocationKey << revisionKey << descriptionKey;
|
||||||
|
for (const QString &line : input) {
|
||||||
|
QString value;
|
||||||
|
for (const auto &key: std::as_const(keys)) {
|
||||||
|
if (valueForKey(key, line, &value)) {
|
||||||
|
if (key == installLocationKey)
|
||||||
|
output.installedLocation = Utils::FilePath::fromUserInput(value);
|
||||||
|
else if (key == revisionKey)
|
||||||
|
output.revision = QVersionNumber::fromString(value);
|
||||||
|
else if (key == descriptionKey)
|
||||||
|
output.description = value;
|
||||||
|
else
|
||||||
|
output.extraData[key] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const
|
||||||
|
{
|
||||||
|
SdkPlatform *platform = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 2, "Platform")) {
|
||||||
|
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||||
|
if (apiLevel == -1) {
|
||||||
|
qCDebug(sdkManagerLog) << "Platform: Cannot parse api level:"<< data;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
platform = new SdkPlatform(packageData.revision, data.at(0), apiLevel);
|
||||||
|
platform->setExtension(convertNameToExtension(packageData.headerParts.at(1)));
|
||||||
|
platform->setInstalledLocation(packageData.installedLocation);
|
||||||
|
platform->setDescriptionText(packageData.description);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:"
|
||||||
|
<< data;
|
||||||
|
}
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<SystemImage *, int> SdkManagerOutputParser::parseSystemImage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
QPair <SystemImage *, int> result(nullptr, -1);
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 4, "System-image")) {
|
||||||
|
const int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1));
|
||||||
|
if (apiLevel == -1) {
|
||||||
|
qCDebug(sdkManagerLog) << "System-image: Cannot parse api level:"<< data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto image = new SystemImage(packageData.revision, data.at(0),
|
||||||
|
packageData.headerParts.at(3));
|
||||||
|
image->setInstalledLocation(packageData.installedLocation);
|
||||||
|
image->setDisplayText(packageData.description);
|
||||||
|
image->setDescriptionText(packageData.description);
|
||||||
|
image->setApiLevel(apiLevel);
|
||||||
|
result = {image, apiLevel};
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildTools *SdkManagerOutputParser::parseBuildToolsPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
BuildTools *buildTools = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 2, "Build-tools")) {
|
||||||
|
buildTools = new BuildTools(packageData.revision, data.at(0));
|
||||||
|
buildTools->setDescriptionText(packageData.description);
|
||||||
|
buildTools->setDisplayText(packageData.description);
|
||||||
|
buildTools->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Build-tools: Parsing failed. Minimum required data unavailable:"
|
||||||
|
<< data;
|
||||||
|
}
|
||||||
|
return buildTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkTools *SdkManagerOutputParser::parseSdkToolsPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
SdkTools *sdkTools = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "SDK-tools")) {
|
||||||
|
sdkTools = new SdkTools(packageData.revision, data.at(0));
|
||||||
|
sdkTools->setDescriptionText(packageData.description);
|
||||||
|
sdkTools->setDisplayText(packageData.description);
|
||||||
|
sdkTools->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "SDK-tools: Parsing failed. Minimum required data unavailable:"
|
||||||
|
<< data;
|
||||||
|
}
|
||||||
|
return sdkTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlatformTools *SdkManagerOutputParser::parsePlatformToolsPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
PlatformTools *platformTools = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "Platform-tools")) {
|
||||||
|
platformTools = new PlatformTools(packageData.revision, data.at(0));
|
||||||
|
platformTools->setDescriptionText(packageData.description);
|
||||||
|
platformTools->setDisplayText(packageData.description);
|
||||||
|
platformTools->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Platform-tools: Parsing failed. Minimum required data "
|
||||||
|
"unavailable:" << data;
|
||||||
|
}
|
||||||
|
return platformTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulatorTools *SdkManagerOutputParser::parseEmulatorToolsPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
EmulatorTools *emulatorTools = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "Emulator-tools")) {
|
||||||
|
emulatorTools = new EmulatorTools(packageData.revision, data.at(0));
|
||||||
|
emulatorTools->setDescriptionText(packageData.description);
|
||||||
|
emulatorTools->setDisplayText(packageData.description);
|
||||||
|
emulatorTools->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Emulator-tools: Parsing failed. Minimum required data "
|
||||||
|
"unavailable:" << data;
|
||||||
|
}
|
||||||
|
return emulatorTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ndk *SdkManagerOutputParser::parseNdkPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
Ndk *ndk = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "NDK")) {
|
||||||
|
ndk = new Ndk(packageData.revision, data.at(0));
|
||||||
|
ndk->setDescriptionText(packageData.description);
|
||||||
|
ndk->setDisplayText(packageData.description);
|
||||||
|
ndk->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "NDK: Parsing failed. Minimum required data unavailable:"
|
||||||
|
<< data;
|
||||||
|
}
|
||||||
|
return ndk;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
ExtraTools *extraTools = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "Extras")) {
|
||||||
|
extraTools = new ExtraTools(packageData.revision, data.at(0));
|
||||||
|
extraTools->setDescriptionText(packageData.description);
|
||||||
|
extraTools->setDisplayText(packageData.description);
|
||||||
|
extraTools->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Extra-tools: Parsing failed. Minimum required data "
|
||||||
|
"unavailable:" << data;
|
||||||
|
}
|
||||||
|
return extraTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericSdkPackage *SdkManagerOutputParser::parseGenericTools(const QStringList &data) const
|
||||||
|
{
|
||||||
|
GenericSdkPackage *sdkPackage = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "Generic")) {
|
||||||
|
sdkPackage = new GenericSdkPackage(packageData.revision, data.at(0));
|
||||||
|
sdkPackage->setDescriptionText(packageData.description);
|
||||||
|
sdkPackage->setDisplayText(packageData.description);
|
||||||
|
sdkPackage->setInstalledLocation(packageData.installedLocation);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "Generic: Parsing failed. Minimum required data "
|
||||||
|
"unavailable:" << data;
|
||||||
|
}
|
||||||
|
return sdkPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line)
|
||||||
|
{
|
||||||
|
if (line.isEmpty())
|
||||||
|
return EmptyMarker;
|
||||||
|
|
||||||
|
for (auto pair : *markerTags) {
|
||||||
|
if (line.startsWith(QLatin1String(pair.second)))
|
||||||
|
return pair.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegularExpressionMatch match = QRegularExpression("^[a-zA-Z]+[A-Za-z0-9;._-]+").match(line);
|
||||||
|
if (match.hasMatch() && match.captured(0) == line)
|
||||||
|
return GenericToolMarker;
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
79
src/plugins/android/sdkmanageroutputparser.h
Normal file
79
src/plugins/android/sdkmanageroutputparser.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "androidconstants.h"
|
||||||
|
#include "androidsdkpackage.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
/*!
|
||||||
|
\class SdkManagerOutputParser
|
||||||
|
\brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager
|
||||||
|
commands.
|
||||||
|
*/
|
||||||
|
class SdkManagerOutputParser
|
||||||
|
{
|
||||||
|
class GenericPackageData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isValid() const { return !revision.isNull() && !description.isNull(); }
|
||||||
|
QStringList headerParts;
|
||||||
|
QVersionNumber revision;
|
||||||
|
QString description;
|
||||||
|
Utils::FilePath installedLocation;
|
||||||
|
QMap<QString, QString> extraData;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum MarkerTag
|
||||||
|
{
|
||||||
|
None = 0x001,
|
||||||
|
InstalledPackagesMarker = 0x002,
|
||||||
|
AvailablePackagesMarkers = 0x004,
|
||||||
|
AvailableUpdatesMarker = 0x008,
|
||||||
|
EmptyMarker = 0x010,
|
||||||
|
PlatformMarker = 0x020,
|
||||||
|
SystemImageMarker = 0x040,
|
||||||
|
BuildToolsMarker = 0x080,
|
||||||
|
SdkToolsMarker = 0x100,
|
||||||
|
PlatformToolsMarker = 0x200,
|
||||||
|
EmulatorToolsMarker = 0x400,
|
||||||
|
NdkMarker = 0x800,
|
||||||
|
ExtrasMarker = 0x1000,
|
||||||
|
CmdlineSdkToolsMarker = 0x2000,
|
||||||
|
GenericToolMarker = 0x4000,
|
||||||
|
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
||||||
|
};
|
||||||
|
|
||||||
|
SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {}
|
||||||
|
void parsePackageListing(const QString &output);
|
||||||
|
|
||||||
|
AndroidSdkPackageList &m_packages;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void compilePackageAssociations();
|
||||||
|
void parsePackageData(MarkerTag packageMarker, const QStringList &data);
|
||||||
|
bool parseAbstractData(GenericPackageData &output, const QStringList &input, int minParts,
|
||||||
|
const QString &logStrTag,
|
||||||
|
const QStringList &extraKeys = QStringList()) const;
|
||||||
|
AndroidSdkPackage *parsePlatform(const QStringList &data) const;
|
||||||
|
QPair<SystemImage *, int> parseSystemImage(const QStringList &data) const;
|
||||||
|
BuildTools *parseBuildToolsPackage(const QStringList &data) const;
|
||||||
|
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
||||||
|
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
||||||
|
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
||||||
|
Ndk *parseNdkPackage(const QStringList &data) const;
|
||||||
|
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
||||||
|
GenericSdkPackage *parseGenericTools(const QStringList &data) const;
|
||||||
|
MarkerTag parseMarkers(const QString &line);
|
||||||
|
|
||||||
|
MarkerTag m_currentSection = MarkerTag::None;
|
||||||
|
QHash<AndroidSdkPackage *, int> m_systemImages;
|
||||||
|
};
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
||||||
Reference in New Issue
Block a user