File indexing completed on 2024-04-28 04:38:39

0001 /*
0002     SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "nativeappjob.h"
0008 
0009 #include <QAbstractButton>
0010 #include <QFileInfo>
0011 #include <QMessageBox>
0012 #include <QPointer>
0013 #include <QCheckBox>
0014 
0015 #include <KConfigGroup>
0016 #include <KLocalizedString>
0017 #include <KShell>
0018 #include <KSharedConfig>
0019 
0020 #include <interfaces/ilaunchconfiguration.h>
0021 #include <interfaces/iruncontroller.h>
0022 #include <outputview/outputmodel.h>
0023 #include <util/environmentprofilelist.h>
0024 
0025 #include <interfaces/icore.h>
0026 #include <interfaces/iplugincontroller.h>
0027 #include <project/projectmodel.h>
0028 
0029 #include "executeplugin.h"
0030 #include "debug.h"
0031 
0032 using namespace KDevelop;
0033 
0034 NativeAppJob::NativeAppJob(QObject* parent, KDevelop::ILaunchConfiguration* cfg)
0035     : KDevelop::OutputExecuteJob( parent )
0036     , m_name(cfg->name())
0037 {
0038     {
0039         auto cfgGroup = cfg->config();
0040         if (cfgGroup.readEntry(ExecutePlugin::isExecutableEntry, false)) {
0041             m_name = cfgGroup.readEntry(ExecutePlugin::executableEntry, cfg->name()).section(QLatin1Char('/'), -1);
0042         }
0043         if (!cfgGroup.readEntry<bool>(ExecutePlugin::configuredByCTest, false)) {
0044             m_killBeforeExecutingAgain = cfgGroup.readEntry<int>(ExecutePlugin::killBeforeExecutingAgain, askIfRunning);
0045         }
0046     }
0047     setCapabilities(Killable);
0048 
0049     auto* iface = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))->extension<IExecutePlugin>();
0050     Q_ASSERT(iface);
0051 
0052     const KDevelop::EnvironmentProfileList environmentProfiles(KSharedConfig::openConfig());
0053     QString envProfileName = iface->environmentProfileName(cfg);
0054 
0055     QString err;
0056     QUrl executable = iface->executable( cfg, err );
0057 
0058     if( !err.isEmpty() )
0059     {
0060         setError( -1 );
0061         setErrorText( err );
0062         return;
0063     }
0064 
0065     if (envProfileName.isEmpty()) {
0066         qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << i18n("No environment profile specified, looks like a broken "
0067                        "configuration, please check run configuration '%1'. "
0068                        "Using default environment profile.", cfg->name() );
0069         envProfileName = environmentProfiles.defaultProfileName();
0070     }
0071     setEnvironmentProfile(envProfileName);
0072 
0073     QStringList arguments = iface->arguments( cfg, err );
0074     if( !err.isEmpty() )
0075     {
0076         setError( -2 );
0077         setErrorText( err );
0078     }
0079 
0080     if( error() != 0 )
0081     {
0082         qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << "oops, problem" << errorText();
0083         return;
0084     }
0085 
0086     setStandardToolView(KDevelop::IOutputView::RunView);
0087     setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll);
0088     setFilteringStrategy(OutputModel::NativeAppErrorFilter);
0089     setProperties(DisplayStdout | DisplayStderr);
0090 
0091     // Now setup the process parameters
0092 
0093     QUrl wc = iface->workingDirectory( cfg );
0094     if( !wc.isValid() || wc.isEmpty() ) {
0095         wc = QUrl::fromLocalFile( QFileInfo( executable.toLocalFile() ).absolutePath() );
0096     }
0097     setWorkingDirectory( wc );
0098 
0099     qCDebug(PLUGIN_EXECUTE) << "setting app:" << executable << arguments;
0100 
0101     if (iface->useTerminal(cfg)) {
0102         QString terminalCommand = iface->terminal(cfg);
0103         terminalCommand.replace(QLatin1String("%exe"), KShell::quoteArg( executable.toLocalFile()) );
0104         terminalCommand.replace(QLatin1String("%workdir"), KShell::quoteArg( wc.toLocalFile()) );
0105         QStringList args = KShell::splitArgs(terminalCommand);
0106         args.append( arguments );
0107         *this << args;
0108     } else {
0109         *this << executable.toLocalFile();
0110         *this << arguments;
0111     }
0112 
0113     setJobName(m_name);
0114 }
0115 
0116 NativeAppJob* findNativeJob(KJob* j)
0117 {
0118     auto* job = qobject_cast<NativeAppJob*>(j);
0119     if (!job) {
0120         const QList<NativeAppJob*> jobs = j->findChildren<NativeAppJob*>();
0121         if (!jobs.isEmpty())
0122             job = jobs.first();
0123     }
0124     return job;
0125 }
0126 
0127 void NativeAppJob::start()
0128 {
0129     QVector<QPointer<NativeAppJob> > currentJobs;
0130     // collect running instances of the same type
0131     const auto& allCurrentJobs = ICore::self()->runController()->currentJobs();
0132     for (auto j : allCurrentJobs) {
0133         NativeAppJob* njob = findNativeJob(j);
0134         if (njob && njob != this && njob->m_name == m_name)
0135             currentJobs << njob;
0136     }
0137 
0138     if (!currentJobs.isEmpty()) {
0139         int oldJobAction = m_killBeforeExecutingAgain;
0140         if (oldJobAction == askIfRunning) {
0141             QMessageBox msgBox(QMessageBox::Question,
0142                         i18nc("@title:window", "Job Already Running"),
0143                         i18n("'%1' is already being executed.", m_name),
0144                         startAnother | killAllInstances | QMessageBox::Cancel /* aka askIfRunning */);
0145             msgBox.button(killAllInstances)->setText(i18nc("@action:button", "Kill All Instances"));
0146             msgBox.button(startAnother)->setText(i18nc("@action:button", "Start Another"));
0147             msgBox.setDefaultButton(QMessageBox::Cancel);
0148 
0149             QCheckBox* remember = new QCheckBox(i18nc("@option:check", "Remember choice"));
0150             msgBox.setCheckBox(remember);
0151 
0152             oldJobAction = msgBox.exec();
0153             if (remember->isChecked() && oldJobAction != QMessageBox::Cancel) {
0154                 Q_EMIT killBeforeExecutingAgainChanged(oldJobAction);
0155             }
0156         }
0157 
0158         switch (oldJobAction) {
0159             case startAnother:
0160                 break;
0161             case killAllInstances:
0162                 for (auto & job : currentJobs) {
0163                     if (job)
0164                         job->kill();
0165                 }
0166                 break;
0167             default: // cancel starting a new job
0168                 kill();
0169                 return;
0170         }
0171     }
0172 
0173     OutputExecuteJob::start();
0174 }
0175 
0176 #include "moc_nativeappjob.cpp"