/*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "builderjob.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; struct SubJobData { BuilderJob::BuildType type; KJob* job; ProjectBaseItem* item; }; Q_DECLARE_TYPEINFO(SubJobData, Q_MOVABLE_TYPE); namespace KDevelop { class BuilderJobPrivate { public: BuilderJobPrivate( BuilderJob* job ) : q(job) , failOnFirstError(true) { } BuilderJob* q; void addJob( BuilderJob::BuildType, ProjectBaseItem* ); bool failOnFirstError; QString buildTypeToString( BuilderJob::BuildType type ) const; bool hasJobForProject( BuilderJob::BuildType type, IProject* project ) const { foreach(const SubJobData& data, m_metadata) { if (data.type == type && data.item->project() == project) { return true; } } return false; } /** * a structure to keep metadata of all registered jobs */ QVector m_metadata; /** * get the subjob list and clear this composite job */ QVector takeJobList(); }; } QString BuilderJobPrivate::buildTypeToString(BuilderJob::BuildType type) const { switch( type ) { case BuilderJob::Build: return i18nc( "@info:status", "build" ); case BuilderJob::Clean: return i18nc( "@info:status", "clean" ); case BuilderJob::Configure: return i18nc( "@info:status", "configure" ); case BuilderJob::Install: return i18nc( "@info:status", "install" ); case BuilderJob::Prune: return i18nc( "@info:status", "prune" ); default: return QString(); } } void BuilderJobPrivate::addJob( BuilderJob::BuildType t, ProjectBaseItem* item ) { Q_ASSERT(item); kDebug() << "adding build job for item:" << item->text(); Q_ASSERT(item->project()); kDebug() << "project for item:" << item->project()->name(); Q_ASSERT(item->project()->projectItem()); kDebug() << "project item for the project:" << item->project()->projectItem()->text(); if( !item->project()->buildSystemManager() ) { kWarning() << "no buildsystem manager for:" << item->text() << item->project()->name(); return; } kDebug() << "got build system manager"; Q_ASSERT(item->project()->buildSystemManager()->builder()); KJob* j = 0; switch( t ) { case BuilderJob::Build: j = item->project()->buildSystemManager()->builder()->build( item ); break; case BuilderJob::Clean: j = item->project()->buildSystemManager()->builder()->clean( item ); break; case BuilderJob::Install: j = item->project()->buildSystemManager()->builder()->install( item ); break; case BuilderJob::Prune: if (!hasJobForProject(t, item->project())) { j = item->project()->buildSystemManager()->builder()->prune( item->project() ); } break; case BuilderJob::Configure: if (!hasJobForProject(t, item->project())) { j = item->project()->buildSystemManager()->builder()->configure( item->project() ); } break; default: break; } if( j ) { q->addCustomJob( t, j, item ); } } BuilderJob::BuilderJob() : d( new BuilderJobPrivate( this ) ) { } BuilderJob::~BuilderJob() { delete d; } void BuilderJob::addItems( BuildType t, const QList& items ) { foreach( ProjectBaseItem* item, items ) { d->addJob( t, item ); } } void BuilderJob::addProjects( BuildType t, const QList& projects ) { foreach( IProject* project, projects ) { d->addJob( t, project->projectItem() ); } } void BuilderJob::addItem( BuildType t, ProjectBaseItem* item ) { d->addJob( t, item ); } void BuilderJob::addCustomJob( BuilderJob::BuildType type, KJob* job, ProjectBaseItem* item ) { if( BuilderJob* builderJob = dynamic_cast( job ) ) { // If a subjob is a builder job itself, re-own its job list to avoid having recursive composite jobs. QVector subjobs = builderJob->d->takeJobList(); builderJob->deleteLater(); foreach( const SubJobData& subjob, subjobs ) { addSubjob( subjob.job ); } d->m_metadata << subjobs; } else { addSubjob( job ); SubJobData data; data.type = type; data.job = job; data.item = item; d->m_metadata << data; } } QVector< SubJobData > BuilderJobPrivate::takeJobList() { QVector< SubJobData > ret = m_metadata; m_metadata.clear(); q->clearSubjobs(); q->setObjectName( QString() ); return ret; } void BuilderJob::updateJobName() { // Which items are mentioned in the set // Make it a list to preserve ordering; search overhead (n^2) isn't too big QList< ProjectBaseItem* > registeredItems; // Which build types are mentioned in the set // (Same rationale applies) QList< BuildType > buildTypes; // Whether there are jobs without any specific item bool hasNullItems = false; foreach( const SubJobData& subjob, d->m_metadata ) { if( subjob.item ) { if( !registeredItems.contains( subjob.item ) ) { registeredItems.append( subjob.item ); } if( !buildTypes.contains( subjob.type ) ) { buildTypes.append( subjob.type ); } } else { hasNullItems = true; } } QString itemNames; if( !hasNullItems ) { QStringList itemNamesList; foreach( ProjectBaseItem* item, registeredItems ) { itemNamesList << item->text(); } itemNames = itemNamesList.join(", "); } else { itemNames = i18nc( "Unspecified set of build items (e. g. projects, targets)", "Various items" ); } QString methodNames; QStringList methodNamesList; foreach( BuildType type, buildTypes ) { methodNamesList << d->buildTypeToString( type ); } methodNames = methodNamesList.join( ", " ); QString jobName = QString( "%1: %2" ).arg( itemNames ).arg( methodNames ); setObjectName( jobName ); } void BuilderJob::start() { #if 0 ///Running the same builder twice may result in serious problems, so kill jobs already running on the same project QList > jobs; foreach(KJob* job, ICore::self()->runController()->currentJobs()) { kDebug() << "running" << job; jobs << job; } for(QList< QPointer< KJob > >::iterator it = jobs.begin(); it != jobs.end(); ++it) { QPointer< KJob > job = *it; if(!job) continue; BuilderJob* bJob = dynamic_cast(job.data()); if( bJob && bJob != this ) { kDebug() << "killing running builder job, due to new started build"; //Copy the subjobs into QPointers first, as we never know what is deleted when QList > subJobs; foreach(KJob* subJob, bJob->subjobs()) subJobs << subJob; // while(!subJobs.empty()) { // if(subJobs.back() && subJobs.back()->capabilities() & KJob::Killable) // subJobs.back()->kill(EmitResult); // subJobs.pop_front(); // } if(job && job->capabilities() & KJob::Killable) job->kill(EmitResult); } } #endif // Automatically save all documents before starting to build // might need an option to turn off at some point // 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?? if( ICore::self()->activeSession()->config()->group("Project Manager").readEntry( "Save All Documents Before Building", true ) ) { ICore::self()->documentController()->saveAllDocuments( IDocument::Silent ); } if(hasSubjobs()) ICore::self()->runController()->registerJob( subjobs().first() ); else emitResult(); } void BuilderJob::slotResult( KJob* job ) { //call parent implementation for default behaviour KCompositeJob::slotResult( job ); if( ( !error() || !d->failOnFirstError ) && hasSubjobs() ) { // start next build; ICore::self()->runController()->registerJob( subjobs().first() ); } else { emitResult(); } } bool BuilderJob::addSubjob(KJob* job) { // work-around for kdelibs bug: https://bugs.kde.org/show_bug.cgi?id=230692 // (fixed in 4.11) // TODO: remove this method overwrite as soon as we depend on kdelibs 4.11 bool success = KCompositeJob::addSubjob(job); // if job's parent != this => we're using kdelibs <= 4.10 if (job->parent() != this) { // set the parent to 0, so the original parent won't delete this job // KCompositeJob takes care of deletion job->setParent(0); } return success; } void BuilderJob::setStopOnFail( bool stopOnFail ) { d->failOnFirstError = stopOnFail; } bool BuilderJob::stopOnFail() const { return d->failOnFirstError; } #include "moc_builderjob.cpp"