File indexing completed on 2024-04-28 04:38:24
0001 /* 0002 SPDX-FileCopyrightText: 2010 Andreas Pakulat <apaku@gmx.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later 0005 */ 0006 0007 #include "custombuildjob.h" 0008 0009 #include <KLocalizedString> 0010 #include <KConfigGroup> 0011 #include <KShell> 0012 0013 #include <interfaces/iproject.h> 0014 #include <outputview/outputmodel.h> 0015 #include <outputview/outputdelegate.h> 0016 #include <util/environmentprofilelist.h> 0017 #include <util/commandexecutor.h> 0018 #include <project/projectmodel.h> 0019 0020 #include "custombuildsystemplugin.h" 0021 #include "configconstants.h" 0022 0023 using namespace KDevelop; 0024 0025 CustomBuildJob::CustomBuildJob( CustomBuildSystem* plugin, KDevelop::ProjectBaseItem* item, CustomBuildSystemTool::ActionType t ) 0026 : OutputJob( plugin ) 0027 , type( t ) 0028 , exec(nullptr) 0029 , killed( false ) 0030 , enabled( false ) 0031 { 0032 setCapabilities( Killable ); 0033 QString subgrpname; 0034 switch( type ) { 0035 case CustomBuildSystemTool::Build: 0036 subgrpname = ConfigConstants::toolGroupPrefix() + QLatin1String("Build"); 0037 break; 0038 case CustomBuildSystemTool::Clean: 0039 subgrpname = ConfigConstants::toolGroupPrefix() + QLatin1String("Clean"); 0040 break; 0041 case CustomBuildSystemTool::Install: 0042 subgrpname = ConfigConstants::toolGroupPrefix() + QLatin1String("Install"); 0043 break; 0044 case CustomBuildSystemTool::Configure: 0045 subgrpname = ConfigConstants::toolGroupPrefix() + QLatin1String("Configure"); 0046 break; 0047 case CustomBuildSystemTool::Prune: 0048 subgrpname = ConfigConstants::toolGroupPrefix() + QLatin1String("Prune"); 0049 break; 0050 case CustomBuildSystemTool::Undefined: 0051 return; 0052 } 0053 projectName = item->project()->name(); 0054 builddir = plugin->buildDirectory( item ).toLocalFile(); 0055 KConfigGroup g = plugin->configuration( item->project() ); 0056 if(g.isValid()) { 0057 KConfigGroup grp = g.group( subgrpname ); 0058 enabled = grp.readEntry(ConfigConstants::toolEnabled(), false); 0059 cmd = grp.readEntry(ConfigConstants::toolExecutable(), QUrl()).toLocalFile(); 0060 environment = grp.readEntry(ConfigConstants::toolEnvironment(), QString()); 0061 arguments = grp.readEntry(ConfigConstants::toolArguments(), QString()); 0062 } 0063 0064 QString title; 0065 switch (type) { 0066 case CustomBuildSystemTool::Build: 0067 title = i18nc("Building: <command> <project item name>", "Building: %1 %2", cmd, item->text()); 0068 break; 0069 case CustomBuildSystemTool::Clean: 0070 title = i18nc("Cleaning: <command> <project item name>", "Cleaning: %1 %2", cmd, item->text()); 0071 break; 0072 case CustomBuildSystemTool::Install: 0073 title = installPrefix.isEmpty() ? i18nc("Installing: <command> <project item name>", "Installing: %1 %2", cmd, item->text()) 0074 : i18nc("Installing: <command> <project item name> <installPrefix>", "Installing: %1 %2 %3", cmd, item->text(), installPrefix.toDisplayString(QUrl::PreferLocalFile)); 0075 break; 0076 case CustomBuildSystemTool::Configure: 0077 title = i18nc("Configuring: <command> <project item name>", "Configuring: %1 %2", cmd, item->text()); 0078 break; 0079 case CustomBuildSystemTool::Prune: 0080 title = i18nc("Pruning: <command> <project item name>", "Pruning: %1 %2", cmd, item->text()); 0081 break; 0082 default: 0083 title = QStringLiteral("Internal Error: CustomBuildJob"); 0084 break; 0085 } 0086 setTitle(title); 0087 setObjectName(title); 0088 setDelegate( new KDevelop::OutputDelegate ); 0089 } 0090 0091 void CustomBuildJob::start() 0092 { 0093 if( type == CustomBuildSystemTool::Undefined ) { 0094 setError( UndefinedBuildType ); 0095 setErrorText( i18n( "Undefined Build type" ) ); 0096 emitResult(); 0097 } else if( cmd.isEmpty() ) { 0098 setError( NoCommand ); 0099 setErrorText(i18n("No command given for custom %1 tool in project \"%2\".", 0100 CustomBuildSystemTool::toolName(type), projectName)); 0101 emitResult(); 0102 } else if( !enabled ) { 0103 setError( ToolDisabled ); 0104 setErrorText(i18n("The custom %1 tool in project \"%2\" is disabled", 0105 CustomBuildSystemTool::toolName(type), projectName)); 0106 emitResult(); 0107 } else { 0108 // prepend the command name to the argument string 0109 // so that splitArgs works correctly 0110 const QString allargv = KShell::quoteArg(cmd) + QLatin1Char(' ') + arguments; 0111 0112 KShell::Errors err; 0113 QStringList strargs = KShell::splitArgs( allargv, KShell::AbortOnMeta, &err ); 0114 if( err != KShell::NoError ) { 0115 setError( WrongArgs ); 0116 setErrorText( i18n( "The given arguments would need a real shell, this is not supported currently." ) ); 0117 emitResult(); 0118 return; 0119 } 0120 // and remove the command name back out of the split argument list 0121 Q_ASSERT(!strargs.isEmpty()); 0122 strargs.removeFirst(); 0123 0124 setStandardToolView( KDevelop::IOutputView::BuildView ); 0125 setBehaviours( KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll ); 0126 auto* model = new KDevelop::OutputModel( QUrl::fromLocalFile(builddir) ); 0127 model->setFilteringStrategy( KDevelop::OutputModel::CompilerFilter ); 0128 setModel( model ); 0129 0130 startOutput(); 0131 0132 exec = new KDevelop::CommandExecutor( cmd, this ); 0133 0134 auto env = KDevelop::EnvironmentProfileList(KSharedConfig::openConfig()).createEnvironment(environment, QProcess::systemEnvironment()); 0135 if (!installPrefix.isEmpty()) 0136 env.append(QLatin1String("DESTDIR=")+installPrefix.toDisplayString(QUrl::PreferLocalFile)); 0137 0138 exec->setArguments( strargs ); 0139 exec->setEnvironment( env ); 0140 exec->setWorkingDirectory( builddir ); 0141 0142 0143 connect( exec, &CommandExecutor::completed, this, &CustomBuildJob::procFinished ); 0144 connect( exec, &CommandExecutor::failed, this, &CustomBuildJob::procError ); 0145 0146 connect( exec, &CommandExecutor::receivedStandardError, model, &OutputModel::appendLines ); 0147 connect( exec, &CommandExecutor::receivedStandardOutput, model, &OutputModel::appendLines ); 0148 0149 model->appendLine(QStringLiteral("%1> %2 %3").arg(builddir, cmd, arguments)); 0150 exec->start(); 0151 } 0152 } 0153 0154 bool CustomBuildJob::doKill() 0155 { 0156 killed = true; 0157 exec->kill(); 0158 return true; 0159 } 0160 0161 void CustomBuildJob::procError( QProcess::ProcessError err ) 0162 { 0163 if( !killed ) { 0164 if( err == QProcess::FailedToStart ) { 0165 setError( FailedToStart ); 0166 setErrorText( i18n( "Failed to start command." ) ); 0167 } else if( err == QProcess::Crashed ) { 0168 setError( Crashed ); 0169 setErrorText( i18n( "Command crashed." ) ); 0170 } else { 0171 setError( UnknownExecError ); 0172 setErrorText( i18n( "Unknown error executing command." ) ); 0173 } 0174 } 0175 emitResult(); 0176 } 0177 0178 KDevelop::OutputModel* CustomBuildJob::model() 0179 { 0180 return qobject_cast<KDevelop::OutputModel*>( OutputJob::model() ); 0181 } 0182 0183 void CustomBuildJob::procFinished(int code) 0184 { 0185 //TODO: Make this configurable when the first report comes in from a tool 0186 // where non-zero does not indicate error status 0187 if( code != 0 ) { 0188 setError( FailedShownError ); 0189 model()->appendLine( i18n( "*** Failed ***" ) ); 0190 } else { 0191 model()->appendLine( i18n( "*** Finished ***" ) ); 0192 } 0193 emitResult(); 0194 } 0195 0196 #include "moc_custombuildjob.cpp"