diff --git a/plugins/meson/mesonbuilder.cpp b/plugins/meson/mesonbuilder.cpp index 18b8d97a78..e0cb6fc65c 100644 --- a/plugins/meson/mesonbuilder.cpp +++ b/plugins/meson/mesonbuilder.cpp @@ -1,304 +1,308 @@ /* 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 "mesonmanager.h" -#include -#include #include + #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 QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) if (dir.isEmpty(QDir::NoDotAndDotDot | QDir::Hidden | QDir::AllEntries)) { return CLEAN; } #else dir.setFilter(QDir::NoDotAndDotDot | QDir::Hidden | QDir::AllEntries); if (dir.count() == 0) { return CLEAN; } #endif // 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, QStringList args, 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, args, this); connect(job, &KJob::result, this, [this, project]() { emit configured(project); }); return job; case MESON_CONFIGURED: job = new MesonJob(buildDir, project, MesonJob::RE_CONFIGURE, args, 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); if(!buildDir.isValid()) { auto *bsm = project->buildSystemManager(); MesonManager *manager = dynamic_cast(bsm); if(!manager) { return new ErrorJob(this, i18n("Internal error: The buildsystem manager is not the MesonManager")); } KJob *newBDJob = manager->newBuildDirectory(project); if(!newBDJob) { return new ErrorJob(this, i18n("Failed to create a new build directory")); } return newBDJob; } 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; } KJob *configureJob = nullptr; if(buildDir.isValid()) { configureJob = configure(project, buildDir, {}, status); } else { // Create a new build directory auto *bsm = project->buildSystemManager(); MesonManager *manager = dynamic_cast(bsm); if(!manager) { return new ErrorJob(this, i18n("Internal error: The buildsystem manager is not the MesonManager")); } configureJob = manager->newBuildDirectory(project); if(!configureJob) { return new ErrorJob(this, i18n("Failed to create a new build directory")); } } 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/mesonconfig.cpp b/plugins/meson/mesonconfig.cpp index 870d93517d..731c48fbe9 100644 --- a/plugins/meson/mesonconfig.cpp +++ b/plugins/meson/mesonconfig.cpp @@ -1,184 +1,188 @@ /* 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 +#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 MESON_EXE = QStringLiteral("Meson executable"); static const QString EXTRA_ARGS = QStringLiteral("Additional meson arguments"); 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.mesonExecutable = Path(current.readEntry(MESON_EXE, QString())); 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(MESON_EXE, i.mesonExecutable.path()); 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 // Return an invalid build dir if (cfg.buildDirs.isEmpty()) { return BuildDir(); } } return cfg.buildDirs[cfg.currentIndex]; } bool Meson::BuildDir::isValid() const { return !(buildDir.isEmpty() || mesonExecutable.isEmpty()); } void Meson::BuildDir::canonicalizePaths() { for (auto* i : { &buildDir, &mesonExecutable }) { // canonicalFilePath checks if the file / directory exists and returns "" if it doesn't. QString tmp = QFileInfo(i->toLocalFile()).canonicalFilePath(); if (!tmp.isEmpty()) { *i = Path(tmp); } } } diff --git a/plugins/meson/mesonconfig.h b/plugins/meson/mesonconfig.h index e2500a987f..5ba078bb6b 100644 --- a/plugins/meson/mesonconfig.h +++ b/plugins/meson/mesonconfig.h @@ -1,58 +1,59 @@ /* 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 +#include + namespace KDevelop { class IProject; } namespace Meson { struct BuildDir { KDevelop::Path buildDir; KDevelop::Path mesonExecutable; 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/mesonintrospectjob.cpp b/plugins/meson/mesonintrospectjob.cpp index ba3602c97d..772bc9d4e6 100644 --- a/plugins/meson/mesonintrospectjob.cpp +++ b/plugins/meson/mesonintrospectjob.cpp @@ -1,259 +1,261 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonintrospectjob.h" + #include "mesonconfig.h" #include "mesonmanager.h" #include "mesonoptions.h" +#include #include +#include + #include #include #include #include #include -#include -#include using namespace Meson; using namespace KDevelop; MesonIntrospectJob::MesonIntrospectJob(KDevelop::IProject* project, QVector types, MesonIntrospectJob::Mode mode, QObject* parent) : KJob(parent) , m_types(types) , m_mode(mode) { Q_ASSERT(project); if (mode == MESON_FILE) { // Since we are parsing the meson file in this mode, no build directory // is required and we have to fake a build directory m_buildDir.buildDir = project->path(); auto* bsm = project->buildSystemManager(); MesonManager* manager = dynamic_cast(bsm); if (manager) { m_buildDir.mesonExecutable = manager->findMeson(); } } else { m_buildDir = Meson::currentBuildDir(project); } m_projectPath = project->path(); connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); } MesonIntrospectJob::MesonIntrospectJob(KDevelop::Path projectPath, KDevelop::Path meson, QVector types, QObject* parent) : KJob(parent) , m_types(types) , m_mode(MESON_FILE) , m_projectPath(projectPath) { // Since we are parsing the meson file in this mode, no build directory // is required and we have to fake a build directory m_buildDir.buildDir = m_projectPath; m_buildDir.mesonExecutable = meson; connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); } MesonIntrospectJob::MesonIntrospectJob(KDevelop::Path projectPath, Meson::BuildDir buildDir, QVector types, MesonIntrospectJob::Mode mode, QObject* parent) : KJob(parent) , m_types(types) , m_mode(mode) , m_buildDir(buildDir) , m_projectPath(projectPath) { connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); } QString MesonIntrospectJob::getTypeString(MesonIntrospectJob::Type type) const { switch (type) { case BENCHMARKS: return QStringLiteral("benchmarks"); case BUILDOPTIONS: return QStringLiteral("buildoptions"); case BUILDSYSTEM_FILES: return QStringLiteral("buildsystem_files"); case DEPENDENCIES: return QStringLiteral("dependencies"); case INSTALLED: return QStringLiteral("installed"); case PROJECTINFO: return QStringLiteral("projectinfo"); case TARGETS: return QStringLiteral("targets"); case TESTS: return QStringLiteral("tests"); } return QStringLiteral("error"); } QString MesonIntrospectJob::importJSONFile(const BuildDir& buildDir, MesonIntrospectJob::Type type, QJsonObject* out) { QString typeStr = getTypeString(type); QString fileName = QStringLiteral("intro-") + typeStr + QStringLiteral(".json"); QString infoDir = buildDir.buildDir.toLocalFile() + QStringLiteral("/") + QStringLiteral("meson-info"); QFile introFile(infoDir + QStringLiteral("/") + fileName); if (!introFile.exists()) { return i18n("Introspection file '%1' does not exist", QFileInfo(introFile).canonicalFilePath()); } if (!introFile.open(QFile::ReadOnly | QFile::Text)) { return i18n("Failed to open introspection file '%1'", QFileInfo(introFile).canonicalFilePath()); } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(introFile.readAll(), &error); if (error.error) { return i18n("In %1:%2: %3", QFileInfo(introFile).canonicalFilePath(), error.offset, error.errorString()); } if (doc.isArray()) { (*out)[typeStr] = doc.array(); } else if (doc.isObject()) { (*out)[typeStr] = doc.object(); } else { return i18n("The introspection file '%1' contains neither an array nor an object", QFileInfo(introFile).canonicalFilePath()); } return QString(); } QString MesonIntrospectJob::importMesonAPI(const BuildDir& buildDir, MesonIntrospectJob::Type type, QJsonObject* out) { QString typeStr = getTypeString(type); QString option = QStringLiteral("--") + typeStr; option.replace(QChar::fromLatin1('_'), QChar::fromLatin1('-')); KProcess proc(this); proc.setWorkingDirectory(m_projectPath.toLocalFile()); proc.setOutputChannelMode(KProcess::SeparateChannels); proc.setProgram(buildDir.mesonExecutable.toLocalFile()); proc << QStringLiteral("introspect") << option << QStringLiteral("meson.build"); int ret = proc.execute(); if (ret != 0) { return i18n("%1 returned %2", proc.program().join(QChar::fromLatin1(' ')), ret); } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(proc.readAll(), &error); if (error.error) { return i18n("JSON parser error: %1", error.errorString()); } if (doc.isArray()) { (*out)[typeStr] = doc.array(); } else if (doc.isObject()) { (*out)[typeStr] = doc.object(); } else { return i18n("The introspection output of '%1' contains neither an array nor an object", proc.program().join(QChar::fromLatin1(' '))); } return QString(); } QString MesonIntrospectJob::import(BuildDir buildDir) { QJsonObject rawData; // First load the complete JSON data for (auto i : m_types) { QString err; switch (m_mode) { case BUILD_DIR: err = importJSONFile(buildDir, i, &rawData); break; case MESON_FILE: err = importMesonAPI(buildDir, i, &rawData); break; } if (!err.isEmpty()) { qCWarning(KDEV_Meson) << "MINTRO: " << err; setError(true); setErrorText(err); return err; } } auto buildOptionsJSON = rawData[QStringLiteral("buildoptions")]; if (buildOptionsJSON.isArray()) { m_res_options = std::make_shared(buildOptionsJSON.toArray()); if (m_res_options) { qCDebug(KDEV_Meson) << "MINTRO: Imported " << m_res_options->options().size() << " buildoptions"; } else { qCWarning(KDEV_Meson) << "MINTRO: Failed to parse buildoptions"; } } auto targetsJSON = rawData[QStringLiteral("targets")]; if (targetsJSON.isArray()) { m_res_targets = std::make_shared(targetsJSON.toArray()); } return QString(); } void MesonIntrospectJob::start() { qCDebug(KDEV_Meson) << "MINTRO: Starting meson introspection job"; if (!m_buildDir.isValid()) { qCWarning(KDEV_Meson) << "The current build directory is invalid"; setError(true); setErrorText(i18n("The current build directory is invalid")); emitResult(); return; } auto future = QtConcurrent::run(this, &MesonIntrospectJob::import, m_buildDir); m_futureWatcher.setFuture(future); } void MesonIntrospectJob::finished() { qCDebug(KDEV_Meson) << "MINTRO: Meson introspection job finished"; emitResult(); } bool MesonIntrospectJob::doKill() { if (m_futureWatcher.isRunning()) { m_futureWatcher.cancel(); } return true; } MesonOptsPtr MesonIntrospectJob::buildOptions() { return m_res_options; } MesonTargetsPtr MesonIntrospectJob::targets() { return m_res_targets; } diff --git a/plugins/meson/mesonintrospectjob.h b/plugins/meson/mesonintrospectjob.h index 3e5ba0f841..d9babe07e2 100644 --- a/plugins/meson/mesonintrospectjob.h +++ b/plugins/meson/mesonintrospectjob.h @@ -1,71 +1,75 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptions.h" #include "mesontargets.h" -#include + #include -#include + +#include + +#include + #include class QJsonObject; class MesonIntrospectJob : public KJob { Q_OBJECT public: enum Type { BENCHMARKS, BUILDOPTIONS, BUILDSYSTEM_FILES, DEPENDENCIES, INSTALLED, PROJECTINFO, TARGETS, TESTS }; enum Mode { BUILD_DIR, MESON_FILE }; public: explicit MesonIntrospectJob(KDevelop::IProject* project, QVector types, Mode mode, QObject* parent); explicit MesonIntrospectJob(KDevelop::Path projectPath, KDevelop::Path meson, QVector types, QObject* parent); explicit MesonIntrospectJob(KDevelop::Path projectPath, Meson::BuildDir buildDir, QVector types, Mode mode, QObject* parent); void start() override; bool doKill() override; QString getTypeString(Type type) const; MesonOptsPtr buildOptions(); MesonTargetsPtr targets(); private: QString importJSONFile(Meson::BuildDir const& buildDir, Type type, QJsonObject* out); QString importMesonAPI(Meson::BuildDir const& buildDir, Type type, QJsonObject* out); QString import(Meson::BuildDir buildDir); void finished(); QFutureWatcher m_futureWatcher; // The commands to execute QVector m_types = {}; Mode m_mode = BUILD_DIR; Meson::BuildDir m_buildDir; KDevelop::Path m_projectPath; // The results MesonOptsPtr m_res_options = nullptr; MesonTargetsPtr m_res_targets = nullptr; }; diff --git a/plugins/meson/mesonjob.cpp b/plugins/meson/mesonjob.cpp index 4e1a5e74ba..846102f8d4 100644 --- a/plugins/meson/mesonjob.cpp +++ b/plugins/meson/mesonjob.cpp @@ -1,71 +1,74 @@ /* 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 +#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: *this << QStringLiteral("--backend") << buildDir.mesonBackend; break; case RE_CONFIGURE: *this << QStringLiteral("--reconfigure"); break; case SET_CONFIG: *this << QStringLiteral("configure"); break; } *this << m_arguments; for (auto i : buildDir.mesonArgs.split(QChar::fromLatin1(' '))) { if (!i.isEmpty()) { *this << i; } } *this << buildDir.buildDir.toLocalFile(); } QUrl MesonJob::workingDirectory() const { return m_project->path().toUrl(); } diff --git a/plugins/meson/mesonjobprune.cpp b/plugins/meson/mesonjobprune.cpp index 248cf98078..12e145edd8 100644 --- a/plugins/meson/mesonjobprune.cpp +++ b/plugins/meson/mesonjobprune.cpp @@ -1,97 +1,101 @@ /* 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 -#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/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index 09ecd49ee3..416e18a577 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,333 +1,335 @@ /* 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 "mesonintrospectjob.h" #include "mesontargets.h" +#include "debug.h" #include "settings/mesonconfigpage.h" #include "settings/mesonnewbuilddir.h" -#include + #include #include #include #include #include #include #include #include #include -#include "debug.h" +#include using namespace KDevelop; using namespace std; static const QString GENERATOR_NINJA = QStringLiteral("ninja"); K_PLUGIN_FACTORY_WITH_JSON(MesonSupportFactory, "kdevmesonmanager.json", registerPlugin();) // *********************************** // * Meson specific executable class * // *********************************** class MesonProjectExecutableTargetItem final : public ProjectExecutableTargetItem { public: MesonProjectExecutableTargetItem(IProject* project, const QString& name, ProjectBaseItem* parent, Path buildPath, Path installPath = Path()) : ProjectExecutableTargetItem(project, name, parent) , m_buildPath(buildPath) , m_installPath(installPath) { } QUrl builtUrl() const override { return m_buildPath.toUrl(); } QUrl installedUrl() const override { return m_installPath.toUrl(); } private: Path m_buildPath; Path m_installPath; }; // *************** // * Constructor * // *************** 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: %1", 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 * // *************************** void MesonManager::populateTargets(ProjectFolderItem* item, QVector targets) { // Remove all old targets for (ProjectTargetItem* i : item->targetList()) { delete i; } // Add the new targets auto dirPath = item->path(); for (MesonTarget* i : targets) { if (!dirPath.isDirectParentOf(i->definedIn())) { continue; } if (i->type().contains(QStringLiteral("executable"))) { auto outFiles = i->filename(); Path outFile; if (outFiles.size() > 0) { outFile = outFiles[0]; } new MesonProjectExecutableTargetItem(item->project(), i->name(), item, outFile); } else if (i->type().contains(QStringLiteral("library"))) { new ProjectLibraryTargetItem(item->project(), i->name(), item); } else { new ProjectTargetItem(item->project(), i->name(), item); } } // Recurse for (ProjectFolderItem* i : item->folderList()) { QVector filteredTargets; copy_if(begin(targets), end(targets), back_inserter(filteredTargets), [i](MesonTarget* t) -> bool { return i->path().isParentOf(t->definedIn()); }); populateTargets(i, filteredTargets); } } QList MesonManager::targets(ProjectFolderItem* item) const { Q_ASSERT(item); QList res = item->targetList(); for (ProjectFolderItem* i : item->folderList()) { res << targets(i); } return res; } KJob* MesonManager::createImportJob(ProjectFolderItem* item) { IProject* project = item->project(); auto buildDir = Meson::currentBuildDir(project); auto introJob = new MesonIntrospectJob(project->path(), buildDir, { MesonIntrospectJob::TARGETS }, MesonIntrospectJob::BUILD_DIR, this); connect(introJob, &KJob::result, this, [this, introJob, item, project]() { auto targets = introJob->targets(); if (!targets) { return; } m_projectTargets[project] = targets; auto tgtList = targets->targets(); QVector tgtCopy; tgtCopy.reserve(tgtList.size()); transform(begin(tgtList), end(tgtList), back_inserter(tgtCopy), [](auto const& a) { return a.get(); }); populateTargets(item, tgtCopy); }); const QList jobs = { builder()->configure(project), // Make sure the project is configured AbstractFileManagerPlugin::createImportJob(item), // generate the file system listing introJob // Load targets from the build directory introspection files }; 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; } MesonSourcePtr MesonManager::sourceFromItem(KDevelop::ProjectBaseItem* item) const { Q_ASSERT(item); auto it = m_projectTargets.find(item->project()); if (it == end(m_projectTargets)) { qCDebug(KDEV_Meson) << item->path().toLocalFile() << "not found"; return {}; } auto targets = *it; return targets->fileSource(item->path()); } KDevelop::Path::List MesonManager::includeDirectories(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->includeDirs(); } KDevelop::Path::List MesonManager::frameworkDirectories(KDevelop::ProjectBaseItem*) const { return {}; } QHash MesonManager::defines(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->defines(); } QString MesonManager::extraArguments(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return {}; } return src->extraArgs().join(QChar::fromLatin1(' ')); } bool MesonManager::hasBuildInfo(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return false; } return true; } // ******************** // * Custom functions * // ******************** KJob* 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 nullptr; } Meson::BuildDir buildDir = newBD.currentConfig(); Meson::MesonConfig mesonCfg = Meson::getMesonConfig(project); buildDir.canonicalizePaths(); mesonCfg.currentIndex = mesonCfg.addBuildDir(buildDir); Meson::writeMesonConfig(project, mesonCfg); return m_builder->configure(project, buildDir, newBD.mesonArgs()); } 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); } // *********** // * IPlugin * // *********** 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 index ceec5648d6..d54e82073e 100644 --- a/plugins/meson/mesonmanager.h +++ b/plugins/meson/mesonmanager.h @@ -1,117 +1,119 @@ /* 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 #include +#include + class MesonBuilder; class MesonTarget; class MesonTargets; class MesonTargetSources; using MesonSourcePtr = std::shared_ptr; using MesonTargetsPtr = std::shared_ptr; 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. * @returns The configuration job on success or nullptr on error. */ KJob* 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; // ********************************* // * 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; KDevelop::Path::List includeDirectories(KDevelop::ProjectBaseItem* item) const override; KDevelop::Path::List frameworkDirectories(KDevelop::ProjectBaseItem* item) const override; QHash defines(KDevelop::ProjectBaseItem* item) const override; QString extraArguments(KDevelop::ProjectBaseItem* item) const override; bool hasBuildInfo(KDevelop::ProjectBaseItem* item) const override; KDevelop::Path buildDirectory(KDevelop::ProjectBaseItem*) const override; QList targets(KDevelop::ProjectFolderItem*) const override; // Not sure when/if these will be implemented. This would require modifying meson files. Regardless: 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_projectTargets; MesonSourcePtr sourceFromItem(KDevelop::ProjectBaseItem* item) const; void populateTargets(KDevelop::ProjectFolderItem *item, QVector targets); }; #endif diff --git a/plugins/meson/mesonoptions.cpp b/plugins/meson/mesonoptions.cpp index 80089f6707..2554892c5e 100644 --- a/plugins/meson/mesonoptions.cpp +++ b/plugins/meson/mesonoptions.cpp @@ -1,437 +1,439 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptions.h" +#include + #include #include #include #include #include + #include -#include #include using namespace std; static const QHash STRING2SECTION = { { QStringLiteral("core"), MesonOptionBase::CORE }, { QStringLiteral("backend"), MesonOptionBase::BACKEND }, { QStringLiteral("base"), MesonOptionBase::BASE }, { QStringLiteral("compiler"), MesonOptionBase::COMPILER }, { QStringLiteral("directory"), MesonOptionBase::DIRECTORY }, { QStringLiteral("user"), MesonOptionBase::USER }, { QStringLiteral("test"), MesonOptionBase::TEST }, }; static const QHash STRING2TYPE = { { QStringLiteral("array"), MesonOptionBase::ARRAY }, { QStringLiteral("boolean"), MesonOptionBase::BOOLEAN }, { QStringLiteral("combo"), MesonOptionBase::COMBO }, { QStringLiteral("integer"), MesonOptionBase::INTEGER }, { QStringLiteral("string"), MesonOptionBase::STRING }, }; MesonOptions::MesonOptions(QJsonArray const& arr) { fromJSON(arr); } // Option constructors MesonOptionBase::MesonOptionBase(QString name, QString description, MesonOptionBase::Section section) : m_name(name) , m_description(description) , m_section(section) { } MesonOptionArray::MesonOptionArray(QString name, QString description, MesonOptionBase::Section section, QStringList value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionBool::MesonOptionBool(QString name, QString description, MesonOptionBase::Section section, bool value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionCombo::MesonOptionCombo(QString name, QString description, MesonOptionBase::Section section, QString value, QStringList choices) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) , m_choices(choices) { } MesonOptionInteger::MesonOptionInteger(QString name, QString description, MesonOptionBase::Section section, int value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionString::MesonOptionString(QString name, QString description, MesonOptionBase::Section section, QString value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } QStringList MesonOptionCombo::choices() const { return m_choices; } // Type functions MesonOptionBase::Type MesonOptionArray::type() const { return ARRAY; } MesonOptionBase::Type MesonOptionBool::type() const { return BOOLEAN; } MesonOptionBase::Type MesonOptionCombo::type() const { return COMBO; } MesonOptionBase::Type MesonOptionInteger::type() const { return INTEGER; } MesonOptionBase::Type MesonOptionString::type() const { return STRING; } // Value functions QString MesonOptionArray::value() const { QStringList tmp; tmp.reserve(m_value.size()); transform(begin(m_value), end(m_value), back_inserter(tmp), [](QString const& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); }); return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]"); } QString MesonOptionBool::value() const { return m_value ? QStringLiteral("true") : QStringLiteral("false"); } QString MesonOptionCombo::value() const { return m_value; } QString MesonOptionInteger::value() const { return QString::number(m_value); } QString MesonOptionString::value() const { return m_value; } // Initial value functions QString MesonOptionArray::initialValue() const { QStringList tmp; tmp.reserve(m_initialValue.size()); transform(begin(m_initialValue), end(m_initialValue), back_inserter(tmp), [](QString const& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); }); return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]"); } QString MesonOptionBool::initialValue() const { return m_initialValue ? QStringLiteral("true") : QStringLiteral("false"); } QString MesonOptionCombo::initialValue() const { return m_initialValue; } QString MesonOptionInteger::initialValue() const { return QString::number(m_initialValue); } QString MesonOptionString::initialValue() const { return m_initialValue; } // Reset functions void MesonOptionArray::reset() { m_value = m_initialValue; } void MesonOptionBool::reset() { m_value = m_initialValue; } void MesonOptionCombo::reset() { m_value = m_initialValue; } void MesonOptionInteger::reset() { m_value = m_initialValue; } void MesonOptionString::reset() { m_value = m_initialValue; } // Raw value functions QStringList MesonOptionArray::rawValue() const { return m_value; } bool MesonOptionBool::rawValue() const { return m_value; } QString MesonOptionCombo::rawValue() const { return m_value; } int MesonOptionInteger::rawValue() const { return m_value; } QString MesonOptionString::rawValue() const { return m_value; } // Set value functions void MesonOptionArray::setValue(QStringList val) { m_value = val; } void MesonOptionBool::setValue(bool val) { m_value = val; } void MesonOptionCombo::setValue(QString val) { m_value = val; } void MesonOptionInteger::setValue(int val) { m_value = val; } void MesonOptionString::setValue(QString val) { m_value = val; } // Base option functions MesonOptionBase::~MesonOptionBase() {} QString MesonOptionBase::name() const { return m_name; } QString MesonOptionBase::description() const { return m_description; } MesonOptionBase::Section MesonOptionBase::section() const { return m_section; } QString MesonOptionBase::mesonArg() const { return QStringLiteral("-D") + m_name + QStringLiteral("=") + value(); } bool MesonOptionBase::isUpdated() const { return value() != initialValue(); } MesonOptionPtr MesonOptionBase::fromJSON(const QJsonObject& obj) { auto nameJV = obj[QStringLiteral("name")]; auto descriptionJV = obj[QStringLiteral("description")]; auto sectionJV = obj[QStringLiteral("section")]; auto typeJV = obj[QStringLiteral("type")]; auto valueJV = obj[QStringLiteral("value")]; // Check all values for (auto const& i : { nameJV, descriptionJV, sectionJV, typeJV }) { if (!i.isString()) { qCWarning(KDEV_Meson) << "OPT: Base type validation failed" << typeJV.toString(); return nullptr; } } auto sectionIter = STRING2SECTION.find(sectionJV.toString()); auto typeIter = STRING2TYPE.find(typeJV.toString()); if (sectionIter == end(STRING2SECTION) || typeIter == end(STRING2TYPE)) { qCWarning(KDEV_Meson) << "OPT: Unknown type or section " << typeJV.toString() << " / " << sectionJV.toString(); return nullptr; } Section section = *sectionIter; Type type = *typeIter; QString name = nameJV.toString(); QString description = descriptionJV.toString(); switch (type) { case ARRAY: { if (!valueJV.isArray()) { return nullptr; } QJsonArray raw = valueJV.toArray(); QStringList values; values.reserve(raw.size()); transform(begin(raw), end(raw), back_inserter(values), [](QJsonValue const& v) { return v.toString(); }); return make_shared(name, description, section, values); } case BOOLEAN: if (!valueJV.isBool()) { return nullptr; } return make_shared(name, description, section, valueJV.toBool()); case COMBO: { auto choicesJV = obj[QStringLiteral("choices")]; if (!valueJV.isString() || !choicesJV.isArray()) { return nullptr; } QJsonArray raw = choicesJV.toArray(); QStringList choices; choices.reserve(raw.size()); transform(begin(raw), end(raw), back_inserter(choices), [](QJsonValue const& v) { return v.toString(); }); return make_shared(name, description, section, valueJV.toString(), choices); } case INTEGER: if (!valueJV.isDouble()) { return nullptr; } return make_shared(name, description, section, valueJV.toInt()); case STRING: if (!valueJV.isString()) { return nullptr; } return make_shared(name, description, section, valueJV.toString()); } qCWarning(KDEV_Meson) << "OPT: Unknown type " << typeJV.toString(); return nullptr; } int MesonOptions::numChanged() const { int sum = 0; for(auto i : m_options) { if (i && i->isUpdated()) { ++sum; } } return sum; } QStringList MesonOptions::getMesonArgs() const { QStringList result; result.reserve(m_options.size()); for(auto i : m_options) { if (i && i->isUpdated()) { result << i->mesonArg(); } } return result; } void MesonOptions::fromJSON(const QJsonArray& arr) { m_options.clear(); m_options.reserve(arr.size()); for (QJsonValue const& i : arr) { if (!i.isObject()) { continue; } auto ptr = MesonOptionBase::fromJSON(i.toObject()); if (ptr) { m_options += ptr; } else { qCWarning(KDEV_Meson) << "OPT: Failed to parse " << i.toObject(); } } } void MesonOptions::print() const { for (auto const& i : m_options) { qCDebug(KDEV_Meson) << i->name() << " = " << i->value() << " [" << i->type() << "] -- " << i->section(); } } QVector MesonOptions::options() { return m_options; } diff --git a/plugins/meson/mesontargets.cpp b/plugins/meson/mesontargets.cpp index a755d931c0..11f0a8e38e 100644 --- a/plugins/meson/mesontargets.cpp +++ b/plugins/meson/mesontargets.cpp @@ -1,255 +1,258 @@ /* This file is part of KDevelop Copyright 2019 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 "mesontargets.h" + +#include + #include #include + #include -#include using namespace std; using namespace KDevelop; // MesonTargetSources MesonTargetSources::MesonTargetSources(const QJsonObject& json, MesonTarget* target) : m_target(target) { fromJSON(json); } MesonTargetSources::~MesonTargetSources() {} QString MesonTargetSources::language() const { return m_language; } QStringList MesonTargetSources::compiler() const { return m_compiler; } QStringList MesonTargetSources::paramerters() const { return m_paramerters; } KDevelop::Path::List MesonTargetSources::sources() const { return m_sources; } KDevelop::Path::List MesonTargetSources::generatedSources() const { return m_generatedSources; } KDevelop::Path::List MesonTargetSources::allSources() const { return m_sources + m_generatedSources; } KDevelop::Path::List MesonTargetSources::includeDirs() const { return m_includeDirs; } QHash MesonTargetSources::defines() const { return m_defines; } QStringList MesonTargetSources::extraArgs() const { return m_extraArgs; } MesonTarget* MesonTargetSources::target() { return m_target; } void MesonTargetSources::fromJSON(const QJsonObject& json) { m_language = json[QStringLiteral("language")].toString(); QJsonArray comp = json[QStringLiteral("compiler")].toArray(); QJsonArray param = json[QStringLiteral("parameters")].toArray(); QJsonArray src = json[QStringLiteral("sources")].toArray(); QJsonArray gensrc = json[QStringLiteral("generated_sources")].toArray(); transform(begin(comp), end(comp), back_inserter(m_compiler), [](auto const& x) { return x.toString(); }); transform(begin(param), end(param), back_inserter(m_paramerters), [](auto const& x) { return x.toString(); }); transform(begin(src), end(src), back_inserter(m_sources), [](auto const& x) { return Path(x.toString()); }); transform(begin(gensrc), end(gensrc), back_inserter(m_generatedSources), [](auto const& x) { return Path(x.toString()); }); splitParamerters(); qCDebug(KDEV_Meson) << " - language:" << m_language << "has" << m_sources.count() + m_generatedSources.count() << "files with" << m_includeDirs.count() << "include directories and" << m_defines.count() << "defines"; } void MesonTargetSources::splitParamerters() { for (QString const& i : m_paramerters) { [&]() { for (auto j : { QStringLiteral("-I"), QStringLiteral("/I"), QStringLiteral("-isystem") }) { if (i.startsWith(j)) { m_includeDirs << Path(i.mid(j.size())); return; } } for (auto j : { QStringLiteral("-D"), QStringLiteral("/D") }) { if (i.startsWith(j)) { QString define = i.mid(j.size()); QString name = define; QString value; int equalPos = define.indexOf(QChar::fromLatin1('=')); if (equalPos > 0) { name = define.left(equalPos); value = define.mid(equalPos + 1); } m_defines[name] = value; return; } } m_extraArgs << i; }(); } } // MesonTarget MesonTarget::MesonTarget(const QJsonObject& json) { fromJSON(json); } MesonTarget::~MesonTarget() {} QString MesonTarget::name() const { return m_name; } QString MesonTarget::type() const { return m_type; } KDevelop::Path MesonTarget::definedIn() const { return m_definedIn; } KDevelop::Path::List MesonTarget::filename() const { return m_filename; } bool MesonTarget::buildByDefault() const { return m_buildByDefault; } bool MesonTarget::installed() const { return m_installed; } QVector MesonTarget::targetSources() { return m_targetSources; } void MesonTarget::fromJSON(const QJsonObject& json) { m_name = json[QStringLiteral("name")].toString(); m_type = json[QStringLiteral("type")].toString(); m_definedIn = Path(json[QStringLiteral("defined_in")].toString()); m_buildByDefault = json[QStringLiteral("build_by_default")].toBool(); m_installed = json[QStringLiteral("installed")].toBool(); QJsonArray files = json[QStringLiteral("filename")].toArray(); transform(begin(files), end(files), back_inserter(m_filename), [](auto const& x) { return Path(x.toString()); }); qCDebug(KDEV_Meson) << " - " << m_type << m_name; for (auto const& i : json[QStringLiteral("target_sources")].toArray()) { m_targetSources << make_shared(i.toObject(), this); } } // MesonTargets MesonTargets::MesonTargets(const QJsonArray& json) { fromJSON(json); } MesonTargets::~MesonTargets() {} QVector MesonTargets::targets() { return m_targets; } MesonSourcePtr MesonTargets::fileSource(KDevelop::Path p) { auto it = m_sourceHash.find(p); if (it == end(m_sourceHash)) { return nullptr; } return *it; } MesonSourcePtr MesonTargets::operator[](KDevelop::Path p) { return fileSource(p); } void MesonTargets::fromJSON(const QJsonArray& json) { qCDebug(KDEV_Meson) << "MINTRO: Loading targets from json..."; for (auto const& i : json) { m_targets << make_shared(i.toObject()); } buildHashMap(); qCDebug(KDEV_Meson) << "MINTRO: Loaded" << m_targets.count() << "targets with" << m_sourceHash.count() << "total files"; } void MesonTargets::buildHashMap() { for (auto& i : m_targets) { for (auto j : i->targetSources()) { for (auto k : j->allSources()) { m_sourceHash[k] = j; } } } } diff --git a/plugins/meson/mesontargets.h b/plugins/meson/mesontargets.h index 889429c4cb..fcf8b121da 100644 --- a/plugins/meson/mesontargets.h +++ b/plugins/meson/mesontargets.h @@ -1,120 +1,122 @@ /* This file is part of KDevelop Copyright 2019 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 #include + #include -#include class QJsonArray; class QJsonObject; class MesonTarget; class MesonTargets; class MesonTargetSources; using MesonSourcePtr = std::shared_ptr; using MesonTargetPtr = std::shared_ptr; using MesonTargetsPtr = std::shared_ptr; class MesonTargetSources { public: explicit MesonTargetSources(QJsonObject const& json, MesonTarget* target); virtual ~MesonTargetSources(); QString language() const; QStringList compiler() const; QStringList paramerters() const; KDevelop::Path::List sources() const; KDevelop::Path::List generatedSources() const; KDevelop::Path::List allSources() const; KDevelop::Path::List includeDirs() const; QHash defines() const; QStringList extraArgs() const; MesonTarget* target(); void fromJSON(QJsonObject const& json); private: QString m_language; QStringList m_compiler; QStringList m_paramerters; KDevelop::Path::List m_sources; KDevelop::Path::List m_generatedSources; KDevelop::Path::List m_includeDirs; QHash m_defines; QStringList m_extraArgs; MesonTarget* m_target; // Store a pointer to the parent target void splitParamerters(); }; class MesonTarget { public: explicit MesonTarget(QJsonObject const& json); virtual ~MesonTarget(); QString name() const; QString type() const; KDevelop::Path definedIn() const; KDevelop::Path::List filename() const; bool buildByDefault() const; bool installed() const; QVector targetSources(); void fromJSON(QJsonObject const& json); private: QString m_name; QString m_type; KDevelop::Path m_definedIn; KDevelop::Path::List m_filename; bool m_buildByDefault; bool m_installed; QVector m_targetSources; }; class MesonTargets { public: explicit MesonTargets(QJsonArray const& json); virtual ~MesonTargets(); QVector targets(); MesonSourcePtr fileSource(KDevelop::Path p); MesonSourcePtr operator[](KDevelop::Path p); void fromJSON(QJsonArray const& json); private: QVector m_targets; QHash m_sourceHash; void buildHashMap(); }; diff --git a/plugins/meson/settings/mesonadvancedsettings.cpp b/plugins/meson/settings/mesonadvancedsettings.cpp index 9f8dbadf5c..0a6a1525d6 100644 --- a/plugins/meson/settings/mesonadvancedsettings.cpp +++ b/plugins/meson/settings/mesonadvancedsettings.cpp @@ -1,77 +1,78 @@ /* 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(); } /// Check if meson has changed since the last call bool MesonAdvancedSettings::hasMesonChanged() { if(m_mesonOldPath != Path(m_ui->i_mesonExe->url())) { m_mesonOldPath = Path(m_ui->i_mesonExe->url()); // Reset return true; } return false; } diff --git a/plugins/meson/settings/mesonadvancedsettings.h b/plugins/meson/settings/mesonadvancedsettings.h index 4925552f69..35cbb6eab0 100644 --- a/plugins/meson/settings/mesonadvancedsettings.h +++ b/plugins/meson/settings/mesonadvancedsettings.h @@ -1,61 +1,62 @@ /* 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); bool hasMesonChanged(); Q_SIGNALS: void configChanged(); public Q_SLOTS: void updated(); private: Ui::MesonAdvancedSettings* m_ui = nullptr; QStringList m_backendList; KDevelop::Path m_mesonOldPath; }; diff --git a/plugins/meson/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp index 48e1b3df2c..a49429d189 100644 --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -1,351 +1,356 @@ /* 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 "mesonintrospectjob.h" #include "mesonjob.h" #include "mesonmanager.h" #include "mesonnewbuilddir.h" #include "ui_mesonconfigpage.h" -#include #include + #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; auto options = m_ui->options->options(); if (!options) { qCWarning(KDEV_Meson) << "Options is nullptr. Can not update meson config"; return; } QStringList mesonArgs = options->getMesonArgs(); if (mesonArgs.empty()) { qCDebug(KDEV_Meson) << "Config has not changed --> nothing has to be updated"; return; } // 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, mesonArgs, nullptr); } joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, mesonArgs, nullptr); joblist << m_ui->options->repopulateFromBuildDir(m_project->path(), m_current); 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(); m_ui->options->resetAll(); 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]; m_ui->options->repopulateFromBuildDir(m_project->path(), m_current)->start(); updateUI(); } void MesonConfigPage::checkStatus() { // 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 directory does not exist"), 1); break; case MesonBuilder::CLEAN: setStatus(i18n("The current build directory is empty"), 1); break; case MesonBuilder::MESON_CONFIGURED: setStatus(i18n("Build directory configured"), 0); break; case MesonBuilder::MESON_FAILED_CONFIGURATION: setStatus(i18n("This meson build directory 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 directory"), 2); break; case MesonBuilder::EMPTY_STRING: setStatus(i18n("Invalid build directory configuration (empty build directory string)"), 2); break; case MesonBuilder::___UNDEFINED___: setStatus(i18n("Something went very wrong. This is a bug"), 2); break; } KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; int numChanged = 0; auto options = m_ui->options->options(); if (options) { numChanged = options->numChanged(); } if (numChanged == 0) { role = KColorScheme::NormalText; m_ui->l_changed->setText(i18n("No changes")); } else { role = KColorScheme::NeutralText; m_ui->l_changed->setText(i18n("%1 options changed", numChanged)); } QPalette pal = m_ui->l_changed->palette(); pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); m_ui->l_changed->setPalette(pal); } void MesonConfigPage::updateUI() { 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); checkStatus(); } void MesonConfigPage::readUI() { qCDebug(KDEV_Meson) << "Reading current build configuration from the UI " << m_current.buildDir.toLocalFile(); 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->i_buildDirs->setDisabled(disabled); m_ui->b_addDir->setDisabled(disabled); m_ui->b_rmDir->setDisabled(disabled); m_ui->options->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_current.canonicalizePaths(); 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, m_current, newBD.mesonArgs()); 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; checkStatus(); 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 index 23a9874977..e1864675ad 100644 --- a/plugins/meson/settings/mesonconfigpage.h +++ b/plugins/meson/settings/mesonconfigpage.h @@ -1,71 +1,72 @@ /* 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" #include "mesonoptions.h" +#include + 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 checkStatus(); 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; MesonOptsPtr m_options = nullptr; }; diff --git a/plugins/meson/settings/mesonlisteditor.cpp b/plugins/meson/settings/mesonlisteditor.cpp index e7b59eec60..63247ea826 100644 --- a/plugins/meson/settings/mesonlisteditor.cpp +++ b/plugins/meson/settings/mesonlisteditor.cpp @@ -1,145 +1,146 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonlisteditor.h" + #include "ui_mesonlisteditor.h" QListWidgetItem* genItem(QString const& label) { QListWidgetItem* item = new QListWidgetItem(label); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled); return item; } MesonListEditor::MesonListEditor(QStringList content, QWidget* parent) : QDialog(parent) { m_ui = new Ui::MesonListEditor; m_ui->setupUi(this); for (auto const& i : content) { m_ui->array->addItem(genItem(i)); } currentItemChanged(); } MesonListEditor::~MesonListEditor() { if (m_ui) { delete m_ui; } } QStringList MesonListEditor::content() const { QStringList content; content.reserve(m_ui->array->count()); for (int i = 0; i < m_ui->array->count(); ++i) { content << m_ui->array->item(i)->text(); } return content; } void MesonListEditor::add() { auto* item = genItem(i18n("")); m_ui->array->addItem(item); m_ui->array->setCurrentItem(item); m_ui->array->editItem(item); } void MesonListEditor::moveItem(int src, int dst) { auto* item = m_ui->array->takeItem(src); if (!item) { return; } m_ui->array->insertItem(dst, item); m_ui->array->setCurrentItem(item); } void MesonListEditor::remove() { qDeleteAll(m_ui->array->selectedItems()); } void MesonListEditor::first() { int row = m_ui->array->currentRow(); moveItem(row, 0); } void MesonListEditor::up() { int row = m_ui->array->currentRow(); moveItem(row, row - 1); } void MesonListEditor::down() { int row = m_ui->array->currentRow(); moveItem(row, row + 1); } void MesonListEditor::last() { int row = m_ui->array->currentRow(); moveItem(row, m_ui->array->count() - 1); } void MesonListEditor::currentItemChanged() { auto* current = m_ui->array->currentItem(); if (!current || m_ui->array->count() == 0) { m_ui->b_first->setDisabled(true); m_ui->b_up->setDisabled(true); m_ui->b_down->setDisabled(true); m_ui->b_last->setDisabled(true); m_ui->b_del->setDisabled(true); return; } int row = m_ui->array->row(current); m_ui->b_del->setDisabled(false); if (m_ui->array->count() < 2) { m_ui->b_first->setDisabled(true); m_ui->b_up->setDisabled(true); m_ui->b_down->setDisabled(true); m_ui->b_last->setDisabled(true); } else if (row == 0) { m_ui->b_first->setDisabled(true); m_ui->b_up->setDisabled(true); m_ui->b_down->setDisabled(false); m_ui->b_last->setDisabled(false); } else if (row >= m_ui->array->count() - 1) { m_ui->b_first->setDisabled(false); m_ui->b_up->setDisabled(false); m_ui->b_down->setDisabled(true); m_ui->b_last->setDisabled(true); } else { m_ui->b_first->setDisabled(false); m_ui->b_up->setDisabled(false); m_ui->b_down->setDisabled(false); m_ui->b_last->setDisabled(false); } } diff --git a/plugins/meson/settings/mesonnewbuilddir.cpp b/plugins/meson/settings/mesonnewbuilddir.cpp index 737638ca8a..f46f5687c8 100644 --- a/plugins/meson/settings/mesonnewbuilddir.cpp +++ b/plugins/meson/settings/mesonnewbuilddir.cpp @@ -1,229 +1,232 @@ /* 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 + #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(); } }); #if KIO_VERSION >= QT_VERSION_CHECK(5, 33, 0) m_ui->i_buildDir->setAcceptMode(QFileDialog::AcceptSave); #else m_ui->i_buildDir->fileDialog()->setAcceptMode(QFileDialog::AcceptSave); #endif 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()); // 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.isFile() || !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; } bool buildDirChanged = false; if(m_oldBuildDir != buildDir.toLocalFile()) { m_oldBuildDir = buildDir.toLocalFile(); buildDirChanged = true; } bool mesonHasChanged = m_ui->advanced->hasMesonChanged(); // Outside if to prevent lazy evaluation if (!m_ui->options->options() || mesonHasChanged || buildDirChanged) { if (status == MesonBuilder::MESON_CONFIGURED) { m_ui->options->repopulateFromBuildDir(m_project->path(), currentConfig())->start(); } else { m_ui->options->repopulateFromMesonFile(m_project->path(), advanced.meson)->start(); } } } 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.mesonArgs = advanced.args; buildDir.mesonBackend = advanced.backend; buildDir.mesonExecutable = advanced.meson; return buildDir; } QStringList MesonNewBuildDir::mesonArgs() const { auto options = m_ui->options->options(); if (!options) { return {}; } return options->getMesonArgs(); } bool MesonNewBuildDir::isConfigValid() const { return m_configIsValid; } diff --git a/plugins/meson/settings/mesonnewbuilddir.h b/plugins/meson/settings/mesonnewbuilddir.h index bf448bb334..be02cc045e 100644 --- a/plugins/meson/settings/mesonnewbuilddir.h +++ b/plugins/meson/settings/mesonnewbuilddir.h @@ -1,59 +1,60 @@ /* 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; QStringList mesonArgs() const; private Q_SLOTS: void resetFields(); void updated(); private: bool m_configIsValid = false; KDevelop::IProject* m_project = nullptr; Ui::MesonNewBuildDir* m_ui = nullptr; QString m_oldBuildDir; }; diff --git a/plugins/meson/settings/mesonoptionbaseview.cpp b/plugins/meson/settings/mesonoptionbaseview.cpp index 873c795220..92b3368856 100644 --- a/plugins/meson/settings/mesonoptionbaseview.cpp +++ b/plugins/meson/settings/mesonoptionbaseview.cpp @@ -1,283 +1,286 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptionbaseview.h" + #include "mesonlisteditor.h" #include "ui_mesonoptionbaseview.h" +#include + +#include + #include #include #include #include #include #include #include -#include -#include using namespace std; // Helper class for RAII signal blocking class SignalBlocker { public: SignalBlocker(QWidget* widget) : m_widget(widget) { if (m_widget) { m_widget->blockSignals(true); } } ~SignalBlocker() { if (m_widget) { m_widget->blockSignals(false); } } private: QWidget* m_widget = nullptr; }; MesonOptionBaseView::MesonOptionBaseView(MesonOptionPtr option, QWidget* parent) : QWidget(parent) { Q_ASSERT(option); m_ui = new Ui::MesonOptionBaseView; m_ui->setupUi(this); m_ui->l_name->setText(option->name() + QStringLiteral(":")); m_ui->l_name->setToolTip(option->description()); setToolTip(option->description()); } MesonOptionBaseView::~MesonOptionBaseView() { if (m_ui) { delete m_ui; } } // Base class functions int MesonOptionBaseView::nameWidth() { return m_ui->l_name->fontMetrics().boundingRect(m_ui->l_name->text()).width() + 25; } void MesonOptionBaseView::setMinNameWidth(int width) { m_ui->l_name->setMinimumWidth(width); } void MesonOptionBaseView::setInputWidget(QWidget* input) { QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(input->sizePolicy().hasHeightForWidth()); input->setSizePolicy(sizePolicy); input->setToolTip(option()->description()); m_ui->layout->insertWidget(1, input); updateInput(); setChanged(false); } void MesonOptionBaseView::reset() { option()->reset(); updateInput(); setChanged(false); } void MesonOptionBaseView::setChanged(bool changed) { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (changed) { m_ui->l_name->setStyleSheet(QStringLiteral("font-weight: bold")); m_ui->b_reset->setDisabled(false); role = KColorScheme::NeutralText; } else { m_ui->l_name->setStyleSheet(QString()); m_ui->b_reset->setDisabled(true); role = KColorScheme::NormalText; } QPalette pal = m_ui->l_name->palette(); pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); m_ui->l_name->setPalette(pal); emit configChanged(); } // Derived class constructors MesonOptionArrayView::MesonOptionArrayView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QPushButton(this); connect(m_input, &QPushButton::clicked, this, [this]() { MesonListEditor editor(m_option->rawValue(), this); if (editor.exec() == QDialog::Accepted) { m_option->setValue(editor.content()); m_input->setText(m_option->value()); setChanged(m_option->isUpdated()); } }); setInputWidget(m_input); } MesonOptionBoolView::MesonOptionBoolView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QCheckBox(this); connect(m_input, &QCheckBox::stateChanged, this, &MesonOptionBoolView::updated); setInputWidget(m_input); } MesonOptionComboView::MesonOptionComboView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QComboBox(this); m_input->clear(); m_input->addItems(m_option->choices()); m_input->setEditable(false); connect(m_input, QOverload::of(&QComboBox::currentIndexChanged), this, &MesonOptionComboView::updated); setInputWidget(m_input); } MesonOptionIntegerView::MesonOptionIntegerView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QSpinBox(this); m_input->setMinimum(INT32_MIN); m_input->setMaximum(INT32_MAX); connect(m_input, QOverload::of(&QSpinBox::valueChanged), this, &MesonOptionIntegerView::updated); setInputWidget(m_input); } MesonOptionStringView::MesonOptionStringView(MesonOptionPtr option, QWidget* parent) : MesonOptionBaseView(option, parent) , m_option(dynamic_pointer_cast(option)) { Q_ASSERT(m_option); m_input = new QLineEdit(this); connect(m_input, &QLineEdit::textChanged, this, &MesonOptionStringView::updated); setInputWidget(m_input); } // Option getters MesonOptionBase* MesonOptionArrayView::option() { return m_option.get(); } MesonOptionBase* MesonOptionBoolView::option() { return m_option.get(); } MesonOptionBase* MesonOptionComboView::option() { return m_option.get(); } MesonOptionBase* MesonOptionIntegerView::option() { return m_option.get(); } MesonOptionBase* MesonOptionStringView::option() { return m_option.get(); } // Updaters for the input widget void MesonOptionArrayView::updateInput() { SignalBlocker block(m_input); m_input->setText(m_option->value()); } void MesonOptionBoolView::updateInput() { SignalBlocker block(m_input); m_input->setCheckState(m_option->rawValue() ? Qt::Checked : Qt::Unchecked); } void MesonOptionComboView::updateInput() { SignalBlocker block(m_input); m_input->setCurrentText(m_option->rawValue()); } void MesonOptionIntegerView::updateInput() { SignalBlocker block(m_input); m_input->setValue(m_option->rawValue()); } void MesonOptionStringView::updateInput() { SignalBlocker block(m_input); m_input->setText(m_option->rawValue()); } // Slots to update the option value void MesonOptionBoolView::updated() { m_option->setValue(m_input->isChecked()); setChanged(m_option->isUpdated()); } void MesonOptionComboView::updated() { m_option->setValue(m_input->currentText()); setChanged(m_option->isUpdated()); } void MesonOptionIntegerView::updated() { m_option->setValue(m_input->value()); setChanged(m_option->isUpdated()); } void MesonOptionStringView::updated() { m_option->setValue(m_input->text()); setChanged(m_option->isUpdated()); } diff --git a/plugins/meson/settings/mesonoptionbaseview.h b/plugins/meson/settings/mesonoptionbaseview.h index bc37ef7703..f7d6037a07 100644 --- a/plugins/meson/settings/mesonoptionbaseview.h +++ b/plugins/meson/settings/mesonoptionbaseview.h @@ -1,150 +1,151 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptions.h" + #include namespace Ui { class MesonOptionBaseView; } class QCheckBox; class QComboBox; class QLineEdit; class QPushButton; class QSpinBox; class MesonOptionBaseView : public QWidget { Q_OBJECT public: explicit MesonOptionBaseView(MesonOptionPtr option, QWidget* parent); virtual ~MesonOptionBaseView(); int nameWidth(); void setMinNameWidth(int width); virtual MesonOptionBase* option() = 0; virtual void updateInput() = 0; void setInputWidget(QWidget* input); protected: void setChanged(bool changed); public Q_SLOTS: void reset(); Q_SIGNALS: void configChanged(); private: Ui::MesonOptionBaseView* m_ui = nullptr; }; class MesonOptionArrayView : public MesonOptionBaseView { Q_OBJECT public: MesonOptionArrayView(MesonOptionPtr option, QWidget* parent); MesonOptionBase* option() override; void updateInput() override; private: std::shared_ptr m_option; QPushButton* m_input = nullptr; }; class MesonOptionBoolView : public MesonOptionBaseView { Q_OBJECT public: MesonOptionBoolView(MesonOptionPtr option, QWidget* parent); MesonOptionBase* option() override; void updateInput() override; public Q_SLOTS: void updated(); private: std::shared_ptr m_option; QCheckBox* m_input = nullptr; }; class MesonOptionComboView : public MesonOptionBaseView { Q_OBJECT public: MesonOptionComboView(MesonOptionPtr option, QWidget* parent); MesonOptionBase* option() override; void updateInput() override; public Q_SLOTS: void updated(); private: std::shared_ptr m_option; QComboBox* m_input = nullptr; }; class MesonOptionIntegerView : public MesonOptionBaseView { Q_OBJECT public: MesonOptionIntegerView(MesonOptionPtr option, QWidget* parent); MesonOptionBase* option() override; void updateInput() override; public Q_SLOTS: void updated(); private: std::shared_ptr m_option; QSpinBox* m_input = nullptr; }; class MesonOptionStringView : public MesonOptionBaseView { Q_OBJECT public: MesonOptionStringView(MesonOptionPtr option, QWidget* parent); MesonOptionBase* option() override; void updateInput() override; public Q_SLOTS: void updated(); private: std::shared_ptr m_option; QLineEdit* m_input = nullptr; }; diff --git a/plugins/meson/settings/mesonoptionsview.cpp b/plugins/meson/settings/mesonoptionsview.cpp index 7f723bb8c9..11e99bbeb7 100644 --- a/plugins/meson/settings/mesonoptionsview.cpp +++ b/plugins/meson/settings/mesonoptionsview.cpp @@ -1,165 +1,168 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptionsview.h" + #include "mesonconfig.h" #include "mesonintrospectjob.h" #include "mesonoptionbaseview.h" #include "ui_mesonoptionsview.h" -#include #include + #include +#include + using namespace std; MesonOptionsView::MesonOptionsView(QWidget* parent) : QWidget(parent) { m_ui = new Ui::MesonOptionsView; m_ui->setupUi(this); setDisabled(true); } MesonOptionsView::~MesonOptionsView() { m_optViews.clear(); if (m_ui) { delete m_ui; } } void MesonOptionsView::clear() { setDisabled(true); m_optViews.clear(); } void MesonOptionsView::resetAll() { for (auto& i : m_optViews) { i->reset(); } } KJob* MesonOptionsView::repopulateFromBuildDir(KDevelop::Path projectPath, Meson::BuildDir const& buildDir) { return repopulate(new MesonIntrospectJob(projectPath, buildDir, { MesonIntrospectJob::BUILDOPTIONS }, MesonIntrospectJob::BUILD_DIR, this)); } KJob* MesonOptionsView::repopulateFromMesonFile(KDevelop::Path projectPath, KDevelop::Path mesonExe) { return repopulate(new MesonIntrospectJob(projectPath, mesonExe, { MesonIntrospectJob::BUILDOPTIONS }, this)); } KJob* MesonOptionsView::repopulate(MesonIntrospectJob* introJob) { setDisabled(true); connect(introJob, &KJob::result, this, [this, introJob]() { m_optViews.clear(); m_options = introJob->buildOptions(); if (!m_options) { qCWarning(KDEV_Meson) << "Failed to get introspection data"; return; } for (auto i : m_options->options()) { OPT_VIEW_PTR opt = nullptr; switch (i->type()) { case MesonOptionBase::ARRAY: opt = make_shared(i, m_ui->tabWidget); break; case MesonOptionBase::BOOLEAN: opt = make_shared(i, m_ui->tabWidget); break; case MesonOptionBase::COMBO: opt = make_shared(i, m_ui->tabWidget); break; case MesonOptionBase::INTEGER: opt = make_shared(i, m_ui->tabWidget); break; case MesonOptionBase::STRING: opt = make_shared(i, m_ui->tabWidget); break; } if (!opt) { qCWarning(KDEV_Meson) << "Unhandled option type " << i->type(); continue; } QVBoxLayout* layout = nullptr; switch (i->section()) { case MesonOptionBase::CORE: layout = m_ui->l_core; break; case MesonOptionBase::BACKEND: layout = m_ui->l_backend; break; case MesonOptionBase::BASE: layout = m_ui->l_base; break; case MesonOptionBase::COMPILER: layout = m_ui->l_compiler; break; case MesonOptionBase::DIRECTORY: layout = m_ui->l_directory; break; case MesonOptionBase::USER: layout = m_ui->l_user; break; case MesonOptionBase::TEST: layout = m_ui->l_test; break; } if (!layout) { qCWarning(KDEV_Meson) << "Unknown section " << i->section(); } connect(opt.get(), &MesonOptionBaseView::configChanged, this, &MesonOptionsView::emitChanged); // Insert at count() - 1 to keep the stretch at the bottom layout->insertWidget(layout->count() - 1, opt.get()); m_optViews << opt; } auto maxEl = max_element(begin(m_optViews), end(m_optViews), [](auto a, auto b) { return a->nameWidth() < b->nameWidth(); }); int maxWidth = (**maxEl).nameWidth(); for_each(begin(m_optViews), end(m_optViews), [maxWidth](auto a) { a->setMinNameWidth(maxWidth); }); setDisabled(false); }); return introJob; } void MesonOptionsView::emitChanged() { emit configChanged(); } MesonOptsPtr MesonOptionsView::options() { return m_options; } diff --git a/plugins/meson/settings/mesonoptionsview.h b/plugins/meson/settings/mesonoptionsview.h index 0978fe905a..76270a2f2d 100644 --- a/plugins/meson/settings/mesonoptionsview.h +++ b/plugins/meson/settings/mesonoptionsview.h @@ -1,76 +1,79 @@ /* This file is part of KDevelop Copyright 2019 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 "mesonoptions.h" + +#include + #include #include + #include -#include namespace Ui { class MesonOptionsView; } namespace KDevelop { class IProject; } namespace Meson { struct BuildDir; } class MesonOptionBaseView; class MesonIntrospectJob; class KJob; using OPT_VIEW_PTR = std::shared_ptr; class MesonOptionsView : public QWidget { Q_OBJECT public: explicit MesonOptionsView(QWidget* parent = nullptr); virtual ~MesonOptionsView(); void clear(); void resetAll(); KJob* repopulateFromBuildDir(KDevelop::Path projectPath, Meson::BuildDir const& buildDir); KJob* repopulateFromMesonFile(KDevelop::Path projectPath, KDevelop::Path mesonExe); MesonOptsPtr options(); public Q_SLOTS: void emitChanged(); Q_SIGNALS: void configChanged(); private: Ui::MesonOptionsView* m_ui = nullptr; QVector m_optViews; MesonOptsPtr m_options; KJob* repopulate(MesonIntrospectJob* introJob); };