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"