From 26463a22190a941a6b1ba2965dfe296747beb0d5 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 6 Mar 2019 14:17:17 +0200 Subject: [PATCH] Say hello to Android CMake support Requirements: - NDKr19 or newer - Qt 5.12.1 or newer QtCreator supports the following variables: - ANDROID_PACKAGE_SOURCE_DIR - ANDROID_EXTRA_LIBS Be aware, that there is a lot of magic done on QtCreator side, and you can't use only cmake to build an Android APK. [ChangeLog][Android][CMake] Add Android support for CMake projects. Change-Id: I1d351976ed56f424c2bc972f4ff7b5968147a2ed Reviewed-by: hjk --- .../qtquickapplication/CMakeLists.txt | 18 ++- src/plugins/android/androidbuildapkstep.cpp | 105 +++++++++++++++++- src/plugins/android/androidbuildapkstep.h | 1 + src/plugins/android/androidconstants.h | 9 +- src/plugins/android/androidmanager.cpp | 33 +++++- src/plugins/android/androidmanager.h | 4 +- .../androidpackageinstallationstep.cpp | 1 + .../cmakebuildconfiguration.cpp | 50 ++++++++- .../cmakebuildconfiguration.h | 3 +- .../cmakeprojectmanager/cmakeproject.cpp | 47 +++++++- .../cmakeprojectmanager/cmakeprojectnodes.cpp | 60 +++++++++- .../cmakeprojectmanager/cmakeprojectnodes.h | 7 +- src/plugins/projectexplorer/buildstep.cpp | 6 + src/plugins/projectexplorer/buildstep.h | 1 + .../qmakeprojectmanager/qmakenodes.cpp | 4 + 15 files changed, 327 insertions(+), 22 deletions(-) diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/CMakeLists.txt b/share/qtcreator/templates/wizards/projects/qtquickapplication/CMakeLists.txt index f28d772a1a4..0b7b8321556 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/CMakeLists.txt +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/CMakeLists.txt @@ -10,6 +10,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt5 COMPONENTS Core Quick REQUIRED) -add_executable(${PROJECT_NAME} "%{MainCppFileName}" "qml.qrc") +if(ANDROID) + add_library(${PROJECT_NAME} SHARED "%{MainCppFileName}" "qml.qrc") +else() + add_executable(${PROJECT_NAME} "%{MainCppFileName}" "qml.qrc") +endif() + target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) + +# QtCreator supports the following variables for Android, which are identical to qmake Android variables. +# Check http://doc.qt.io/qt-5/deployment-android.html for more information. +# These variables must use CACHE, otherwise QtCreator won't see them. + +#if(ANDROID) +# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" CACHE INTERNAL "") +# if (ANDROID_ABI STREQUAL "armeabi-v7a") +# set(ANDROID_EXTRA_LIBS ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so CACHE INTERNAL "") +# endif() +#endif() diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 85a8a02503d..908ac3de3e2 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -54,10 +54,11 @@ #include #include -#include - +#include #include #include +#include +#include #include #include #include @@ -192,9 +193,14 @@ bool AndroidBuildApkStep::init() &Utils::FileName::toString)); RunConfiguration *rc = target()->activeRunConfiguration(); - const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(rc->buildKey()) : nullptr; + const QString buildKey = rc ? rc->buildKey() : QString(); + const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(buildKey) : nullptr; - QFileInfo sourceDirInfo(node ? node->data(Constants::AndroidPackageSourceDir).toString() : QString()); + QString sourceDirName; + if (node) + sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString(); + + QFileInfo sourceDirInfo(sourceDirName); parser->setSourceDirectory(Utils::FileName::fromString(sourceDirInfo.canonicalFilePath())); parser->setBuildDirectory(Utils::FileName::fromString(bc->buildDirectory().appendPath(Constants::ANDROID_BUILDDIRECTORY).toString())); setOutputParser(parser); @@ -340,13 +346,89 @@ bool AndroidBuildApkStep::verifyCertificatePassword() return success; } + +static bool copyFileIfNewer(const QString &sourceFileName, + const QString &destinationFileName) +{ + if (QFile::exists(destinationFileName)) { + QFileInfo destinationFileInfo(destinationFileName); + QFileInfo sourceFileInfo(sourceFileName); + if (sourceFileInfo.lastModified() <= destinationFileInfo.lastModified()) + return true; + if (!QFile(destinationFileName).remove()) + return false; + } + + if (!QDir().mkpath(QFileInfo(destinationFileName).path())) + return false; + return QFile::copy(sourceFileName, destinationFileName); +} + void AndroidBuildApkStep::doRun() { if (m_skipBuilding) { - emit addOutput(tr("No application .pro file found, not building an APK."), BuildStep::OutputFormat::ErrorMessage); + emit addOutput(tr("Android deploy settings file not found, not building an APK."), BuildStep::OutputFormat::ErrorMessage); emit finished(true); return; } + + auto setup = [this] { + auto bc = target()->activeBuildConfiguration(); + Utils::FileName androidLibsDir = bc->buildDirectory() + .appendPath("android-build/libs") + .appendPath(AndroidManager::targetArch(target())); + if (!androidLibsDir.exists() && !QDir{bc->buildDirectory().toString()}.mkpath(androidLibsDir.toString())) + return false; + + + QJsonObject deploySettings = Android::AndroidManager::deploymentSettings(target()); + RunConfiguration *rc = target()->activeRunConfiguration(); + const QString buildKey = rc ? rc->buildKey() : QString(); + const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(buildKey) : nullptr; + + if (!node) + return false; + + auto targets = node->data(Android::Constants::AndroidTargets).toStringList(); + if (targets.isEmpty()) + return true; // qmake does this job for us + + // Copy targets to android build folder + for (const auto &target : targets) { + if (!copyFileIfNewer(target, androidLibsDir.appendPath(QFileInfo{target}.fileName()).toString())) + return false; + } + + QString extraLibs = node->data(Android::Constants::AndroidExtraLibs).toString(); + if (!extraLibs.isEmpty()) + deploySettings["android-extra-libs"] = extraLibs; + + QString androidSrcs = node->data(Android::Constants::AndroidPackageSourceDir).toString(); + if (!androidSrcs.isEmpty()) + deploySettings["android-package-source-directory"] = androidSrcs; + + QString qmlImportPath = node->data("QML_IMPORT_PATH").toString(); + if (!qmlImportPath.isEmpty()) + deploySettings["qml-import-paths"] = qmlImportPath; + + QString qmlRootPath = node->data("QML_ROOT_PATH").toString(); + if (qmlRootPath.isEmpty()) + qmlRootPath = target()->project()->rootProjectDirectory().toString(); + deploySettings["qml-root-path"] = qmlImportPath; + + QFile f{bc->buildDirectory().appendPath("android_deployment_settings.json").toString()}; + if (!f.open(QIODevice::WriteOnly)) + return false; + f.write(QJsonDocument{deploySettings}.toJson()); + return true; + }; + + if (!setup()) { + emit addOutput(tr("Cannot set up Android, not building an APK."), BuildStep::OutputFormat::ErrorMessage); + emit finished(false); + return; + } + AbstractProcessStep::doRun(); } @@ -398,6 +480,18 @@ void AndroidBuildApkStep::setBuildTargetSdk(const QString &sdk) AndroidManager::updateGradleProperties(target()); } +QVariant AndroidBuildApkStep::data(Core::Id id) const +{ + if (id == Constants::AndroidNdkPlatform) + return AndroidConfigurations::currentConfig().bestNdkPlatformMatch(AndroidManager::minimumSDK(target())).mid(8); + if (id == Constants::NdkLocation) + return QVariant::fromValue(AndroidConfigurations::currentConfig().ndkLocation()); + if (id == Constants::AndroidABI) + return AndroidManager::targetArch(target()); + + return AbstractProcessStep::data(id); +} + void AndroidBuildApkStep::setKeystorePath(const Utils::FileName &path) { m_keystorePath = path; @@ -568,7 +662,6 @@ namespace Internal { AndroidBuildApkStepFactory::AndroidBuildApkStepFactory() { registerStep(Constants::ANDROID_BUILD_APK_ID); - setSupportedProjectType(QmakeProjectManager::Constants::QMAKEPROJECT_ID); setSupportedDeviceType(Constants::ANDROID_DEVICE_TYPE); setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); setDisplayName(AndroidBuildApkStep::tr("Build Android APK")); diff --git a/src/plugins/android/androidbuildapkstep.h b/src/plugins/android/androidbuildapkstep.h index e336d5c5c7f..8c1028c6560 100644 --- a/src/plugins/android/androidbuildapkstep.h +++ b/src/plugins/android/androidbuildapkstep.h @@ -76,6 +76,7 @@ public: QString buildTargetSdk() const; void setBuildTargetSdk(const QString &sdk); + QVariant data(Core::Id id) const override; private: Q_INVOKABLE void showInGraphicalShell(); diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index ee2630c1987..44d52b1d94f 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -72,14 +72,19 @@ const char ANDROID_PACKAGE_SOURCE_DIR[] = "AndroidPackageSourceDir"; const char ANDROID_EXTRA_LIBS[] = "AndroidExtraLibs"; const char ANDROID_PACKAGENAME[] = "Android.PackageName"; -const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[] = "Qt4ProjectManager.AndroidPackageInstallationStep"; -const char ANDROID_BUILD_APK_ID[] = "QmakeProjectManager.AndroidBuildApkStep"; +const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[] = "Android.PackageInstallationStep"; +const char ANDROID_BUILD_APK_ID[] = "Android.BuildApkStep"; const char AndroidPackageSourceDir[] = "AndroidPackageSourceDir"; // QString const char AndroidDeploySettingsFile[] = "AndroidDeploySettingsFile"; // QString const char AndroidExtraLibs[] = "AndroidExtraLibs"; // QStringList const char AndroidArch[] = "AndroidArch"; // QString const char AndroidSoLibPath[] = "AndroidSoLibPath"; // QStringList +const char AndroidTargets[] = "AndroidTargets"; // QStringList + +const char AndroidNdkPlatform[] = "AndroidNdkPlatform"; //QString +const char NdkLocation[] = "NdkLocation"; // FileName +const char AndroidABI[] = "AndroidABI"; // QString } // namespace Constants; } // namespace Android diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 0fdaba96a6c..18d0ff7a88d 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -258,12 +258,40 @@ QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target) return fallback; } -QString AndroidManager::targetArch(ProjectExplorer::Target *target) +QString AndroidManager::targetArch(const Target *target) { auto qt = static_cast(QtSupport::QtKitAspect::qtVersion(target->kit())); return qt->targetArch(); } +QJsonObject AndroidManager::deploymentSettings(const Target *target) +{ + QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit()); + if (!qt) + return {}; + + auto tc = ProjectExplorer::ToolChainKitAspect::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID); + if (!tc || tc->typeId() != Constants::ANDROID_TOOLCHAIN_ID) + return {}; + QJsonObject settings; + settings["description"] = "This file is generated by QtCreator to be read by androiddeployqt and should not be modified by hand."; + settings["qt"] = qt->qmakeProperty("QT_INSTALL_PREFIX"); + settings["ndk"] = AndroidConfigurations::currentConfig().ndkLocation().toString(); + settings["sdk"] = AndroidConfigurations::currentConfig().sdkLocation().toString(); + settings["sdkBuildToolsRevision"] = AndroidConfigurations::currentConfig().buildToolsVersion().toString(); + settings["application-binary"] = target->activeRunConfiguration()->buildTargetInfo().targetFilePath.toString(); + settings["target-architecture"] = targetArch(target); + settings["toolchain-prefix"] = "llvm"; + settings["tool-prefix"] = "llvm"; + settings["useLLVM"] = true; + settings["ndk-host"] = AndroidConfigurations::currentConfig().toolchainHost(); + settings["stdcpp-path"] = AndroidConfigurations::currentConfig().ndkLocation() + .appendPath("/sources/cxx-stl/llvm-libc++/libs/") + .appendString(targetArch(target)) + .appendPath("libc++_shared.so").toString(); + return settings; +} + Utils::FileName AndroidManager::dirPath(const ProjectExplorer::Target *target) { if (target->activeBuildConfiguration()) @@ -605,7 +633,8 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target) if (!node) return false; - QFileInfo sourceDirInfo(node->data(Constants::AndroidPackageSourceDir).toString()); + const QString sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString(); + QFileInfo sourceDirInfo(sourceDirName); FileName packageSourceDir = FileName::fromString(sourceDirInfo.canonicalFilePath()); if (!packageSourceDir.appendPath("gradlew").exists()) return false; diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h index 7766625c943..1e7cb516af5 100644 --- a/src/plugins/android/androidmanager.h +++ b/src/plugins/android/androidmanager.h @@ -87,7 +87,7 @@ public: static int minimumSDK(ProjectExplorer::Target *target); static int minimumSDK(const ProjectExplorer::Kit *kit); - static QString targetArch(ProjectExplorer::Target *target); + static QString targetArch(const ProjectExplorer::Target *target); static Utils::FileName dirPath(const ProjectExplorer::Target *target); static Utils::FileName manifestPath(ProjectExplorer::Target *target); @@ -114,6 +114,8 @@ public: int timeoutS = 30); static SdkToolResult runAaptCommand(const QStringList &args, int timeoutS = 30); + static QJsonObject deploymentSettings(const ProjectExplorer::Target *target); + private: static SdkToolResult runCommand(const QString &executable, const QStringList &args, const QByteArray &writeData = {}, int timeoutS = 30); diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 58fb637240b..851eaf64228 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -68,6 +68,7 @@ bool AndroidPackageInstallationStep::init() ToolChain *tc = ToolChainKitAspect::toolChain(target()->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID); + QTC_ASSERT(tc, return false); ProcessParameters *pp = processParameters(); pp->setMacroExpander(bc->macroExpander()); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 4e835d5bc7d..1e7e66655ad 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -34,6 +34,8 @@ #include "cmakeprojectmanager.h" #include "cmakeprojectnodes.h" +#include + #include #include @@ -91,6 +93,37 @@ void CMakeBuildConfiguration::initialize(const BuildInfo &info) BuildStepList *buildSteps = stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); buildSteps->appendStep(new CMakeBuildStep(buildSteps)); + if (DeviceTypeKitAspect::deviceTypeId(target()->kit()) + == Android::Constants::ANDROID_DEVICE_TYPE) { + buildSteps->appendStep(Android::Constants::ANDROID_BUILD_APK_ID); + const auto &bs = buildSteps->steps().constLast(); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NATIVE_API_LEVEL", + CMakeProjectManager::CMakeConfigItem::Type::STRING, + "Android native API level", + bs->data(Android::Constants::AndroidNdkPlatform).toString().toUtf8()}); + auto ndkLocation = bs->data(Android::Constants::NdkLocation).value(); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NDK", + CMakeProjectManager::CMakeConfigItem::Type::PATH, + "Android NDK PATH", + ndkLocation.toUserOutput().toUtf8()}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_TOOLCHAIN_FILE", + CMakeProjectManager::CMakeConfigItem::Type::PATH, + "Android CMake toolchain file", + ndkLocation.appendPath("build/cmake/android.toolchain.cmake").toUserOutput().toUtf8()}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_ABI", + CMakeProjectManager::CMakeConfigItem::Type::STRING, + "Android ABI", + bs->data(Android::Constants::AndroidABI).toString().toUtf8()}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_STL", + CMakeProjectManager::CMakeConfigItem::Type::STRING, + "Android STL", + "c++_shared"}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "BOTH"}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "BOTH"}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH_MODE_INCLUDE", "BOTH"}); + m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH_MODE_PACKAGE", "BOTH"}); + } + BuildStepList *cleanSteps = stepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN); cleanSteps->appendStep(new CMakeBuildStep(cleanSteps)); @@ -162,19 +195,19 @@ bool CMakeBuildConfiguration::isParsing() const BuildTargetInfoList CMakeBuildConfiguration::appTargets() const { BuildTargetInfoList appTargetList; - + bool forAndroid = DeviceTypeKitAspect::deviceTypeId(target()->kit()) == Android::Constants::ANDROID_DEVICE_TYPE; for (const CMakeBuildTarget &ct : m_buildTargets) { if (ct.targetType == UtilityType) continue; - if (ct.targetType == ExecutableType) { + if (ct.targetType == ExecutableType || (forAndroid && ct.targetType == DynamicLibraryType)) { BuildTargetInfo bti; bti.displayName = ct.title; bti.targetFilePath = ct.executable; bti.projectFilePath = ct.sourceDirectory; bti.projectFilePath.appendString('/'); bti.workingDirectory = ct.workingDirectory; - bti.buildKey = ct.title + QChar('\n') + bti.projectFilePath.toString(); + bti.buildKey = CMakeTargetNode::generateId(ct.sourceDirectory, ct.title); appTargetList.list.append(bti); } } @@ -220,6 +253,11 @@ QStringList CMakeBuildConfiguration::buildTargetTitles() const return transform(m_buildTargets, &CMakeBuildTarget::title); } +const QList &CMakeBuildConfiguration::buildTargets() const +{ + return m_buildTargets; +} + FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath, const Kit *k, const QString &bcName, @@ -341,7 +379,11 @@ static CMakeConfig removeDuplicates(const CMakeConfig &config) void CMakeBuildConfiguration::setConfigurationForCMake(const CMakeConfig &config) { - m_configurationForCMake = removeDuplicates(config); + auto configs = removeDuplicates(config); + if (m_configurationForCMake.isEmpty()) + m_configurationForCMake = removeDuplicates(configs + m_initialConfiguration); + else + m_configurationForCMake = configs; const Kit *k = target()->kit(); CMakeConfig kitConfig = CMakeConfigurationKitAspect::configuration(k); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 587666b0bf7..9dfbc470f69 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -63,6 +63,7 @@ public: QString warning() const; QStringList buildTargetTitles() const; + const QList &buildTargets() const; ProjectExplorer::BuildTargetInfoList appTargets() const; ProjectExplorer::DeploymentData deploymentData() const; @@ -72,7 +73,6 @@ public: // Context menu action: void buildTarget(const QString &buildTarget); - signals: void errorOccured(const QString &message); void warningOccured(const QString &message); @@ -104,6 +104,7 @@ private: void setWarning(const QString &message); CMakeConfig m_configurationForCMake; + CMakeConfig m_initialConfiguration; QString m_error; QString m_warning; diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 989cc7b4465..5005caa29e8 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -265,13 +265,56 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) QTC_ASSERT(bc == aBc, return); QTC_ASSERT(m_treeScanner.isFinished() && !m_buildDirManager.isParsing(), return); - bc->setBuildTargets(m_buildDirManager.takeBuildTargets()); - bc->setConfigurationFromCMake(m_buildDirManager.takeCMakeConfiguration()); + const QList buildTargets = m_buildDirManager.takeBuildTargets(); + bc->setBuildTargets(buildTargets); + const CMakeConfig cmakeConfig = m_buildDirManager.takeCMakeConfiguration(); + bc->setConfigurationFromCMake(cmakeConfig); + + CMakeConfig patchedConfig = cmakeConfig; + { + CMakeConfigItem settingFileItem; + settingFileItem.key = "ANDROID_DEPLOYMENT_SETTINGS_FILE"; + settingFileItem.value = bc->buildDirectory().appendPath("android_deployment_settings.json").toString().toUtf8(); + patchedConfig.append(settingFileItem); + } + + QSet res; + QStringList apps; + for (const auto &target : bc->buildTargets()) { + if (target.targetType == CMakeProjectManager::DynamicLibraryType) { + res.insert(target.executable.parentDir().toString()); + apps.push_back(target.executable.toUserOutput()); + } + // ### shall we add also the ExecutableType ? + } + { + CMakeConfigItem paths; + paths.key = "ANDROID_SO_LIBS_PATHS"; + paths.values = res.toList(); + patchedConfig.append(paths); + } + + apps.sort(); + { + CMakeConfigItem appsPaths; + appsPaths.key = "TARGETS_BUILD_PATH"; + appsPaths.values = apps; + patchedConfig.append(appsPaths); + } + auto newRoot = generateProjectTree(m_allFiles); if (newRoot) { setDisplayName(newRoot->displayName()); setRootProjectNode(std::move(newRoot)); + + for (const CMakeBuildTarget &bt : buildTargets) { + const QString buildKey = CMakeTargetNode::generateId(bt.sourceDirectory, bt.title); + if (ProjectNode *node = findNodeForBuildKey(buildKey)) { + if (auto targetNode = dynamic_cast(node)) + targetNode->setConfig(patchedConfig); + } + } } Target *t = bc->target(); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp index feffd990fe9..178acc62c04 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -25,22 +25,31 @@ #include "cmakeprojectnodes.h" +#include "cmakeconfigitem.h" #include "cmakeprojectconstants.h" #include "cmakeprojectplugin.h" + +#include + #include #include #include + +#include + #include #include #include #include - +#include #include #include #include #include +using namespace ProjectExplorer; + using namespace CMakeProjectManager; using namespace CMakeProjectManager::Internal; @@ -159,8 +168,9 @@ bool CMakeProjectNode::addFiles(const QStringList &filePaths, QStringList *) } CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory, const QString &target) : - ProjectExplorer::ProjectNode(directory), m_target(target) + ProjectExplorer::ProjectNode(directory) { + m_target = target; setPriority(Node::DefaultProjectPriority + 900); setIcon(QIcon(":/projectexplorer/images/build.png")); // TODO: Use proper icon! setListInProject(false); @@ -181,6 +191,52 @@ QString CMakeTargetNode::buildKey() const return generateId(filePath(), m_target); } +QVariant CMakeTargetNode::data(Core::Id role) const +{ + auto value = [this](const QByteArray &key) -> QVariant { + for (const CMakeConfigItem &configItem : m_config) { + if (configItem.key == key) + return configItem.value; + } + return {}; + }; + + auto values = [this](const QByteArray &key) -> QVariant { + for (const CMakeConfigItem &configItem : m_config) { + if (configItem.key == key) + return configItem.values; + } + return {}; + }; + + if (role == Android::Constants::AndroidPackageSourceDir) + return value("ANDROID_PACKAGE_SOURCE_DIR"); + + if (role == Android::Constants::AndroidDeploySettingsFile) + return value("ANDROID_DEPLOYMENT_SETTINGS_FILE"); + + if (role == Android::Constants::AndroidExtraLibs) + return value("ANDROID_EXTRA_LIBS"); + + if (role == Android::Constants::AndroidArch) + return value("ANDROID_ABI"); + + if (role == Android::Constants::AndroidSoLibPath) + return values("ANDROID_SO_LIBS_PATHS"); + + if (role == Android::Constants::AndroidTargets) + return values("TARGETS_BUILD_PATH"); + + QTC_CHECK(false); + // Better guess than "not present". + return value(role.toString().toUtf8()); +} + +void CMakeTargetNode::setConfig(const CMakeConfig &config) +{ + m_config = config; +} + bool CMakeTargetNode::supportsAction(ProjectExplorer::ProjectAction action, const ProjectExplorer::Node *) const { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h index 60bae806af6..82cbb73dbbd 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h @@ -25,6 +25,8 @@ #pragma once +#include "cmakeconfigitem.h" + #include namespace CMakeProjectManager { @@ -72,9 +74,12 @@ public: bool addFiles(const QStringList &filePaths, QStringList *notAdded) override; Utils::optional visibleAfterAddFileAction() const override; + QVariant data(Core::Id role) const override; + void setConfig(const CMakeConfig &config); + private: QString m_tooltip; - QString m_target; + CMakeConfig m_config; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index e5bf324dcc5..35e02b8972f 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -225,6 +225,12 @@ void BuildStep::setWidgetExpandedByDefault(bool widgetExpandedByDefault) m_widgetExpandedByDefault = widgetExpandedByDefault; } +QVariant BuildStep::data(Core::Id id) const +{ + Q_UNUSED(id); + return {}; +} + /*! \fn BuildStep::isImmutable() diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index 0796b87582a..4d2cf7bea21 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -91,6 +91,7 @@ public: bool isImmutable() const { return m_immutable; } void setImmutable(bool immutable) { m_immutable = immutable; } + virtual QVariant data(Core::Id id) const; signals: /// Adds a \p task to the Issues pane. /// Do note that for linking compile output with tasks, you should first emit the task diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index a3c06295aea..cc10b3d5e08 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -288,6 +288,10 @@ QVariant QmakeProFileNode::data(Core::Id role) const res.removeDuplicates(); return res; } + + if (role == Android::Constants::AndroidTargets) + return {}; + QTC_CHECK(false); return {}; }