diff --git a/project/builderjob.cpp b/project/builderjob.cpp index 44519d3266..92395c250a 100644 --- a/project/builderjob.cpp +++ b/project/builderjob.cpp @@ -1,195 +1,287 @@ /*************************************************************************** * 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 namespace KDevelop { class BuilderJobPrivate { public: BuilderJobPrivate( BuilderJob* job ) { q = job; failOnFirstError = true; } BuilderJob* q; void addJob( BuilderJob::BuildType, KDevelop::ProjectBaseItem* ); bool failOnFirstError; }; void BuilderJobPrivate::addJob( BuilderJob::BuildType t, KDevelop::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: j = item->project()->buildSystemManager()->builder()->prune( item->project() ); break; case BuilderJob::Configure: j = item->project()->buildSystemManager()->builder()->configure( item->project() ); break; default: break; } if( j ) { - q->addSubjob( j ); + q->addCustomJob( t, j, item ); } } BuilderJob::BuilderJob() : d( new BuilderJobPrivate( this ) ) { } void BuilderJob::addItems( BuildType t, const QList& items ) { foreach( KDevelop::ProjectBaseItem* item, items ) { d->addJob( t, item ); } } void BuilderJob::addProjects( BuildType t, const QList& projects ) { foreach( KDevelop::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. + QList< BuilderJob::SubJobData > subjobs = builderJob->takeJobList(); + builderJob->deleteLater(); + foreach( const BuilderJob::SubJobData& subjob, subjobs ) { + addSubjob( subjob.job ); + } + m_metadata << subjobs; + } else { + addSubjob( job ); + + SubJobData data; + data.type = type; + data.job = job; + data.item = item; + m_metadata << data; + } +} + +QList< BuilderJob::SubJobData > BuilderJob::takeJobList() +{ + QList< SubJobData > ret = m_metadata; + m_metadata.clear(); + clearSubjobs(); + 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; + // Whether there are jobs without any specific item + bool hasNullItems = false; + // Which build types are mentioned in the set + QSet< BuildType > buildTypes; + + foreach( const SubJobData& subjob, m_metadata ) { + if( subjob.item ) { + if( !registeredItems.contains( subjob.item ) ) { + registeredItems.append( subjob.item ); + } + buildTypes.insert( 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 << buildTypeToString( type ); + } + methodNames = methodNamesList.join( ", " ); + + QString jobName = QString( "%1: %2" ).arg( itemNames ).arg( methodNames ); + setObjectName( jobName ); +} + +QString BuilderJob::buildTypeToString( BuilderJob::BuildType type ) +{ + switch( type ) { + case Build: + return i18nc( "@info:status", "build" ); + case Clean: + return i18nc( "@info:status", "clean" ); + case Configure: + return i18nc( "@info:status", "configure" ); + case Install: + return i18nc( "@info:status", "install" ); + case Prune: + return i18nc( "@info:status", "prune" ); + default: + return QString(); + } +} + 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, KDevelop::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 ) ) { KDevelop::ICore::self()->documentController()->saveAllDocuments( KDevelop::IDocument::Silent ); } if(hasSubjobs()) KDevelop::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; KDevelop::ICore::self()->runController()->registerJob( subjobs().first() ); } else { emitResult(); } } void BuilderJob::setStopOnFail( bool stopOnFail ) { d->failOnFirstError = stopOnFail; } bool BuilderJob::stopOnFail() const { return d->failOnFirstError; } } #include "builderjob.moc" diff --git a/project/builderjob.h b/project/builderjob.h index 297f18768d..b875736f48 100644 --- a/project/builderjob.h +++ b/project/builderjob.h @@ -1,121 +1,158 @@ /*************************************************************************** * 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. * ***************************************************************************/ #ifndef BUILDERJOB_H #define BUILDERJOB_H #include #include #include "projectexport.h" class KConfigGroup; namespace KDevelop { class ProjectBaseItem; class IProject; /** * Allows to build a list of project items or projects sequentially, where * failing to build one item in the list will fail the whole job. */ class KDEVPLATFORMPROJECT_EXPORT BuilderJob : public KCompositeJob { Q_OBJECT public: /** * Defines what action to do on the Project builder */ enum BuildType { Build /**< Build the selected items */, Prune /**< Prune the selected items */, Configure /**< Configure the selected items */, Install /**< Install the selected items */, Clean /**< Clean the selected items */ }; /** * Creates a Builder job */ BuilderJob(); /** * Allows to easily schedule building a couple of @p items using the * method identified by @p type * * @param type the build method to use * @param items the project items to add */ void addItems( BuildType type, const QList& items ); /** * Allows to easily schedule building a couple of @p projects using the * method identified by @p type * * @param type the build method to use * @param projects the projects to add */ void addProjects( BuildType type, const QList& projects ); /** * Allows to add a single @p item to the end of the list. The item will be * built using the method identified by @p type * * @param item The item to add to the list * @param type The build method to be used for the item */ void addItem( BuildType type, ProjectBaseItem* item ); - + + /** + * Allows to add a custom @p job to the end of the list. The build method specified by @p type + * and (optionally) an item specified by @p item are needed to create a human-readable job name. + * + * @param type The build method which is represented by the @p job + * @param job The job to add to the list + * @param item The item which is build by the @p job + */ + void addCustomJob( BuildType type, KJob* job, ProjectBaseItem* item = 0 ); + + /** + * Updates the job's name. + * + * Shall be called before registering this job in the run controller, but after + * adding all required tasks to the job. + */ + void updateJobName(); + /** * Allows to choose between stopping and failing the composite job * when the first item could not be built, or building all items * The default for this is true. * @param stopOnFail if set to true this job will stop and fail when the first * item in the list cannot be build */ void setStopOnFail( bool stopOnFail ); /** * Find out whether this builderjob stops building items on the first failed * item. * @returns true if this job stops and fails when the first subjob failed */ bool stopOnFail() const; /** * Starts this job */ void start(); protected Q_SLOTS: /** * @internal slot to handle the result from subjobs */ virtual void slotResult( KJob* ); private: class BuilderJobPrivate* const d; friend class BuilderJobPrivate; + + QString buildTypeToString( BuildType type ); + + /** + * @internal a structure to keep metadata of all registered jobs + */ + struct SubJobData + { + BuildType type; + KJob* job; + ProjectBaseItem* item; + }; + QList m_metadata; + + /** + * @internal get the subjob list and clear this composite job + */ + QList takeJobList(); + }; } #endif