forked from qt-creator/qt-creator
Android: Add and organize error cases for different steps
Add error messages for cases in the different steps (e.g. build, deploy, etc.) for user. This makes it easier for the user to know what's wrong instead of just failing with no explanation. Change-Id: I96ea65f5c73edf14c2214b699503211b740d029c Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -423,7 +423,7 @@ void AndroidBuildApkWidget::onOpenSslCheckBoxChanged()
|
|||||||
Utils::FilePath projectPath = m_step->buildConfiguration()->buildSystem()->projectFilePath();
|
Utils::FilePath projectPath = m_step->buildConfiguration()->buildSystem()->projectFilePath();
|
||||||
QFile projectFile(projectPath.toString());
|
QFile projectFile(projectPath.toString());
|
||||||
if (!projectFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
if (!projectFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||||
qWarning() << "Cound't open project file to add OpenSSL extra libs: " << projectPath;
|
qWarning() << "Cannot open project file to add OpenSSL extra libs: " << projectPath;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,27 +494,31 @@ AndroidBuildApkStep::AndroidBuildApkStep(BuildStepList *parent, Utils::Id id)
|
|||||||
|
|
||||||
bool AndroidBuildApkStep::init()
|
bool AndroidBuildApkStep::init()
|
||||||
{
|
{
|
||||||
if (!AbstractProcessStep::init())
|
if (!AbstractProcessStep::init()) {
|
||||||
|
reportWarningOrError(tr("\"%1\" step failed initialization.").arg(displayName()),
|
||||||
|
Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_signPackage) {
|
if (m_signPackage) {
|
||||||
qCDebug(buildapkstepLog) << "Signing enabled";
|
qCDebug(buildapkstepLog) << "Signing enabled";
|
||||||
// check keystore and certificate passwords
|
// check keystore and certificate passwords
|
||||||
if (!verifyKeystorePassword() || !verifyCertificatePassword()) {
|
if (!verifyKeystorePassword() || !verifyCertificatePassword()) {
|
||||||
qCDebug(buildapkstepLog) << "Init failed. Keystore/Certificate password verification failed.";
|
reportWarningOrError(tr("Keystore/Certificate password verification failed."),
|
||||||
|
Task::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildType() != BuildConfiguration::Release) {
|
if (buildType() != BuildConfiguration::Release)
|
||||||
const QString error = tr("Warning: Signing a debug or profile package.");
|
reportWarningOrError(tr("Warning: Signing a debug or profile package."), Task::Warning);
|
||||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Warning, error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
||||||
if (!version)
|
if (!version) {
|
||||||
|
reportWarningOrError(tr("The Qt version for kit %1 is invalid.").arg(kit()->displayName()),
|
||||||
|
Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion();
|
const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion();
|
||||||
if (sdkToolsVersion >= QVersionNumber(25, 3, 0)
|
if (sdkToolsVersion >= QVersionNumber(25, 3, 0)
|
||||||
@@ -526,16 +530,14 @@ bool AndroidBuildApkStep::init()
|
|||||||
"is %2")
|
"is %2")
|
||||||
.arg(sdkToolsVersion.toString())
|
.arg(sdkToolsVersion.toString())
|
||||||
.arg("5.9.0/5.6.3");
|
.arg("5.9.0/5.6.3");
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (version->qtVersion() < QtSupport::QtVersionNumber(5, 4, 0)) {
|
} else if (version->qtVersion() < QtSupport::QtVersionNumber(5, 4, 0)) {
|
||||||
const QString error = tr("The minimum Qt version required for Gradle build to work is %1. "
|
const QString error = tr("The minimum Qt version required for Gradle build to work is %1. "
|
||||||
"It is recommended to install the latest Qt version.")
|
"It is recommended to install the latest Qt version.")
|
||||||
.arg("5.4.0");
|
.arg("5.4.0");
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,8 +547,7 @@ bool AndroidBuildApkStep::init()
|
|||||||
= tr("The API level set for the APK is less than the minimum required by the kit."
|
= tr("The API level set for the APK is less than the minimum required by the kit."
|
||||||
"\nThe minimum API level required by the kit is %1.")
|
"\nThe minimum API level required by the kit is %1.")
|
||||||
.arg(minSDKForKit);
|
.arg(minSDKForKit);
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,16 +569,16 @@ bool AndroidBuildApkStep::init()
|
|||||||
|
|
||||||
m_inputFile = AndroidQtVersion::androidDeploymentSettings(target());
|
m_inputFile = AndroidQtVersion::androidDeploymentSettings(target());
|
||||||
if (m_inputFile.isEmpty()) {
|
if (m_inputFile.isEmpty()) {
|
||||||
qCDebug(buildapkstepLog) << "no input file" << target()->activeBuildKey();
|
|
||||||
m_skipBuilding = true;
|
m_skipBuilding = true;
|
||||||
|
reportWarningOrError(tr("No valid input file for \"%1\".").arg(target()->activeBuildKey()),
|
||||||
|
Task::Warning);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
m_skipBuilding = false;
|
m_skipBuilding = false;
|
||||||
|
|
||||||
if (m_buildTargetSdk.isEmpty()) {
|
if (m_buildTargetSdk.isEmpty()) {
|
||||||
const QString error = tr("Android build SDK not defined. Check Android settings.");
|
reportWarningOrError(tr("Android build SDK version is not defined. Check Android settings.")
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
, Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,10 +666,8 @@ void AndroidBuildApkStep::processFinished(int exitCode, QProcess::ExitStatus sta
|
|||||||
bool AndroidBuildApkStep::verifyKeystorePassword()
|
bool AndroidBuildApkStep::verifyKeystorePassword()
|
||||||
{
|
{
|
||||||
if (!m_keystorePath.exists()) {
|
if (!m_keystorePath.exists()) {
|
||||||
const QString error = tr("Cannot sign the package. Invalid keystore path (%1).")
|
reportWarningOrError(tr("Cannot sign the package. Invalid keystore path (%1).")
|
||||||
.arg(m_keystorePath.toString());
|
.arg(m_keystorePath.toString()), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,10 +686,8 @@ bool AndroidBuildApkStep::verifyCertificatePassword()
|
|||||||
{
|
{
|
||||||
if (!AndroidManager::checkCertificateExists(m_keystorePath.toString(), m_keystorePasswd,
|
if (!AndroidManager::checkCertificateExists(m_keystorePath.toString(), m_keystorePasswd,
|
||||||
m_certificateAlias)) {
|
m_certificateAlias)) {
|
||||||
const QString error = tr("Cannot sign the package. Certificate alias %1 does not exist.")
|
reportWarningOrError(tr("Cannot sign the package. Certificate alias %1 does not exist.")
|
||||||
.arg(m_certificateAlias);
|
.arg(m_certificateAlias), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,9 +728,8 @@ static bool copyFileIfNewer(const FilePath &sourceFilePath,
|
|||||||
void AndroidBuildApkStep::doRun()
|
void AndroidBuildApkStep::doRun()
|
||||||
{
|
{
|
||||||
if (m_skipBuilding) {
|
if (m_skipBuilding) {
|
||||||
const QString error = tr("Android deploy settings file not found, not building an APK.");
|
reportWarningOrError(tr("Android deploy settings file not found, not building an APK."),
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
emit finished(true);
|
emit finished(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -743,8 +739,11 @@ void AndroidBuildApkStep::doRun()
|
|||||||
const QString buildKey = target()->activeBuildKey();
|
const QString buildKey = target()->activeBuildKey();
|
||||||
|
|
||||||
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
||||||
if (!version)
|
if (!version) {
|
||||||
|
reportWarningOrError(tr("The Qt version for kit %1 is invalid.")
|
||||||
|
.arg(kit()->displayName()), Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const FilePath buildDir = buildDirectory();
|
const FilePath buildDir = buildDirectory();
|
||||||
const FilePath androidBuildDir = AndroidManager::androidBuildDirectory(target());
|
const FilePath androidBuildDir = AndroidManager::androidBuildDirectory(target());
|
||||||
@@ -752,10 +751,9 @@ void AndroidBuildApkStep::doRun()
|
|||||||
FilePath androidLibsDir = androidBuildDir / "libs" / abi;
|
FilePath androidLibsDir = androidBuildDir / "libs" / abi;
|
||||||
if (!androidLibsDir.exists()) {
|
if (!androidLibsDir.exists()) {
|
||||||
if (!androidLibsDir.ensureWritableDir()) {
|
if (!androidLibsDir.ensureWritableDir()) {
|
||||||
const QString error = tr("The Android build folder %1 wasn't found and "
|
reportWarningOrError(tr("The Android build folder %1 was not found and could "
|
||||||
"couldn't be created.").arg(androidLibsDir.toUserOutput());
|
"not be created.").arg(androidLibsDir.toUserOutput()),
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
} else if (version->qtVersion() >= QtSupport::QtVersionNumber{6, 0, 0}
|
} else if (version->qtVersion() >= QtSupport::QtVersionNumber{6, 0, 0}
|
||||||
&& version->qtVersion() <= QtSupport::QtVersionNumber{6, 1, 1}) {
|
&& version->qtVersion() <= QtSupport::QtVersionNumber{6, 1, 1}) {
|
||||||
@@ -769,11 +767,10 @@ void AndroidBuildApkStep::doRun()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!from.copyFile(to)) {
|
if (!from.copyFile(to)) {
|
||||||
const QString error = tr("Couldn't copy the target's lib file %1 to the "
|
reportWarningOrError(tr("Cannot copy the target's lib file %1 to the "
|
||||||
"Android build folder %2.")
|
"Android build folder %2.")
|
||||||
.arg(fileName, androidLibsDir.toUserOutput());
|
.arg(fileName, androidLibsDir.toUserOutput()),
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -799,8 +796,13 @@ void AndroidBuildApkStep::doRun()
|
|||||||
applicationBinary = buildSystem()->buildTarget(buildKey).targetFilePath.toString();
|
applicationBinary = buildSystem()->buildTarget(buildKey).targetFilePath.toString();
|
||||||
FilePath androidLibsDir = androidBuildDir / "libs" / androidAbis.first();
|
FilePath androidLibsDir = androidBuildDir / "libs" / androidAbis.first();
|
||||||
for (const FilePath &target : targets) {
|
for (const FilePath &target : targets) {
|
||||||
if (!copyFileIfNewer(target, androidLibsDir.pathAppended(target.fileName())))
|
if (!copyFileIfNewer(target, androidLibsDir.pathAppended(target.fileName()))) {
|
||||||
|
reportWarningOrError(
|
||||||
|
tr("Cannot copy file \"%1\" to Android build libs folder \"%2\".")
|
||||||
|
.arg(target.toUserOutput()).arg(androidLibsDir.toUserOutput()),
|
||||||
|
Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
deploySettings["target-architecture"] = androidAbis.first();
|
deploySettings["target-architecture"] = androidAbis.first();
|
||||||
} else {
|
} else {
|
||||||
@@ -818,8 +820,14 @@ void AndroidBuildApkStep::doRun()
|
|||||||
FilePath androidLibsDir = androidBuildDir / "libs" / abi;
|
FilePath androidLibsDir = androidBuildDir / "libs" / abi;
|
||||||
for (const FilePath &target : targets) {
|
for (const FilePath &target : targets) {
|
||||||
if (target.endsWith(targetSuffix)) {
|
if (target.endsWith(targetSuffix)) {
|
||||||
if (!copyFileIfNewer(target, androidLibsDir.pathAppended(target.fileName())))
|
const FilePath destination = androidLibsDir.pathAppended(target.fileName());
|
||||||
|
if (!copyFileIfNewer(target, destination)) {
|
||||||
|
reportWarningOrError(
|
||||||
|
tr("Cannot copy file \"%1\" to Android build libs folder \"%2\".")
|
||||||
|
.arg(target.toUserOutput()).arg(androidLibsDir.toUserOutput()),
|
||||||
|
Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
architectures[abi] = AndroidManager::archTriplet(abi);
|
architectures[abi] = AndroidManager::archTriplet(abi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -846,16 +854,18 @@ void AndroidBuildApkStep::doRun()
|
|||||||
deploySettings["qml-root-path"] = qmlRootPath;
|
deploySettings["qml-root-path"] = qmlRootPath;
|
||||||
|
|
||||||
QFile f{m_inputFile.toString()};
|
QFile f{m_inputFile.toString()};
|
||||||
if (!f.open(QIODevice::WriteOnly))
|
if (!f.open(QIODevice::WriteOnly)) {
|
||||||
|
reportWarningOrError(tr("Cannot open androiddeployqt input file \"%1\" for writing.")
|
||||||
|
.arg(m_inputFile.toUserOutput()), Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
f.write(QJsonDocument{deploySettings}.toJson());
|
f.write(QJsonDocument{deploySettings}.toJson());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!setup()) {
|
if (!setup()) {
|
||||||
const QString error = tr("Cannot set up Android, not building an APK.");
|
reportWarningOrError(tr("Cannot set up \"%1\", not building an APK.").arg(displayName()),
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
Task::Error);
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
|
||||||
emit finished(false);
|
emit finished(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -863,6 +873,13 @@ void AndroidBuildApkStep::doRun()
|
|||||||
AbstractProcessStep::doRun();
|
AbstractProcessStep::doRun();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidBuildApkStep::reportWarningOrError(const QString &message, Task::TaskType type)
|
||||||
|
{
|
||||||
|
qCDebug(buildapkstepLog) << message;
|
||||||
|
emit addOutput(message, OutputFormat::ErrorMessage);
|
||||||
|
TaskHub::addTask(BuildSystemTask(type, message));
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidBuildApkStep::processStarted()
|
void AndroidBuildApkStep::processStarted()
|
||||||
{
|
{
|
||||||
emit addOutput(tr("Starting: \"%1\" %2")
|
emit addOutput(tr("Starting: \"%1\" %2")
|
||||||
|
@@ -92,6 +92,8 @@ private:
|
|||||||
|
|
||||||
void doRun() override;
|
void doRun() override;
|
||||||
|
|
||||||
|
void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type);
|
||||||
|
|
||||||
bool m_buildAAB = false;
|
bool m_buildAAB = false;
|
||||||
bool m_signPackage = false;
|
bool m_signPackage = false;
|
||||||
bool m_verbose = false;
|
bool m_verbose = false;
|
||||||
|
@@ -112,9 +112,8 @@ bool AndroidDeployQtStep::init()
|
|||||||
{
|
{
|
||||||
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit());
|
||||||
if (!version) {
|
if (!version) {
|
||||||
qCDebug(deployStepLog,
|
reportWarningOrError(tr("The Qt version for kit %1 is invalid.").arg(kit()->displayName()),
|
||||||
"The Qt version for kit %s is not valid.",
|
Task::Error);
|
||||||
qPrintable(kit()->displayName()));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,18 +121,21 @@ bool AndroidDeployQtStep::init()
|
|||||||
|
|
||||||
m_androidABIs = AndroidManager::applicationAbis(target());
|
m_androidABIs = AndroidManager::applicationAbis(target());
|
||||||
if (m_androidABIs.isEmpty()) {
|
if (m_androidABIs.isEmpty()) {
|
||||||
const QString error = tr("No Android arch set by the .pro file.");
|
reportWarningOrError(tr("No Android architecture (ABI) is set by the project."),
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit addOutput(tr("Initializing deployment to Android device/simulator"), OutputFormat::Stdout);
|
emit addOutput(tr("Initializing deployment to Android device/simulator"),
|
||||||
|
OutputFormat::NormalMessage);
|
||||||
|
|
||||||
RunConfiguration *rc = target()->activeRunConfiguration();
|
RunConfiguration *rc = target()->activeRunConfiguration();
|
||||||
QTC_ASSERT(rc, return false);
|
QTC_ASSERT(rc, reportWarningOrError(tr("The kit's run configuration is invalid."), Task::Error);
|
||||||
|
return false);
|
||||||
BuildConfiguration *bc = target()->activeBuildConfiguration();
|
BuildConfiguration *bc = target()->activeBuildConfiguration();
|
||||||
QTC_ASSERT(bc, return false);
|
QTC_ASSERT(bc, reportWarningOrError(tr("The kit's build configuration is invalid."),
|
||||||
|
Task::Error);
|
||||||
|
return false);
|
||||||
|
|
||||||
auto androidBuildApkStep = bc->buildSteps()->firstOfType<AndroidBuildApkStep>();
|
auto androidBuildApkStep = bc->buildSteps()->firstOfType<AndroidBuildApkStep>();
|
||||||
const int minTargetApi = AndroidManager::minimumSDK(target());
|
const int minTargetApi = AndroidManager::minimumSDK(target());
|
||||||
@@ -142,9 +144,12 @@ bool AndroidDeployQtStep::init()
|
|||||||
|
|
||||||
// Try to re-use user-provided information from an earlier step of the same type.
|
// Try to re-use user-provided information from an earlier step of the same type.
|
||||||
BuildStepList *bsl = stepList();
|
BuildStepList *bsl = stepList();
|
||||||
QTC_ASSERT(bsl, return false);
|
QTC_ASSERT(bsl, reportWarningOrError(tr("The kit's build steps list is invalid."), Task::Error);
|
||||||
|
return false);
|
||||||
auto androidDeployQtStep = bsl->firstOfType<AndroidDeployQtStep>();
|
auto androidDeployQtStep = bsl->firstOfType<AndroidDeployQtStep>();
|
||||||
QTC_ASSERT(androidDeployQtStep, return false);
|
QTC_ASSERT(androidDeployQtStep,
|
||||||
|
reportWarningOrError(tr("The kit's deploy configuration is invalid."), Task::Error);
|
||||||
|
return false);
|
||||||
AndroidDeviceInfo info;
|
AndroidDeviceInfo info;
|
||||||
if (androidDeployQtStep != this)
|
if (androidDeployQtStep != this)
|
||||||
info = androidDeployQtStep->m_deviceInfo;
|
info = androidDeployQtStep->m_deviceInfo;
|
||||||
@@ -159,44 +164,44 @@ bool AndroidDeployQtStep::init()
|
|||||||
if (selectedAbis.isEmpty())
|
if (selectedAbis.isEmpty())
|
||||||
selectedAbis.append(bs->extraData(buildKey, Constants::AndroidAbi).toString());
|
selectedAbis.append(bs->extraData(buildKey, Constants::AndroidAbi).toString());
|
||||||
|
|
||||||
|
// TODO: use AndroidDevice directly instead of AndroidDeviceInfo.
|
||||||
if (!info.isValid()) {
|
if (!info.isValid()) {
|
||||||
const IDevice *dev = DeviceKitAspect::device(kit()).data();
|
const IDevice *dev = DeviceKitAspect::device(kit()).data();
|
||||||
|
if (!dev) {
|
||||||
|
reportWarningOrError(tr("The deployment device \"%1\" is invalid."), Task::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
info = AndroidDevice::androidDeviceInfoFromIDevice(dev);
|
info = AndroidDevice::androidDeviceInfoFromIDevice(dev);
|
||||||
m_deviceInfo = info; // Keep around for later steps
|
m_deviceInfo = info; // Keep around for later steps
|
||||||
|
|
||||||
if (!info.isValid()) {
|
if (!info.isValid()) {
|
||||||
const QString error = tr("The deployment device \"%1\" is invalid.")
|
reportWarningOrError(tr("The deployment device \"%1\" is invalid.")
|
||||||
.arg(dev->displayName());
|
.arg(dev->displayName()), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(dev);
|
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(dev);
|
||||||
if (androidDev && !androidDev->canSupportAbis(selectedAbis)) {
|
if (androidDev && !androidDev->canSupportAbis(selectedAbis)) {
|
||||||
const QString error = tr("The deployment device \"%1\" doesn't support the "
|
const QString error = tr("The deployment device \"%1\" does not support the "
|
||||||
"architectures used by the kit.\n"
|
"architectures used by the kit.\n"
|
||||||
"The kit supports \"%2\", but the device uses \"%3\".")
|
"The kit supports \"%2\", but the device uses \"%3\".")
|
||||||
.arg(dev->displayName()).arg(selectedAbis.join(", "))
|
.arg(dev->displayName()).arg(selectedAbis.join(", "))
|
||||||
.arg(androidDev->supportedAbis().join(", "));
|
.arg(androidDev->supportedAbis().join(", "));
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (androidDev && !androidDev->canHandleDeployments()) {
|
if (androidDev && !androidDev->canHandleDeployments()) {
|
||||||
const QString error = tr("The deployment device \"%1\" is disconnected.")
|
reportWarningOrError(tr("The deployment device \"%1\" is disconnected.")
|
||||||
.arg(dev->displayName());
|
.arg(dev->displayName()), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
|
const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
|
||||||
if (qt && qt->supportsMultipleQtAbis() && !selectedAbis.contains(info.cpuAbi.first())) {
|
if (qt && qt->supportsMultipleQtAbis() && !selectedAbis.contains(info.cpuAbi.first())) {
|
||||||
TaskHub::addTask(DeploymentTask(
|
TaskHub::addTask(DeploymentTask(Task::Warning,
|
||||||
Task::Warning,
|
|
||||||
tr("Android: The main ABI of the deployment device (%1) is not selected. The app "
|
tr("Android: The main ABI of the deployment device (%1) is not selected. The app "
|
||||||
"execution or debugging might not work properly. Add it from Projects > Build > "
|
"execution or debugging might not work properly. Add it from Projects > Build > "
|
||||||
"Build Steps > qmake > ABIs.")
|
"Build Steps > qmake > ABIs.")
|
||||||
@@ -213,7 +218,7 @@ bool AndroidDeployQtStep::init()
|
|||||||
AndroidManager::setDeviceApiLevel(target(), info.sdk);
|
AndroidManager::setDeviceApiLevel(target(), info.sdk);
|
||||||
AndroidManager::setDeviceAbis(target(), info.cpuAbi);
|
AndroidManager::setDeviceAbis(target(), info.cpuAbi);
|
||||||
|
|
||||||
emit addOutput(tr("Deploying to %1").arg(m_serialNumber), OutputFormat::Stdout);
|
emit addOutput(tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage);
|
||||||
|
|
||||||
m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value();
|
m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value();
|
||||||
if (m_uninstallPreviousPackageRun)
|
if (m_uninstallPreviousPackageRun)
|
||||||
@@ -223,8 +228,10 @@ bool AndroidDeployQtStep::init()
|
|||||||
if (m_useAndroiddeployqt) {
|
if (m_useAndroiddeployqt) {
|
||||||
const QString buildKey = target()->activeBuildKey();
|
const QString buildKey = target()->activeBuildKey();
|
||||||
const ProjectNode *node = target()->project()->findNodeForBuildKey(buildKey);
|
const ProjectNode *node = target()->project()->findNodeForBuildKey(buildKey);
|
||||||
if (!node)
|
if (!node) {
|
||||||
|
reportWarningOrError(tr("The deployment step's project node is invalid."), Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
m_apkPath = Utils::FilePath::fromString(node->data(Constants::AndroidApk).toString());
|
m_apkPath = Utils::FilePath::fromString(node->data(Constants::AndroidApk).toString());
|
||||||
if (!m_apkPath.isEmpty()) {
|
if (!m_apkPath.isEmpty()) {
|
||||||
m_manifestName = Utils::FilePath::fromString(node->data(Constants::AndroidManifest).toString());
|
m_manifestName = Utils::FilePath::fromString(node->data(Constants::AndroidManifest).toString());
|
||||||
@@ -233,16 +240,13 @@ bool AndroidDeployQtStep::init()
|
|||||||
} else {
|
} else {
|
||||||
QString jsonFile = AndroidQtVersion::androidDeploymentSettings(target()).toString();
|
QString jsonFile = AndroidQtVersion::androidDeploymentSettings(target()).toString();
|
||||||
if (jsonFile.isEmpty()) {
|
if (jsonFile.isEmpty()) {
|
||||||
const QString error = tr("Cannot find the androiddeploy Json file.");
|
reportWarningOrError(tr("Cannot find the androiddeployqt input JSON file."),
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_command = version->hostBinPath();
|
m_command = version->hostBinPath();
|
||||||
if (m_command.isEmpty()) {
|
if (m_command.isEmpty()) {
|
||||||
const QString error = tr("Cannot find the androiddeployqt tool.");
|
reportWarningOrError(tr("Cannot find the androiddeployqt tool."), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_command = m_command.pathAppended("androiddeployqt").withExecutableSuffix();
|
m_command = m_command.pathAppended("androiddeployqt").withExecutableSuffix();
|
||||||
@@ -302,13 +306,14 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy()
|
|||||||
if (m_uninstallPreviousPackageRun) {
|
if (m_uninstallPreviousPackageRun) {
|
||||||
packageName = AndroidManager::packageName(m_manifestName);
|
packageName = AndroidManager::packageName(m_manifestName);
|
||||||
if (packageName.isEmpty()) {
|
if (packageName.isEmpty()) {
|
||||||
const QString error = tr("Cannot find the package name.");
|
reportWarningOrError(tr("Cannot find the package name from the Android Manifest "
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
"file \"%1\".").arg(m_manifestName.toUserOutput()),
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
Task::Error);
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
qCDebug(deployStepLog) << "Uninstalling previous package";
|
const QString msg = tr("Uninstalling the previous package \"%1\".").arg(packageName);
|
||||||
emit addOutput(tr("Uninstall previous package %1.").arg(packageName), OutputFormat::NormalMessage);
|
qCDebug(deployStepLog) << msg;
|
||||||
|
emit addOutput(msg, OutputFormat::NormalMessage);
|
||||||
runCommand({m_adbPath,
|
runCommand({m_adbPath,
|
||||||
AndroidDeviceInfo::adbSelector(m_serialNumber)
|
AndroidDeviceInfo::adbSelector(m_serialNumber)
|
||||||
<< "uninstall" << packageName});
|
<< "uninstall" << packageName});
|
||||||
@@ -337,8 +342,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy()
|
|||||||
|
|
||||||
m_process->start();
|
m_process->start();
|
||||||
|
|
||||||
emit addOutput(tr("Starting: \"%1\"").arg(cmd.toUserOutput()),
|
emit addOutput(tr("Starting: \"%1\"").arg(cmd.toUserOutput()), OutputFormat::NormalMessage);
|
||||||
BuildStep::OutputFormat::NormalMessage);
|
|
||||||
|
|
||||||
while (!m_process->waitForFinished(200)) {
|
while (!m_process->waitForFinished(200)) {
|
||||||
if (m_process->state() == QProcess::NotRunning)
|
if (m_process->state() == QProcess::NotRunning)
|
||||||
@@ -369,24 +373,27 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy()
|
|||||||
|
|
||||||
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
||||||
emit addOutput(tr("The process \"%1\" exited normally.").arg(m_command.toUserOutput()),
|
emit addOutput(tr("The process \"%1\" exited normally.").arg(m_command.toUserOutput()),
|
||||||
BuildStep::OutputFormat::NormalMessage);
|
OutputFormat::NormalMessage);
|
||||||
} else if (exitStatus == QProcess::NormalExit) {
|
} else if (exitStatus == QProcess::NormalExit) {
|
||||||
const QString error = tr("The process \"%1\" exited with code %2.")
|
const QString error = tr("The process \"%1\" exited with code %2.")
|
||||||
.arg(m_command.toUserOutput(), QString::number(exitCode));
|
.arg(m_command.toUserOutput(), QString::number(exitCode));
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
} else {
|
} else {
|
||||||
const QString error = tr("The process \"%1\" crashed.").arg(m_command.toUserOutput());
|
const QString error = tr("The process \"%1\" crashed.").arg(m_command.toUserOutput());
|
||||||
emit addOutput(error, BuildStep::OutputFormat::ErrorMessage);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deployError != NoError) {
|
if (deployError != NoError) {
|
||||||
if (m_uninstallPreviousPackageRun)
|
if (m_uninstallPreviousPackageRun) {
|
||||||
deployError = Failure; // Even re-install failed. Set to Failure.
|
deployError = Failure; // Even re-install failed. Set to Failure.
|
||||||
|
reportWarningOrError(
|
||||||
|
tr("Installing the app failed even after uninstalling the previous one."),
|
||||||
|
Task::Error);
|
||||||
|
}
|
||||||
} else if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
|
} else if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
|
||||||
// Set the deployError to Failure when no deployError code was detected
|
// Set the deployError to Failure when no deployError code was detected
|
||||||
// but the adb tool failed otherwise relay the detected deployError.
|
// but the adb tool failed otherwise relay the detected deployError.
|
||||||
|
reportWarningOrError(tr("Installing the app failed with an unknown error."), Task::Error);
|
||||||
deployError = Failure;
|
deployError = Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +428,8 @@ void AndroidDeployQtStep::slotAskForUninstall(DeployErrorCode errorCode)
|
|||||||
mask <<= 1;
|
mask <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstallMsg.append(tr("\nUninstalling the installed package may solve the issue.\nDo you want to uninstall the existing package?"));
|
uninstallMsg.append(tr("\nUninstalling the installed package may solve the issue.\n"
|
||||||
|
"Do you want to uninstall the existing package?"));
|
||||||
int button = QMessageBox::critical(nullptr, tr("Install failed"), uninstallMsg,
|
int button = QMessageBox::critical(nullptr, tr("Install failed"), uninstallMsg,
|
||||||
QMessageBox::Yes, QMessageBox::No);
|
QMessageBox::Yes, QMessageBox::No);
|
||||||
m_askForUninstall = button == QMessageBox::Yes;
|
m_askForUninstall = button == QMessageBox::Yes;
|
||||||
@@ -432,10 +440,13 @@ bool AndroidDeployQtStep::runImpl()
|
|||||||
if (!m_avdName.isEmpty()) {
|
if (!m_avdName.isEmpty()) {
|
||||||
QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, cancelChecker());
|
QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, cancelChecker());
|
||||||
qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber;
|
qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber;
|
||||||
if (serialNumber.isEmpty())
|
if (serialNumber.isEmpty()) {
|
||||||
|
reportWarningOrError(tr("The deployment AVD \"%1\" cannot be started.")
|
||||||
|
.arg(m_avdName), Task::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
m_serialNumber = serialNumber;
|
m_serialNumber = serialNumber;
|
||||||
qCDebug(deployStepLog) << "Target device serial number change:" << serialNumber;
|
qCDebug(deployStepLog) << "Deployment device serial number changed:" << serialNumber;
|
||||||
AndroidManager::setDeviceSerialNumber(target(), serialNumber);
|
AndroidManager::setDeviceSerialNumber(target(), serialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,7 +462,8 @@ bool AndroidDeployQtStep::runImpl()
|
|||||||
if (!m_filesToPull.isEmpty())
|
if (!m_filesToPull.isEmpty())
|
||||||
emit addOutput(tr("Pulling files necessary for debugging."), OutputFormat::NormalMessage);
|
emit addOutput(tr("Pulling files necessary for debugging."), OutputFormat::NormalMessage);
|
||||||
|
|
||||||
// Note that values are not necessarily unique, e.g. app_process is looked up in several directories
|
// Note that values are not necessarily unique, e.g. app_process is looked up in several
|
||||||
|
// directories
|
||||||
for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr) {
|
for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr) {
|
||||||
QFile::remove(itr.value());
|
QFile::remove(itr.value());
|
||||||
}
|
}
|
||||||
@@ -464,8 +476,7 @@ bool AndroidDeployQtStep::runImpl()
|
|||||||
const QString error = tr("Package deploy: Failed to pull \"%1\" to \"%2\".")
|
const QString error = tr("Package deploy: Failed to pull \"%1\" to \"%2\".")
|
||||||
.arg(itr.key())
|
.arg(itr.key())
|
||||||
.arg(itr.value());
|
.arg(itr.value());
|
||||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
reportWarningOrError(error, Task::Error);
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,11 +529,8 @@ void AndroidDeployQtStep::runCommand(const CommandLine &command)
|
|||||||
buildProc.setCommand(command);
|
buildProc.setCommand(command);
|
||||||
buildProc.setProcessUserEventWhileRunning();
|
buildProc.setProcessUserEventWhileRunning();
|
||||||
buildProc.runBlocking();
|
buildProc.runBlocking();
|
||||||
if (buildProc.result() != QtcProcess::FinishedWithSuccess) {
|
if (buildProc.result() != QtcProcess::FinishedWithSuccess)
|
||||||
const QString error = buildProc.exitMessage();
|
reportWarningOrError(buildProc.exitMessage(), Task::Error);
|
||||||
emit addOutput(error, OutputFormat::ErrorMessage);
|
|
||||||
TaskHub::addTask(DeploymentTask(Task::Error, error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *AndroidDeployQtStep::createConfigWidget()
|
QWidget *AndroidDeployQtStep::createConfigWidget()
|
||||||
@@ -590,6 +598,13 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::parseDeployErrors(
|
|||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidDeployQtStep::reportWarningOrError(const QString &message, Task::TaskType type)
|
||||||
|
{
|
||||||
|
qCDebug(deployStepLog) << message;
|
||||||
|
emit addOutput(message, OutputFormat::ErrorMessage);
|
||||||
|
TaskHub::addTask(DeploymentTask(type, message));
|
||||||
|
}
|
||||||
|
|
||||||
// AndroidDeployQtStepFactory
|
// AndroidDeployQtStepFactory
|
||||||
|
|
||||||
AndroidDeployQtStepFactory::AndroidDeployQtStepFactory()
|
AndroidDeployQtStepFactory::AndroidDeployQtStepFactory()
|
||||||
|
@@ -83,8 +83,15 @@ private:
|
|||||||
void stdError(const QString &line);
|
void stdError(const QString &line);
|
||||||
DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
|
DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
|
||||||
|
|
||||||
friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) { e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2); }
|
friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) {
|
||||||
friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) { return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2); }
|
e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) {
|
||||||
|
return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type);
|
||||||
|
|
||||||
Utils::FilePath m_manifestName;
|
Utils::FilePath m_manifestName;
|
||||||
QString m_serialNumber;
|
QString m_serialNumber;
|
||||||
|
@@ -72,6 +72,8 @@ private:
|
|||||||
void setupOutputFormatter(OutputFormatter *formatter) final;
|
void setupOutputFormatter(OutputFormatter *formatter) final;
|
||||||
void doRun() final;
|
void doRun() final;
|
||||||
|
|
||||||
|
void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type);
|
||||||
|
|
||||||
QStringList m_androidDirsToClean;
|
QStringList m_androidDirsToClean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,11 +91,16 @@ AndroidPackageInstallationStep::AndroidPackageInstallationStep(BuildStepList *bs
|
|||||||
|
|
||||||
bool AndroidPackageInstallationStep::init()
|
bool AndroidPackageInstallationStep::init()
|
||||||
{
|
{
|
||||||
if (!AbstractProcessStep::init())
|
if (!AbstractProcessStep::init()) {
|
||||||
|
reportWarningOrError(tr("\"%1\" step failed initialization.").arg(displayName()),
|
||||||
|
Task::TaskType::Error);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ToolChain *tc = ToolChainKitAspect::cxxToolChain(kit());
|
ToolChain *tc = ToolChainKitAspect::cxxToolChain(kit());
|
||||||
QTC_ASSERT(tc, return false);
|
QTC_ASSERT(tc, reportWarningOrError(tr("\"%1\" step has an invalid C++ toolchain.")
|
||||||
|
.arg(displayName()), Task::TaskType::Error);
|
||||||
|
return false);
|
||||||
|
|
||||||
QString dirPath = nativeAndroidBuildPath();
|
QString dirPath = nativeAndroidBuildPath();
|
||||||
const QString innerQuoted = ProcessArgs::quoteArg(dirPath);
|
const QString innerQuoted = ProcessArgs::quoteArg(dirPath);
|
||||||
@@ -140,8 +147,9 @@ void AndroidPackageInstallationStep::doRun()
|
|||||||
if (!dir.isEmpty() && androidDir.exists()) {
|
if (!dir.isEmpty() && androidDir.exists()) {
|
||||||
emit addOutput(tr("Removing directory %1").arg(dir), OutputFormat::NormalMessage);
|
emit addOutput(tr("Removing directory %1").arg(dir), OutputFormat::NormalMessage);
|
||||||
if (!androidDir.removeRecursively(&error)) {
|
if (!androidDir.removeRecursively(&error)) {
|
||||||
emit addOutput(error, OutputFormat::Stderr);
|
reportWarningOrError(tr("Failed to clean \"%1\" from the previous build, with "
|
||||||
TaskHub::addTask(BuildSystemTask(Task::Error, error));
|
"error:\n%2").arg(androidDir.toUserOutput()).arg(error),
|
||||||
|
Task::TaskType::Error);
|
||||||
emit finished(false);
|
emit finished(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -167,13 +175,21 @@ void AndroidPackageInstallationStep::doRun()
|
|||||||
qPrintable(file.fileName()));
|
qPrintable(file.fileName()));
|
||||||
} else {
|
} else {
|
||||||
qCDebug(packageInstallationStepLog,
|
qCDebug(packageInstallationStepLog,
|
||||||
"Cound't add %s to the package. The QML debugger might not work properly.",
|
"Cannot add %s to the package. The QML debugger might not work properly.",
|
||||||
qPrintable(file.fileName()));
|
qPrintable(file.fileName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidPackageInstallationStep::reportWarningOrError(const QString &message,
|
||||||
|
Task::TaskType type)
|
||||||
|
{
|
||||||
|
qCDebug(packageInstallationStepLog) << message;
|
||||||
|
emit addOutput(message, OutputFormat::ErrorMessage);
|
||||||
|
TaskHub::addTask(BuildSystemTask(type, message));
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// AndroidPackageInstallationStepFactory
|
// AndroidPackageInstallationStepFactory
|
||||||
//
|
//
|
||||||
|
Reference in New Issue
Block a user