diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ add_subdirectory(utils) add_subdirectory(file_templates) add_subdirectory(providers) +add_subdirectory(runtimes) add_subdirectory(shortcuts) add_subdirectory(doc) diff --git a/app/kdevelopui.rc b/app/kdevelopui.rc --- a/app/kdevelopui.rc +++ b/app/kdevelopui.rc @@ -34,6 +34,7 @@ + Examine Core File @@ -54,6 +55,9 @@ + + + @@ -182,6 +186,7 @@ + @@ -192,6 +197,7 @@ + diff --git a/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.h b/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.h --- a/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.h +++ b/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.h @@ -26,16 +26,19 @@ #include "icompiler.h" -class GccLikeCompiler : public ICompiler +class GccLikeCompiler : public QObject, public ICompiler { + Q_OBJECT public: GccLikeCompiler( const QString& name, const QString& path, bool editable, const QString& factoryName ); KDevelop::Defines defines(const QString& arguments) const override; KDevelop::Path::List includes(const QString& arguments) const override; private: + void invalidateCache(); + struct DefinesIncludes { KDevelop::Defines definedMacros; KDevelop::Path::List includePaths; diff --git a/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp b/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp --- a/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp +++ b/languages/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include @@ -85,6 +87,7 @@ // #define a QRegExp defineExpression( "#define\\s+(\\S+)(?:\\s+(.*)\\s*)?"); + const auto rt = ICore::self()->runtimeController()->currentRuntime(); QProcess proc; proc.setProcessChannelMode( QProcess::MergedChannels ); @@ -94,7 +97,9 @@ compilerArguments.append(QStringLiteral("-E")); compilerArguments.append(QProcess::nullDevice()); - proc.start(path(), compilerArguments); + proc.setProgram(path()); + proc.setArguments(compilerArguments); + rt->startProcess(&proc); if ( !proc.waitForStarted( 1000 ) || !proc.waitForFinished( 1000 ) ) { qCDebug(DEFINESANDINCLUDES) << "Unable to read standard macro definitions from "<< path(); @@ -118,6 +123,7 @@ return m_definesIncludes.value(arguments).includePaths; } + const auto rt = ICore::self()->runtimeController()->currentRuntime(); QProcess proc; proc.setProcessChannelMode( QProcess::MergedChannels ); @@ -139,7 +145,9 @@ compilerArguments.append(QStringLiteral("-v")); compilerArguments.append(QProcess::nullDevice()); - proc.start(path(), compilerArguments); + proc.setProgram(path()); + proc.setArguments(compilerArguments); + rt->startProcess(&proc); if ( !proc.waitForStarted( 1000 ) || !proc.waitForFinished( 1000 ) ) { qCDebug(DEFINESANDINCLUDES) << "Unable to read standard include paths from " << path(); @@ -155,6 +163,7 @@ }; Status mode = Initial; + Path::List ret; foreach( const QString &line, QString::fromLocal8Bit( proc.readAllStandardOutput() ).split( '\n' ) ) { switch ( mode ) { case Initial: @@ -174,7 +183,8 @@ mode = Finished; } else { // This is an include path, add it to the list. - m_definesIncludes[arguments].includePaths << Path(QFileInfo(line.trimmed()).canonicalFilePath()); + auto hostPath = rt->pathInHost(Path(line.trimmed())); + ret << Path(QFileInfo(hostPath.toLocalFile()).canonicalFilePath()); } break; default: @@ -185,9 +195,17 @@ } } - return m_definesIncludes[arguments].includePaths; + m_definesIncludes[arguments].includePaths = ret; + return ret; +} + +void GccLikeCompiler::invalidateCache() +{ + m_definesIncludes.clear(); } GccLikeCompiler::GccLikeCompiler(const QString& name, const QString& path, bool editable, const QString& factoryName): ICompiler(name, path, factoryName, editable) -{} +{ + connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &GccLikeCompiler::invalidateCache); +} diff --git a/projectbuilders/cmakebuilder/cmakejob.cpp b/projectbuilders/cmakebuilder/cmakejob.cpp --- a/projectbuilders/cmakebuilder/cmakejob.cpp +++ b/projectbuilders/cmakebuilder/cmakejob.cpp @@ -30,6 +30,9 @@ #include #include +#include +#include +#include #include #include @@ -98,7 +101,7 @@ } //if we are creating a new build directory, we'll want to specify the generator - QDir builddir(CMake::currentBuildDir( m_project ).toLocalFile()); + QDir builddir(ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(CMake::currentBuildDir( m_project )).toLocalFile()); if(!builddir.exists() || !builddir.exists(QStringLiteral("CMakeCache.txt"))) { CMakeBuilderSettings::self()->load(); args << QStringLiteral("-G") << CMake::defaultGenerator(); @@ -118,7 +121,7 @@ } } } - args << CMake::projectRoot( m_project ).toLocalFile(); + args << ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(CMake::projectRoot( m_project )).toLocalFile(); return args; } diff --git a/projectmanagers/cmake/cmakeimportjsonjob.cpp b/projectmanagers/cmake/cmakeimportjsonjob.cpp --- a/projectmanagers/cmake/cmakeimportjsonjob.cpp +++ b/projectmanagers/cmake/cmakeimportjsonjob.cpp @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #include @@ -42,6 +45,16 @@ namespace { +template +static T kTransform(const Q& list, W func) +{ + T ret; + ret.reserve(list.size()); + for (auto it = list.constBegin(), itEnd = list.constEnd(); it!=itEnd; ++it) + ret += func(*it); + return ret; +} + CMakeFilesCompilationData importCommands(const Path& commandsFile) { // NOTE: to get compile_commands.json, you need -DCMAKE_EXPORT_COMPILE_COMMANDS=ON @@ -71,6 +84,7 @@ static const QString KEY_COMMAND = QStringLiteral("command"); static const QString KEY_DIRECTORY = QStringLiteral("directory"); static const QString KEY_FILE = QStringLiteral("file"); + auto rt = ICore::self()->runtimeController()->currentRuntime(); foreach(const QJsonValue& value, document.array()) { if (!value.isObject()) { qCWarning(CMAKE) << "JSON command file entry is not an object:" << value; @@ -84,13 +98,14 @@ PathResolutionResult result = resolver.processOutput(entry[KEY_COMMAND].toString(), entry[KEY_DIRECTORY].toString()); + auto convert = [rt](const Path &path) { return rt->pathInHost(path); }; + CMakeFile ret; - ret.includes = result.paths; - ret.frameworkDirectories = result.frameworkDirectories; + ret.includes = kTransform(result.paths, convert); + ret.frameworkDirectories = kTransform(result.frameworkDirectories, convert); ret.defines = result.defines; - // NOTE: we use the canonical file path to prevent issues with symlinks in the path - // leading to lookup failures - const auto path = Path(QFileInfo(entry[KEY_FILE].toString()).canonicalFilePath()); + const Path path(rt->pathInHost(Path(entry[KEY_FILE].toString()))); + qDebug() << "entering..." << path << entry[KEY_FILE]; data.files[path] = ret; } diff --git a/projectmanagers/cmake/cmakemanager.h b/projectmanagers/cmake/cmakemanager.h --- a/projectmanagers/cmake/cmakemanager.h +++ b/projectmanagers/cmake/cmakemanager.h @@ -56,6 +56,7 @@ class ParseJob; class ContextMenuExtension; class Context; + class IRuntime; } class CMakeFolderItem; @@ -150,6 +151,7 @@ // void filesystemBuffererTimeout(); private: + void reloadProjects(); CMakeFile fileInformation(KDevelop::ProjectBaseItem* item) const; void folderAdded(KDevelop::ProjectFolderItem* folder); diff --git a/projectmanagers/cmake/cmakemanager.cpp b/projectmanagers/cmake/cmakemanager.cpp --- a/projectmanagers/cmake/cmakemanager.cpp +++ b/projectmanagers/cmake/cmakemanager.cpp @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include #include @@ -91,6 +93,7 @@ new CodeCompletion(this, new CMakeCodeCompletionModel(this), name()); connect(ICore::self()->projectController(), &IProjectController::projectClosing, this, &CMakeManager::projectClosing); + connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &CMakeManager::reloadProjects); connect(this, &KDevelop::AbstractFileManagerPlugin::folderAdded, this, &CMakeManager::folderAdded); // m_fileSystemChangeTimer = new QTimer(this); @@ -180,7 +183,7 @@ addSubjob(manager->builder()->configure(project)); } - connect(job, &CMakeImportJsonJob::result, this, [this, job](){ + connect(job, &CMakeImportJsonJob::result, this, [this, job]() { if (job->error() == 0) { manager->integrateData(job->projectData(), job->project()); } @@ -302,6 +305,14 @@ KJob *job = createImportJob(folder); project->setReloadJob(job); ICore::self()->runController()->registerJob( job ); + if (folder == project->projectItem()) { + connect(job, &KJob::finished, this, [project](KJob* job) { + if (job->error()) + return; + KDevelop::ICore::self()->projectController()->reparseProject(project, true); + }); + } + return true; } @@ -945,4 +956,12 @@ return nullptr; } +void CMakeManager::reloadProjects() +{ + for(IProject* project: m_projects.keys()) { + CMake::checkForNeedingConfigure(project); + reload(project->projectItem()); + } +} + #include "cmakemanager.moc" diff --git a/projectmanagers/cmake/cmakeserver.cpp b/projectmanagers/cmake/cmakeserver.cpp --- a/projectmanagers/cmake/cmakeserver.cpp +++ b/projectmanagers/cmake/cmakeserver.cpp @@ -21,6 +21,11 @@ #include "cmakeserver.h" #include "cmakeprojectdata.h" #include "cmakeutils.h" + +#include +#include +#include + #include #include #include @@ -35,7 +40,7 @@ { QString path; { - QTemporaryFile file(QDir::tempPath() + "/kdevelopcmake-"); + QTemporaryFile file(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/kdevelopcmake-"); file.open(); file.close(); path = file.fileName(); @@ -65,11 +70,13 @@ connect(&m_process, &QProcess::started, this, [this, path](){ //Once the process has started, wait for the file to be created, then connect to it - QTimer::singleShot(100, this, [this, path]() { + QTimer::singleShot(1000, this, [this, path]() { m_localSocket->connectToServer(path, QIODevice::ReadWrite); }); }); - m_process.start(CMake::findExecutable(), {"-E", "server", "--experimental", "--pipe=" + path}); + m_process.setProgram(CMake::findExecutable()); + m_process.setArguments({"-E", "server", "--experimental", "--pipe=" + path}); + KDevelop::ICore::self()->runtimeController()->currentRuntime()->startProcess(&m_process); } CMakeServer::~CMakeServer() diff --git a/projectmanagers/cmake/cmakeserverimportjob.cpp b/projectmanagers/cmake/cmakeserverimportjob.cpp --- a/projectmanagers/cmake/cmakeserverimportjob.cpp +++ b/projectmanagers/cmake/cmakeserverimportjob.cpp @@ -23,6 +23,9 @@ #include "cmakeserver.h" #include +#include +#include +#include #include #include @@ -90,13 +93,15 @@ { const auto configs = response.value(QStringLiteral("configurations")).toArray(); qCDebug(CMAKE) << "process response" << response; + + const auto rt = KDevelop::ICore::self()->runtimeController()->currentRuntime(); for (const auto &config: configs) { const auto projects = config.toObject().value(QStringLiteral("projects")).toArray(); for (const auto &project: projects) { const auto targets = project.toObject().value(QStringLiteral("targets")).toArray(); for (const auto &targetObject: targets) { const auto target = targetObject.toObject(); - const KDevelop::Path targetDir(target.value(QStringLiteral("sourceDirectory")).toString()); + const KDevelop::Path targetDir = rt->pathInHost(KDevelop::Path(target.value(QStringLiteral("sourceDirectory")).toString())); data.targets[targetDir] += target.value(QStringLiteral("name")).toString(); @@ -112,9 +117,9 @@ for (const auto& source: sources) { // NOTE: we use the canonical file path to prevent issues with symlinks in the path // leading to lookup failures - const auto localFile = source.toLocalFile(); + const auto localFile = rt->pathInHost(source); const auto canonicalFile = QFileInfo(source.toLocalFile()).canonicalFilePath(); - const auto sourcePath = localFile == canonicalFile ? source : KDevelop::Path(canonicalFile); + const auto sourcePath = localFile.toLocalFile() == canonicalFile ? localFile : KDevelop::Path(canonicalFile); data.compilationData.files[sourcePath] = file; } qCDebug(CMAKE) << "registering..." << sources << file; diff --git a/projectmanagers/cmake/cmakeutils.cpp b/projectmanagers/cmake/cmakeutils.cpp --- a/projectmanagers/cmake/cmakeutils.cpp +++ b/projectmanagers/cmake/cmakeutils.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -58,10 +60,16 @@ static const QString projectBuildDirs = QStringLiteral("BuildDirs"); } -static const QString buildDirIndexKey = QStringLiteral("Current Build Directory Index"); +static const QString buildDirIndexKey_ = QStringLiteral("Current Build Directory Index"); static const QString buildDirOverrideIndexKey = QStringLiteral("Temporary Build Directory Index"); static const QString buildDirCountKey = QStringLiteral("Build Directory Count"); +//the used builddir will change for every runtime +static QString buildDirIndexKey() { + const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); + return buildDirIndexKey_ + '-' + currentRuntime; +} + namespace Specific { static const QString buildDirPathKey = QStringLiteral("Build Directory Path"); @@ -182,8 +190,9 @@ ///NOTE: when you change this, update @c defaultConfigure in cmakemanagertest.cpp bool checkForNeedingConfigure( KDevelop::IProject* project ) { + const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); const KDevelop::Path builddir = currentBuildDir(project); - const bool isValid = builddir.isValid(); + const bool isValid = (buildDirRuntime(project, -1) == currentRuntime || buildDirRuntime(project, -1).isEmpty()) && builddir.isValid(); if( !isValid ) { @@ -228,16 +237,19 @@ CMake::setCurrentCMakeExecutable(project, bd.cmakeExecutable()); CMake::setCurrentEnvironment( project, QString() ); } + setBuildDirRuntime( project, currentRuntime ); return true; } else if( !QFile::exists( KDevelop::Path(builddir, QStringLiteral("CMakeCache.txt")).toLocalFile() ) || //TODO: maybe we could use the builder for that? !(QFile::exists( KDevelop::Path(builddir, QStringLiteral("Makefile")).toLocalFile() ) || QFile::exists( KDevelop::Path(builddir, QStringLiteral("build.ninja")).toLocalFile() ) ) ) { // User entered information already, but cmake hasn't actually been run yet. + setBuildDirRuntime( project, currentRuntime ); return true; } + setBuildDirRuntime( project, currentRuntime ); return false; } @@ -391,13 +403,15 @@ if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) ) return baseGrp.readEntry( Config::buildDirOverrideIndexKey, 0 ); + else if (baseGrp.hasKey(Config::buildDirIndexKey())) + return baseGrp.readEntry( Config::buildDirIndexKey(), 0 ); else - return baseGrp.readEntry( Config::buildDirIndexKey, 0 ); // default is 0 because QString::number(0) apparently returns an empty string + return baseGrp.readEntry( Config::buildDirIndexKey_, 0 ); // backwards compatibility } void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex ) { - writeProjectBaseParameter( project, Config::buildDirIndexKey, QString::number (buildDirIndex) ); + writeProjectBaseParameter( project, Config::buildDirIndexKey(), QString::number (buildDirIndex) ); } void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment ) @@ -542,7 +556,7 @@ qCDebug(CMAKE) << "CMake settings migration: current build directory" << buildDir << "(index" << buildDirIndex << ")"; baseGrp.writeEntry( Config::buildDirCountKey, buildDirsCount ); - baseGrp.writeEntry( Config::buildDirIndexKey, buildDirIndex ); + baseGrp.writeEntry( Config::buildDirIndexKey(), buildDirIndex ); for (int i = 0; i < buildDirsCount; ++i) { @@ -573,7 +587,7 @@ if( !baseGrp.hasKey(Config::buildDirOverrideIndexKey) ) return; if( writeToMainIndex ) - baseGrp.writeEntry( Config::buildDirIndexKey, baseGrp.readEntry(Config::buildDirOverrideIndexKey) ); + baseGrp.writeEntry( Config::buildDirIndexKey(), baseGrp.readEntry(Config::buildDirOverrideIndexKey) ); baseGrp.deleteEntry(Config::buildDirOverrideIndexKey); } diff --git a/projectmanagers/cmake/tests/test_cmakemanager.cpp b/projectmanagers/cmake/tests/test_cmakemanager.cpp --- a/projectmanagers/cmake/tests/test_cmakemanager.cpp +++ b/projectmanagers/cmake/tests/test_cmakemanager.cpp @@ -39,7 +39,7 @@ void TestCMakeManager::initTestCase() { - QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.projectmanagers.cmake.debug=true\n")); + QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\n")); AutoTestShell::init(); TestCore::initialize(); @@ -80,10 +80,10 @@ Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem); QVERIFY(includeDirs.size() >= 3); - Path buildDir(sourceDir, QStringLiteral("build/")); + Path buildDir(project->buildSystemManager()->buildDirectory(fooCppItem)); QVERIFY(includeDirs.contains(buildDir)); - Path subBuildDir(sourceDir, QStringLiteral("build/subdir/")); + Path subBuildDir(buildDir, QStringLiteral("subdir/")); QVERIFY(includeDirs.contains(subBuildDir)); Path subDir(sourceDir, QStringLiteral("subdir/")); @@ -339,10 +339,6 @@ { IProject* project = loadProject(QStringLiteral("parentheses_in_test_arguments")); - Path sourceDir = project->path(); - Path buildDir(sourceDir, QStringLiteral("build/")); - auto job = new CMakeImportJsonJob(project, this); - job->start(); - + QVERIFY(job->exec()); } diff --git a/projectmanagers/cmake/tests/test_cmakeserver.cpp b/projectmanagers/cmake/tests/test_cmakeserver.cpp --- a/projectmanagers/cmake/tests/test_cmakeserver.cpp +++ b/projectmanagers/cmake/tests/test_cmakeserver.cpp @@ -19,16 +19,29 @@ */ #include +#include +#include +#include #include "testhelpers.h" #include #include +#include using namespace KDevelop; class CMakeServerTest : public QObject { Q_OBJECT +public: + CMakeServerTest() + { + QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.projectmanagers.cmake.debug=true\n")); + + AutoTestShell::init(); + TestCore::initialize(); + } + private slots: void testRun() { diff --git a/projectmanagers/cmake/tests/testhelpers.h b/projectmanagers/cmake/tests/testhelpers.h --- a/projectmanagers/cmake/tests/testhelpers.h +++ b/projectmanagers/cmake/tests/testhelpers.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include static QString currentBuildDirKey = QStringLiteral("Build Directory Path"); @@ -41,6 +43,7 @@ static QString projectBuildDirectoryCount = QStringLiteral("Build Directory Count"); static QString projectRootRelativeKey = QStringLiteral("ProjectRootRelative"); static QString projectBuildDirs = QStringLiteral("BuildDirs"); +static const QString buildDirRuntime = QStringLiteral("Runtime"); struct TestProjectPaths { // foo/ @@ -93,6 +96,7 @@ // apply default configuration CMakeBuildDirChooser bd; bd.setSourceFolder(paths.sourceDir); + bd.setBuildFolder(KDevelop::Path(CMAKE_TESTS_BINARY_DIR + QStringLiteral("/build-") + paths.sourceDir.lastPathSegment())); // we don't want to execute, just pick the defaults from the dialog KConfigGroup cmakeGrp = config.group("CMake"); @@ -115,6 +119,7 @@ buildDirGrp.writeEntry( currentExtraArgumentsKey, bd.extraArguments() ); buildDirGrp.writeEntry( currentBuildTypeKey, bd.buildType() ); buildDirGrp.writeEntry( projectBuildDirs, QStringList() << bd.buildFolder().toLocalFile()); + buildDirGrp.writeEntry( buildDirRuntime, KDevelop::ICore::self()->runtimeController()->currentRuntime()->name()); config.sync(); } diff --git a/runtimes/CMakeLists.txt b/runtimes/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/runtimes/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(android) diff --git a/runtimes/android/CMakeLists.txt b/runtimes/android/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/runtimes/android/CMakeLists.txt @@ -0,0 +1,19 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevandroid\") + +ecm_qt_declare_logging_category(androidplugin_SRCS + HEADER debug_android.h + IDENTIFIER ANDROID + CATEGORY_NAME kdevplatform.plugins.android +) +qt5_add_resources(androidplugin_SRCS kdevandroidplugin.qrc) +ki18n_wrap_ui(androidplugin_SRCS androidpreferences.ui) +kconfig_add_kcfg_files(androidplugin_SRCS androidpreferencessettings.kcfgc) + +kdevplatform_add_plugin(kdevandroid JSON kdevandroid.json SOURCES androidplugin.cpp androidruntime.cpp androidpreferences.cpp ${androidplugin_SRCS}) +target_link_libraries(kdevandroid + KF5::CoreAddons + KDev::Interfaces + KDev::Util + KDev::OutputView + KDev::Project +) diff --git a/runtimes/android/Messages.sh b/runtimes/android/Messages.sh new file mode 100644 --- /dev/null +++ b/runtimes/android/Messages.sh @@ -0,0 +1,4 @@ +#!/bin/sh +$EXTRACTRC `find . -name \*.rc` `find . -name \*.ui` >> rc.cpp +$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/kdevandroid.pot +rm -f rc.cpp diff --git a/runtimes/android/androidplugin.h b/runtimes/android/androidplugin.h new file mode 100644 --- /dev/null +++ b/runtimes/android/androidplugin.h @@ -0,0 +1,43 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ANDROIDPLUGIN_H +#define ANDROIDPLUGIN_H + +#include +#include +#include +#include + +class AndroidPreferencesSettings; + +class AndroidPlugin : public KDevelop::IPlugin +{ +Q_OBJECT +public: + AndroidPlugin(QObject *parent, const QVariantList & args); + ~AndroidPlugin() override; + + int configPages() const override; + KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; + +private: + QScopedPointer m_settings; +}; + +#endif diff --git a/runtimes/android/androidplugin.cpp b/runtimes/android/androidplugin.cpp new file mode 100644 --- /dev/null +++ b/runtimes/android/androidplugin.cpp @@ -0,0 +1,78 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "androidplugin.h" +#include "androidruntime.h" +#include "androidpreferences.h" +#include "androidpreferencessettings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KDevAndroidFactory, "kdevandroid.json", registerPlugin();) + +using namespace KDevelop; + +AndroidPlugin::AndroidPlugin(QObject *parent, const QVariantList & /*args*/) + : KDevelop::IPlugin( QStringLiteral("kdevandroid"), parent ) + , m_settings(new AndroidPreferencesSettings) +{ + setXMLFile( QStringLiteral("kdevandroidplugin.rc") ); + + AndroidRuntime::s_settings = m_settings.data(); + + ICore::self()->runtimeController()->addRuntimes({ new AndroidRuntime }); +} + +AndroidPlugin::~AndroidPlugin() +{ + AndroidRuntime::s_settings = nullptr; +} + +int AndroidPlugin::configPages() const +{ + return 1; +} + +KDevelop::ConfigPage* AndroidPlugin::configPage(int number, QWidget* parent) +{ + if (number == 0) { + return new AndroidPreferences(this, m_settings.data(), parent); + } + return nullptr; +} + +#include "androidplugin.moc" diff --git a/runtimes/android/androidpreferences.h b/runtimes/android/androidpreferences.h new file mode 100644 --- /dev/null +++ b/runtimes/android/androidpreferences.h @@ -0,0 +1,39 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ANDROIDPREFERENCES_H +#define ANDROIDPREFERENCES_H + +#include +#include + +namespace Ui { class AndroidPreferences; } + +class AndroidPreferences : public KDevelop::ConfigPage +{ + Q_OBJECT + public: + explicit AndroidPreferences(KDevelop::IPlugin* plugin, KCoreConfigSkeleton* config, QWidget* parent = nullptr); + ~AndroidPreferences() override; + + QString name() const override; + private: + QScopedPointer m_prefsUi; +}; + +#endif diff --git a/runtimes/android/androidpreferences.cpp b/runtimes/android/androidpreferences.cpp new file mode 100644 --- /dev/null +++ b/runtimes/android/androidpreferences.cpp @@ -0,0 +1,34 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "androidpreferences.h" +#include "ui_androidpreferences.h" + +AndroidPreferences::AndroidPreferences(KDevelop::IPlugin* plugin, KCoreConfigSkeleton* config, QWidget* parent) + : KDevelop::ConfigPage(plugin, config, parent) +{ + auto m_prefsUi = new Ui::AndroidPreferences; + m_prefsUi->setupUi(this); +} + +AndroidPreferences::~AndroidPreferences() = default; + +QString AndroidPreferences::name() const +{ + return QStringLiteral("Android"); +} diff --git a/runtimes/android/androidpreferences.ui b/runtimes/android/androidpreferences.ui new file mode 100644 --- /dev/null +++ b/runtimes/android/androidpreferences.ui @@ -0,0 +1,91 @@ + + + AndroidPreferences + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Android KDE extra-cmake-modules toolchain file + + + Android Toolchain + + + + + + + + + + Android NDK + + + + + + + + + + Android SDK + + + + + + + + + + Architecture + + + + + + + + + + Toolchain + + + + + + + + + + ABI + + + + + + + + + + + KUrlRequester + QWidget +
kurlrequester.h
+
+
+ + +
diff --git a/runtimes/android/androidpreferencessettings.kcfg b/runtimes/android/androidpreferencessettings.kcfg new file mode 100644 --- /dev/null +++ b/runtimes/android/androidpreferencessettings.kcfg @@ -0,0 +1,18 @@ + + + + + QStandardPaths::locate(QStandardPaths::GenericDataLocation, "ECM/toolchain/Android.cmake") + qgetenv("ANDROID_NDK") + qgetenv("ANDROID_HOME") + arm + arm-linux-androideabi + armeabi-v7a + 4.9 + 14 + 21.1.1 + + diff --git a/runtimes/android/androidpreferencessettings.kcfgc b/runtimes/android/androidpreferencessettings.kcfgc new file mode 100644 --- /dev/null +++ b/runtimes/android/androidpreferencessettings.kcfgc @@ -0,0 +1,3 @@ +ClassName=AndroidPreferencesSettings +File=androidpreferencessettings.kcfg + diff --git a/runtimes/android/androidruntime.h b/runtimes/android/androidruntime.h new file mode 100644 --- /dev/null +++ b/runtimes/android/androidruntime.h @@ -0,0 +1,49 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ANDROIDRUNTIME_H +#define ANDROIDRUNTIME_H + +#include +#include +#include + +class KJob; +class AndroidPreferencesSettings; + +class AndroidRuntime : public KDevelop::IRuntime +{ + Q_OBJECT +public: + AndroidRuntime(); + ~AndroidRuntime() override; + + QString name() const override { return QStringLiteral("Android"); } + + void setEnabled(bool enabled) override; + + void startProcess(KProcess *process) const override; + void startProcess(QProcess *process) const override; + KDevelop::Path pathInHost(const KDevelop::Path & runtimePath) const override { return runtimePath; } + KDevelop::Path pathInRuntime(const KDevelop::Path & localPath) const override { return localPath; } + QByteArray getenv(const QByteArray &varname) const override; + + static AndroidPreferencesSettings* s_settings; +}; + +#endif diff --git a/runtimes/android/androidruntime.cpp b/runtimes/android/androidruntime.cpp new file mode 100644 --- /dev/null +++ b/runtimes/android/androidruntime.cpp @@ -0,0 +1,91 @@ +/* + Copyright 2017 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "androidruntime.h" +#include "androidpreferencessettings.h" +#include "debug_android.h" + +#include +#include + +using namespace KDevelop; + +AndroidPreferencesSettings* AndroidRuntime::s_settings = nullptr; + +AndroidRuntime::AndroidRuntime() + : KDevelop::IRuntime() +{ +} + +AndroidRuntime::~AndroidRuntime() +{ +} + +void AndroidRuntime::setEnabled(bool /*enable*/) +{ +} + +static void setEnvironmentVariables(QProcess* process) +{ + auto env = process->processEnvironment(); + env.insert("ANDROID_NDK", AndroidRuntime::s_settings->ndk()); + env.insert("ANDROID_SDK_ROOT", AndroidRuntime::s_settings->sdk()); + process->setProcessEnvironment(env); +} + +//integrates with ECM +static QStringList args() +{ + return { + "-DCMAKE_TOOLCHAIN_FILE=" + AndroidRuntime::s_settings->cmakeToolchain(), + + "-DANDROID_ABI=" + AndroidRuntime::s_settings->abi(), + "-DANDROID_NDK=" + AndroidRuntime::s_settings->ndk(), + "-DANDROID_TOOLCHAIN=" + AndroidRuntime::s_settings->toolchain(), + "-DANDROID_API_LEVEL=" + AndroidRuntime::s_settings->api(), + "-DANDROID_ARCHITECTURE=" + AndroidRuntime::s_settings->arch(), + "-DANDROID_SDK_BUILD_TOOLS_REVISION=" + AndroidRuntime::s_settings->buildtools() + }; +} + +void AndroidRuntime::startProcess(QProcess* process) const +{ + if (process->program().endsWith(QLatin1String("cmake"))) { + process->setArguments(process->arguments() << args()); + setEnvironmentVariables(process); + } + + qCDebug(ANDROID) << "starting qprocess" << process->program() << process->arguments(); + process->start(); +} + +void AndroidRuntime::startProcess(KProcess* process) const +{ + if (process->program().constFirst().endsWith(QLatin1String("cmake"))) { + process->setProgram(process->program() << args()); + setEnvironmentVariables(process); + } + + qCDebug(ANDROID) << "starting kprocess" << process->program().join(' '); + process->start(); +} + +QByteArray AndroidRuntime::getenv(const QByteArray &varname) const +{ + return qgetenv(varname); +} diff --git a/runtimes/android/kdevandroid.json b/runtimes/android/kdevandroid.json new file mode 100644 --- /dev/null +++ b/runtimes/android/kdevandroid.json @@ -0,0 +1,17 @@ +{ + "KPlugin": { + "Authors": [ + { "Name": "Aleix Pol", "Email": "aleixpol@kde.org" } + ], + "Category": "Global", + "Description": "Exposes Android runtimes", + "Icon": "android", + "Id": "kdevandroid", + "License": "GPL", + "Name": "Android Support", + "ServiceTypes": [ "KDevelop/Plugin" ], + "Version": "0.1" + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/runtimes/android/kdevandroidplugin.qrc b/runtimes/android/kdevandroidplugin.qrc new file mode 100644 --- /dev/null +++ b/runtimes/android/kdevandroidplugin.qrc @@ -0,0 +1,6 @@ + + + + kdevandroidplugin.rc + + diff --git a/runtimes/android/kdevandroidplugin.rc b/runtimes/android/kdevandroidplugin.rc new file mode 100644 --- /dev/null +++ b/runtimes/android/kdevandroidplugin.rc @@ -0,0 +1,7 @@ + + + + + + +