1 /*
2     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "executeplugin.h"
8 
9 #include <KConfigGroup>
10 #include <KJob>
11 #include <KLocalizedString>
12 #include <KParts/MainWindow>
13 #include <KPluginFactory>
14 #include <KShell>
15 
16 #include <interfaces/icore.h>
17 #include <interfaces/iruncontroller.h>
18 #include <interfaces/ilaunchconfiguration.h>
19 #include <interfaces/iprojectcontroller.h>
20 #include <interfaces/iuicontroller.h>
21 #include <sublime/message.h>
22 
23 #include "nativeappconfig.h"
24 #include "debug.h"
25 #include <project/projectmodel.h>
26 #include <project/builderjob.h>
27 #include <util/kdevstringhandler.h>
28 
29 using namespace KDevelop;
30 
31 K_PLUGIN_FACTORY_WITH_JSON(KDevExecuteFactory, "kdevexecute.json", registerPlugin<ExecutePlugin>();)
32 
ExecutePlugin(QObject * parent,const QVariantList &)33 ExecutePlugin::ExecutePlugin(QObject *parent, const QVariantList&)
34     : KDevelop::IPlugin(QStringLiteral("kdevexecute"), parent)
35 {
36     m_configType = new NativeAppConfigType();
37     m_configType->addLauncher( new NativeAppLauncher() );
38     qCDebug(PLUGIN_EXECUTE) << "adding native app launch config";
39     core()->runController()->addConfigurationType( m_configType );
40 }
41 
~ExecutePlugin()42 ExecutePlugin::~ExecutePlugin()
43 {
44 }
45 
unload()46 void ExecutePlugin::unload()
47 {
48     core()->runController()->removeConfigurationType( m_configType );
49     delete m_configType;
50     m_configType = nullptr;
51 }
52 
arguments(KDevelop::ILaunchConfiguration * cfg,QString & err_) const53 QStringList ExecutePlugin::arguments( KDevelop::ILaunchConfiguration* cfg, QString& err_ ) const
54 {
55 
56     if( !cfg )
57     {
58         return QStringList();
59     }
60 
61     KShell::Errors err;
62     QStringList args = KShell::splitArgs( cfg->config().readEntry( ExecutePlugin::argumentsEntry, "" ), KShell::TildeExpand | KShell::AbortOnMeta, &err );
63     if( err != KShell::NoError )
64     {
65 
66         if( err == KShell::BadQuoting )
67         {
68             err_ = i18n("There is a quoting error in the arguments for "
69             "the launch configuration '%1'. Aborting start.", cfg->name() );
70         } else
71         {
72             err_ = i18n("A shell meta character was included in the "
73             "arguments for the launch configuration '%1', "
74             "this is not supported currently. Aborting start.", cfg->name() );
75         }
76         args = QStringList();
77         qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << "arguments have meta characters";
78     }
79     return args;
80 }
81 
82 
dependencyJob(KDevelop::ILaunchConfiguration * cfg) const83 KJob* ExecutePlugin::dependencyJob( KDevelop::ILaunchConfiguration* cfg ) const
84 {
85     const QVariantList deps = KDevelop::stringToQVariant( cfg->config().readEntry( dependencyEntry, QString() ) ).toList();
86     QString depAction = cfg->config().readEntry( dependencyActionEntry, "Nothing" );
87     if( depAction != QLatin1String("Nothing") && !deps.isEmpty() )
88     {
89         KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel();
90         QList<KDevelop::ProjectBaseItem*> items;
91         for (const QVariant& dep : deps) {
92             KDevelop::ProjectBaseItem* item = model->itemFromIndex( model->pathToIndex( dep.toStringList() ) );
93             if( item )
94             {
95                 items << item;
96             }
97             else
98             {
99                 const QString messageText = i18n("Couldn't resolve the dependency: %1", dep.toString());
100                 auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
101                 ICore::self()->uiController()->postMessage(message);
102             }
103         }
104         auto* job = new KDevelop::BuilderJob();
105         if( depAction == QLatin1String("Build") )
106         {
107             job->addItems( KDevelop::BuilderJob::Build, items );
108         } else if( depAction == QLatin1String("Install") )
109         {
110             job->addItems( KDevelop::BuilderJob::Install, items );
111         }
112         job->updateJobName();
113         return job;
114     }
115     return nullptr;
116 }
117 
118 
environmentProfileName(KDevelop::ILaunchConfiguration * cfg) const119 QString ExecutePlugin::environmentProfileName(KDevelop::ILaunchConfiguration* cfg) const
120 {
121     if( !cfg )
122     {
123         return QString();
124     }
125 
126     return cfg->config().readEntry(ExecutePlugin::environmentProfileEntry, QString());
127 }
128 
129 
executable(KDevelop::ILaunchConfiguration * cfg,QString & err) const130 QUrl ExecutePlugin::executable( KDevelop::ILaunchConfiguration* cfg, QString& err ) const
131 {
132     QUrl executable;
133     if( !cfg )
134     {
135         return executable;
136     }
137     KConfigGroup grp = cfg->config();
138     if( grp.readEntry(ExecutePlugin::isExecutableEntry, false ) )
139     {
140         executable = grp.readEntry( ExecutePlugin::executableEntry, QUrl() );
141     } else
142     {
143         QStringList prjitem = grp.readEntry( ExecutePlugin::projectTargetEntry, QStringList() );
144         KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel();
145         KDevelop::ProjectBaseItem* item = model->itemFromIndex( model->pathToIndex(prjitem) );
146         if( item && item->executable() )
147         {
148             // TODO: Need an option in the gui to choose between installed and builddir url here, currently cmake only supports builddir url
149             executable = item->executable()->builtUrl();
150         }
151     }
152     if( executable.isEmpty() )
153     {
154         err = i18n("No valid executable specified");
155         qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << "no valid executable set";
156     } else
157     {
158         KShell::Errors err_;
159         if( KShell::splitArgs( executable.toLocalFile(), KShell::TildeExpand | KShell::AbortOnMeta, &err_ ).isEmpty() || err_ != KShell::NoError )
160         {
161             executable = QUrl();
162             if( err_ == KShell::BadQuoting )
163             {
164                 err = i18n("There is a quoting error in the executable "
165                 "for the launch configuration '%1'. "
166                 "Aborting start.", cfg->name() );
167             } else
168             {
169                 err = i18n("A shell meta character was included in the "
170                 "executable for the launch configuration '%1', "
171                 "this is not supported currently. Aborting start.", cfg->name() );
172             }
173             qCWarning(PLUGIN_EXECUTE) << "Launch Configuration:" << cfg->name() << "executable has meta characters";
174         }
175     }
176     return executable;
177 }
178 
179 
useTerminal(KDevelop::ILaunchConfiguration * cfg) const180 bool ExecutePlugin::useTerminal( KDevelop::ILaunchConfiguration* cfg ) const
181 {
182     if( !cfg )
183     {
184         return false;
185     }
186 
187     return cfg->config().readEntry( ExecutePlugin::useTerminalEntry, false );
188 }
189 
190 
terminal(KDevelop::ILaunchConfiguration * cfg) const191 QString ExecutePlugin::terminal( KDevelop::ILaunchConfiguration* cfg ) const
192 {
193     if( !cfg )
194     {
195         return QString();
196     }
197 
198     return cfg->config().readEntry( ExecutePlugin::terminalEntry, QString() );
199 }
200 
201 
workingDirectory(KDevelop::ILaunchConfiguration * cfg) const202 QUrl ExecutePlugin::workingDirectory( KDevelop::ILaunchConfiguration* cfg ) const
203 {
204     if( !cfg )
205     {
206         return QUrl();
207     }
208 
209     return cfg->config().readEntry( ExecutePlugin::workingDirEntry, QUrl() );
210 }
211 
212 
nativeAppConfigTypeId() const213 QString ExecutePlugin::nativeAppConfigTypeId() const
214 {
215     return NativeAppConfigType::sharedId();
216 }
217 
218 
219 #include "executeplugin.moc"
220