File indexing completed on 2024-04-28 04:37:03

0001 /*
0002     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "builderjob.h"
0008 #include "debug.h"
0009 
0010 #include <KConfigGroup>
0011 #include <KLocalizedString>
0012 
0013 #include <interfaces/iproject.h>
0014 #include <interfaces/icore.h>
0015 #include <interfaces/isession.h>
0016 #include <interfaces/idocumentcontroller.h>
0017 #include <interfaces/idocument.h>
0018 #include <project/projectmodel.h>
0019 #include <project/interfaces/iprojectbuilder.h>
0020 #include <project/interfaces/ibuildsystemmanager.h>
0021 
0022 using namespace KDevelop;
0023 
0024 struct SubJobData
0025 {
0026     BuilderJob::BuildType type;
0027     KJob* job;
0028     ProjectBaseItem* item;
0029 };
0030 Q_DECLARE_TYPEINFO(SubJobData, Q_MOVABLE_TYPE);
0031 
0032 namespace KDevelop
0033 {
0034 class BuilderJobPrivate
0035 {
0036 public:
0037     explicit BuilderJobPrivate( BuilderJob* job )
0038         : q(job)
0039         , failOnFirstError(true)
0040     {
0041     }
0042 
0043     BuilderJob* q;
0044 
0045     void addJob( BuilderJob::BuildType, ProjectBaseItem* );
0046     bool failOnFirstError;
0047 
0048     QString buildTypeToString( BuilderJob::BuildType type ) const;
0049 
0050     bool hasJobForProject( BuilderJob::BuildType type, IProject* project ) const
0051     {
0052         for (const SubJobData& data : m_metadata) {
0053             if (data.type == type && data.item->project() == project) {
0054                 return true;
0055             }
0056         }
0057         return false;
0058     }
0059 
0060     /**
0061      * a structure to keep metadata of all registered jobs
0062      */
0063     QVector<SubJobData> m_metadata;
0064 
0065     /**
0066      * get the subjob list and clear this composite job
0067      */
0068     QVector<SubJobData> takeJobList();
0069 };
0070 }
0071 
0072 QString BuilderJobPrivate::buildTypeToString(BuilderJob::BuildType type) const
0073 {
0074     switch( type ) {
0075         case BuilderJob::Build:
0076             return i18nc( "@info:status", "build" );
0077         case BuilderJob::Clean:
0078             return i18nc( "@info:status", "clean" );
0079         case BuilderJob::Configure:
0080             return i18nc( "@info:status", "configure" );
0081         case BuilderJob::Install:
0082             return i18nc( "@info:status", "install" );
0083         case BuilderJob::Prune:
0084             return i18nc( "@info:status", "prune" );
0085     }
0086     return QString();
0087 }
0088 
0089 void BuilderJobPrivate::addJob( BuilderJob::BuildType t, ProjectBaseItem* item )
0090 {
0091     Q_ASSERT(item);
0092     qCDebug(PROJECT) << "adding build job for item:" << item->text();
0093     Q_ASSERT(item->project());
0094     qCDebug(PROJECT) << "project for item:" << item->project()->name();
0095     Q_ASSERT(item->project()->projectItem());
0096     qCDebug(PROJECT) << "project item for the project:" << item->project()->projectItem()->text();
0097     if( !item->project()->buildSystemManager() )
0098     {
0099         qCWarning(PROJECT) << "no buildsystem manager for:" << item->text() << item->project()->name();
0100         return;
0101     }
0102     qCDebug(PROJECT) << "got build system manager";
0103     Q_ASSERT(item->project()->buildSystemManager()->builder());
0104     KJob* j = nullptr;
0105     switch( t )
0106     {
0107         case BuilderJob::Build:
0108             j = item->project()->buildSystemManager()->builder()->build( item );
0109             break;
0110         case BuilderJob::Clean:
0111             j = item->project()->buildSystemManager()->builder()->clean( item );
0112             break;
0113         case BuilderJob::Install:
0114             j = item->project()->buildSystemManager()->builder()->install( item );
0115             break;
0116         case BuilderJob::Prune:
0117             if (!hasJobForProject(t, item->project())) {
0118                 j = item->project()->buildSystemManager()->builder()->prune( item->project() );
0119             }
0120             break;
0121         case BuilderJob::Configure:
0122             if (!hasJobForProject(t, item->project())) {
0123                 j = item->project()->buildSystemManager()->builder()->configure( item->project() );
0124             }
0125             break;
0126     }
0127     if( j )
0128     {
0129         q->addCustomJob( t, j, item );
0130     }
0131 }
0132 
0133 BuilderJob::BuilderJob()
0134     : d_ptr(new BuilderJobPrivate(this))
0135 {
0136 }
0137 
0138 BuilderJob::~BuilderJob() = default;
0139 
0140 void BuilderJob::addItems( BuildType t, const QList<ProjectBaseItem*>& items )
0141 {
0142     Q_D(BuilderJob);
0143 
0144     for (ProjectBaseItem* item : items) {
0145         d->addJob( t, item );
0146     }
0147 }
0148 
0149 void BuilderJob::addProjects( BuildType t, const QList<IProject*>& projects )
0150 {
0151     Q_D(BuilderJob);
0152 
0153     for (IProject* project : projects) {
0154         d->addJob( t, project->projectItem() );
0155     }
0156 }
0157 
0158 void BuilderJob::addItem( BuildType t, ProjectBaseItem* item )
0159 {
0160     Q_D(BuilderJob);
0161 
0162     d->addJob( t, item );
0163 }
0164 
0165 void BuilderJob::addCustomJob( BuilderJob::BuildType type, KJob* job, ProjectBaseItem* item )
0166 {
0167     Q_D(BuilderJob);
0168 
0169     if (auto* builderJob = qobject_cast<BuilderJob*>(job)) {
0170         // If a subjob is a builder job itself, re-own its job list to avoid having recursive composite jobs.
0171         const QVector<SubJobData> subjobs = builderJob->d_func()->takeJobList();
0172         builderJob->deleteLater();
0173         for (const SubJobData& subjob : subjobs) {
0174             subjob.job->setParent(this);
0175             addSubjob( subjob.job );
0176         }
0177         d->m_metadata << subjobs;
0178     } else {
0179         job->setParent(this);
0180         addSubjob( job );
0181 
0182         SubJobData data;
0183         data.type = type;
0184         data.job = job;
0185         data.item = item;
0186         d->m_metadata << data;
0187     }
0188 }
0189 
0190 QVector< SubJobData > BuilderJobPrivate::takeJobList()
0191 {
0192     QVector< SubJobData > ret = m_metadata;
0193     m_metadata.clear();
0194     q->clearSubjobs();
0195     q->setObjectName( QString() );
0196     return ret;
0197 }
0198 
0199 void BuilderJob::updateJobName()
0200 {
0201     Q_D(BuilderJob);
0202 
0203     // Which items are mentioned in the set
0204     // Make it a list to preserve ordering; search overhead (n^2) isn't too big
0205     QList< ProjectBaseItem* > registeredItems;
0206     // Which build types are mentioned in the set
0207     // (Same rationale applies)
0208     QList< BuildType > buildTypes;
0209     // Whether there are jobs without any specific item
0210     bool hasNullItems = false;
0211 
0212     for (const SubJobData& subjob : qAsConst(d->m_metadata)) {
0213         if( subjob.item ) {
0214             if( !registeredItems.contains( subjob.item ) ) {
0215                 registeredItems.append( subjob.item );
0216             }
0217             if( !buildTypes.contains( subjob.type ) ) {
0218                 buildTypes.append( subjob.type );
0219             }
0220         } else {
0221             hasNullItems = true;
0222         }
0223     }
0224 
0225     QString itemNames;
0226     if( !hasNullItems ) {
0227         QStringList itemNamesList;
0228         itemNamesList.reserve(registeredItems.size());
0229         for (ProjectBaseItem* item : qAsConst(registeredItems)) {
0230             itemNamesList << item->text();
0231         }
0232         itemNames = itemNamesList.join(QLatin1String(", "));
0233     } else {
0234         itemNames = i18nc( "Unspecified set of build items (e. g. projects, targets)", "Various items" );
0235     }
0236 
0237     QString methodNames;
0238     QStringList methodNamesList;
0239     methodNamesList.reserve(buildTypes.size());
0240     for (BuildType type : qAsConst(buildTypes)) {
0241         methodNamesList << d->buildTypeToString( type );
0242     }
0243     methodNames = methodNamesList.join(QLatin1String(", "));
0244 
0245     QString jobName = QStringLiteral( "%1: %2" ).arg( itemNames, methodNames );
0246     setObjectName( jobName );
0247 }
0248 
0249 void BuilderJob::start()
0250 {
0251     // Automatically save all documents before starting to build
0252     // might need an option to turn off at some point
0253     // Also should be moved into the builder and there try to find target(s) for the given item and then just save the documents of that target -> list??
0254     if( ICore::self()->activeSession()->config()->group("Project Manager").readEntry( "Save All Documents Before Building", true ) )
0255     {
0256         ICore::self()->documentController()->saveAllDocuments( IDocument::Silent );
0257     }
0258 
0259     ExecuteCompositeJob::start();
0260 }
0261 
0262 #include "moc_builderjob.cpp"