File indexing completed on 2024-05-19 04:41:21

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 "mesonnewbuilddir.h"
0008 
0009 #include "mesonbuilder.h"
0010 #include "mesonmanager.h"
0011 #include "ui_mesonnewbuilddir.h"
0012 #include <debug.h>
0013 
0014 #include <interfaces/icore.h>
0015 #include <interfaces/iproject.h>
0016 #include <interfaces/iruncontroller.h>
0017 #include <interfaces/iruntime.h>
0018 #include <interfaces/iruntimecontroller.h>
0019 #include <project/helper.h>
0020 
0021 #include <KColorScheme>
0022 
0023 #include <QDialogButtonBox>
0024 #include <QFileInfo>
0025 
0026 #include <algorithm>
0027 
0028 using namespace KDevelop;
0029 
0030 MesonNewBuildDir::MesonNewBuildDir(IProject* project, QWidget* parent)
0031     : QDialog(parent)
0032     , m_project(project)
0033 {
0034     Q_ASSERT(project); // Just in case
0035     auto* mgr = dynamic_cast<MesonManager*>(m_project->buildSystemManager());
0036     Q_ASSERT(mgr); // This dialog only works with the MesonManager
0037 
0038     setWindowTitle(
0039         i18nc("@title:window", "Configure a Build Directory - %1", ICore::self()->runtimeController()->currentRuntime()->name()));
0040 
0041     m_ui = new Ui::MesonNewBuildDir;
0042     m_ui->setupUi(this);
0043 
0044     m_ui->advanced->setSupportedBackends(mgr->supportedMesonBackends());
0045 
0046     connect(m_ui->b_buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* b) {
0047         if (m_ui->b_buttonBox->buttonRole(b) == QDialogButtonBox::ResetRole) {
0048             resetFields();
0049         }
0050     });
0051 
0052     m_ui->i_buildDir->setAcceptMode(QFileDialog::AcceptSave);
0053 
0054     resetFields();
0055 }
0056 
0057 MesonNewBuildDir::~MesonNewBuildDir()
0058 {
0059     delete m_ui;
0060 }
0061 
0062 void MesonNewBuildDir::resetFields()
0063 {
0064     Meson::MesonConfig cfg = Meson::getMesonConfig(m_project);
0065     Path projectPath = m_project->path();
0066     auto* mgr = dynamic_cast<MesonManager*>(m_project->buildSystemManager());
0067     Q_ASSERT(mgr); // This dialog only works with the MesonManager
0068 
0069     auto aConf = m_ui->advanced->getConfig();
0070 
0071     // Find a build dir that is not already configured
0072     Path buildDirPath = projectPath;
0073     buildDirPath.addPath(QStringLiteral("build"));
0074 
0075     auto checkInCfg = [](const Meson::MesonConfig& cfg, const Path& p) -> bool {
0076         for (const auto& i : cfg.buildDirs) {
0077             if (i.buildDir == p) {
0078                 return true;
0079             }
0080         }
0081         return false;
0082     };
0083 
0084     for (int i = 2; checkInCfg(cfg, buildDirPath); ++i) {
0085         buildDirPath = projectPath;
0086         buildDirPath.addPath(QStringLiteral("build%1").arg(i));
0087     }
0088 
0089     m_ui->i_buildDir->setUrl(buildDirPath.toUrl());
0090 
0091     // Extra args
0092     aConf.args.clear();
0093 
0094     // Backend
0095     aConf.backend = mgr->defaultMesonBackend();
0096 
0097     // Meson exe
0098     aConf.meson = mgr->findMeson();
0099 
0100     m_ui->advanced->setConfig(aConf);
0101     updated();
0102 }
0103 
0104 void MesonNewBuildDir::setStatus(const QString& str, bool validConfig)
0105 {
0106     m_configIsValid = validConfig;
0107 
0108     KColorScheme scheme(QPalette::Normal);
0109     KColorScheme::ForegroundRole role;
0110     if (validConfig) {
0111         role = KColorScheme::PositiveText;
0112     } else {
0113         role = KColorScheme::NegativeText;
0114     }
0115 
0116     QPalette pal = m_ui->l_statusMessage->palette();
0117     pal.setColor(QPalette::WindowText, scheme.foreground(role).color());
0118     m_ui->l_statusMessage->setPalette(pal);
0119     m_ui->l_statusMessage->setText(str);
0120 
0121     auto okButton = m_ui->b_buttonBox->button(QDialogButtonBox::Ok);
0122     okButton->setEnabled(m_configIsValid);
0123     if (m_configIsValid) {
0124         auto cancelButton = m_ui->b_buttonBox->button(QDialogButtonBox::Cancel);
0125         cancelButton->clearFocus();
0126     }
0127 }
0128 
0129 void MesonNewBuildDir::updated()
0130 {
0131     auto advanced = m_ui->advanced->getConfig();
0132     Path buildDir = Path(m_ui->i_buildDir->url());
0133     QFileInfo mesonExe(advanced.meson.toLocalFile());
0134 
0135     if (!mesonExe.exists() || !mesonExe.isExecutable() || !mesonExe.isFile()
0136         || !mesonExe.permission(QFileDevice::ReadUser | QFileDevice::ExeUser)) {
0137         setStatus(i18n("Specified meson executable does not exist"), false);
0138         return;
0139     }
0140 
0141     MesonBuilder::DirectoryStatus status = MesonBuilder::evaluateBuildDirectory(buildDir, advanced.backend);
0142     switch (status) {
0143     case MesonBuilder::CLEAN:
0144     case MesonBuilder::DOES_NOT_EXIST:
0145         setStatus(i18n("Creating new build directory"), true);
0146         break;
0147     case MesonBuilder::MESON_CONFIGURED:
0148         setStatus(i18n("Using an already configured build directory"), true);
0149         break;
0150     case MesonBuilder::MESON_FAILED_CONFIGURATION:
0151         setStatus(i18n("Using a broken meson build directory (this should be fine)"), true);
0152         break;
0153     case MesonBuilder::INVALID_BUILD_DIR:
0154         setStatus(i18n("Cannot use specified directory"), false);
0155         break;
0156     case MesonBuilder::DIR_NOT_EMPTY:
0157         setStatus(i18n("There are already files in the build directory"), false);
0158         break;
0159     case MesonBuilder::EMPTY_STRING:
0160         setStatus(i18n("The build directory field must not be empty"), false);
0161         break;
0162     case MesonBuilder::___UNDEFINED___:
0163         setStatus(i18n("You have reached unreachable code. This is a bug"), false);
0164         break;
0165     }
0166 
0167     bool buildDirChanged = false;
0168     if (m_oldBuildDir != buildDir.toLocalFile()) {
0169         m_oldBuildDir = buildDir.toLocalFile();
0170         buildDirChanged = true;
0171     }
0172 
0173     bool mesonHasChanged = m_ui->advanced->hasMesonChanged(); // Outside if to prevent lazy evaluation
0174     if (!m_ui->options->options() || mesonHasChanged || buildDirChanged) {
0175         if (status == MesonBuilder::MESON_CONFIGURED) {
0176             m_ui->options->repopulateFromBuildDir(m_project, currentConfig())->start();
0177         } else {
0178             m_ui->options->repopulateFromMesonFile(m_project, advanced.meson)->start();
0179         }
0180     }
0181 }
0182 
0183 Meson::BuildDir MesonNewBuildDir::currentConfig() const
0184 {
0185     Meson::BuildDir buildDir;
0186     if (!m_configIsValid) {
0187         qCDebug(KDEV_Meson) << "Cannot generate build dir config from invalid config";
0188         return buildDir;
0189     }
0190 
0191     auto advanced = m_ui->advanced->getConfig();
0192 
0193     buildDir.buildDir = Path(m_ui->i_buildDir->url());
0194     buildDir.mesonArgs = advanced.args;
0195     buildDir.mesonBackend = advanced.backend;
0196     buildDir.mesonExecutable = advanced.meson;
0197 
0198     return buildDir;
0199 }
0200 
0201 QStringList MesonNewBuildDir::mesonArgs() const
0202 {
0203     auto options = m_ui->options->options();
0204     if (!options) {
0205         return {};
0206     }
0207 
0208     return options->getMesonArgs();
0209 }
0210 
0211 bool MesonNewBuildDir::isConfigValid() const
0212 {
0213     return m_configIsValid;
0214 }
0215 
0216 #include "moc_mesonnewbuilddir.cpp"