diff --git a/plugins/meson/mesonintrospectjob.cpp b/plugins/meson/mesonintrospectjob.cpp index 59f4cc4822..c8a38a7656 100644 --- a/plugins/meson/mesonintrospectjob.cpp +++ b/plugins/meson/mesonintrospectjob.cpp @@ -1,271 +1,277 @@ /* 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 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) + , m_mode(mode), + m_project(project) { 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, +MesonIntrospectJob::MesonIntrospectJob(KDevelop::IProject* project, KDevelop::Path meson, QVector types, QObject* parent) : KJob(parent) , m_types(types) , m_mode(MESON_FILE) - , m_projectPath(projectPath) + , m_project(project) { + Q_ASSERT(project); + // 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; + m_projectPath = project->path(); connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); } -MesonIntrospectJob::MesonIntrospectJob(KDevelop::Path projectPath, Meson::BuildDir buildDir, +MesonIntrospectJob::MesonIntrospectJob(KDevelop::IProject* project, Meson::BuildDir buildDir, QVector types, MesonIntrospectJob::Mode mode, QObject* parent) : KJob(parent) , m_types(types) , m_mode(mode) , m_buildDir(buildDir) - , m_projectPath(projectPath) + , m_project(project) { + Q_ASSERT(project); + m_projectPath = project->path(); 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()); } auto testsJSON = rawData[QStringLiteral("tests")]; if (testsJSON.isArray()) { - m_res_tests = std::make_shared(testsJSON.toArray(), nullptr); + m_res_tests = std::make_shared(testsJSON.toArray(), m_project); if (m_res_options) { qCDebug(KDEV_Meson) << "MINTRO: Imported " << m_res_tests->testSuites().size() << " test suites"; } else { qCWarning(KDEV_Meson) << "MINTRO: Failed to parse tests"; } } 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 756d996ac2..9737667b79 100644 --- a/plugins/meson/mesonintrospectjob.h +++ b/plugins/meson/mesonintrospectjob.h @@ -1,78 +1,80 @@ /* 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 "mesontests.h" #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, + explicit MesonIntrospectJob(KDevelop::IProject* project, KDevelop::Path meson, QVector types, + QObject* parent); + explicit MesonIntrospectJob(KDevelop::IProject* project, Meson::BuildDir buildDir, QVector types, Mode mode, QObject* parent); void start() override; bool doKill() override; QString getTypeString(Type type) const; MesonOptsPtr buildOptions(); MesonTargetsPtr targets(); MesonTestSuitesPtr tests(); 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; + KDevelop::IProject* m_project = nullptr; // The results MesonOptsPtr m_res_options = nullptr; MesonTargetsPtr m_res_targets = nullptr; MesonTestSuitesPtr m_res_tests = nullptr; }; diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index 416e18a577..bd33e99356 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,335 +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 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 }, + auto introJob = new MesonIntrospectJob(project, 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/settings/mesonconfigpage.cpp b/plugins/meson/settings/mesonconfigpage.cpp index a49429d189..dc0330866c 100644 --- a/plugins/meson/settings/mesonconfigpage.cpp +++ b/plugins/meson/settings/mesonconfigpage.cpp @@ -1,356 +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 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); + joblist << m_ui->options->repopulateFromBuildDir(m_project, 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(); + m_ui->options->repopulateFromBuildDir(m_project, 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/mesonnewbuilddir.cpp b/plugins/meson/settings/mesonnewbuilddir.cpp index f46f5687c8..174f781f49 100644 --- a/plugins/meson/settings/mesonnewbuilddir.cpp +++ b/plugins/meson/settings/mesonnewbuilddir.cpp @@ -1,232 +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 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(); + m_ui->options->repopulateFromBuildDir(m_project, currentConfig())->start(); } else { - m_ui->options->repopulateFromMesonFile(m_project->path(), advanced.meson)->start(); + m_ui->options->repopulateFromMesonFile(m_project, 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/mesonoptionsview.cpp b/plugins/meson/settings/mesonoptionsview.cpp index 11e99bbeb7..583bd6dabc 100644 --- a/plugins/meson/settings/mesonoptionsview.cpp +++ b/plugins/meson/settings/mesonoptionsview.cpp @@ -1,168 +1,167 @@ /* 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 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) +KJob* MesonOptionsView::repopulateFromBuildDir(KDevelop::IProject* project, Meson::BuildDir const& buildDir) { - return repopulate(new MesonIntrospectJob(projectPath, buildDir, { MesonIntrospectJob::BUILDOPTIONS }, + return repopulate(new MesonIntrospectJob(project, buildDir, { MesonIntrospectJob::BUILDOPTIONS }, MesonIntrospectJob::BUILD_DIR, this)); } -KJob* MesonOptionsView::repopulateFromMesonFile(KDevelop::Path projectPath, KDevelop::Path mesonExe) +KJob* MesonOptionsView::repopulateFromMesonFile(KDevelop::IProject* project, KDevelop::Path mesonExe) { - return repopulate(new MesonIntrospectJob(projectPath, mesonExe, { MesonIntrospectJob::BUILDOPTIONS }, this)); + return repopulate(new MesonIntrospectJob(project, 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 76270a2f2d..407876676a 100644 --- a/plugins/meson/settings/mesonoptionsview.h +++ b/plugins/meson/settings/mesonoptionsview.h @@ -1,79 +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 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); + KJob* repopulateFromBuildDir(KDevelop::IProject *project, Meson::BuildDir const& buildDir); + KJob* repopulateFromMesonFile(KDevelop::IProject *project, 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); };