File indexing completed on 2024-05-19 15:46:03

0001 /*
0002     SPDX-FileCopyrightText: 2018 Daniel Mensinger <daniel@mensinger-ka.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mesonconfigpage.h"
0008 
0009 #include "mesonbuilder.h"
0010 #include "mesonjob.h"
0011 #include "mesonmanager.h"
0012 #include "mesonnewbuilddir.h"
0013 #include "mintro/mesonintrospectjob.h"
0014 #include "ui_mesonconfigpage.h"
0015 #include <debug.h>
0016 
0017 #include <executecompositejob.h>
0018 #include <interfaces/iplugin.h>
0019 #include <interfaces/iproject.h>
0020 
0021 #include <KColorScheme>
0022 
0023 #include <QIcon>
0024 
0025 using namespace KDevelop;
0026 
0027 MesonConfigPage::MesonConfigPage(IPlugin* plugin, IProject* project, QWidget* parent)
0028     : ConfigPage(plugin, nullptr, parent)
0029     , m_project(project)
0030 {
0031     Q_ASSERT(project); // Catch errors early
0032     auto* mgr = dynamic_cast<MesonManager*>(m_project->buildSystemManager());
0033     Q_ASSERT(mgr); // This dialog only works with the MesonManager
0034 
0035     m_ui = new Ui::MesonConfigPage;
0036     m_ui->setupUi(this);
0037     m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends());
0038 
0039     m_config = Meson::getMesonConfig(m_project);
0040     if (m_config.buildDirs.isEmpty()) {
0041         m_config.currentIndex = -1;
0042         reset();
0043         return;
0044     } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) {
0045         m_config.currentIndex = 0;
0046     }
0047 
0048     QStringList buildPathList;
0049     for (auto& i : m_config.buildDirs) {
0050         buildPathList << i.buildDir.toLocalFile();
0051     }
0052 
0053     m_ui->i_buildDirs->blockSignals(true);
0054     m_ui->i_buildDirs->clear();
0055     m_ui->i_buildDirs->addItems(buildPathList);
0056     m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex);
0057     m_ui->i_buildDirs->blockSignals(false);
0058 
0059     reset();
0060 }
0061 
0062 void MesonConfigPage::writeConfig()
0063 {
0064     qCDebug(KDEV_Meson) << "Writing config to file";
0065     if (m_config.currentIndex >= 0) {
0066         m_config.buildDirs[m_config.currentIndex] = m_current;
0067     }
0068 
0069     if (m_config.buildDirs.isEmpty()) {
0070         m_config.currentIndex = -1;
0071     } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) {
0072         m_config.currentIndex = 0;
0073     }
0074     Meson::writeMesonConfig(m_project, m_config);
0075 }
0076 
0077 void MesonConfigPage::apply()
0078 {
0079     qCDebug(KDEV_Meson) << "Applying meson config for build dir " << m_current.buildDir;
0080     readUI();
0081     writeConfig();
0082 
0083     if (m_config.currentIndex >= 0 && m_configChanged) {
0084         QList<KJob*> joblist;
0085 
0086         auto options = m_ui->options->options();
0087         if (!options) {
0088             qCWarning(KDEV_Meson) << "Options is nullptr. Can not update meson config";
0089             return;
0090         }
0091 
0092         QStringList mesonArgs = options->getMesonArgs();
0093         if (mesonArgs.empty()) {
0094             qCDebug(KDEV_Meson) << "Config has not changed --> nothing has to be updated";
0095             return;
0096         }
0097 
0098         // Check if a configuration is required
0099         auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend);
0100         if (status != MesonBuilder::MESON_CONFIGURED) {
0101             joblist << new MesonJob(m_current, m_project, MesonJob::CONFIGURE, mesonArgs, nullptr);
0102         }
0103 
0104         joblist << new MesonJob(m_current, m_project, MesonJob::SET_CONFIG, mesonArgs, nullptr);
0105         joblist << m_ui->options->repopulateFromBuildDir(m_project, m_current);
0106         KJob* job = new ExecuteCompositeJob(nullptr, joblist);
0107         connect(job, &KJob::result, this, [this]() {
0108             setDisabled(false);
0109             updateUI();
0110         });
0111         setDisabled(true);
0112         m_configChanged = false;
0113         job->start();
0114     }
0115 }
0116 
0117 void MesonConfigPage::defaults()
0118 {
0119     qCDebug(KDEV_Meson) << "Restoring build dir " << m_current.buildDir << " to it's default values";
0120     auto* mgr = dynamic_cast<MesonManager*>(m_project->buildSystemManager());
0121     Q_ASSERT(mgr);
0122 
0123     m_current.mesonArgs.clear();
0124     m_current.mesonBackend = mgr->defaultMesonBackend();
0125     m_current.mesonExecutable = mgr->findMeson();
0126     m_ui->options->resetAll();
0127 
0128     updateUI();
0129 }
0130 
0131 void MesonConfigPage::reset()
0132 {
0133     if (m_config.buildDirs.isEmpty()) {
0134         m_config.currentIndex = -1;
0135         m_ui->i_buildDirs->clear();
0136         setWidgetsDisabled(true);
0137         m_ui->b_addDir->setDisabled(false); // Allow adding a new build dir when there are none
0138         return;
0139     } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) {
0140         m_config.currentIndex = 0;
0141         m_ui->i_buildDirs->blockSignals(true);
0142         m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex);
0143         m_ui->i_buildDirs->blockSignals(false);
0144     }
0145 
0146     setWidgetsDisabled(false);
0147     qCDebug(KDEV_Meson) << "Resetting changes for build dir " << m_current.buildDir;
0148 
0149     m_current = m_config.buildDirs[m_config.currentIndex];
0150     m_ui->options->repopulateFromBuildDir(m_project, m_current)->start();
0151     updateUI();
0152 }
0153 
0154 void MesonConfigPage::checkStatus()
0155 {
0156     // Get the config build dir status
0157     auto status = MesonBuilder::evaluateBuildDirectory(m_current.buildDir, m_current.mesonBackend);
0158     auto setStatus = [this](const QString& msg, int color) -> void {
0159         KColorScheme scheme(QPalette::Normal);
0160         KColorScheme::ForegroundRole role;
0161         switch (color) {
0162         case 0:
0163             role = KColorScheme::PositiveText;
0164             break;
0165         case 1:
0166             role = KColorScheme::NeutralText;
0167             break;
0168         case 2:
0169         default:
0170             role = KColorScheme::NegativeText;
0171             break;
0172         }
0173 
0174         QPalette pal = m_ui->l_status->palette();
0175         pal.setColor(QPalette::WindowText, scheme.foreground(role).color());
0176         m_ui->l_status->setPalette(pal);
0177         m_ui->l_status->setText(i18n("Status: %1", msg));
0178     };
0179 
0180     switch (status) {
0181     case MesonBuilder::DOES_NOT_EXIST:
0182         setStatus(i18n("The current build directory does not exist"), 1);
0183         break;
0184     case MesonBuilder::CLEAN:
0185         setStatus(i18n("The current build directory is empty"), 1);
0186         break;
0187     case MesonBuilder::MESON_CONFIGURED:
0188         setStatus(i18n("Build directory configured"), 0);
0189         break;
0190     case MesonBuilder::MESON_FAILED_CONFIGURATION:
0191         setStatus(i18n("This meson build directory is not fully configured"), 1);
0192         break;
0193     case MesonBuilder::INVALID_BUILD_DIR:
0194         setStatus(i18n("The current build directory is invalid"), 2);
0195         break;
0196     case MesonBuilder::DIR_NOT_EMPTY:
0197         setStatus(i18n("This directory does not seem to be a meson build directory"), 2);
0198         break;
0199     case MesonBuilder::EMPTY_STRING:
0200         setStatus(i18n("Invalid build directory configuration (empty build directory string)"), 2);
0201         break;
0202     case MesonBuilder::___UNDEFINED___:
0203         setStatus(i18n("Something went very wrong. This is a bug"), 2);
0204         break;
0205     }
0206 
0207     KColorScheme scheme(QPalette::Normal);
0208     KColorScheme::ForegroundRole role;
0209     int numChanged = 0;
0210     auto options = m_ui->options->options();
0211 
0212     if (options) {
0213         numChanged = options->numChanged();
0214     }
0215 
0216     if (numChanged == 0) {
0217         role = KColorScheme::NormalText;
0218         m_ui->l_changed->setText(i18n("No changes"));
0219     } else {
0220         role = KColorScheme::NeutralText;
0221         m_ui->l_changed->setText(i18np("%1 option changed", "%1 options changed", numChanged));
0222     }
0223 
0224     QPalette pal = m_ui->l_changed->palette();
0225     pal.setColor(QPalette::WindowText, scheme.foreground(role).color());
0226     m_ui->l_changed->setPalette(pal);
0227 }
0228 
0229 void MesonConfigPage::updateUI()
0230 {
0231     auto aConf = m_ui->advanced->getConfig();
0232     aConf.args = m_current.mesonArgs;
0233     aConf.backend = m_current.mesonBackend;
0234     aConf.meson = m_current.mesonExecutable;
0235     m_ui->advanced->setConfig(aConf);
0236 
0237     checkStatus();
0238 }
0239 
0240 void MesonConfigPage::readUI()
0241 {
0242     qCDebug(KDEV_Meson) << "Reading current build configuration from the UI " << m_current.buildDir.toLocalFile();
0243 
0244     auto aConf = m_ui->advanced->getConfig();
0245     m_current.mesonArgs = aConf.args;
0246     m_current.mesonBackend = aConf.backend;
0247     m_current.mesonExecutable = aConf.meson;
0248 }
0249 
0250 void MesonConfigPage::setWidgetsDisabled(bool disabled)
0251 {
0252     m_ui->advanced->setDisabled(disabled);
0253     m_ui->i_buildDirs->setDisabled(disabled);
0254     m_ui->b_addDir->setDisabled(disabled);
0255     m_ui->b_rmDir->setDisabled(disabled);
0256     m_ui->options->setDisabled(disabled);
0257 }
0258 
0259 void MesonConfigPage::addBuildDir()
0260 {
0261     qCDebug(KDEV_Meson) << "Adding build directory";
0262     auto* mgr = dynamic_cast<MesonManager*>(m_project->buildSystemManager());
0263     auto* bld = dynamic_cast<MesonBuilder*>(mgr->builder());
0264     Q_ASSERT(mgr);
0265     Q_ASSERT(bld);
0266     MesonNewBuildDir newBD(m_project);
0267 
0268     if (!newBD.exec() || !newBD.isConfigValid()) {
0269         qCDebug(KDEV_Meson) << "Failed to create a new build directory";
0270         return;
0271     }
0272 
0273     m_current = newBD.currentConfig();
0274     m_current.canonicalizePaths();
0275     m_config.currentIndex = m_config.addBuildDir(m_current);
0276     m_ui->i_buildDirs->blockSignals(true);
0277     m_ui->i_buildDirs->addItem(m_current.buildDir.toLocalFile());
0278     m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex);
0279     m_ui->i_buildDirs->blockSignals(false);
0280 
0281     setWidgetsDisabled(true);
0282     writeConfig();
0283     KJob* job = bld->configure(m_project, m_current, newBD.mesonArgs());
0284     connect(job, &KJob::result, this, [this]() { reset(); });
0285     job->start();
0286 }
0287 
0288 void MesonConfigPage::removeBuildDir()
0289 {
0290     qCDebug(KDEV_Meson) << "Removing current build directory";
0291     m_ui->i_buildDirs->blockSignals(true);
0292     m_ui->i_buildDirs->removeItem(m_config.currentIndex);
0293     m_config.removeBuildDir(m_config.currentIndex);
0294     if (m_config.buildDirs.isEmpty()) {
0295         m_config.currentIndex = -1;
0296     } else if (m_config.currentIndex < 0 || m_config.currentIndex >= m_config.buildDirs.size()) {
0297         m_config.currentIndex = 0;
0298     }
0299     m_ui->i_buildDirs->setCurrentIndex(m_config.currentIndex);
0300     m_ui->i_buildDirs->blockSignals(false);
0301     reset();
0302     writeConfig();
0303 }
0304 
0305 void MesonConfigPage::changeBuildDirIndex(int index)
0306 {
0307     if (index == m_config.currentIndex || m_config.buildDirs.isEmpty()) {
0308         return;
0309     }
0310 
0311     if (index < 0 || index >= m_config.buildDirs.size()) {
0312         qCWarning(KDEV_Meson) << "Invalid build dir index " << index;
0313         return;
0314     }
0315 
0316     qCDebug(KDEV_Meson) << "Changing build directory to index " << index;
0317 
0318     m_config.currentIndex = index;
0319     reset();
0320     writeConfig();
0321 }
0322 
0323 void MesonConfigPage::emitChanged()
0324 {
0325     m_configChanged = true;
0326     checkStatus();
0327     emit changed();
0328 }
0329 
0330 QString MesonConfigPage::name() const
0331 {
0332     return i18nc("@title:tab", "Meson");
0333 }
0334 
0335 QString MesonConfigPage::fullName() const
0336 {
0337     return i18nc("@title:tab", "Meson Project Configuration");
0338 }
0339 
0340 QIcon MesonConfigPage::icon() const
0341 {
0342     return QIcon::fromTheme(QStringLiteral("run-build"));
0343 }
0344 
0345 #include "moc_mesonconfigpage.cpp"