/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** 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 General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** 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-3.0.html. ** ****************************************************************************/ #include "avdmanageroutputparser.h" #include #include #include #include #include #include #include #include namespace { Q_LOGGING_CATEGORY(avdOutputParserLog, "qtc.android.avdOutputParser", QtWarningMsg) } // Avd list keys to parse avd const char avdInfoNameKey[] = "Name:"; const char avdInfoPathKey[] = "Path:"; const char avdInfoAbiKey[] = "abi.type"; const char avdInfoTargetKey[] = "target"; const char avdInfoErrorKey[] = "Error:"; const char avdInfoSdcardKey[] = "Sdcard"; const char avdInfoTargetTypeKey[] = "Target"; const char avdInfoDeviceKey[] = "Device"; const char avdInfoSkinKey[] = "Skin"; namespace Android { namespace Internal { /*! Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns \c true if the key is found, \c false otherwise. The value 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; } static Utils::optional parseAvd(const QStringList &deviceInfo) { AndroidDeviceInfo avd; for (const QString &line : deviceInfo) { QString value; if (valueForKey(avdInfoErrorKey, line)) { qCDebug(avdOutputParserLog) << "Avd Parsing: Skip avd device. Error key found:" << line; return {}; } else if (valueForKey(avdInfoNameKey, line, &value)) { avd.avdname = value; } else if (valueForKey(avdInfoPathKey, line, &value)) { const Utils::FilePath avdPath = Utils::FilePath::fromString(value); if (avdPath.exists()) { // Get ABI. const Utils::FilePath configFile = avdPath.pathAppended("config.ini"); QSettings config(configFile.toString(), QSettings::IniFormat); value = config.value(avdInfoAbiKey).toString(); if (!value.isEmpty()) avd.cpuAbi << value; else qCDebug(avdOutputParserLog) << "Avd Parsing: Cannot find ABI:" << configFile; // Get Target const QString avdInfoFileName = avd.avdname + ".ini"; const Utils::FilePath avdInfoFile = avdPath.parentDir().pathAppended( avdInfoFileName); QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat); value = avdInfo.value(avdInfoTargetKey).toString(); if (!value.isEmpty()) avd.sdk = value.section('-', -1).toInt(); else qCDebug(avdOutputParserLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString(); } } else if (valueForKey(avdInfoDeviceKey, line, &value)) { avd.avdDevice = value.remove(0, 2); } else if (valueForKey(avdInfoTargetTypeKey, line, &value)) { avd.avdTarget = value.remove(0, 2); } else if (valueForKey(avdInfoSkinKey, line, &value)) { avd.avdSkin = value.remove(0, 2); } else if (valueForKey(avdInfoSdcardKey, line, &value)) { avd.avdSdcardSize = value.remove(0, 2); } } if (avd != AndroidDeviceInfo()) return avd; return {}; } AndroidDeviceInfoList parseAvdList(const QString &output, QStringList *avdErrorPaths) { QTC_CHECK(avdErrorPaths); AndroidDeviceInfoList avdList; QStringList avdInfo; using ErrorPath = QString; using AvdResult = Utils::variant; const auto parseAvdInfo = [](const QStringList &avdInfo) { if (!avdInfo.filter(avdManufacturerError).isEmpty()) { for (const QString &line : avdInfo) { QString value; if (valueForKey(avdInfoPathKey, line, &value)) return AvdResult(value); // error path } } else if (Utils::optional avd = parseAvd(avdInfo)) { // armeabi-v7a devices can also run armeabi code if (avd->cpuAbi.contains(ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A)) avd->cpuAbi << ProjectExplorer::Constants::ANDROID_ABI_ARMEABI; avd->state = AndroidDeviceInfo::OkState; avd->type = AndroidDeviceInfo::Emulator; return AvdResult(*avd); } else { qCDebug(avdOutputParserLog) << "Avd Parsing: Parsing failed: " << avdInfo; } return AvdResult(); }; const auto lines = output.split('\n'); for (const QString &line : lines) { if (line.startsWith("---------") || line.isEmpty()) { const AvdResult result = parseAvdInfo(avdInfo); if (auto info = Utils::get_if(&result)) avdList << *info; else if (auto errorPath = Utils::get_if(&result)) *avdErrorPaths << *errorPath; avdInfo.clear(); } else { avdInfo << line; } } Utils::sort(avdList); return avdList; } } // namespace Internal } // namespace Android