diff --git a/plugins/meson/mesonbuilder.cpp b/plugins/meson/mesonbuilder.cpp index e0cb6fc65c..3706a3e3a1 100644 --- a/plugins/meson/mesonbuilder.cpp +++ b/plugins/meson/mesonbuilder.cpp @@ -1,308 +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 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')); + QStringList lines = m_error.split(QLatin1Char('\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/mesonjob.cpp b/plugins/meson/mesonjob.cpp index 846102f8d4..f10e9f1e69 100644 --- a/plugins/meson/mesonjob.cpp +++ b/plugins/meson/mesonjob.cpp @@ -1,74 +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 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(' '))) { + for (auto i : buildDir.mesonArgs.split(QLatin1Char(' '))) { if (!i.isEmpty()) { *this << i; } } *this << buildDir.buildDir.toLocalFile(); } QUrl MesonJob::workingDirectory() const { return m_project->path().toUrl(); } diff --git a/plugins/meson/mesonmanager.cpp b/plugins/meson/mesonmanager.cpp index 10f0c1e583..1235db2ed7 100644 --- a/plugins/meson/mesonmanager.cpp +++ b/plugins/meson/mesonmanager.cpp @@ -1,495 +1,495 @@ /* 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 "debug.h" #include "mesonbuilder.h" #include "mesonconfig.h" #include "mintro/mesonintrospectjob.h" #include "mintro/mesontargets.h" #include "settings/mesonconfigpage.h" #include "settings/mesonnewbuilddir.h" #include "settings/mesonrewriterpage.h" #include #include #include #include #include #include #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();) // ******************************** // * Error job for failed imports * // ******************************** namespace mmanager_internal { class ErrorJob : public KJob { Q_OBJECT public: ErrorJob(QObject* parent, const QString& error) : KJob(parent) , m_error(error) { } void start() override { QMessageBox::critical(nullptr, i18n("Importing project failed"), m_error); setError(KJob::UserDefinedError + 1); // Indicate that there was an error setErrorText(m_error); emitResult(); } private: QString m_error; }; } using namespace mmanager_internal; // *********************************** // * 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); } bool MesonManager::reload(KDevelop::ProjectFolderItem* item) { // "Inspired" by CMakeManager::reload IProject* project = item->project(); if (!project->isReady()) { return false; } qCDebug(KDEV_Meson) << "reloading meson project" << project->name() << "; Path:" << item->path(); KJob* job = createImportJob(item); project->setReloadJob(job); ICore::self()->runController()->registerJob(job); if (item == project->projectItem()) { connect(job, &KJob::finished, this, [project](KJob* job) -> void { if (job->error()) { return; } KDevelop::ICore::self()->projectController()->projectConfigurationChanged(project); KDevelop::ICore::self()->projectController()->reparseProject(project); }); } return true; } // *************************** // * 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; } void MesonManager::onMesonInfoChanged(QString path, QString projectName) { qCDebug(KDEV_Meson) << "File" << path << "changed --> reparsing project"; IProject* foundProject = ICore::self()->projectController()->findProjectByName(projectName); if (!foundProject) { return; } KJob* job = createImportJob(foundProject->projectItem()); foundProject->setReloadJob(job); ICore::self()->runController()->registerJob(job); connect(job, &KJob::finished, this, [foundProject](KJob* job) -> void { if (job->error()) { return; } KDevelop::ICore::self()->projectController()->projectConfigurationChanged(foundProject); KDevelop::ICore::self()->projectController()->reparseProject(foundProject); }); } KJob* MesonManager::createImportJob(ProjectFolderItem* item) { IProject* project = item->project(); Q_ASSERT(project); qCDebug(KDEV_Meson) << "Importing project" << project->name(); auto buildDir = Meson::currentBuildDir(project); KJob* configureJob = nullptr; if (!buildDir.isValid()) { configureJob = newBuildDirectory(project, &buildDir); if (!configureJob) { QString error = i18n("Importing %1 failed because no build directory could be created.", project->name()); qCDebug(KDEV_Meson) << error; return new ErrorJob(this, error); } } auto introJob = new MesonIntrospectJob( project, buildDir, { MesonIntrospectJob::TARGETS, MesonIntrospectJob::TESTS, MesonIntrospectJob::PROJECTINFO }, MesonIntrospectJob::BUILD_DIR, this); KDirWatchPtr watcher = m_projectWatchers[project]; if (!watcher) { // Create a new watcher watcher = m_projectWatchers[project] = make_shared(nullptr); QString projectName = project->name(); connect(watcher.get(), &KDirWatch::dirty, this, [=](QString p) { onMesonInfoChanged(p, projectName); }); connect(watcher.get(), &KDirWatch::created, this, [=](QString p) { onMesonInfoChanged(p, projectName); }); } Path watchFile = buildDir.buildDir; watchFile.addPath(QStringLiteral("meson-info")); watchFile.addPath(QStringLiteral("meson-info.json")); if (!watcher->contains(watchFile.path())) { qCDebug(KDEV_Meson) << "Start watching file" << watchFile; watcher->addFile(watchFile.path()); } connect(introJob, &KJob::result, this, [this, introJob, item, project]() { auto targets = introJob->targets(); auto tests = introJob->tests(); if (!targets || !tests) { return; } // Remove old test suites before deleting them if (m_projectTestSuites[project]) { for (auto i : m_projectTestSuites[project]->testSuites()) { ICore::self()->testController()->removeTestSuite(i.get()); } } m_projectTargets[project] = targets; m_projectTestSuites[project] = tests; 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); // Add test suites for (auto& i : tests->testSuites()) { ICore::self()->testController()->addTestSuite(i.get()); } }); QList jobs; // Configure the project if necessary if (!configureJob && m_builder->evaluateBuildDirectory(buildDir.buildDir, buildDir.mesonBackend) != MesonBuilder::MESON_CONFIGURED) { configureJob = builder()->configure(project); } if (configureJob) { jobs << configureJob; } jobs << AbstractFileManagerPlugin::createImportJob(item); // generate the file system listing jobs << introJob; 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(' ')); + return src->extraArgs().join(QLatin1Char(' ')); } bool MesonManager::hasBuildInfo(KDevelop::ProjectBaseItem* item) const { auto src = sourceFromItem(item); if (!src) { return false; } return true; } KDevelop::Path MesonManager::compiler(KDevelop::ProjectTargetItem* item) const { const auto source = sourceFromItem(item); return source && !source->compiler().isEmpty() ? KDevelop::Path(source->compiler().constFirst()) : KDevelop::Path(); } // ******************** // * Custom functions * // ******************** KJob* MesonManager::newBuildDirectory(IProject* project, Meson::BuildDir* outBuildDir) { 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); if (outBuildDir) { *outBuildDir = buildDir; } 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) { switch (number) { case 0: return new MesonConfigPage(this, options.project, parent); case 1: return new MesonRewriterPage(this, options.project, parent); } return nullptr; } int MesonManager::perProjectConfigPages() const { return 2; } #include "mesonmanager.moc" diff --git a/plugins/meson/mintro/mesonintrospectjob.cpp b/plugins/meson/mintro/mesonintrospectjob.cpp index ab4b863660..c2e917fcd2 100644 --- a/plugins/meson/mintro/mesonintrospectjob.cpp +++ b/plugins/meson/mintro/mesonintrospectjob.cpp @@ -1,295 +1,295 @@ /* 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_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::IProject* project, KDevelop::Path meson, QVector types, QObject* parent) : KJob(parent) , m_types(types) , m_mode(MESON_FILE) , 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_projectPath = project->path(); m_buildDir.buildDir = m_projectPath; m_buildDir.mesonExecutable = meson; connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonIntrospectJob::finished); } 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_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('-')); + option.replace(QLatin1Char('_'), QLatin1Char('-')); 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); + return i18n("%1 returned %2", proc.program().join(QLatin1Char(' ')), 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(' '))); + proc.program().join(QLatin1Char(' '))); } 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 projectInfoJSON = rawData[QStringLiteral("projectinfo")]; if (projectInfoJSON.isObject()) { m_res_projectInfo = std::make_shared(projectInfoJSON.toObject()); if (!m_res_projectInfo) { qCWarning(KDEV_Meson) << "MINTRO: Failed to parse projectinfo"; } } 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(), 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; } MesonProjectInfoPtr MesonIntrospectJob::projectInfo() { return m_res_projectInfo; } MesonTargetsPtr MesonIntrospectJob::targets() { return m_res_targets; } MesonTestSuitesPtr MesonIntrospectJob::tests() { return m_res_tests; } diff --git a/plugins/meson/mintro/mesonoptions.cpp b/plugins/meson/mintro/mesonoptions.cpp index afaa490c4a..2f5dea273b 100644 --- a/plugins/meson/mintro/mesonoptions.cpp +++ b/plugins/meson/mintro/mesonoptions.cpp @@ -1,473 +1,473 @@ /* 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 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 const& name, QString const& description, MesonOptionBase::Section section) : m_name(name) , m_description(description) , m_section(section) { } MesonOptionArray::MesonOptionArray(QString const& name, QString const& description, MesonOptionBase::Section section, QStringList value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionBool::MesonOptionBool(QString const& name, QString const& description, MesonOptionBase::Section section, bool value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionCombo::MesonOptionCombo(QString const& name, QString const& description, MesonOptionBase::Section section, QString value, QStringList choices) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) , m_choices(choices) { } MesonOptionInteger::MesonOptionInteger(QString const& name, QString const& description, MesonOptionBase::Section section, int value) : MesonOptionBase(name, description, section) , m_value(value) , m_initialValue(value) { } MesonOptionString::MesonOptionString(QString const& name, QString const& 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 const& val) { m_value = val; } void MesonOptionBool::setValue(bool val) { m_value = val; } void MesonOptionCombo::setValue(QString const& val) { m_value = val; } void MesonOptionInteger::setValue(int val) { m_value = val; } void MesonOptionString::setValue(QString const& val) { m_value = val; } // Set value from string void MesonOptionArray::setFromString(QString const& value) { setValue({ value }); } void MesonOptionBool::setFromString(QString const& value) { - setValue(value.toLower() == QStringLiteral("true")); + setValue(value.toLower() == QLatin1String("true")); } void MesonOptionCombo::setFromString(QString const& value) { setValue(value); } void MesonOptionInteger::setFromString(QString const& value) { setValue(value.toInt()); } void MesonOptionString::setFromString(QString const& value) { setValue(value); } // 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; } } // Work around meson bug https://github.com/mesonbuild/meson/pull/5646 by // removing the first space and everything after that. QString sectionStr = sectionJV.toString(); int spacePos = sectionStr.indexOf(QLatin1Char(' ')); if (spacePos > 0) { sectionStr = sectionStr.left(spacePos); } auto sectionIter = STRING2SECTION.find(sectionStr); 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/mintro/mesontargets.cpp b/plugins/meson/mintro/mesontargets.cpp index 11f0a8e38e..3a6d870fa1 100644 --- a/plugins/meson/mintro/mesontargets.cpp +++ b/plugins/meson/mintro/mesontargets.cpp @@ -1,258 +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 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('=')); + int equalPos = define.indexOf(QLatin1Char('=')); 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/rewriter/mesonkwargsinfo.cpp b/plugins/meson/rewriter/mesonkwargsinfo.cpp index cfc6e5f8ed..bbfd3ef541 100644 --- a/plugins/meson/rewriter/mesonkwargsinfo.cpp +++ b/plugins/meson/rewriter/mesonkwargsinfo.cpp @@ -1,132 +1,132 @@ /* 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 "mesonkwargsinfo.h" #include "debug.h" #include MesonKWARGSInfo::MesonKWARGSInfo(MesonKWARGSInfo::Function fn, QString const& id) : m_func(fn) , m_id(id) { } QJsonObject MesonKWARGSInfo::command() { QJsonObject res; auto func2str = [](Function fn) -> QString { switch (fn) { case PROJECT: return QStringLiteral("project"); case TARGET: return QStringLiteral("target"); case DEPENDENCY: return QStringLiteral("dependency"); } return QStringLiteral("ERROR"); }; res[QStringLiteral("type")] = QStringLiteral("kwargs"); res[QStringLiteral("function")] = func2str(m_func); res[QStringLiteral("id")] = m_id; res[QStringLiteral("operation")] = QStringLiteral("info"); - m_infoID = func2str(m_func) + QStringLiteral("#") + m_id; + m_infoID = func2str(m_func) + QLatin1Char('#') + m_id; return res; } void MesonKWARGSInfo::parseResult(QJsonObject data) { if (!data[QStringLiteral("kwargs")].isObject()) { qCWarning(KDEV_Meson) << "REWRITR: Failed to parse rewriter result"; return; } QJsonObject kwargs = data[QStringLiteral("kwargs")].toObject(); if (!kwargs[m_infoID].isObject()) { qCWarning(KDEV_Meson) << "REWRITR: Failed to extract info data from object"; return; } m_result = kwargs[m_infoID].toObject(); } MesonKWARGSInfo::Function MesonKWARGSInfo::function() const { return m_func; } QString MesonKWARGSInfo::id() const { return m_id; } bool MesonKWARGSInfo::hasKWARG(QString const& kwarg) const { return m_result.contains(kwarg); } QJsonValue MesonKWARGSInfo::get(QString const& kwarg) const { if (!hasKWARG(kwarg)) { return QJsonValue(); } return m_result[kwarg]; } QString MesonKWARGSInfo::getString(QString const& kwarg) const { return get(kwarg).toString(); } QStringList MesonKWARGSInfo::getArray(QString const& kwarg) const { QStringList result; for (auto i : get(kwarg).toArray()) { result += i.toString(); } return result; } // Constructors MesonKWARGSProjectInfo::MesonKWARGSProjectInfo() : MesonKWARGSInfo(PROJECT, QStringLiteral("/")) { } MesonKWARGSTargetInfo::MesonKWARGSTargetInfo(QString const& id) : MesonKWARGSInfo(TARGET, id) { } MesonKWARGSDependencyInfo::MesonKWARGSDependencyInfo(QString const& id) : MesonKWARGSInfo(DEPENDENCY, id) { } // Destructors MesonKWARGSInfo::~MesonKWARGSInfo() {} MesonKWARGSProjectInfo::~MesonKWARGSProjectInfo() {} MesonKWARGSTargetInfo::~MesonKWARGSTargetInfo() {} MesonKWARGSDependencyInfo::~MesonKWARGSDependencyInfo() {} diff --git a/plugins/meson/rewriter/mesonrewriterjob.cpp b/plugins/meson/rewriter/mesonrewriterjob.cpp index 60e207880e..f2be50b54c 100644 --- a/plugins/meson/rewriter/mesonrewriterjob.cpp +++ b/plugins/meson/rewriter/mesonrewriterjob.cpp @@ -1,124 +1,124 @@ /* 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 "mesonrewriterjob.h" #include "mesonconfig.h" #include "interfaces/iproject.h" #include "util/path.h" #include #include #include #include #include #include #include "debug.h" using namespace KDevelop; MesonRewriterJob::MesonRewriterJob(IProject* project, QVector const& actions, QObject* parent) : KJob(parent) , m_project(project) , m_actions(actions) { connect(&m_futureWatcher, &QFutureWatcher::finished, this, &MesonRewriterJob::finished); } QString MesonRewriterJob::execute() { QJsonArray command; for (auto& i : m_actions) { command.append(i->command()); } QTemporaryFile tempFile; tempFile.setAutoRemove(false); if (!tempFile.open()) { return i18n("Failed to create a temporary file."); } QJsonDocument doc(command); tempFile.write(doc.toJson()); tempFile.flush(); Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); KProcess proc(this); proc.setWorkingDirectory(m_project->path().toLocalFile()); proc.setOutputChannelMode(KProcess::SeparateChannels); proc.setProgram(buildDir.mesonExecutable.toLocalFile()); proc << QStringLiteral("rewrite") << QStringLiteral("command") << tempFile.fileName(); int ret = proc.execute(); if (ret != 0) { - return i18n("%1 returned %2", proc.program().join(QChar::fromLatin1(' ')), ret); + return i18n("%1 returned %2", proc.program().join(QLatin1Char(' ')), ret); } auto rawData = proc.readAllStandardError(); if (rawData.isEmpty()) { return QString(); } QJsonParseError error; QJsonDocument result = QJsonDocument::fromJson(rawData, &error); if (error.error) { return i18n("JSON parser error: %1", error.errorString()); } if (!result.isObject()) { - return i18n("The rewriter output of '%1' is not an object", proc.program().join(QChar::fromLatin1(' '))); + return i18n("The rewriter output of '%1' is not an object", proc.program().join(QLatin1Char(' '))); } for (auto& i : m_actions) { i->parseResult(result.object()); } return QString(); } void MesonRewriterJob::finished() { QString result = m_futureWatcher.result(); if (!result.isEmpty()) { qCWarning(KDEV_Meson) << "REWRITER " << result; setError(true); setErrorText(result); emitResult(); return; } qCDebug(KDEV_Meson) << "REWRITER: Meson rewriter job finished"; emitResult(); } bool MesonRewriterJob::doKill() { if (m_futureWatcher.isRunning()) { m_futureWatcher.cancel(); } return true; } void MesonRewriterJob::start() { auto future = QtConcurrent::run(this, &MesonRewriterJob::execute); m_futureWatcher.setFuture(future); } diff --git a/plugins/meson/settings/mesonrewriterinput.cpp b/plugins/meson/settings/mesonrewriterinput.cpp index d6dd54f3f5..8d3c63bee7 100644 --- a/plugins/meson/settings/mesonrewriterinput.cpp +++ b/plugins/meson/settings/mesonrewriterinput.cpp @@ -1,213 +1,213 @@ /* 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 "mesonrewriterinput.h" #include "mesonoptionbaseview.h" #include "rewriter/mesonkwargsinfo.h" #include "rewriter/mesonkwargsmodify.h" #include "ui_mesonrewriterinput.h" #include "ui_mesonrewriteroptioncontainer.h" #include #include #include MesonRewriterInputBase::MesonRewriterInputBase(QString const& name, QString const& kwarg, QWidget* parent) : QWidget(parent) , m_name(name) , m_kwarg(kwarg) { m_ui = new Ui::MesonRewriterInputBase; m_ui->setupUi(this); - m_ui->l_name->setText(m_name + QStringLiteral(":")); + m_ui->l_name->setText(m_name + QLatin1Char(':')); connect(this, &MesonRewriterInputBase::configChanged, this, &MesonRewriterInputBase::updateUi); } MesonRewriterInputBase::~MesonRewriterInputBase() {} int MesonRewriterInputBase::nameWidth() { // Make the name a bit (by 25) wider than it actually is to create a margin. Maybe do // something smarter in the future (TODO) return m_ui->l_name->fontMetrics().boundingRect(m_ui->l_name->text()).width() + 25; } void MesonRewriterInputBase::setMinNameWidth(int width) { m_ui->l_name->setMinimumWidth(width); } void MesonRewriterInputBase::setInputWidget(QWidget* input) { QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(input->sizePolicy().hasHeightForWidth()); input->setSizePolicy(sizePolicy); m_ui->layout->insertWidget(1, input); updateUi(); } void MesonRewriterInputBase::updateUi() { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (hasChanged()) { m_ui->l_name->setStyleSheet(QStringLiteral("font-weight: bold")); m_ui->b_reset->setDisabled(false || !m_enabled); role = KColorScheme::NeutralText; } else { m_ui->l_name->setStyleSheet(QString()); m_ui->b_reset->setDisabled(true); role = KColorScheme::NormalText; } role = m_enabled ? role : KColorScheme::InactiveText; QPalette pal = m_ui->l_name->palette(); pal.setColor(QPalette::Foreground, scheme.foreground(role).color()); m_ui->l_name->setPalette(pal); m_ui->l_name->setDisabled(!m_enabled); inputWidget()->setDisabled(!m_enabled); m_ui->b_add->setHidden(m_enabled); m_ui->b_delete->setHidden(!m_enabled); } void MesonRewriterInputBase::reset() { doReset(); emit configChanged(); } void MesonRewriterInputBase::remove() { m_enabled = false; reset(); } void MesonRewriterInputBase::add() { m_enabled = true; reset(); } void MesonRewriterInputBase::resetFromAction(MesonKWARGSInfo* action) { resetValue(action->get(m_kwarg)); m_default_enabled = m_enabled = action->hasKWARG(m_kwarg); if (m_enabled) { add(); } else { remove(); } } void MesonRewriterInputBase::writeToAction(MesonKWARGSModify* action) { action->set(m_kwarg, value()); } bool MesonRewriterInputBase::hasChanged() const { return hasValueChanged() || (m_default_enabled != m_enabled); } bool MesonRewriterInputBase::isEnabled() const { return m_enabled; } // String input class MesonRewriterInputString::MesonRewriterInputString(QString const& name, QString const& kwarg, QWidget* parent) : MesonRewriterInputBase(name, kwarg, parent) { m_lineEdit = new QLineEdit(this); - connect(m_lineEdit, &QLineEdit::textChanged, this, [=]() { emit configChanged(); }); + connect(m_lineEdit, &QLineEdit::textChanged, this, [this]() { emit configChanged(); }); setInputWidget(m_lineEdit); } MesonRewriterInputString::~MesonRewriterInputString() {} MesonRewriterInputBase::Type MesonRewriterInputString::type() const { return STRING; } bool MesonRewriterInputString::hasValueChanged() const { return m_lineEdit->text() != m_initialValue; } QWidget* MesonRewriterInputString::inputWidget() { return m_lineEdit; } void MesonRewriterInputString::doReset() { m_lineEdit->setText(m_initialValue); } void MesonRewriterInputString::resetValue(QJsonValue const& val) { m_initialValue = val.toString(); } QJsonValue MesonRewriterInputString::value() { return QJsonValue(m_lineEdit->text()); } // Options container MesonRewriterOptionContainer::MesonRewriterOptionContainer(MesonOptViewPtr optView, QWidget* parent) : QWidget(parent) , m_optView(optView) { m_ui = new Ui::MesonRewriterOptionContainer; m_ui->setupUi(this); m_ui->h_layout->insertWidget(0, m_optView.get()); connect(optView.get(), &MesonOptionBaseView::configChanged, this, [this]() { emit configChanged(); }); } void MesonRewriterOptionContainer::deleteMe() { m_markedForDeletion = true; emit configChanged(); } bool MesonRewriterOptionContainer::shouldDelete() const { return m_markedForDeletion; } bool MesonRewriterOptionContainer::hasChanged() const { return m_optView->option()->isUpdated(); } MesonOptViewPtr MesonRewriterOptionContainer::view() { return m_optView; } diff --git a/plugins/meson/settings/mesonrewriterpage.cpp b/plugins/meson/settings/mesonrewriterpage.cpp index debccb012d..7a9860a1d2 100644 --- a/plugins/meson/settings/mesonrewriterpage.cpp +++ b/plugins/meson/settings/mesonrewriterpage.cpp @@ -1,462 +1,462 @@ /* 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 "mesonrewriterpage.h" #include "mesonconfig.h" #include "mesonmanager.h" #include "mesonoptionbaseview.h" #include "mesonrewriterinput.h" #include "mintro/mesonintrospectjob.h" #include "mintro/mesonprojectinfo.h" #include "rewriter/mesondefaultopts.h" #include "rewriter/mesonkwargsinfo.h" #include "rewriter/mesonkwargsmodify.h" #include "rewriter/mesonrewriterjob.h" #include "ui_mesonrewriterpage.h" #include #include #include #include #include #include #include #include using namespace KDevelop; using namespace std; class JobDeleter { public: explicit JobDeleter(QList jobs) : m_jobs(jobs) { } ~JobDeleter() { for (KJob* i : m_jobs) { delete i; } } private: QList m_jobs; }; MesonRewriterPage::MesonRewriterPage(IPlugin* plugin, IProject* project, QWidget* parent) : ConfigPage(plugin, nullptr, parent) , m_project(project) { Q_ASSERT(m_project); m_ui = new Ui::MesonRewriterPage; m_ui->setupUi(this); m_projectKwargs = constructPojectInputs(); // Initialize widgets for (auto* i : m_projectKwargs) { m_ui->c_project->addWidget(i); connect(i, &MesonRewriterInputBase::configChanged, this, &MesonRewriterPage::emitChanged); } recalculateLengths(); reset(); } #define STRING_INPUT(name, id) new MesonRewriterInputString(QStringLiteral(name), QStringLiteral(id), this) QVector MesonRewriterPage::constructPojectInputs() { return { STRING_INPUT("Version", "version"), STRING_INPUT("License", "license"), STRING_INPUT("Meson version", "meson_version"), STRING_INPUT("Subprojects directory", "subproject_dir"), }; } MesonOptContainerPtr MesonRewriterPage::constructDefaultOpt(QString const& name, QString const& value) { if (!m_opts) { return nullptr; } for (auto& i : m_opts->options()) { if (i->name() != name) { continue; } if (!value.isNull()) { i->setFromString(value); } auto optView = MesonOptionBaseView::fromOption(i, this); if (!optView) { continue; } auto opt = std::make_shared(optView, this); if (!opt) { continue; } connect(opt.get(), &MesonRewriterOptionContainer::configChanged, this, &MesonRewriterPage::emitChanged); return opt; } return nullptr; } void MesonRewriterPage::setWidgetsDisabled(bool disabled) { m_ui->c_tabs->setDisabled(disabled); } void MesonRewriterPage::recalculateLengths() { // Calculate the maximum name width to align all widgets vector lengths; int maxWidth = 50; lengths.reserve(m_projectKwargs.size() + m_defaultOpts.size()); auto input_op = [](MesonRewriterInputBase* x) -> int { return x->nameWidth(); }; auto optCont_op = [](MesonOptContainerPtr x) -> int { return x->view()->nameWidth(); }; transform(begin(m_projectKwargs), end(m_projectKwargs), back_inserter(lengths), input_op); transform(begin(m_defaultOpts), end(m_defaultOpts), back_inserter(lengths), optCont_op); maxWidth = accumulate(begin(lengths), end(lengths), maxWidth, [](int a, int b) -> int { return max(a, b); }); // Set widgets width for (auto* i : m_projectKwargs) { i->setMinNameWidth(maxWidth); } for (auto& i : m_defaultOpts) { i->view()->setMinNameWidth(maxWidth); } m_ui->l_dispProject->setMinimumWidth(maxWidth); } void MesonRewriterPage::checkStatus() { // Get the config build dir status auto setStatus = [this](QString const& msg, int color) -> void { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; switch (color) { case 0: role = KColorScheme::PositiveText; setDisabled(false); break; case 1: role = KColorScheme::NeutralText; setDisabled(true); break; case 2: default: role = KColorScheme::NegativeText; setDisabled(true); 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 (m_state) { case START: setStatus(i18n("Initializing GUI"), 1); break; case LOADING: setStatus(i18n("Loading project data..."), 1); break; case WRITING: setStatus(i18n("Writing project data..."), 1); break; case READY: setStatus(i18n("Initializing GUI"), 0); break; case ERROR: setStatus(i18n("Loading meson rewriter data failed"), 2); break; } // Remove old default options m_defaultOpts.erase(remove_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->shouldDelete(); }), end(m_defaultOpts)); KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; int numChanged = 0; numChanged += count_if(begin(m_projectKwargs), end(m_projectKwargs), [](auto* x) { return x->hasChanged(); }); numChanged += count_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->hasChanged(); }); 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 MesonRewriterPage::setStatus(MesonRewriterPage::State s) { m_state = s; checkStatus(); } void MesonRewriterPage::apply() { qCDebug(KDEV_Meson) << "REWRITER GUI: APPLY"; auto projectSet = make_shared(MesonKWARGSProjectModify::SET); auto projectDel = make_shared(MesonKWARGSProjectModify::DELETE); auto defOptsSet = make_shared(MesonRewriterDefaultOpts::SET); auto defOptsDel = make_shared(MesonRewriterDefaultOpts::DELETE); auto writer = [](MesonRewriterInputBase* widget, MesonKWARGSModifyPtr set, MesonKWARGSModifyPtr del) { if (!widget->hasChanged()) { return; } if (widget->isEnabled()) { widget->writeToAction(set.get()); } else { widget->writeToAction(del.get()); } }; for_each(begin(m_projectKwargs), end(m_projectKwargs), [&](auto* w) { writer(w, projectSet, projectDel); }); QStringList deletedOptions = m_initialDefaultOpts; for (auto &i : m_defaultOpts) { auto opt = i->view()->option(); // Detect deleted options by removing all current present options from the initial option list deletedOptions.removeAll(opt->name()); if (opt->isUpdated() || !m_initialDefaultOpts.contains(opt->name())) { defOptsSet->set(opt->name(), opt->value()); } } for (auto i : deletedOptions) { defOptsDel->set(i, QString()); } QVector actions = { projectSet, projectDel, defOptsSet, defOptsDel }; KJob* rewriterJob = new MesonRewriterJob(m_project, actions, this); // Reload the GUI once the data has been written connect(rewriterJob, &KJob::result, this, &MesonRewriterPage::reset); setStatus(WRITING); rewriterJob->start(); } void MesonRewriterPage::reset() { qCDebug(KDEV_Meson) << "REWRITER GUI: RESET"; Meson::BuildDir buildDir = Meson::currentBuildDir(m_project); if (!buildDir.isValid()) { setStatus(ERROR); return; } auto projectInfo = std::make_shared(); QVector actions = { projectInfo }; QVector types = { MesonIntrospectJob::PROJECTINFO, MesonIntrospectJob::BUILDOPTIONS }; MesonIntrospectJob::Mode mode = MesonIntrospectJob::MESON_FILE; auto introspectJob = new MesonIntrospectJob(m_project, buildDir, types, mode, this); auto rewriterJob = new MesonRewriterJob(m_project, actions, this); QList jobs = { introspectJob, rewriterJob }; // Don't automatically delete jobs beause they are used in the lambda below for (KJob* i : jobs) { i->setAutoDelete(false); } KJob* job = new ExecuteCompositeJob(this, jobs); connect(job, &KJob::result, this, [=]() -> void { JobDeleter deleter(jobs); // Make sure to free all jobs with RAII auto prInfo = introspectJob->projectInfo(); m_opts = introspectJob->buildOptions(); if (!prInfo || !m_opts) { setStatus(ERROR); return; } m_ui->l_project->setText(QStringLiteral("

") + prInfo->name() + QStringLiteral("

")); auto setter = [](MesonRewriterInputBase* w, MesonKWARGSInfoPtr i) { w->resetFromAction(i.get()); }; for_each(begin(m_projectKwargs), end(m_projectKwargs), [=](auto* x) { setter(x, projectInfo); }); // Updated the default options m_defaultOpts.clear(); m_initialDefaultOpts.clear(); if (projectInfo->hasKWARG(QStringLiteral("default_options"))) { auto rawValues = projectInfo->getArray(QStringLiteral("default_options")); auto options = m_opts->options(); for (auto i : rawValues) { - int idx = i.indexOf(QChar::fromLatin1('=')); + int idx = i.indexOf(QLatin1Char('=')); if (idx < 0) { continue; } QString name = i.left(idx); QString val = i.mid(idx + 1); auto opt = constructDefaultOpt(name, val); if (!opt) { continue; } m_defaultOpts += opt; m_initialDefaultOpts += name; m_ui->c_defOpts->addWidget(opt.get()); } } recalculateLengths(); setStatus(READY); return; }); setStatus(LOADING); job->start(); } void MesonRewriterPage::newOption() { // Sort by section QStringList core; QStringList backend; QStringList base; QStringList compiler; QStringList directory; QStringList user; QStringList test; for (auto& i : m_opts->options()) { switch (i->section()) { case MesonOptionBase::CORE: core += i->name(); break; case MesonOptionBase::BACKEND: backend += i->name(); break; case MesonOptionBase::BASE: base += i->name(); break; case MesonOptionBase::COMPILER: compiler += i->name(); break; case MesonOptionBase::DIRECTORY: directory += i->name(); break; case MesonOptionBase::USER: user += i->name(); break; case MesonOptionBase::TEST: test += i->name(); break; } } QStringList total = core + backend + base + compiler + directory + user + test; // Remove already existing options for (auto& i : m_defaultOpts) { total.removeAll(i->view()->option()->name()); } QInputDialog dialog(this); dialog.setOption(QInputDialog::UseListViewForComboBoxItems, true); dialog.setInputMode(QInputDialog::TextInput); dialog.setWindowTitle(i18n("Select meson option to add")); dialog.setLabelText(i18n("Select one new meson option to add")); dialog.setComboBoxItems(total); if (dialog.exec() != QDialog::Accepted) { return; } auto opt = constructDefaultOpt(dialog.textValue(), QString()); if (!opt) { return; } m_defaultOpts += opt; m_ui->c_defOpts->addWidget(opt.get()); recalculateLengths(); } void MesonRewriterPage::defaults() { reset(); } void MesonRewriterPage::emitChanged() { m_configChanged = true; checkStatus(); emit changed(); } QString MesonRewriterPage::name() const { return i18n("Project"); } QString MesonRewriterPage::fullName() const { return i18n("Meson project settings"); } QIcon MesonRewriterPage::icon() const { return QIcon::fromTheme(QStringLiteral("meson")); }