File indexing completed on 2024-04-28 04:38:19

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "cmakebuilder.h"
0008 #include "debug.h"
0009 
0010 #include <cmakebuilderconfig.h>
0011 
0012 #include <project/projectmodel.h>
0013 
0014 #include <interfaces/iproject.h>
0015 #include <interfaces/icore.h>
0016 #include <interfaces/iplugincontroller.h>
0017 #include <project/builderjob.h>
0018 #include <makebuilder/imakebuilder.h>
0019 
0020 #include "cmakejob.h"
0021 #include "prunejob.h"
0022 #include "cmakebuilderpreferences.h"
0023 #include "cmakeutils.h"
0024 #include <cmakemodelitems.h>
0025 
0026 #include <KPluginFactory>
0027 #include <KLocalizedString>
0028 #include <KJob>
0029 
0030 #include <QUrl>
0031 
0032 K_PLUGIN_FACTORY_WITH_JSON(CMakeBuilderFactory, "kdevcmakebuilder.json", registerPlugin<CMakeBuilder>(); )
0033 
0034 class ErrorJob : public KJob
0035 {
0036     Q_OBJECT
0037 public:
0038     ErrorJob(QObject* parent, const QString& error)
0039         : KJob(parent)
0040         , m_error(error)
0041     {}
0042 
0043     void start() override {
0044         setError(!m_error.isEmpty());
0045         setErrorText(m_error);
0046         emitResult();
0047     }
0048 
0049 private:
0050     QString m_error;
0051 };
0052 
0053 CMakeBuilder::CMakeBuilder(QObject *parent, const QVariantList &)
0054     : KDevelop::IPlugin(QStringLiteral("kdevcmakebuilder"), parent)
0055 {
0056     addBuilder(QStringLiteral("Makefile"), QStringList{QStringLiteral("Unix Makefiles"),
0057                                                        QStringLiteral("NMake Makefiles"),
0058                                                        QStringLiteral("MinGW Makefiles")},
0059                core()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IMakeBuilder")));
0060     addBuilder(QStringLiteral("build.ninja"), QStringList(QStringLiteral("Ninja")), core()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IProjectBuilder"), QStringLiteral("KDevNinjaBuilder")));
0061 }
0062 
0063 CMakeBuilder::~CMakeBuilder()
0064 {
0065 }
0066 
0067 void CMakeBuilder::addBuilder(const QString& neededfile, const QStringList& generators, KDevelop::IPlugin* i)
0068 {
0069     if( i )
0070     {
0071         auto* b = i->extension<KDevelop::IProjectBuilder>();
0072         if( b )
0073         {
0074             m_builders[neededfile] = b;
0075             for (const QString& gen : generators) {
0076                 m_buildersForGenerator[gen] = b;
0077             }
0078             // can't use new signal/slot syntax here, IProjectBuilder is not a QObject
0079             connect(i, SIGNAL(built(KDevelop::ProjectBaseItem*)), this, SIGNAL(built(KDevelop::ProjectBaseItem*)));
0080             connect(i, SIGNAL(failed(KDevelop::ProjectBaseItem*)), this, SIGNAL(failed(KDevelop::ProjectBaseItem*)));
0081             connect(i, SIGNAL(cleaned(KDevelop::ProjectBaseItem*)), this, SIGNAL(cleaned(KDevelop::ProjectBaseItem*)));
0082             connect(i, SIGNAL(installed(KDevelop::ProjectBaseItem*)), this, SIGNAL(installed(KDevelop::ProjectBaseItem*)));
0083 
0084             qCDebug(KDEV_CMAKEBUILDER) << "Added builder " << i->metaObject()->className() << "for" << neededfile;
0085         }
0086         else
0087             qCWarning(KDEV_CMAKEBUILDER) << "Couldn't add" << i->metaObject()->className();
0088     }
0089 }
0090 
0091 KJob* CMakeBuilder::build(KDevelop::ProjectBaseItem *dom)
0092 {
0093     KDevelop::IProject* p = dom->project();
0094     IProjectBuilder* builder = builderForProject(p);
0095     if( builder )
0096     {
0097         bool valid;
0098         KJob* configure = checkConfigureJob(dom->project(), valid);
0099 
0100         KJob* build = nullptr;
0101         if(dom->file())
0102         {
0103             auto* makeBuilder = dynamic_cast<IMakeBuilder*>(builder);
0104             if (!makeBuilder) {
0105                 return new ErrorJob(this, i18n("Could not find the make builder. Check your installation"));
0106             }
0107             KDevelop::ProjectFileItem* file = dom->file();
0108             int lastDot = file->text().lastIndexOf(QLatin1Char('.'));
0109             const QString target = file->text().midRef(0, lastDot) + QLatin1String(".o");
0110             build = makeBuilder->executeMakeTarget(dom->parent(), target);
0111             qCDebug(KDEV_CMAKEBUILDER) << "create build job for target" << build << dom << target;
0112         }
0113         qCDebug(KDEV_CMAKEBUILDER) << "Building with" << builder;
0114         if (!build)
0115         {
0116             build = builder->build(dom);
0117         }
0118         if( configure )
0119         {
0120             qCDebug(KDEV_CMAKEBUILDER) << "creating composite job";
0121             auto* builderJob = new KDevelop::BuilderJob;
0122             builderJob->addCustomJob( KDevelop::BuilderJob::Configure, configure, dom );
0123             builderJob->addCustomJob( KDevelop::BuilderJob::Build, build, dom );
0124             builderJob->updateJobName();
0125             build = builderJob;
0126         }
0127         return build;
0128     }
0129     return new ErrorJob(this, i18n("Could not find a builder for %1", p->name()));
0130 }
0131 
0132 KJob* CMakeBuilder::clean(KDevelop::ProjectBaseItem *dom)
0133 {
0134     IProjectBuilder* builder = builderForProject(dom->project());
0135     if( builder )
0136     {
0137         bool valid;
0138         KJob* configure = checkConfigureJob(dom->project(), valid);
0139 
0140         KDevelop::ProjectBaseItem* item = dom;
0141         if(dom->file()) //It doesn't work to compile a file
0142             item=(KDevelop::ProjectBaseItem*) dom->parent();
0143 
0144         qCDebug(KDEV_CMAKEBUILDER) << "Cleaning with" << builder;
0145         KJob* clean = builder->clean(item);
0146         if( configure ) {
0147             auto* builderJob = new KDevelop::BuilderJob;
0148             builderJob->addCustomJob( KDevelop::BuilderJob::Configure, configure, item );
0149             builderJob->addCustomJob( KDevelop::BuilderJob::Clean, clean, item );
0150             builderJob->updateJobName();
0151             clean = builderJob;
0152         }
0153         return clean;
0154     }
0155     return new ErrorJob(this, i18n("Could not find a builder for %1", dom->project()->name()));
0156 }
0157 
0158 KJob* CMakeBuilder::install(KDevelop::ProjectBaseItem *dom, const QUrl &installPrefix)
0159 {
0160     IProjectBuilder* builder = builderForProject(dom->project());
0161     if( builder )
0162     {
0163         bool valid;
0164         KJob* configure = checkConfigureJob(dom->project(), valid);
0165 
0166         KDevelop::ProjectBaseItem* item = dom;
0167         if(dom->file())
0168             item=(KDevelop::ProjectBaseItem*) dom->parent();
0169 
0170         qCDebug(KDEV_CMAKEBUILDER) << "Installing with" << builder;
0171         KJob* install = builder->install(item, installPrefix);
0172         if( configure ) {
0173             auto* builderJob = new KDevelop::BuilderJob;
0174             builderJob->addCustomJob( KDevelop::BuilderJob::Configure, configure, item );
0175             builderJob->addCustomJob( KDevelop::BuilderJob::Install, install, item );
0176             builderJob->updateJobName();
0177             install = builderJob;
0178         }
0179         return install;
0180 
0181     }
0182     return new ErrorJob(this, i18n("Could not find a builder for %1", dom->project()->name()));
0183 }
0184 
0185 KJob* CMakeBuilder::checkConfigureJob(KDevelop::IProject* project, bool& valid)
0186 {
0187     valid = false;
0188     KJob* configure = nullptr;
0189     if( CMake::checkForNeedingConfigure(project) )
0190     {
0191         configure = this->configure(project);
0192     }
0193     else if( CMake::currentBuildDir(project).isEmpty() )
0194     {
0195         return new ErrorJob(this, i18n("No build directory configured, cannot install"));
0196     }
0197     valid = true;
0198     return configure;
0199 }
0200 
0201 KJob* CMakeBuilder::configure( KDevelop::IProject* project )
0202 {
0203     if( CMake::currentBuildDir( project ).isEmpty() )
0204     {
0205         return new ErrorJob(this, i18n("No build directory configured, cannot configure"));
0206     }
0207     auto* job = new CMakeJob(this);
0208     job->setProject(project);
0209     connect(job, &KJob::result, this, [this, project] {
0210         emit configured(project);
0211     });
0212     return job;
0213 }
0214 
0215 KJob* CMakeBuilder::prune( KDevelop::IProject* project )
0216 {
0217     return new PruneJob(project);
0218 }
0219 
0220 KDevelop::IProjectBuilder* CMakeBuilder::builderForProject(KDevelop::IProject* p) const
0221 {
0222     QString builddir = CMake::currentBuildDir( p ).toLocalFile();
0223     QMap<QString, IProjectBuilder*>::const_iterator it = m_builders.constBegin(), itEnd = m_builders.constEnd();
0224     for(; it!=itEnd; ++it) {
0225         if (QFile::exists(builddir+QLatin1Char('/')+it.key()))
0226             return it.value();
0227     }
0228     //It means that it still has to be generated, so use the builder for
0229     //the generator we use
0230     return m_buildersForGenerator[CMake::defaultGenerator()];
0231 }
0232 
0233 QList< KDevelop::IProjectBuilder* > CMakeBuilder::additionalBuilderPlugins( KDevelop::IProject* project  ) const
0234 {
0235     IProjectBuilder* b = builderForProject( project );
0236     QList< KDevelop::IProjectBuilder* > ret;
0237     if(b)
0238         ret << b;
0239     return ret;
0240 }
0241 
0242 int CMakeBuilder::configPages() const
0243 {
0244     return 1;
0245 }
0246 
0247 KDevelop::ConfigPage* CMakeBuilder::configPage(int number, QWidget* parent)
0248 {
0249     if (number == 0) {
0250         return new CMakeBuilderPreferences(this, parent);
0251     }
0252     return nullptr;
0253 }
0254 
0255 
0256 #include "cmakebuilder.moc"
0257 #include "moc_cmakebuilder.cpp"