diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 535c739674..3ea4b29bb6 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,159 +1,160 @@ # BEGIN: Analyzers add_subdirectory(clangtidy) add_subdirectory(clazy) add_subdirectory(cppcheck) if(UNIX AND NOT (APPLE OR CYGWIN)) add_subdirectory(heaptrack) endif() # END: Analyzers # BEGIN: Debuggers add_subdirectory(debuggercommon) add_subdirectory(lldb) add_subdirectory(gdb) # END: Debuggers # BEGIN: Documentation find_package(Qt5Help CONFIG) set_package_properties(Qt5Help PROPERTIES PURPOSE "The Help module for the Qt toolkit, needed for the qthelp plugin" URL "https://www.qt.io/" TYPE OPTIONAL) if(Qt5Help_FOUND) ecm_optional_add_subdirectory(qthelp) endif() ecm_optional_add_subdirectory(manpage) # END: Documentation # BEGIN: Formatters add_subdirectory(astyle) add_subdirectory(customscript) # END: Formatters # BEGIN: Languages ecm_optional_add_subdirectory(custom-definesandincludes) # 3rdparty/qtcreator-libs uses QT_CONFIG which was added for 5.8 if(NOT Qt5_VERSION VERSION_LESS "5.8.0") ecm_optional_add_subdirectory(qmljs) else() message(WARNING "Will not build QML/JS plugin, needs Qt >= 5.8.0") endif() find_package(Clang 3.8) set(clangSearchHint "") if (NOT CLANG_FOUND) set(clangSearchHint "Please install a package providing libclang. Either pass -DLLVM_ROOT=/path/to/llvm-prefix or install the 'llvm-config' command-line utility for auto-detection.") endif() set_package_properties(Clang PROPERTIES DESCRIPTION "Clang libraries from the LLVM project. ${clangSearchHint}" PURPOSE "Used for KDevelop's C++/C support plugin." TYPE REQUIRED ) if (CLANG_FOUND) if (NOT CLANG_CLANG_LIB) message(FATAL_ERROR "Could not find the Clang C library: libclang") endif() add_library(Clang::clang UNKNOWN IMPORTED) set_property(TARGET Clang::clang PROPERTY IMPORTED_LOCATION ${CLANG_CLANG_LIB}) ecm_optional_add_subdirectory(clang) endif() # END: Languages # BEGIN: Project builders add_subdirectory(makebuilder) add_subdirectory(ninjabuilder) ecm_optional_add_subdirectory(cmakebuilder) if (KDevelop-PG-Qt_FOUND) ecm_optional_add_subdirectory(qmakebuilder) endif() # END: Project builders # BEGIN: Project managers ecm_optional_add_subdirectory(cmake) ecm_optional_add_subdirectory(custommake) ecm_optional_add_subdirectory(custom-buildsystem) if (KDevelop-PG-Qt_FOUND) ecm_optional_add_subdirectory(qmakemanager) endif() ecm_optional_add_subdirectory(genericprojectmanager) # END: Project managers # BEGIN: Runtimes add_subdirectory(android) if (UNIX) add_subdirectory(docker) add_subdirectory(flatpak) endif() # END: Runtimes # BEGIN: VCS ecm_optional_add_subdirectory(bazaar) ecm_optional_add_subdirectory(git) ecm_optional_add_subdirectory(perforce) find_package(SubversionLibrary) set_package_properties(SubversionLibrary PROPERTIES PURPOSE "Support for Subversion integration" URL "http://subversion.tigris.org" TYPE OPTIONAL) if(SubversionLibrary_FOUND) ecm_optional_add_subdirectory(subversion) endif() add_subdirectory(vcschangesview) # END: VCS # BEGIN: Others add_subdirectory(appwizard) add_subdirectory(codeutils) add_subdirectory(contextbrowser) add_subdirectory(documentswitcher) add_subdirectory(documentview) add_subdirectory(execute) add_subdirectory(executescript) add_subdirectory(externalscript) add_subdirectory(filemanager) add_subdirectory(filetemplates) add_subdirectory(grepview) add_subdirectory(openwith) add_subdirectory(outlineview) add_subdirectory(patchreview) add_subdirectory(problemreporter) add_subdirectory(projectfilter) add_subdirectory(projectmanagerview) add_subdirectory(quickopen) add_subdirectory(sourceformatter) add_subdirectory(standardoutputview) add_subdirectory(switchtobuddy) add_subdirectory(testview) add_subdirectory(scratchpad) +add_subdirectory(meson) ecm_optional_add_subdirectory(classbrowser) ecm_optional_add_subdirectory(executeplasmoid) ecm_optional_add_subdirectory(ghprovider) ecm_optional_add_subdirectory(kdeprovider) ecm_optional_add_subdirectory(konsole) if (Qt5QuickWidgets_FOUND) add_subdirectory(welcomepage) endif() find_package(OktetaKastenControllers 0.3.1 CONFIG) set_package_properties(OktetaKastenControllers PROPERTIES PURPOSE "Required for building Okteta KDevelop plugin." URL "http://kde.org/" TYPE OPTIONAL) if (OktetaKastenControllers_FOUND) find_package(KastenControllers CONFIG) set_package_properties(KastenControllers PROPERTIES PURPOSE "Required for building Okteta KDevelop plugin." URL "http://kde.org/" TYPE OPTIONAL) endif() if (OktetaKastenControllers_FOUND AND KastenControllers_FOUND) add_subdirectory(okteta) endif() # END: Others diff --git a/plugins/meson/CMakeLists.txt b/plugins/meson/CMakeLists.txt new file mode 100644 index 0000000000..3e1a0497cd --- /dev/null +++ b/plugins/meson/CMakeLists.txt @@ -0,0 +1,37 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevmesonmanager\") + +set(mesonbuilder_SRCS + mesonbuilder.cpp + mesonconfig.cpp + mesonimportjob.cpp + mesonjob.cpp + mesonjobprune.cpp + mesonmanager.cpp + + settings/mesonadvancedsettings.cpp + settings/mesonconfigpage.cpp + settings/mesonnewbuilddir.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +ki18n_wrap_ui(mesonbuilder_SRCS settings/mesonadvancedsettings.ui settings/mesonconfigpage.ui settings/mesonnewbuilddir.ui) +ecm_qt_declare_logging_category(mesonbuilder_SRCS + HEADER debug.h + IDENTIFIER KDEV_Meson + CATEGORY_NAME "kdevelop.plugins.meson" +) + +kdevplatform_add_plugin(kdevmesonmanager + JSON kdevmesonmanager.json + SOURCES ${mesonbuilder_SRCS}) + +add_subdirectory(icons) + +target_link_libraries(kdevmesonmanager + Qt5::Concurrent + KDev::Interfaces + KDev::Project + KDev::Util + KDev::OutputView +) diff --git a/plugins/meson/Messages.sh b/plugins/meson/Messages.sh new file mode 100644 index 0000000000..0a9213a91a --- /dev/null +++ b/plugins/meson/Messages.sh @@ -0,0 +1,4 @@ +#!/bin/sh +$EXTRACTRC `find . -name \*.rc -o -name \*.ui | grep -v '/tests/'` >>rc.cpp +$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h | grep -v '/tests/'` -o $podir/kdevmesonmanager.pot +rm -f rc.cpp diff --git a/plugins/meson/icons/128-apps-meson.png b/plugins/meson/icons/128-apps-meson.png new file mode 100644 index 0000000000..f09f8fd3c4 Binary files /dev/null and b/plugins/meson/icons/128-apps-meson.png differ diff --git a/plugins/meson/icons/16-apps-meson.png b/plugins/meson/icons/16-apps-meson.png new file mode 100644 index 0000000000..00f73111ce Binary files /dev/null and b/plugins/meson/icons/16-apps-meson.png differ diff --git a/plugins/meson/icons/256-apps-meson.png b/plugins/meson/icons/256-apps-meson.png new file mode 100644 index 0000000000..03868d680e Binary files /dev/null and b/plugins/meson/icons/256-apps-meson.png differ diff --git a/plugins/meson/icons/32-apps-meson.png b/plugins/meson/icons/32-apps-meson.png new file mode 100644 index 0000000000..9e188071c9 Binary files /dev/null and b/plugins/meson/icons/32-apps-meson.png differ diff --git a/plugins/meson/icons/64-apps-meson.png b/plugins/meson/icons/64-apps-meson.png new file mode 100644 index 0000000000..a80d901deb Binary files /dev/null and b/plugins/meson/icons/64-apps-meson.png differ diff --git a/plugins/meson/icons/CMakeLists.txt b/plugins/meson/icons/CMakeLists.txt new file mode 100644 index 0000000000..4dd530f40a --- /dev/null +++ b/plugins/meson/icons/CMakeLists.txt @@ -0,0 +1,8 @@ +ecm_install_icons(ICONS + 256-apps-meson.png + 128-apps-meson.png + 64-apps-meson.png + 32-apps-meson.png + 16-apps-meson.png + DESTINATION ${KDE_INSTALL_ICONDIR}) + diff --git a/plugins/meson/icons/meson_logo.svg b/plugins/meson/icons/meson_logo.svg new file mode 100644 index 0000000000..b8dd59371b --- /dev/null +++ b/plugins/meson/icons/meson_logo.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/plugins/meson/kdevmesonmanager.json b/plugins/meson/kdevmesonmanager.json new file mode 100644 index 0000000000..06c3aba6d0 --- /dev/null +++ b/plugins/meson/kdevmesonmanager.json @@ -0,0 +1,26 @@ +{ + "KPlugin": { + "Category": "Project Management", + "Description": "Imports and edits Meson projects", + "Icon": "meson", + "Id": "KDevMesonManager", + "Name": "Meson Project Manager", + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Project", + "X-KDevelop-FileManager": "Meson", + "X-KDevelop-IRequired": [ + "org.kdevelop.IProjectBuilder" + ], + "X-KDevelop-Interfaces": [ + "org.kdevelop.IBuildSystemManager", + "org.kdevelop.IProjectFileManager" + ], + "X-KDevelop-Mode": "NoGUI", + "X-KDevelop-ProjectFilesFilter": [ + "meson.build" + ], + "X-KDevelop-ProjectFilesFilterDescription": "Meson Project Files" +} diff --git a/plugins/meson/mesonbuilder.cpp b/plugins/meson/mesonbuilder.cpp new file mode 100644 index 0000000000..bb3e3ebfe7 --- /dev/null +++ b/plugins/meson/mesonbuilder.cpp @@ -0,0 +1,265 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonbuilder.h" +#include "mesonconfig.h" +#include "mesonjob.h" +#include "mesonjobprune.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; + +class ErrorJob : public OutputJob +{ + Q_OBJECT +public: + ErrorJob(QObject* parent, const QString& error) + : OutputJob(parent) + , m_error(error) + { + setStandardToolView(IOutputView::BuildView); + } + + void start() override + { + auto* output = new OutputModel(this); + setModel(output); + startOutput(); + + output->appendLine(i18n(" *** MESON ERROR ***\n")); + QStringList lines = m_error.split(QChar::fromLatin1('\n')); + output->appendLines(lines); + + setError(!m_error.isEmpty()); + setErrorText(m_error); + emitResult(); + } + +private: + QString m_error; +}; + +MesonBuilder::MesonBuilder(QObject* parent) + : QObject(parent) +{ + auto p = KDevelop::ICore::self()->pluginController()->pluginForExtension( + QStringLiteral("org.kdevelop.IProjectBuilder"), QStringLiteral("KDevNinjaBuilder")); + if (p) { + m_ninjaBuilder = p->extension(); + if (m_ninjaBuilder) { + connect(p, SIGNAL(built(KDevelop::ProjectBaseItem*)), this, SIGNAL(built(KDevelop::ProjectBaseItem*))); + connect(p, SIGNAL(installed(KDevelop::ProjectBaseItem*)), this, + SIGNAL(installed(KDevelop::ProjectBaseItem*))); + connect(p, SIGNAL(cleaned(KDevelop::ProjectBaseItem*)), this, SIGNAL(cleaned(KDevelop::ProjectBaseItem*))); + connect(p, SIGNAL(failed(KDevelop::ProjectBaseItem*)), this, SIGNAL(failed(KDevelop::ProjectBaseItem*))); + } else { + m_errorString = i18n("Failed to set the internally used Ninja builder"); + } + } else { + m_errorString = i18n("Failed to acquire the Ninja builder plugin"); + } +} + +MesonBuilder::DirectoryStatus MesonBuilder::evaluateBuildDirectory(const Path& path, QString const& backend) +{ + QString pathSTR = path.toLocalFile(); + if (pathSTR.isEmpty()) { + return EMPTY_STRING; + } + + QFileInfo info(pathSTR); + if (!info.exists()) { + return DOES_NOT_EXIST; + } + + if (!info.isDir() || !info.isReadable() || !info.isWritable()) { + return INVALID_BUILD_DIR; + } + + QDir dir(path.toLocalFile()); + if (dir.isEmpty(QDir::NoDotAndDotDot | QDir::Hidden | QDir::AllEntries)) { + return CLEAN; + } + + // Check if the directory is a meson directory + const static QStringList mesonPaths = { QStringLiteral("meson-logs"), QStringLiteral("meson-private") }; + for (auto const& i : mesonPaths) { + Path curr = path; + curr.addPath(i); + QFileInfo currFI(curr.toLocalFile()); + if (!currFI.exists()) { + return DIR_NOT_EMPTY; + } + } + + // Also check if the meson configuration succeeded. This should be the case if the backend file exists. + // Meson actually checks for meson-private/coredata.dat, this might change in the future. + // see: https://github.com/mesonbuild/meson/blob/master/mesonbuild/msetup.py#L117 + QStringList configured = {}; + if (backend == QStringLiteral("ninja")) { + configured << QStringLiteral("build.ninja"); + } + + // Check if this is a CONFIGURED meson directory + for (auto const& i : configured) { + Path curr = path; + curr.addPath(i); + QFileInfo currFI(curr.toLocalFile()); + if (!currFI.exists()) { + return MESON_FAILED_CONFIGURATION; + } + } + + return MESON_CONFIGURED; +} + +KJob* MesonBuilder::configure(IProject* project, const Meson::BuildDir& buildDir, DirectoryStatus status) +{ + Q_ASSERT(project); + + if (!buildDir.isValid()) { + return new ErrorJob(this, i18n("The current build directory for %1 is invalid", project->name())); + } + + if (status == ___UNDEFINED___) { + status = evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend); + } + + KJob* job = nullptr; + + switch (status) { + case DOES_NOT_EXIST: + case CLEAN: + case MESON_FAILED_CONFIGURATION: + job = new MesonJob(buildDir, project, MesonJob::CONFIGURE, {}, this); + connect(job, &KJob::result, this, [this, project]() { emit configured(project); }); + return job; + case MESON_CONFIGURED: + job = new MesonJob(buildDir, project, MesonJob::RE_CONFIGURE, {}, this); + connect(job, &KJob::result, this, [this, project]() { emit configured(project); }); + return job; + case DIR_NOT_EMPTY: + return new ErrorJob( + this, + i18n("The directory '%1' is not empty and does not seem to be an already configured build directory", + buildDir.buildDir.toLocalFile())); + case INVALID_BUILD_DIR: + return new ErrorJob( + this, + i18n("The directory '%1' cannot be used as a meson build directory", buildDir.buildDir.toLocalFile())); + case EMPTY_STRING: + return new ErrorJob( + this, i18n("The current build configuration is broken, because the build directory is not specified")); + default: + // This code should NEVER be reached + return new ErrorJob(this, + i18n("Congratulations: You have reached unreachable code!\n" + "Please report a bug at https://bugs.kde.org/\n" + "FILE: %1:%2", + QStringLiteral(__FILE__), __LINE__)); + } +} + +KJob* MesonBuilder::configure(KDevelop::IProject* project) +{ + Q_ASSERT(project); + auto buildDir = Meson::currentBuildDir(project); + return configure(project, buildDir); +} + +KJob* MesonBuilder::configureIfRequired(KDevelop::IProject* project, KJob* realJob) +{ + Q_ASSERT(project); + Meson::BuildDir buildDir = Meson::currentBuildDir(project); + DirectoryStatus status = evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend); + + if (status == MESON_CONFIGURED) { + return realJob; + } + + QList jobs = { + configure(project, buildDir, status), // First configure the build directory + realJob // If this succeeds execute the real job + }; + + return new ExecuteCompositeJob(this, jobs); +} + +KJob* MesonBuilder::build(KDevelop::ProjectBaseItem* item) +{ + Q_ASSERT(item); + Q_ASSERT(m_ninjaBuilder); + return configureIfRequired(item->project(), m_ninjaBuilder->build(item)); +} + +KJob* MesonBuilder::clean(KDevelop::ProjectBaseItem* item) +{ + Q_ASSERT(item); + Q_ASSERT(m_ninjaBuilder); + return configureIfRequired(item->project(), m_ninjaBuilder->clean(item)); +} + +KJob* MesonBuilder::install(KDevelop::ProjectBaseItem* item, const QUrl& installPath) +{ + Q_ASSERT(item); + Q_ASSERT(m_ninjaBuilder); + return configureIfRequired(item->project(), m_ninjaBuilder->install(item, installPath)); +} + +KJob* MesonBuilder::prune(KDevelop::IProject* project) +{ + Q_ASSERT(project); + Meson::BuildDir buildDir = Meson::currentBuildDir(project); + if (!buildDir.isValid()) { + qCWarning(KDEV_Meson) << "The current build directory is invalid"; + return new ErrorJob(this, i18n("The current build directory for %1 is invalid", project->name())); + } + + KJob* job = new MesonJobPrune(buildDir, this); + connect(job, &KJob::result, this, [this, project]() { emit pruned(project); }); + return job; +} + +QList MesonBuilder::additionalBuilderPlugins(IProject*) const +{ + return { m_ninjaBuilder }; +} + +bool MesonBuilder::hasError() const +{ + return m_errorString.size() > 0; +} + +QString MesonBuilder::errorDescription() const +{ + return m_errorString; +} + +#include "mesonbuilder.moc" diff --git a/plugins/meson/mesonbuilder.h b/plugins/meson/mesonbuilder.h new file mode 100644 index 0000000000..ad94218ddc --- /dev/null +++ b/plugins/meson/mesonbuilder.h @@ -0,0 +1,79 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include +#include + +namespace Meson +{ +struct BuildDir; +} + +class MesonBuilder : public QObject, public KDevelop::IProjectBuilder +{ + Q_OBJECT + Q_INTERFACES(KDevelop::IProjectBuilder) +public: + enum DirectoryStatus { + DOES_NOT_EXIST = 0, + CLEAN, + MESON_CONFIGURED, + MESON_FAILED_CONFIGURATION, + INVALID_BUILD_DIR, + DIR_NOT_EMPTY, + EMPTY_STRING, + ___UNDEFINED___ + }; + + explicit MesonBuilder(QObject* parent); + + KJob* build(KDevelop::ProjectBaseItem* item) override; + KJob* clean(KDevelop::ProjectBaseItem* item) override; + KJob* install(KDevelop::ProjectBaseItem* dom, const QUrl& installPath) override; + KJob* prune(KDevelop::IProject* project) override; + + KJob* configure(KDevelop::IProject* project) override; + KJob* configure(KDevelop::IProject* project, Meson::BuildDir const& buildDir, + DirectoryStatus status = ___UNDEFINED___); + + /// Evaluate a directory for the use with meson + static DirectoryStatus evaluateBuildDirectory(KDevelop::Path const& path, QString const& backend); + + bool hasError() const; + QString errorDescription() const; + + QList additionalBuilderPlugins(KDevelop::IProject * project) const override; + +Q_SIGNALS: + void built(KDevelop::ProjectBaseItem*); + void installed(KDevelop::ProjectBaseItem*); + void cleaned(KDevelop::ProjectBaseItem*); + void failed(KDevelop::ProjectBaseItem*); + void configured(KDevelop::IProject*); + void pruned(KDevelop::IProject*); + +private: + KDevelop::IProjectBuilder* m_ninjaBuilder = nullptr; + QString m_errorString = QStringLiteral(""); + + KJob* configureIfRequired(KDevelop::IProject* project, KJob *realJob); +}; diff --git a/plugins/meson/mesonconfig.cpp b/plugins/meson/mesonconfig.cpp new file mode 100644 index 0000000000..4e6a21c447 --- /dev/null +++ b/plugins/meson/mesonconfig.cpp @@ -0,0 +1,195 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonconfig.h" +#include "mesonmanager.h" +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; +using namespace Meson; + +static const QString ROOT_CONFIG = QStringLiteral("MesonManager"); +static const QString NUM_BUILD_DIRS = QStringLiteral("Number of Build Directories"); +static const QString CURRENT_INDEX = QStringLiteral("Current Build Directory Index"); + +static const QString BUILD_DIR_SEC = QStringLiteral("BuildDir %1"); +static const QString BUILD_DIR_PATH = QStringLiteral("Build Directory Path"); +static const QString INSTALL_PREFIX = QStringLiteral("Installation prefix"); +static const QString MESON_EXE = QStringLiteral("Meson executable"); +static const QString EXTRA_ARGS = QStringLiteral("Additional meson arguments"); +static const QString BUILD_TYPE = QStringLiteral("Build type"); +static const QString BACKEND = QStringLiteral("Meson Generator Backend"); + +int MesonConfig::addBuildDir(BuildDir dir) +{ + int newIndex = buildDirs.size(); + dir.canonicalizePaths(); + qCDebug(KDEV_Meson) << "BuildDirectories::addBuildDir()=" << dir.buildDir; + buildDirs.push_back(dir); + + // Make sure m_currentIndex is valid + if (currentIndex < 0) { + currentIndex = newIndex; + } + + return newIndex; +} + +bool MesonConfig::removeBuildDir(int index) +{ + if (index > buildDirs.size() || index < 0) { + return false; + } + + buildDirs.removeAt(index); + + if (currentIndex >= buildDirs.size()) { + currentIndex = buildDirs.size() - 1; + } + + return true; +} + +KConfigGroup Meson::rootGroup(IProject* project) +{ + if (!project) { + qCWarning(KDEV_Meson) << "Meson::rootGroup: IProject pointer is nullptr"; + return KConfigGroup(); + } + + return project->projectConfiguration()->group(ROOT_CONFIG); +} + +MesonConfig Meson::getMesonConfig(IProject* project) +{ + KConfigGroup root = rootGroup(project); + MesonConfig result; + + int numDirs = root.readEntry(NUM_BUILD_DIRS, 0); + result.currentIndex = root.readEntry(CURRENT_INDEX, -1); + + for (int i = 0; i < numDirs; ++i) { + QString section = BUILD_DIR_SEC.arg(i); + if (!root.hasGroup(section)) { + continue; + } + + KConfigGroup current = root.group(section); + BuildDir currBD; + currBD.buildDir = Path(current.readEntry(BUILD_DIR_PATH, QString())); + currBD.installPrefix = Path(current.readEntry(INSTALL_PREFIX, QString())); + currBD.mesonExecutable = Path(current.readEntry(MESON_EXE, QString())); + currBD.buildType = current.readEntry(BUILD_TYPE, QStringLiteral("debug")); + currBD.mesonBackend = current.readEntry(BACKEND, QString()); + currBD.mesonArgs = current.readEntry(EXTRA_ARGS, QString()); + + currBD.canonicalizePaths(); + + // Try to find meson if the config is bad + if (currBD.mesonExecutable.isEmpty()) { + Q_ASSERT(project); + IBuildSystemManager* ibsm = project->buildSystemManager(); + MesonManager* bsm = dynamic_cast(ibsm); + if (bsm) { + currBD.mesonExecutable = bsm->findMeson(); + } + } + + result.buildDirs.push_back(currBD); + } + + if (result.buildDirs.isEmpty()) { + result.currentIndex = -1; + } else if (result.currentIndex < 0 || result.currentIndex >= result.buildDirs.size()) { + result.currentIndex = 0; + } + + return result; +} + +void Meson::writeMesonConfig(IProject* project, const MesonConfig& cfg) +{ + KConfigGroup root = rootGroup(project); + + // Make sure that the config we write is valid + int currentIndex = cfg.currentIndex; + if (cfg.buildDirs.isEmpty()) { + currentIndex = -1; + } else if (currentIndex < 0 || currentIndex >= cfg.buildDirs.size()) { + currentIndex = 0; + } + + root.writeEntry(NUM_BUILD_DIRS, cfg.buildDirs.size()); + root.writeEntry(CURRENT_INDEX, currentIndex); + + int counter = 0; + for (auto const& i : cfg.buildDirs) { + KConfigGroup current = root.group(BUILD_DIR_SEC.arg(counter++)); + + current.writeEntry(BUILD_DIR_PATH, i.buildDir.path()); + current.writeEntry(INSTALL_PREFIX, i.installPrefix.path()); + current.writeEntry(MESON_EXE, i.mesonExecutable.path()); + current.writeEntry(BUILD_TYPE, i.buildType); + current.writeEntry(BACKEND, i.mesonBackend); + current.writeEntry(EXTRA_ARGS, i.mesonArgs); + } +} + +BuildDir Meson::currentBuildDir(IProject* project) +{ + Q_ASSERT(project); + MesonConfig cfg = getMesonConfig(project); + if (cfg.currentIndex < 0 || cfg.currentIndex >= cfg.buildDirs.size()) { + cfg.currentIndex = 0; // Default to the first build dir + + // Request a new build directory if neccessary + if (cfg.buildDirs.isEmpty()) { + IBuildSystemManager* ibsm = project->buildSystemManager(); + MesonManager* bsm = dynamic_cast(ibsm); + if (!bsm) { + qCCritical(KDEV_Meson) << "Invalid build system manager for mesonconfig"; + qCCritical(KDEV_Meson) << "Project " << project->name() << "is probably broken"; + return BuildDir(); + } + + // newBuildDirectory() will add the build dir to the config and set cfg.currentIndex + return bsm->newBuildDirectory(project); + } + } + + return cfg.buildDirs[cfg.currentIndex]; +} + +bool Meson::BuildDir::isValid() const +{ + return !(buildDir.isEmpty() || mesonExecutable.isEmpty() || buildType.isEmpty()); +} + +void Meson::BuildDir::canonicalizePaths() +{ + for (auto* i : { &buildDir, &installPrefix, &mesonExecutable }) { + *i = Path(QFileInfo(i->toLocalFile()).canonicalFilePath()); + } +} diff --git a/plugins/meson/mesonconfig.h b/plugins/meson/mesonconfig.h new file mode 100644 index 0000000000..12217d37e0 --- /dev/null +++ b/plugins/meson/mesonconfig.h @@ -0,0 +1,60 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include +#include + +namespace KDevelop +{ +class IProject; +} + +namespace Meson +{ + +struct BuildDir { + KDevelop::Path buildDir; + KDevelop::Path installPrefix; + KDevelop::Path mesonExecutable; + QString buildType; + QString mesonBackend; + QString mesonArgs; + + bool isValid() const; + void canonicalizePaths(); +}; + +struct MesonConfig +{ + int currentIndex = -1; + QVector buildDirs; + + int addBuildDir(BuildDir dir); + bool removeBuildDir(int index); +}; + +KConfigGroup rootGroup(KDevelop::IProject* project); +BuildDir currentBuildDir(KDevelop::IProject* project); +MesonConfig getMesonConfig(KDevelop::IProject* project); +void writeMesonConfig(KDevelop::IProject* project, MesonConfig const& cfg); + +} diff --git a/plugins/meson/mesonimportjob.cpp b/plugins/meson/mesonimportjob.cpp new file mode 100644 index 0000000000..264d478778 --- /dev/null +++ b/plugins/meson/mesonimportjob.cpp @@ -0,0 +1,82 @@ +/* This file is part of KDevelop + 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 as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonimportjob.h" +#include "mesonconfig.h" +#include "mesonmanager.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; + +MesonImportJob::MesonImportJob(MesonManager* manager, KDevelop::IProject* project, QObject* parent) + : KJob(parent) + , m_project(project) + , m_manager(manager) +{ + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonImportJob::importFinished); +} + +// TODO: probably want to process the object in this function (e.g. see CMakeImportJsonJob) +QJsonObject import() +{ + qCDebug(KDEV_Meson) << "import(): This is a stub."; + return QJsonObject(); +} + +void MesonImportJob::start() +{ + Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); + if (!buildDir.isValid()) { + qCWarning(KDEV_Meson) << "The current build directory is invalid"; + setError(true); + setErrorText(i18n("The current build directory for %1 is invalid").arg(m_project->name())); + emitResult(); + return; + } + + auto future = QtConcurrent::run(import); + m_futureWatcher.setFuture(future); +} + +void MesonImportJob::importFinished() +{ + auto future = m_futureWatcher.future(); + auto data = future.result(); + m_manager->setProjectData(m_project, data); + emitResult(); +} + +bool MesonImportJob::doKill() +{ + if (m_futureWatcher.isRunning()) { + m_futureWatcher.cancel(); + } + return true; +} diff --git a/plugins/meson/mesonimportjob.h b/plugins/meson/mesonimportjob.h new file mode 100644 index 0000000000..7000681cc9 --- /dev/null +++ b/plugins/meson/mesonimportjob.h @@ -0,0 +1,48 @@ +/* This file is part of KDevelop + 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 as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 MESONIMPORTJOB_H +#define MESONIMPORTJOB_H + +#include +#include +#include +#include + +class MesonManager; + +class MesonImportJob : public KJob +{ + Q_OBJECT + +public: + MesonImportJob(MesonManager* manager, KDevelop::IProject* project, QObject* parent); + + void start() override; + bool doKill() override; + +private: + void importFinished(); + + KDevelop::IProject* m_project; + QFutureWatcher m_futureWatcher; + MesonManager* m_manager; +}; + +#endif // MESONIMPORTJOB_H diff --git a/plugins/meson/mesonjob.cpp b/plugins/meson/mesonjob.cpp new file mode 100644 index 0000000000..a2502c5a78 --- /dev/null +++ b/plugins/meson/mesonjob.cpp @@ -0,0 +1,77 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonjob.h" +#include "mesonconfig.h" +#include +#include +#include + +using namespace KDevelop; + +MesonJob::MesonJob(Meson::BuildDir const& buildDir, IProject* project, MesonJob::CommandType commandType, + const QStringList& arguments, QObject* parent) + : OutputExecuteJob(parent) + , m_project(project) + , m_commandType(commandType) + , m_arguments(arguments) +{ + Q_ASSERT(m_project); + + setToolTitle(i18n("Meson")); + setCapabilities(Killable); + setStandardToolView(KDevelop::IOutputView::BuildView); + setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll); + setProperties(NeedWorkingDirectory | PortableMessages | DisplayStdout | DisplayStderr); + + *this << buildDir.mesonExecutable.toLocalFile(); + + switch (m_commandType) { + case CONFIGURE: + if (!buildDir.installPrefix.isEmpty()) { + *this << QStringLiteral("--prefix") << buildDir.installPrefix.toLocalFile(); + } + + *this << QStringLiteral("--backend") << buildDir.mesonBackend; + *this << QStringLiteral("--buildtype") << buildDir.buildType; + + for (auto const& i : buildDir.mesonArgs) { + *this << i; + } + + break; + case RE_CONFIGURE: + *this << QStringLiteral("--reconfigure"); + break; + case SET_CONFIG: + *this << QStringLiteral("configure"); + if (!buildDir.installPrefix.isEmpty()) { + *this << QStringLiteral("-Dprefix=") + buildDir.installPrefix.toLocalFile(); + } + *this << QStringLiteral("-Dbuildtype=") + buildDir.buildType; + } + + *this << m_arguments; + *this << buildDir.buildDir.toLocalFile(); +} + +QUrl MesonJob::workingDirectory() const +{ + return m_project->path().toUrl(); +} diff --git a/plugins/meson/mesonjob.h b/plugins/meson/mesonjob.h new file mode 100644 index 0000000000..1d3fb3affa --- /dev/null +++ b/plugins/meson/mesonjob.h @@ -0,0 +1,50 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include + +namespace Meson +{ +struct BuildDir; +} + +namespace KDevelop +{ +class IProject; +} + +class MesonJob : public KDevelop::OutputExecuteJob +{ + Q_OBJECT +public: + enum CommandType { CONFIGURE, RE_CONFIGURE, SET_CONFIG }; + +public: + MesonJob(Meson::BuildDir const& buildDir, KDevelop::IProject* project, CommandType commandType, + QStringList const& arguments, QObject* parent); + + QUrl workingDirectory() const override; + +private: + KDevelop::IProject* m_project = nullptr; + CommandType m_commandType; + QStringList m_arguments; +}; diff --git a/plugins/meson/mesonjobprune.cpp b/plugins/meson/mesonjobprune.cpp new file mode 100644 index 0000000000..248cf98078 --- /dev/null +++ b/plugins/meson/mesonjobprune.cpp @@ -0,0 +1,97 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonjobprune.h" +#include "mesonbuilder.h" +#include "mesonconfig.h" +#include +#include +#include +#include + +using namespace KDevelop; + +MesonJobPrune::MesonJobPrune(Meson::BuildDir const& buildDir, QObject* parent) + : OutputJob(parent, Verbose) + , m_buildDir(buildDir.buildDir) + , m_backend(buildDir.mesonBackend) +{ + setCapabilities(Killable); + setToolTitle(i18n("Meson")); + setStandardToolView(KDevelop::IOutputView::BuildView); + setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll); +} + +void MesonJobPrune::start() +{ + auto* output = new OutputModel(this); + setModel(output); + startOutput(); + + auto status = MesonBuilder::evaluateBuildDirectory(m_buildDir, m_backend); + + switch (status) { + case MesonBuilder::DOES_NOT_EXIST: + case MesonBuilder::CLEAN: + output->appendLine(i18n("The directory '%1' is already pruned", m_buildDir.toLocalFile())); + emitResult(); + return; + case MesonBuilder::DIR_NOT_EMPTY: + case MesonBuilder::INVALID_BUILD_DIR: + output->appendLine( + i18n("The directory '%1' does not appear to be a meson build directory", m_buildDir.toLocalFile())); + output->appendLine(i18n("Aborting prune operation")); + emitResult(); + return; + case MesonBuilder::EMPTY_STRING: + output->appendLine( + i18n("The current build configuration is broken, because the build directory is not specified")); + emitResult(); + return; + default: + break; + } + + QDir d(m_buildDir.toLocalFile()); + QList urls; + const auto entries = d.entryList(QDir::NoDotAndDotDot | QDir::Hidden | QDir::AllEntries); + urls.reserve(entries.size()); + for (const auto& entry : entries) { + urls << Path(m_buildDir, entry).toUrl(); + } + + output->appendLine(i18n("Deleting contents of '%1'", m_buildDir.toLocalFile())); + m_job = KIO::del(urls); + m_job->start(); + + connect(m_job, &KJob::finished, this, [this, output](KJob* job) { + if (job->error() == 0) { + output->appendLine(i18n("** Prune successful **")); + } else { + output->appendLine(i18n("** Prune failed: %1 **", job->errorString())); + } + emitResult(); + m_job = nullptr; + }); +} + +bool MesonJobPrune::doKill() +{ + return m_job->kill(); +} diff --git a/plugins/meson/mesonjobprune.h b/plugins/meson/mesonjobprune.h new file mode 100644 index 0000000000..6552f57409 --- /dev/null +++ b/plugins/meson/mesonjobprune.h @@ -0,0 +1,43 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include +#include + +namespace Meson +{ +struct BuildDir; +} + +// "Inspired" by the CMake prune job +class MesonJobPrune : public KDevelop::OutputJob +{ + Q_OBJECT +public: + explicit MesonJobPrune(Meson::BuildDir const& buildDir, QObject* parent); + void start() override; + bool doKill() override; + +private: + KDevelop::Path m_buildDir; + QString m_backend; + KJob* m_job = nullptr; +}; diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp new file mode 100644 index 0000000000..41644dacd8 --- /dev/null +++ b/plugins/meson/mesonmanager.cpp @@ -0,0 +1,189 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonmanager.h" +#include "mesonbuilder.h" +#include "mesonconfig.h" +#include "mesonimportjob.h" +#include "settings/mesonconfigpage.h" +#include "settings/mesonnewbuilddir.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "debug.h" + +using namespace KDevelop; + +static const QString GENERATOR_NINJA = QStringLiteral("ninja"); + +K_PLUGIN_FACTORY_WITH_JSON(MesonSupportFactory, "kdevmesonmanager.json", registerPlugin();) + +MesonManager::MesonManager(QObject* parent, const QVariantList& args) + : AbstractFileManagerPlugin(QStringLiteral("KDevMesonManager"), parent, args) + , m_builder(new MesonBuilder(this)) +{ + if (m_builder->hasError()) { + setErrorDescription(i18n("Meson builder error: %s", m_builder->errorDescription())); + } +} + +MesonManager::~MesonManager() +{ + delete m_builder; +} + +// ********************************* +// * AbstractFileManagerPlugin API * +// ********************************* + +IProjectFileManager::Features MesonManager::features() const +{ + return IProjectFileManager::Files | IProjectFileManager::Folders | IProjectFileManager::Targets; +} + +ProjectFolderItem* MesonManager::createFolderItem(IProject* project, const Path& path, ProjectBaseItem* parent) +{ + // TODO: Maybe use meson targets instead + if (QFile::exists(path.toLocalFile() + QStringLiteral("/meson.build"))) + return new ProjectBuildFolderItem(project, path, parent); + else + return AbstractFileManagerPlugin::createFolderItem(project, path, parent); +} + +// *************************** +// * IBuildSystemManager API * +// *************************** + +KJob* MesonManager::createImportJob(ProjectFolderItem* item) +{ + auto project = item->project(); + auto job = new MesonImportJob(this, project, this); + + connect(job, &KJob::result, this, [this, job, project]() { + if (job->error() != 0) { + qCWarning(KDEV_Meson) << "couldn't load project successfully" << project->name(); + m_projects.remove(project); + } + }); + + const QList jobs = { + builder()->configure(project), // Make sure the project is configured + job, // Import the compile_commands.json file + AbstractFileManagerPlugin::createImportJob(item) // generate the file system listing + }; + + Q_ASSERT(!jobs.contains(nullptr)); + auto composite = new ExecuteCompositeJob(this, jobs); + composite->setAbortOnError(false); + return composite; +} + +Path MesonManager::buildDirectory(ProjectBaseItem* item) const +{ + Q_ASSERT(item); + Meson::BuildDir buildDir = Meson::currentBuildDir(item->project()); + return buildDir.buildDir; +} + +IProjectBuilder* MesonManager::builder() const +{ + return m_builder; +} + +Meson::BuildDir MesonManager::newBuildDirectory(IProject* project) +{ + Q_ASSERT(project); + MesonNewBuildDir newBD(project); + + if (!newBD.exec() || !newBD.isConfigValid()) { + qCWarning(KDEV_Meson) << "Failed to create new build directory for project " << project->name(); + return Meson::BuildDir(); + } + + Meson::BuildDir buildDir = newBD.currentConfig(); + Meson::MesonConfig mesonCfg = Meson::getMesonConfig(project); + buildDir.canonicalizePaths(); + mesonCfg.addBuildDir(buildDir); + Meson::writeMesonConfig(project, mesonCfg); + + return buildDir; +} + +QStringList MesonManager::supportedMesonBackends() const +{ + // Maybe add support for other generators + return { GENERATOR_NINJA }; +} + +QString MesonManager::defaultMesonBackend() const +{ + return GENERATOR_NINJA; +} + +Path MesonManager::findMeson() const +{ + QString mesonPath; + + const static QStringList mesonExecutables = { QStringLiteral("meson"), QStringLiteral("meson.py") }; + const static QStringList mesonPaths + = { QStringLiteral("%1/.local/bin").arg(QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0]) }; + + for (auto const& i : mesonExecutables) { + mesonPath = QStandardPaths::findExecutable(i); + if (!mesonPath.isEmpty()) { + break; + } + + mesonPath = QStandardPaths::findExecutable(i, mesonPaths); + if (!mesonPath.isEmpty()) { + break; + } + } + + return Path(mesonPath); +} + +void MesonManager::setProjectData(IProject* project, const QJsonObject& data) +{ + m_projects[project] = data; +} + +ConfigPage* MesonManager::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) +{ + if (number == 0) { + return new MesonConfigPage(this, options.project, parent); + } + return nullptr; +} + +int MesonManager::perProjectConfigPages() const +{ + return 1; +} + +#include "mesonmanager.moc" diff --git a/plugins/meson/mesonmanager.h b/plugins/meson/mesonmanager.h new file mode 100644 index 0000000000..ce0002e881 --- /dev/null +++ b/plugins/meson/mesonmanager.h @@ -0,0 +1,109 @@ +/* This file is part of KDevelop + Copyright 2017 Aleix Pol Gonzalez + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 KDEVPLATFORM_PLUGIN_MESONMANAGER_H +#define KDEVPLATFORM_PLUGIN_MESONMANAGER_H + +#include "mesonconfig.h" +#include +#include + +class MesonBuilder; + +class MesonManager : public KDevelop::AbstractFileManagerPlugin, public KDevelop::IBuildSystemManager +{ + Q_OBJECT + Q_INTERFACES(KDevelop::IBuildSystemManager) + +public: + explicit MesonManager(QObject* parent = nullptr, const QVariantList& args = QVariantList()); + ~MesonManager() override; + + // ******************** + // * Custom functions * + // ******************** + + /// Create a new build directory and write it into the config + Meson::BuildDir newBuildDirectory(KDevelop::IProject* project); + + /// Returns a list of all supported Meson backends (for now only ninja) + QStringList supportedMesonBackends() const; + QString defaultMesonBackend() const; + + KDevelop::Path findMeson() const; + + void setProjectData(KDevelop::IProject* project, const QJsonObject& data); + + // ********************************* + // * AbstractFileManagerPlugin API * + // ********************************* + + KDevelop::IProjectFileManager::Features features() const override; + KDevelop::ProjectFolderItem* createFolderItem(KDevelop::IProject* project, const KDevelop::Path& path, + KDevelop::ProjectBaseItem* parent = nullptr) override; + + // *********** + // * IPlugin * + // *********** + + KDevelop::ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, + QWidget* parent) override; + int perProjectConfigPages() const override; + + // *************************** + // * IBuildSystemManager API * + // *************************** + + KJob* createImportJob(KDevelop::ProjectFolderItem* item) override; + + KDevelop::IProjectBuilder* builder() const override; + + // FIXME now: should use compile_commands.json for these (i.e. m_projects) + KDevelop::Path::List includeDirectories(KDevelop::ProjectBaseItem*) const override { return {}; } + KDevelop::Path::List frameworkDirectories(KDevelop::ProjectBaseItem*) const override { return {}; } + QHash defines(KDevelop::ProjectBaseItem*) const override { return {}; } + QString extraArguments(KDevelop::ProjectBaseItem* /*item*/) const override { return {}; } + bool hasBuildInfo(KDevelop::ProjectBaseItem* /*item*/) const override { return false; } + + KDevelop::Path buildDirectory(KDevelop::ProjectBaseItem*) const override; + + // fill if&when we have targets + QList targets(KDevelop::ProjectFolderItem*) const override { return {}; } + + // you can ignore these for now I guess, but TODO + KDevelop::ProjectTargetItem* createTarget(const QString& /*target*/, + KDevelop::ProjectFolderItem* /*parent*/) override + { + return nullptr; + } + bool removeTarget(KDevelop::ProjectTargetItem* /*target*/) override { return false; } + bool addFilesToTarget(const QList& /*files*/, + KDevelop::ProjectTargetItem* /*target*/) override + { + return false; + } + bool removeFilesFromTargets(const QList& /*files*/) override { return false; } + +private: + MesonBuilder* m_builder; + QHash m_projects; +}; + +#endif diff --git a/plugins/meson/settings/mesonadvancedsettings.cpp b/plugins/meson/settings/mesonadvancedsettings.cpp new file mode 100644 index 0000000000..db1edc3f39 --- /dev/null +++ b/plugins/meson/settings/mesonadvancedsettings.cpp @@ -0,0 +1,66 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonadvancedsettings.h" +#include "ui_mesonadvancedsettings.h" + +using namespace KDevelop; + +MesonAdvancedSettings::MesonAdvancedSettings(QWidget* parent) + : QWidget(parent) +{ + m_ui = new Ui::MesonAdvancedSettings; + m_ui->setupUi(this); + m_ui->container->hide(); +} + +MesonAdvancedSettings::~MesonAdvancedSettings() +{ + delete m_ui; +} + +MesonAdvancedSettings::Data MesonAdvancedSettings::getConfig() const +{ + Data res; + + res.args = m_ui->i_mesonArgs->text(); + res.backend = m_ui->i_backend->currentText(); + res.meson = Path(m_ui->i_mesonExe->url()); + + return res; +} + +void MesonAdvancedSettings::setConfig(const MesonAdvancedSettings::Data& conf) +{ + m_ui->i_mesonArgs->setText(conf.args); + m_ui->i_mesonExe->setUrl(conf.meson.toUrl()); + m_ui->i_backend->setCurrentIndex(std::max(0, m_backendList.indexOf(conf.backend))); +} + +void MesonAdvancedSettings::setSupportedBackends(const QStringList& backends) +{ + m_backendList = backends; + m_ui->i_backend->clear(); + m_ui->i_backend->addItems(m_backendList); +} + +void MesonAdvancedSettings::updated() +{ + emit configChanged(); +} diff --git a/plugins/meson/settings/mesonadvancedsettings.h b/plugins/meson/settings/mesonadvancedsettings.h new file mode 100644 index 0000000000..d6c396a60f --- /dev/null +++ b/plugins/meson/settings/mesonadvancedsettings.h @@ -0,0 +1,58 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include "util/path.h" +#include + +namespace Ui +{ +class MesonAdvancedSettings; +} + +class MesonAdvancedSettings : public QWidget +{ + Q_OBJECT +public: + struct Data { + QString backend; + QString args; + KDevelop::Path meson; + }; + +public: + explicit MesonAdvancedSettings(QWidget* parent = nullptr); + ~MesonAdvancedSettings() override; + + Data getConfig() const; + void setConfig(Data const& conf); + + void setSupportedBackends(QStringList const& backends); + +Q_SIGNALS: + void configChanged(); + +public Q_SLOTS: + void updated(); + +private: + Ui::MesonAdvancedSettings* m_ui = nullptr; + QStringList m_backendList; +}; diff --git a/plugins/meson/settings/mesonadvancedsettings.ui b/plugins/meson/settings/mesonadvancedsettings.ui new file mode 100644 index 0000000000..f9598345b3 --- /dev/null +++ b/plugins/meson/settings/mesonadvancedsettings.ui @@ -0,0 +1,247 @@ + + + MesonAdvancedSettings + + + + 0 + 0 + 500 + 250 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Show advanced configuration + + + + + + + Advanced configuration + + + + + + + + Extra Meson arguments: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::LeftToRight + + + Meson backend: + + + false + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Meson executable: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Extra meson configuration arguments + + + true + + + + + + + + + + Path to the meson executable + + + + + + + + + Hide advanced configuration + + + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+ + KUrlRequester + QWidget +
kurlrequester.h
+
+
+ + + + b_showAdvanced + clicked() + container + show() + + + 249 + 16 + + + 249 + 144 + + + + + b_showAdvanced + clicked() + b_showAdvanced + hide() + + + 249 + 16 + + + 249 + 16 + + + + + b_hideAdvanced + clicked() + container + hide() + + + 249 + 224 + + + 249 + 144 + + + + + b_hideAdvanced + clicked() + b_showAdvanced + show() + + + 249 + 224 + + + 249 + 16 + + + + + i_mesonArgs + textChanged(QString) + MesonAdvancedSettings + updated() + + + 327 + 90 + + + 249 + 124 + + + + + i_backend + currentIndexChanged(int) + MesonAdvancedSettings + updated() + + + 327 + 128 + + + 249 + 124 + + + + + i_mesonExe + textChanged(QString) + MesonAdvancedSettings + updated() + + + 327 + 175 + + + 249 + 124 + + + + + + configChanged() + updated() + +
diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp new file mode 100644 index 0000000000..9b42ac4c97 --- /dev/null +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -0,0 +1,320 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonconfigpage.h" +#include "mesonbuilder.h" +#include "mesonjob.h" +#include "mesonmanager.h" +#include "mesonnewbuilddir.h" +#include "ui_mesonconfigpage.h" +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; + +MesonConfigPage::MesonConfigPage(IPlugin* plugin, IProject* project, QWidget* parent) + : ConfigPage(plugin, nullptr, parent) + , m_project(project) +{ + Q_ASSERT(project); // Catch errors early + MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); + Q_ASSERT(mgr); // This dialog only works with the MesonManager + + m_ui = new Ui::MesonConfigPage; + m_ui->setupUi(this); + m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends()); + + m_config = Meson::getMesonConfig(m_project); + if (m_config.buildDirs.isEmpty()) { + m_config.currentIndex = -1; + reset(); + return; + } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { + m_config.currentIndex = 0; + } + + QStringList buildPathList; + for (auto& i : m_config.buildDirs) { + buildPathList << i.buildDir.toLocalFile(); + } + + m_ui->i_buildDirs->blockSignals(true); + m_ui->i_buildDirs->clear(); + m_ui->i_buildDirs->addItems(buildPathList); + m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); + m_ui->i_buildDirs->blockSignals(false); + + reset(); +} + +void MesonConfigPage::writeConfig() +{ + qCDebug(KDEV_Meson) << "Writing config to file"; + if (m_config.currentIndex >= 0) { + m_config.buildDirs[m_config.currentIndex] = m_current; + } + + if (m_config.buildDirs.isEmpty()) { + m_config.currentIndex = -1; + } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { + m_config.currentIndex = 0; + } + Meson::writeMesonConfig(m_project, m_config); +} + +void MesonConfigPage::apply() +{ + qCDebug(KDEV_Meson) << "Applying meson config for build dir " << m_current.buildDir; + readUI(); + writeConfig(); + + if (m_config.currentIndex >= 0 && m_configChanged) { + QList joblist; + + // Check if a configuration is required + auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); + if (status != MesonBuilder::MESON_CONFIGURED) { + joblist << new MesonJob(m_current, m_project, MesonJob::CONFIGURE, {}, nullptr); + } + + joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, {}, nullptr); + joblist << new MesonJob(m_current, m_project, MesonJob::RE_CONFIGURE, {}, nullptr); + KJob* job = new ExecuteCompositeJob(nullptr, joblist); + connect(job, &KJob::result, this, [this]() { + setDisabled(false); + updateUI(); + }); + setDisabled(true); + m_configChanged = false; + job->start(); + } +} + +void MesonConfigPage::defaults() +{ + qCDebug(KDEV_Meson) << "Restoring build dir " << m_current.buildDir << " to it's default values"; + MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); + Q_ASSERT(mgr); + + m_current.mesonArgs.clear(); + m_current.mesonBackend = mgr->defaultMesonBackend(); + m_current.mesonExecutable = mgr->findMeson(); + + updateUI(); +} + +void MesonConfigPage::reset() +{ + if (m_config.buildDirs.isEmpty()) { + m_config.currentIndex = -1; + m_ui->i_buildDirs->clear(); + setWidgetsDisabled(true); + return; + } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { + m_config.currentIndex = 0; + m_ui->i_buildDirs->blockSignals(true); + m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); + m_ui->i_buildDirs->blockSignals(false); + } + + setWidgetsDisabled(false); + qCDebug(KDEV_Meson) << "Resetting changes for build dir " << m_current.buildDir; + + m_current = m_config.buildDirs[m_config.currentIndex]; + updateUI(); +} + +void MesonConfigPage::updateUI() +{ + m_ui->i_buildType->setCurrentIndex(1); + + QStringList buildTypes = { QStringLiteral("plain"), QStringLiteral("debug"), QStringLiteral("debugoptimized"), + QStringLiteral("release"), QStringLiteral("minsize"), QStringLiteral("custom") }; + + m_ui->i_buildType->clear(); + m_ui->i_buildType->addItems(buildTypes); + m_ui->i_buildType->setCurrentIndex(std::max(0, buildTypes.indexOf(m_current.buildType))); + + m_ui->i_installPrefix->setUrl(m_current.installPrefix.toUrl()); + + auto aConf = m_ui->advanced->getConfig(); + aConf.args = m_current.mesonArgs; + aConf.backend = m_current.mesonBackend; + aConf.meson = m_current.mesonExecutable; + m_ui->advanced->setConfig(aConf); + + // Get the config build dir status + auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend); + auto setStatus = [this](QString const& msg, int color) -> void { + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + switch (color) { + case 0: + role = KColorScheme::PositiveText; + break; + case 1: + role = KColorScheme::NeutralText; + break; + case 2: + default: + role = KColorScheme::NegativeText; + break; + } + + QPalette pal = m_ui->l_status->palette(); + pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + m_ui->l_status->setPalette(pal); + m_ui->l_status->setText(i18n("Status: %1", msg)); + }; + + switch (status) { + case MesonBuilder::DOES_NOT_EXIST: + setStatus(i18n("The current build direcory does not exist"), 1); + break; + case MesonBuilder::CLEAN: + setStatus(i18n("The current build direcory is empty"), 1); + break; + case MesonBuilder::MESON_CONFIGURED: + setStatus(i18n("Build direcory configured"), 0); + break; + case MesonBuilder::MESON_FAILED_CONFIGURATION: + setStatus(i18n("This meson build direcory is not fully configured"), 1); + break; + case MesonBuilder::INVALID_BUILD_DIR: + setStatus(i18n("The current build directory is invalid"), 2); + break; + case MesonBuilder::DIR_NOT_EMPTY: + setStatus(i18n("This directory does not seem to be a meson build direcory"), 2); + break; + case MesonBuilder::EMPTY_STRING: + setStatus(i18n("Invalid build directory configuration (empty build direcory string)"), 2); + break; + case MesonBuilder::___UNDEFINED___: + setStatus(i18n("Something went verry wrong. This is a bug"), 2); + break; + } +} + +void MesonConfigPage::readUI() +{ + qCDebug(KDEV_Meson) << "Reading current build configuration from the UI " << m_current.buildDir.toLocalFile(); + m_current.installPrefix = Path(m_ui->i_installPrefix->url()); + m_current.buildType = m_ui->i_buildType->currentText(); + + auto aConf = m_ui->advanced->getConfig(); + m_current.mesonArgs = aConf.args; + m_current.mesonBackend = aConf.backend; + m_current.mesonExecutable = aConf.meson; +} + +void MesonConfigPage::setWidgetsDisabled(bool disabled) +{ + m_ui->advanced->setDisabled(disabled); + m_ui->c_01_basic->setDisabled(disabled); + m_ui->c_02_buildConfig->setDisabled(disabled); + m_ui->b_rmDir->setDisabled(disabled); +} + +void MesonConfigPage::addBuildDir() +{ + qCDebug(KDEV_Meson) << "Adding build directory"; + MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); + MesonBuilder* bld = dynamic_cast(mgr->builder()); + Q_ASSERT(mgr); + Q_ASSERT(bld); + MesonNewBuildDir newBD(m_project); + + if (!newBD.exec() || !newBD.isConfigValid()) { + qCDebug(KDEV_Meson) << "Failed to create a new build directory"; + return; + } + + m_current = newBD.currentConfig(); + m_config.currentIndex = m_config.addBuildDir(m_current); + m_ui->i_buildDirs->blockSignals(true); + m_ui->i_buildDirs->addItem(m_current.buildDir.toLocalFile()); + m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); + m_ui->i_buildDirs->blockSignals(false); + + setWidgetsDisabled(true); + writeConfig(); + KJob* job = bld->configure(m_project); + connect(job, &KJob::result, this, [this]() { reset(); }); + job->start(); +} + +void MesonConfigPage::removeBuildDir() +{ + qCDebug(KDEV_Meson) << "Removing current build directory"; + m_ui->i_buildDirs->blockSignals(true); + m_ui->i_buildDirs->removeItem(m_config.currentIndex); + m_config.removeBuildDir(m_config.currentIndex); + if (m_config.buildDirs.isEmpty()) { + m_config.currentIndex = -1; + } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) { + m_config.currentIndex = 0; + } + m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex); + m_ui->i_buildDirs->blockSignals(false); + reset(); + writeConfig(); +} + +void MesonConfigPage::changeBuildDirIndex(int index) +{ + if (index == m_config.currentIndex || m_config.buildDirs.isEmpty()) { + return; + } + + if (index < 0 || index >= m_config.buildDirs.size()) { + qCWarning(KDEV_Meson) << "Invalid build dir index " << index; + return; + } + + qCDebug(KDEV_Meson) << "Changing build directory to index " << index; + + m_config.currentIndex = index; + reset(); + writeConfig(); +} + +void MesonConfigPage::emitChanged() +{ + m_configChanged = true; + emit changed(); +} + +QString MesonConfigPage::name() const +{ + return i18n("Meson"); +} + +QString MesonConfigPage::fullName() const +{ + return i18n("Meson project configuration"); +} + +QIcon MesonConfigPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("meson")); +} diff --git a/plugins/meson/settings/mesonconfigpage.h b/plugins/meson/settings/mesonconfigpage.h new file mode 100644 index 0000000000..9aa35d8fb2 --- /dev/null +++ b/plugins/meson/settings/mesonconfigpage.h @@ -0,0 +1,67 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include +#include "mesonconfig.h" + +namespace KDevelop +{ +class IPlugin; +class IProject; +} + +namespace Ui { + class MesonConfigPage; +} + +class MesonConfigPage : public KDevelop::ConfigPage +{ + Q_OBJECT +public: + explicit MesonConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject *project, QWidget* parent = nullptr); + + QString name() const override; + QString fullName() const override; + QIcon icon() const override; + +public Q_SLOTS: + void apply() override; + void defaults() override; + void reset() override; + + void addBuildDir(); + void removeBuildDir(); + void changeBuildDirIndex(int index); + void emitChanged(); + +private: + void updateUI(); + void readUI(); + void writeConfig(); + void setWidgetsDisabled(bool disabled); + +private: + KDevelop::IProject *m_project = nullptr; + Ui::MesonConfigPage *m_ui = nullptr; + Meson::MesonConfig m_config; + Meson::BuildDir m_current; + bool m_configChanged = false; +}; diff --git a/plugins/meson/settings/mesonconfigpage.ui b/plugins/meson/settings/mesonconfigpage.ui new file mode 100644 index 0000000000..7b14f0edcb --- /dev/null +++ b/plugins/meson/settings/mesonconfigpage.ui @@ -0,0 +1,303 @@ + + + MesonConfigPage + + + + 0 + 0 + 587 + 499 + + + + + + + + + + 0 + 0 + + + + + + + + ... + + + + ../../../../../.designer/backup../../../../../.designer/backup + + + + + + + ... + + + + ../../../../../.designer/backup../../../../../.designer/backup + + + + + + + + + Basic configuration + + + + + + Build type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + Full path to the installation prefix + + + + + + + Installation prefix: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Build configuration + + + + + + true + + + + + 0 + 0 + 555 + 237 + + + + + + + false + + + TODO -- Implement in seperate UI file + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + + + + + Status message... + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+ + KUrlRequester + QWidget +
kurlrequester.h
+
+ + MesonAdvancedSettings + QWidget +
settings/mesonadvancedsettings.h
+ 1 + + configChanged() + +
+
+ + + + b_addDir + clicked() + MesonConfigPage + addBuildDir() + + + 529 + 22 + + + 295 + 246 + + + + + b_rmDir + clicked() + MesonConfigPage + removeBuildDir() + + + 567 + 22 + + + 295 + 246 + + + + + i_buildDirs + currentIndexChanged(int) + MesonConfigPage + changeBuildDirIndex(int) + + + 257 + 22 + + + 295 + 246 + + + + + i_buildDirs + currentIndexChanged(int) + MesonConfigPage + emitChanged() + + + 255 + 22 + + + 293 + 249 + + + + + i_installPrefix + textChanged(QString) + MesonConfigPage + emitChanged() + + + 351 + 133 + + + 293 + 249 + + + + + advanced + configChanged() + MesonConfigPage + emitChanged() + + + 293 + 487 + + + 293 + 249 + + + + + i_buildType + currentIndexChanged(int) + MesonConfigPage + emitChanged() + + + 351 + 95 + + + 293 + 249 + + + + + + addBuildDir() + removeBuildDir() + changeBuildDirIndex(int) + emitChanged() + +
diff --git a/plugins/meson/settings/mesonnewbuilddir.cpp b/plugins/meson/settings/mesonnewbuilddir.cpp new file mode 100644 index 0000000000..a0300c5bb4 --- /dev/null +++ b/plugins/meson/settings/mesonnewbuilddir.cpp @@ -0,0 +1,211 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "mesonnewbuilddir.h" +#include "mesonbuilder.h" +#include "mesonmanager.h" +#include "ui_mesonnewbuilddir.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace KDevelop; + +MesonNewBuildDir::MesonNewBuildDir(IProject* project, QWidget* parent) + : QDialog(parent) + , m_project(project) +{ + Q_ASSERT(project); // Just in case + MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); + Q_ASSERT(mgr); // This dialog only works with the MesonManager + + setWindowTitle( + i18n("Configure a build directory - %1", ICore::self()->runtimeController()->currentRuntime()->name())); + + m_ui = new Ui::MesonNewBuildDir; + m_ui->setupUi(this); + + m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends()); + + connect(m_ui->b_buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* b) { + if (m_ui->b_buttonBox->buttonRole(b) == QDialogButtonBox::ResetRole) { + resetFields(); + } + }); + + resetFields(); +} + +MesonNewBuildDir::~MesonNewBuildDir() +{ + delete m_ui; +} + +void MesonNewBuildDir::resetFields() +{ + Meson::MesonConfig cfg = Meson::getMesonConfig(m_project); + Path projectPath = m_project->path(); + MesonManager* mgr = dynamic_cast(m_project->buildSystemManager()); + Q_ASSERT(mgr); // This dialog only works with the MesonManager + + auto aConf = m_ui->advanced->getConfig(); + + // Find a build dir that is not already configured + Path buildDirPath = projectPath; + buildDirPath.addPath(QStringLiteral("build")); + + auto checkInCfg = [](Meson::MesonConfig const& cfg, Path const& p) -> bool { + for (auto const& i : cfg.buildDirs) { + if (i.buildDir == p) { + return true; + } + } + return false; + }; + + for (int i = 2; checkInCfg(cfg, buildDirPath); ++i) { + buildDirPath = projectPath; + buildDirPath.addPath(QStringLiteral("build%1").arg(i)); + } + + m_ui->i_buildDir->setUrl(buildDirPath.toUrl()); + + // Init build type + // TODO use introspection once https://github.com/mesonbuild/meson/pull/4564 is merged + QStringList buildTypes = { QStringLiteral("plain"), QStringLiteral("debug"), QStringLiteral("debugoptimized"), + QStringLiteral("release"), QStringLiteral("minsize"), QStringLiteral("custom") }; + + m_ui->i_buildType->clear(); + m_ui->i_buildType->addItems(buildTypes); + m_ui->i_buildType->setCurrentIndex(std::max(0, buildTypes.indexOf(QStringLiteral("debug")))); + + // Install prefix + m_ui->i_installPrefix->clear(); + + // Extra args + aConf.args.clear(); + + // Backend + aConf.backend = mgr->defaultMesonBackend(); + + // Meson exe + aConf.meson = mgr->findMeson(); + + m_ui->advanced->setConfig(aConf); + updated(); +} + +void MesonNewBuildDir::setStatus(const QString& str, bool validConfig) +{ + m_configIsValid = validConfig; + + KColorScheme scheme(QPalette::Normal); + KColorScheme::ForegroundRole role; + if (validConfig) { + role = KColorScheme::PositiveText; + } else { + role = KColorScheme::NegativeText; + } + + QPalette pal = m_ui->l_statusMessage->palette(); + pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); + m_ui->l_statusMessage->setPalette(pal); + m_ui->l_statusMessage->setText(str); + + auto okButton = m_ui->b_buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(m_configIsValid); + if (m_configIsValid) { + auto cancelButton = m_ui->b_buttonBox->button(QDialogButtonBox::Cancel); + cancelButton->clearFocus(); + } +} + +void MesonNewBuildDir::updated() +{ + auto advanced = m_ui->advanced->getConfig(); + Path buildDir = Path(m_ui->i_buildDir->url()); + QFileInfo mesonExe(advanced.meson.toLocalFile()); + + if (!mesonExe.exists() || !mesonExe.isExecutable() + || !mesonExe.permission(QFileDevice::ReadUser | QFileDevice::ExeUser)) { + setStatus(i18n("Specified meson executable does not exist"), false); + return; + } + + MesonBuilder::DirectoryStatus status = MesonBuilder::evaluateBuildDirectory(buildDir, advanced.backend); + switch (status) { + case MesonBuilder::CLEAN: + case MesonBuilder::DOES_NOT_EXIST: + setStatus(i18n("Creating new build directory"), true); + break; + case MesonBuilder::MESON_CONFIGURED: + setStatus(i18n("Using an already configured build directory"), true); + break; + case MesonBuilder::MESON_FAILED_CONFIGURATION: + setStatus(i18n("Using a broken meson build directory (this should be fine)"), true); + break; + case MesonBuilder::INVALID_BUILD_DIR: + setStatus(i18n("Cannot use specified directory"), false); + break; + case MesonBuilder::DIR_NOT_EMPTY: + setStatus(i18n("There are already files in the build directory"), false); + break; + case MesonBuilder::EMPTY_STRING: + setStatus(i18n("The build directory field must not be empty"), false); + break; + case MesonBuilder::___UNDEFINED___: + setStatus(i18n("You have reached unreachable code. This is a bug"), false); + break; + } +} + +Meson::BuildDir MesonNewBuildDir::currentConfig() const +{ + Meson::BuildDir buildDir; + if (!m_configIsValid) { + qCDebug(KDEV_Meson) << "Cannot generate build dir config from invalid config"; + return buildDir; + } + + auto advanced = m_ui->advanced->getConfig(); + + buildDir.buildDir = Path(m_ui->i_buildDir->url()); + buildDir.buildType = m_ui->i_buildType->currentText(); + buildDir.installPrefix = Path(m_ui->i_installPrefix->url()); + buildDir.mesonArgs = advanced.args; + buildDir.mesonBackend = advanced.backend; + buildDir.mesonExecutable = advanced.meson; + + return buildDir; +} + +bool MesonNewBuildDir::isConfigValid() const +{ + return m_configIsValid; +} diff --git a/plugins/meson/settings/mesonnewbuilddir.h b/plugins/meson/settings/mesonnewbuilddir.h new file mode 100644 index 0000000000..151f5e6ed2 --- /dev/null +++ b/plugins/meson/settings/mesonnewbuilddir.h @@ -0,0 +1,57 @@ +/* This file is part of KDevelop + Copyright 2018 Daniel Mensinger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#pragma once + +#include "mesonconfig.h" +#include + +class QDialogButtonBox; + +namespace KDevelop { + class IProject; +} + +namespace Ui +{ +class MesonNewBuildDir; +} + +class MesonNewBuildDir : public QDialog +{ + Q_OBJECT +public: + explicit MesonNewBuildDir(KDevelop::IProject *project, QWidget* parent = nullptr); + ~MesonNewBuildDir() override; + MesonNewBuildDir() = delete; + + void setStatus(QString const& str, bool validConfig); + + bool isConfigValid() const; + Meson::BuildDir currentConfig() const; + +private Q_SLOTS: + void resetFields(); + void updated(); + +private: + bool m_configIsValid = false; + KDevelop::IProject* m_project = nullptr; + Ui::MesonNewBuildDir* m_ui = nullptr; +}; diff --git a/plugins/meson/settings/mesonnewbuilddir.ui b/plugins/meson/settings/mesonnewbuilddir.ui new file mode 100644 index 0000000000..e5293cc419 --- /dev/null +++ b/plugins/meson/settings/mesonnewbuilddir.ui @@ -0,0 +1,260 @@ + + + MesonNewBuildDir + + + + 0 + 0 + 650 + 625 + + + + + + + + + + Basic configuration + + + + + + Build directory: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Installation prefix: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + QFileDialog::AcceptSave + + + Full Path to the new build directory + + + + + + + Build type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + Full path to the installation prefix + + + + + + + + + + true + + + Build configuration + + + + + + true + + + + + 0 + 0 + 618 + 353 + + + + + + + false + + + TODO -- Implement in seperate UI file + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + Status message... + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+ + KUrlRequester + QWidget +
kurlrequester.h
+
+ + MesonAdvancedSettings + QWidget +
settings/mesonadvancedsettings.h
+ 1 + + configChanged() + +
+
+ + + + b_buttonBox + accepted() + MesonNewBuildDir + accept() + + + 564 + 643 + + + 329 + 333 + + + + + b_buttonBox + rejected() + MesonNewBuildDir + reject() + + + 564 + 643 + + + 329 + 333 + + + + + i_buildDir + textChanged(QString) + MesonNewBuildDir + updated() + + + 383 + 55 + + + 324 + 312 + + + + + advanced + configChanged() + MesonNewBuildDir + updated() + + + 324 + 571 + + + 324 + 312 + + + + + + resetFields() + updated() + +