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"