nativeappjob.cpp 6.5 KB
Newer Older
Andreas Pakulat's avatar
Andreas Pakulat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*  This file is part of KDevelop
    Copyright 2009 Andreas Pakulat <apaku@gmx.de>

    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 "nativeappjob.h"

22
#include <QAbstractButton>
23
#include <QFileInfo>
24
#include <QMessageBox>
25
#include <QPointer>
26
#include <QCheckBox>
Andreas Pakulat's avatar
Andreas Pakulat committed
27

28
#include <KConfigGroup>
29
#include <KLocalizedString>
30 31
#include <KShell>
#include <KSharedConfig>
Andreas Pakulat's avatar
Andreas Pakulat committed
32 33

#include <interfaces/ilaunchconfiguration.h>
34
#include <interfaces/iruncontroller.h>
Andreas Pakulat's avatar
Andreas Pakulat committed
35
#include <outputview/outputmodel.h>
36
#include <util/environmentprofilelist.h>
Andreas Pakulat's avatar
Andreas Pakulat committed
37 38

#include <interfaces/icore.h>
39
#include <interfaces/iplugincontroller.h>
Andreas Pakulat's avatar
Andreas Pakulat committed
40 41
#include <project/projectmodel.h>

42
#include "executeplugin.h"
Dāvis Mosāns's avatar
Dāvis Mosāns committed
43
#include "debug.h"
44

45 46
using namespace KDevelop;

Dāvis Mosāns's avatar
Dāvis Mosāns committed
47
NativeAppJob::NativeAppJob(QObject* parent, KDevelop::ILaunchConfiguration* cfg)
48
    : KDevelop::OutputExecuteJob( parent )
49
    , m_name(cfg->name())
Andreas Pakulat's avatar
Andreas Pakulat committed
50
{
51 52 53
    {
        auto cfgGroup = cfg->config();
        if (cfgGroup.readEntry(ExecutePlugin::isExecutableEntry, false)) {
54
            m_name = cfgGroup.readEntry(ExecutePlugin::executableEntry, cfg->name()).section(QLatin1Char('/'), -1);
55
        }
56
        m_killBeforeExecutingAgain = cfgGroup.readEntry<int>(ExecutePlugin::killBeforeExecutingAgain, QMessageBox::Cancel);
57
    }
Andreas Pakulat's avatar
Andreas Pakulat committed
58
    setCapabilities(Killable);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
59

60
    auto* iface = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))->extension<IExecutePlugin>();
61
    Q_ASSERT(iface);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
62

63 64
    const KDevelop::EnvironmentProfileList environmentProfiles(KSharedConfig::openConfig());
    QString envProfileName = iface->environmentProfileName(cfg);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
65

66
    QString err;
Milian Wolff's avatar
Milian Wolff committed
67
    QUrl executable = iface->executable( cfg, err );
Dāvis Mosāns's avatar
Dāvis Mosāns committed
68 69

    if( !err.isEmpty() )
Andreas Pakulat's avatar
Andreas Pakulat committed
70
    {
71 72 73
        setError( -1 );
        setErrorText( err );
        return;
Andreas Pakulat's avatar
Andreas Pakulat committed
74
    }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
75

76
    if (envProfileName.isEmpty()) {
77
        qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << i18n("No environment profile specified, looks like a broken "
Andreas Pakulat's avatar
Andreas Pakulat committed
78
                       "configuration, please check run configuration '%1'. "
79 80
                       "Using default environment profile.", cfg->name() );
        envProfileName = environmentProfiles.defaultProfileName();
Andreas Pakulat's avatar
Andreas Pakulat committed
81
    }
82
    setEnvironmentProfile(envProfileName);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
83

84
    QStringList arguments = iface->arguments( cfg, err );
Dāvis Mosāns's avatar
Dāvis Mosāns committed
85
    if( !err.isEmpty() )
Andreas Pakulat's avatar
Andreas Pakulat committed
86
    {
87 88
        setError( -2 );
        setErrorText( err );
Andreas Pakulat's avatar
Andreas Pakulat committed
89
    }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
90

Andreas Pakulat's avatar
Andreas Pakulat committed
91 92
    if( error() != 0 )
    {
93
        qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << "oops, problem" << errorText();
Andreas Pakulat's avatar
Andreas Pakulat committed
94 95
        return;
    }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
96

Andreas Pakulat's avatar
Andreas Pakulat committed
97 98
    setStandardToolView(KDevelop::IOutputView::RunView);
    setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll);
99 100
    setFilteringStrategy(OutputModel::NativeAppErrorFilter);
    setProperties(DisplayStdout | DisplayStderr);
Andreas Pakulat's avatar
Andreas Pakulat committed
101 102

    // Now setup the process parameters
Dāvis Mosāns's avatar
Dāvis Mosāns committed
103

Milian Wolff's avatar
Milian Wolff committed
104
    QUrl wc = iface->workingDirectory( cfg );
105
    if( !wc.isValid() || wc.isEmpty() ) {
Milian Wolff's avatar
Milian Wolff committed
106
        wc = QUrl::fromLocalFile( QFileInfo( executable.toLocalFile() ).absolutePath() );
107
    }
108
    setWorkingDirectory( wc );
Dāvis Mosāns's avatar
Dāvis Mosāns committed
109 110 111

    qCDebug(PLUGIN_EXECUTE) << "setting app:" << executable << arguments;

112
    if (iface->useTerminal(cfg)) {
113 114 115 116
        QString terminalCommand = iface->terminal(cfg);
        terminalCommand.replace(QLatin1String("%exe"), KShell::quoteArg( executable.toLocalFile()) );
        terminalCommand.replace(QLatin1String("%workdir"), KShell::quoteArg( wc.toLocalFile()) );
        QStringList args = KShell::splitArgs(terminalCommand);
117
        args.append( arguments );
118
        *this << args;
119
    } else {
120 121
        *this << executable.toLocalFile();
        *this << arguments;
122
    }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
123

124
    setJobName(m_name);
Andreas Pakulat's avatar
Andreas Pakulat committed
125 126
}

127 128
NativeAppJob* findNativeJob(KJob* j)
{
129
    auto* job = qobject_cast<NativeAppJob*>(j);
130 131 132 133 134 135 136
    if (!job) {
        const QList<NativeAppJob*> jobs = j->findChildren<NativeAppJob*>();
        if (!jobs.isEmpty())
            job = jobs.first();
    }
    return job;
}
Andreas Pakulat's avatar
Andreas Pakulat committed
137

Dāvis Mosāns's avatar
Dāvis Mosāns committed
138
void NativeAppJob::start()
Andreas Pakulat's avatar
Andreas Pakulat committed
139
{
140 141
    QVector<QPointer<NativeAppJob> > currentJobs;
    // collect running instances of the same type
142 143
    const auto& allCurrentJobs = ICore::self()->runController()->currentJobs();
    for (auto j : allCurrentJobs) {
144 145 146 147 148 149
        NativeAppJob* njob = findNativeJob(j);
        if (njob && njob != this && njob->m_name == m_name)
            currentJobs << njob;
    }

    if (!currentJobs.isEmpty()) {
150 151 152
        int killAllInstances = m_killBeforeExecutingAgain;
        if (killAllInstances == QMessageBox::Cancel) {
            QMessageBox msgBox(QMessageBox::Question,
153
                        i18nc("@title:window", "Job Already Running"),
154 155
                        i18n("'%1' is already being executed.", m_name),
                        QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
156 157
            msgBox.button(QMessageBox::No)->setText(i18nc("@action:button", "Kill All Instances"));
            msgBox.button(QMessageBox::Yes)->setText(i18nc("@action:button", "Start Another"));
158 159
            msgBox.setDefaultButton(QMessageBox::Cancel);

160
            QCheckBox* remember = new QCheckBox(i18nc("@option:check", "Remember choice"));
161 162 163 164 165 166 167 168 169
            msgBox.setCheckBox(remember);

            killAllInstances = msgBox.exec();
            if (remember->isChecked() && killAllInstances != QMessageBox::Cancel) {
                Q_EMIT killBeforeExecutingAgainChanged(killAllInstances);
            }
        }

        switch (killAllInstances) {
170 171 172 173 174
            case QMessageBox::Yes: // simply start another job
                break;
            case QMessageBox::No: // kill the running instance
                for (auto & job : currentJobs) {
                    if (job)
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
175
                        job->kill(EmitResult);
176 177 178
                }
                break;
            default: // cancel starting a new job
179
                kill(EmitResult);
180
                return;
181 182 183
        }
    }

184
    OutputExecuteJob::start();
Andreas Pakulat's avatar
Andreas Pakulat committed
185
}