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"