From 00951b6b969c439a36530bc5ec8dfbf8d8641288 Mon Sep 17 00:00:00 2001
From: dt <qtc-committer@nokia.com>
Date: Mon, 16 Mar 2009 17:33:05 +0100
Subject: [PATCH] More progress on the CMake plugin

Made the cmake plugin even more usable by implementing:
Pop up a wizard if there is a .user file but no .cbp file. (Fixes empty
project reported on irc.)
Pop up a wizard if the .cbp file is older then the CMakeLists.txt file,
thus reparsing the file. (Note: There is a bug that we need to actually
also check the last modified of all included files.)
Reparse the cbp file to add new RunConfigurations / delete no longer
existing RunConfigurations and update those that have changed.-
Show a nicer title in the Projects/RunConfiguration pane
---
 .../cmakeopenprojectwizard.cpp                |  60 ++++++++--
 .../cmakeopenprojectwizard.h                  |   7 ++
 .../cmakeprojectmanager/cmakeproject.cpp      | 113 +++++++++++++-----
 .../cmakerunconfiguration.cpp                 |  28 ++++-
 .../cmakerunconfiguration.h                   |   8 +-
 src/plugins/qt4projectmanager/qt4project.cpp  |   1 +
 6 files changed, 172 insertions(+), 45 deletions(-)

diff --git a/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.cpp b/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.cpp
index e8db79ed21c..4803f26b9f1 100644
--- a/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.cpp
@@ -10,6 +10,7 @@
 #include <QtGui/QPushButton>
 #include <QtGui/QPlainTextEdit>
 #include <QtCore/QDateTime>
+#include <QtCore/QStringList>
 
 using namespace CMakeProjectManager;
 using namespace CMakeProjectManager::Internal;
@@ -27,7 +28,8 @@ using namespace CMakeProjectManager::Internal;
 
 CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory)
     : m_cmakeManager(cmakeManager),
-      m_sourceDirectory(sourceDirectory)
+      m_sourceDirectory(sourceDirectory),
+      m_creatingCbpFiles(false)
 {
     int startid;
     if (hasInSourceBuild()) {
@@ -46,6 +48,18 @@ CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const
     setStartId(startid);
 }
 
+CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory,
+                                               const QStringList &needToCreate, const QStringList &needToUpdate)
+    : m_cmakeManager(cmakeManager),
+      m_sourceDirectory(sourceDirectory),
+      m_creatingCbpFiles(true)
+{
+    foreach(const QString &buildDirectory, needToCreate)
+        addPage(new CMakeRunPage(this, buildDirectory, false));
+    foreach(const QString &buildDirectory, needToUpdate)
+        addPage(new CMakeRunPage(this, buildDirectory, true));
+}
+
 CMakeManager *CMakeOpenProjectWizard::cmakeManager() const
 {
     return m_cmakeManager;
@@ -53,6 +67,8 @@ CMakeManager *CMakeOpenProjectWizard::cmakeManager() const
 
 int CMakeOpenProjectWizard::nextId() const
 {
+    if (m_creatingCbpFiles)
+        return QWizard::nextId();
     int cid = currentId();
     if (cid == InSourcePageId) {
         if (existsUpToDateXmlFile())
@@ -169,15 +185,43 @@ void ShadowBuildPage::buildDirectoryChanged()
 CMakeRunPage::CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard)
     : QWizardPage(cmakeWizard),
       m_cmakeWizard(cmakeWizard),
-      m_complete(false)
+      m_complete(false),
+      m_buildDirectory(m_cmakeWizard->buildDirectory())
+{
+    initWidgets();
+}
+
+CMakeRunPage::CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard, const QString &buildDirectory, bool update)
+    : QWizardPage(cmakeWizard),
+      m_cmakeWizard(cmakeWizard),
+      m_complete(false),
+      m_buildDirectory(buildDirectory)
+{
+    initWidgets();
+    // TODO tell the user more?
+    if (update)
+        m_descriptionLabel->setText(tr("The directory %1 contains an outdated .cbp file. Qt "
+                                       "Creator needs to update this file by running cmake. "
+                                       "If you want to add additional command line arguments, "
+                                       "add them in the below.").arg(m_buildDirectory));
+    else
+        m_descriptionLabel->setText(tr("The directory %1, specified in a buildconfiguration, "
+                                       "does not contain a cbp file. Qt Creator needs to "
+                                       "recreate this file, by running cmake. "
+                                       "Some projects require command line arguments to "
+                                       "the initial cmake call.").arg(m_buildDirectory));
+}
+
+void CMakeRunPage::initWidgets()
 {
     QFormLayout *fl = new QFormLayout;
     setLayout(fl);
-    QLabel *label = new QLabel(this);
-    label->setWordWrap(true);
-    label->setText(tr("The directory %1 does not contain a cbp file. Qt Creator needs to create this file, by running cmake. "
-                   "Some projects require command line arguments to the initial cmake call.").arg(m_cmakeWizard->buildDirectory()));
-    fl->addRow(label);
+    m_descriptionLabel = new QLabel(this);
+    m_descriptionLabel->setWordWrap(true);
+    m_descriptionLabel->setText(tr("The directory %1 does not contain a cbp file. Qt Creator needs to create this file, by running cmake. "
+                   "Some projects require command line arguments to the initial cmake call.").arg(m_buildDirectory));
+
+    fl->addRow(m_descriptionLabel);
 
     m_argumentsLineEdit = new QLineEdit(this);
     fl->addRow(tr("Arguments:"), m_argumentsLineEdit);
@@ -198,7 +242,7 @@ void CMakeRunPage::runCMake()
     m_argumentsLineEdit->setEnabled(false);
     QStringList arguments = ProjectExplorer::Environment::parseCombinedArgString(m_argumentsLineEdit->text());
     CMakeManager *cmakeManager = m_cmakeWizard->cmakeManager();
-    m_cmakeProcess = cmakeManager->createXmlFile(arguments, m_cmakeWizard->sourceDirectory(), m_cmakeWizard->buildDirectory());
+    m_cmakeProcess = cmakeManager->createXmlFile(arguments, m_cmakeWizard->sourceDirectory(), m_buildDirectory);
     connect(m_cmakeProcess, SIGNAL(readyRead()), this, SLOT(cmakeReadyRead()));
     connect(m_cmakeProcess, SIGNAL(finished(int)), this, SLOT(cmakeFinished()));
 }
diff --git a/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.h b/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.h
index 0cbdc49c8e9..8d7e212620d 100644
--- a/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.h
+++ b/src/plugins/cmakeprojectmanager/cmakeopenprojectwizard.h
@@ -4,6 +4,7 @@
 #include <QtCore/QProcess>
 #include <QtGui/QPushButton>
 #include <QtGui/QLineEdit>
+#include <QtGui/QLabel>
 #include <QtGui/QWizard>
 #include <QtGui/QPlainTextEdit>
 
@@ -29,6 +30,7 @@ public:
         CMakeRunPageId
     };
     CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory);
+    CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory, const QStringList &needToCreate, const QStringList &needToUpdate);
     virtual int nextId() const;
     QString buildDirectory() const;
     QString sourceDirectory() const;
@@ -43,6 +45,7 @@ private:
     QString m_buildDirectory;
     QString m_sourceDirectory;
     QStringList m_arguments;
+    bool m_creatingCbpFiles;
 };
 
 class InSourceBuildPage : public QWizardPage
@@ -82,6 +85,7 @@ class CMakeRunPage : public QWizardPage
     Q_OBJECT
 public:
     CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard);
+    CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard, const QString &buildDirectory, bool update);
     virtual void cleanupPage();
     virtual bool isComplete() const;
 private slots:
@@ -89,12 +93,15 @@ private slots:
     void cmakeFinished();
     void cmakeReadyRead();
 private:
+    void initWidgets();
     CMakeOpenProjectWizard *m_cmakeWizard;
     QPlainTextEdit *m_output;
     QPushButton *m_runCMake;
     QProcess *m_cmakeProcess;
     QLineEdit *m_argumentsLineEdit;
+    QLabel *m_descriptionLabel;
     bool m_complete;
+    QString m_buildDirectory;
 };
 
 }
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index e7114fc63dc..dd568b05848 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -40,8 +40,10 @@
 #include <utils/qtcassert.h>
 #include <coreplugin/icore.h>
 
+#include <QtCore/QMap>
 #include <QtCore/QDebug>
 #include <QtCore/QDir>
+#include <QtCore/QDateTime>
 #include <QtCore/QProcess>
 #include <QtGui/QFormLayout>
 #include <QtGui/QMainWindow>
@@ -80,15 +82,14 @@ CMakeProject::~CMakeProject()
 // TODO make this function work even if it is reparsing
 void CMakeProject::parseCMakeLists()
 {
-    ProjectExplorer::ToolChain *newToolChain = 0;
     QString sourceDirectory = QFileInfo(m_fileName).absolutePath();
-
     QString cbpFile = CMakeManager::findCbpFile(buildDirectory(activeBuildConfiguration()));
     m_rootNode->setFolderName(QFileInfo(cbpFile).baseName());
     CMakeCbpParser cbpparser;
     qDebug()<<"Parsing file "<<cbpFile;
     if (cbpparser.parseCbpFile(cbpFile)) {
         qDebug()<<"CodeBlocks Compilername"<<cbpparser.compilerName();
+        ProjectExplorer::ToolChain *newToolChain = 0;
         if (cbpparser.compilerName() == "gcc") {
             newToolChain = ProjectExplorer::ToolChain::createGccToolChain("gcc");
         } else if (cbpparser.compilerName() == "msvc8") {
@@ -97,7 +98,9 @@ void CMakeProject::parseCMakeLists()
             Q_ASSERT(false);
         } else {
             // TODO hmm?
+            qDebug()<<"Not implemented yet!!! Qt Creator doesn't know which toolchain to use for"<<cbpparser.compilerName();
         }
+
         if (ProjectExplorer::ToolChain::equals(newToolChain, m_toolChain)) {
             delete newToolChain;
             newToolChain = 0;
@@ -107,27 +110,29 @@ void CMakeProject::parseCMakeLists()
         }
 
         m_projectName = cbpparser.projectName();
+        m_rootNode->setFolderName(cbpparser.projectName());
         qDebug()<<"Building Tree";
-        // TODO do a intelligent updating of the tree
 
+        // TODO do a intelligent updating of the tree
         QList<ProjectExplorer::FileNode *> fileList = cbpparser.fileList();
         // Manually add the CMakeLists.txt file
         fileList.append(new ProjectExplorer::FileNode(sourceDirectory + "/CMakeLists.txt", ProjectExplorer::ProjectFileType, false));
 
         buildTree(m_rootNode, fileList);
+        m_files.clear();
         foreach (ProjectExplorer::FileNode *fn, fileList)
             m_files.append(fn->path());
         m_files.sort();
 
         qDebug()<<"Adding Targets";
         m_targets = cbpparser.targets();
-        qDebug()<<"Printing targets";
-        foreach(CMakeTarget ct, m_targets) {
-            qDebug()<<ct.title<<" with executable:"<<ct.executable;
-            qDebug()<<"WD:"<<ct.workingDirectory;
-            qDebug()<<ct.makeCommand<<ct.makeCleanCommand;
-            qDebug()<<"";
-        }
+//        qDebug()<<"Printing targets";
+//        foreach(CMakeTarget ct, m_targets) {
+//            qDebug()<<ct.title<<" with executable:"<<ct.executable;
+//            qDebug()<<"WD:"<<ct.workingDirectory;
+//            qDebug()<<ct.makeCommand<<ct.makeCleanCommand;
+//            qDebug()<<"";
+//        }
 
         qDebug()<<"Updating CodeModel";
 
@@ -147,12 +152,62 @@ void CMakeProject::parseCMakeLists()
             pinfo.includePaths = allIncludePaths;
             // TODO we only want C++ files, not all other stuff that might be in the project
             pinfo.sourceFiles = m_files;
-            pinfo.defines = m_toolChain->predefinedMacros();
+            pinfo.defines = m_toolChain->predefinedMacros(); // TODO this is to simplistic
             pinfo.frameworkPaths = allFrameworkPaths;
             modelmanager->updateProjectInfo(pinfo);
         }
+
+        // Create run configurations for m_targets
+        qDebug()<<"Create run configurations of m_targets";
+        QMap<QString, QSharedPointer<CMakeRunConfiguration> > existingRunConfigurations;
+        foreach(QSharedPointer<ProjectExplorer::RunConfiguration> cmakeRunConfiguration, runConfigurations()) {
+            if (QSharedPointer<CMakeRunConfiguration> rc = cmakeRunConfiguration.dynamicCast<CMakeRunConfiguration>()) {
+                existingRunConfigurations.insert(rc->title(), rc);
+            }
+        }
+
+        bool setActive = false;
+        foreach(const CMakeTarget &ct, m_targets) {
+            if (ct.executable.isEmpty())
+                continue;
+            if (ct.title.endsWith("/fast"))
+                continue;
+            QMap<QString, QSharedPointer<CMakeRunConfiguration> >::iterator it =
+                    existingRunConfigurations.find(ct.title);
+            if (it != existingRunConfigurations.end()) {
+                // Already exists, so override the settings...
+                QSharedPointer<CMakeRunConfiguration> rc = it.value();
+                qDebug()<<"Updating Run Configuration with title"<<ct.title;
+                qDebug()<<"  Executable new:"<<ct.executable<< "old:"<<rc->executable();
+                qDebug()<<"  WD new:"<<ct.workingDirectory<<"old:"<<rc->workingDirectory();
+                rc->setExecutable(ct.executable);
+                rc->setWorkingDirectory(ct.workingDirectory);
+                existingRunConfigurations.erase(it);
+            } else {
+                // Does not exist yet
+                qDebug()<<"Adding new run configuration with title"<<ct.title;
+                qDebug()<<"  Executable:"<<ct.executable<<"WD:"<<ct.workingDirectory;
+                QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(this, ct.executable, ct.workingDirectory, ct.title));
+                addRunConfiguration(rc);
+                // The first one gets the honour of beeing the active one
+                if (!setActive) {
+                    setActiveRunConfiguration(rc);
+                    setActive = true;
+                }
+            }
+        }
+        QMap<QString, QSharedPointer<CMakeRunConfiguration> >::const_iterator it =
+                existingRunConfigurations.constBegin();
+        for( ; it != existingRunConfigurations.constEnd(); ++it) {
+            QSharedPointer<CMakeRunConfiguration> rc = it.value();
+            qDebug()<<"Removing old RunConfiguration with title:"<<rc->title();
+            qDebug()<<"  Executable:"<<rc->executable()<<rc->workingDirectory();
+            removeRunConfiguration(rc);
+        }
+        qDebug()<<"\n";
     } else {
         // TODO report error
+        qDebug()<<"Parsing failed";
         delete m_toolChain;
         m_toolChain = 0;
     }
@@ -341,31 +396,27 @@ void CMakeProject::restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader
         // We have a user file, but we could still be missing the cbp file
         // TODO check that we have a cbp file and if not, open up a dialog ?
         // or simply run createXml with the saved settings
-
+        QFileInfo sourceFileInfo(m_fileName);
+        QStringList needToCreate;
+        QStringList needToUpdate;
+        foreach(const QString &buildConfiguration, buildConfigurations()) {
+            QString buildDirectory = value(buildConfiguration, "buildDirectory").toString();
+            QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory));
+            QFileInfo cbpFileFi(cbpFile);
+            if (!cbpFileFi.exists())
+                needToCreate << buildDirectory;
+            else if (cbpFileFi.lastModified() < sourceFileInfo.lastModified())
+                needToUpdate << buildDirectory;
+        }
+        if (!needToCreate.isEmpty() || !needToUpdate.isEmpty()) {
+            CMakeOpenProjectWizard copw(m_manager, sourceFileInfo.absolutePath(), needToCreate, needToUpdate);
+            copw.exec();
+        }
     }
 
     parseCMakeLists(); // Gets the directory from the active buildconfiguration
-
-    if (!hasUserFile) {
-        // Create run configurations for m_targets
-        qDebug()<<"Create run configurations of m_targets";
-        bool setActive = false;
-        foreach(const CMakeTarget &ct, m_targets) {
-            if (ct.executable.isEmpty())
-                continue;
-            QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(this, ct.executable, ct.workingDirectory));
-            addRunConfiguration(rc);
-            // The first one gets the honour of beeing the active one
-            if (!setActive) {
-                setActiveRunConfiguration(rc);
-                setActive = true;
-            }
-        }
-
-    }
 }
 
-
 CMakeFile::CMakeFile(CMakeProject *parent, QString fileName)
     : Core::IFile(parent), m_project(parent), m_fileName(fileName)
 {
diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
index 73007ad7d16..c86d50f0b06 100644
--- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.cpp
@@ -38,10 +38,10 @@
 using namespace CMakeProjectManager;
 using namespace CMakeProjectManager::Internal;
 
-CMakeRunConfiguration::CMakeRunConfiguration(CMakeProject *pro, const QString &target, const QString &workingDirectory)
-    : ProjectExplorer::ApplicationRunConfiguration(pro), m_runMode(Gui), m_target(target), m_workingDirectory(workingDirectory)
+CMakeRunConfiguration::CMakeRunConfiguration(CMakeProject *pro, const QString &target, const QString &workingDirectory, const QString &title)
+    : ProjectExplorer::ApplicationRunConfiguration(pro), m_runMode(Gui), m_target(target), m_workingDirectory(workingDirectory), m_title(title)
 {
-    setName(target);
+    setName(title);
 }
 
 CMakeRunConfiguration::~CMakeRunConfiguration()
@@ -80,12 +80,28 @@ ProjectExplorer::Environment CMakeRunConfiguration::environment() const
     return ProjectExplorer::Environment::systemEnvironment();
 }
 
+QString CMakeRunConfiguration::title() const
+{
+    return m_title;
+}
+
+void CMakeRunConfiguration::setExecutable(const QString &executable)
+{
+    m_target = executable;
+}
+
+void CMakeRunConfiguration::setWorkingDirectory(const QString &workingDirectory)
+{
+    m_workingDirectory = workingDirectory;
+}
+
 void CMakeRunConfiguration::save(ProjectExplorer::PersistentSettingsWriter &writer) const
 {
     ProjectExplorer::ApplicationRunConfiguration::save(writer);
     writer.saveValue("CMakeRunConfiguration.Target", m_target);
     writer.saveValue("CMakeRunConfiguration.WorkingDirectory", m_workingDirectory);
     writer.saveValue("CMakeRunConfiguration.UseTerminal", m_runMode == Console);
+    writer.saveValue("CMakeRunConfiguation.Title", m_title);
 }
 
 void CMakeRunConfiguration::restore(const ProjectExplorer::PersistentSettingsReader &reader)
@@ -94,6 +110,7 @@ void CMakeRunConfiguration::restore(const ProjectExplorer::PersistentSettingsRea
     m_target = reader.restoreValue("CMakeRunConfiguration.Target").toString();
     m_workingDirectory = reader.restoreValue("CMakeRunConfiguration.WorkingDirectory").toString();
     m_runMode = reader.restoreValue("CMakeRunConfiguration.UseTerminal").toBool() ? Console : Gui;
+    m_title = reader.restoreValue("CMakeRunConfiguation.Title").toString();
 }
 
 QWidget *CMakeRunConfiguration::configurationWidget()
@@ -148,12 +165,13 @@ QSharedPointer<ProjectExplorer::RunConfiguration> CMakeRunConfigurationFactory::
     Q_ASSERT(pro);
     if (type == Constants::CMAKERUNCONFIGURATION) {
         // Restoring, filename will be added by restoreSettings
-        QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(pro, QString::null, QString::null));
+        QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(pro, QString::null, QString::null, QString::null));
         return rc;
     } else {
         // Adding new
+        // TODO extract target from type
         QString file = type.mid(QString(Constants::CMAKERUNCONFIGURATION).length());
-        QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(pro, file, QString::null));
+        QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(pro, file, QString::null, QString::null));
         return rc;
     }
 }
diff --git a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h
index 77bbbc7b920..d2a142ff9f4 100644
--- a/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h
+++ b/src/plugins/cmakeprojectmanager/cmakerunconfiguration.h
@@ -42,7 +42,7 @@ class CMakeProject;
 class CMakeRunConfiguration : public ProjectExplorer::ApplicationRunConfiguration
 {
 public:
-    CMakeRunConfiguration(CMakeProject *pro, const QString &target, const QString &workingDirectory);
+    CMakeRunConfiguration(CMakeProject *pro, const QString &target, const QString &workingDirectory, const QString &title);
     virtual ~CMakeRunConfiguration();
     virtual QString type() const;
     virtual QString executable() const;
@@ -52,12 +52,18 @@ public:
     virtual ProjectExplorer::Environment environment() const;
     virtual QWidget *configurationWidget();
 
+    void setExecutable(const QString &executable);
+    void setWorkingDirectory(const QString &workingDirectory);
+
+    QString title() const;
+
     virtual void save(ProjectExplorer::PersistentSettingsWriter &writer) const;
     virtual void restore(const ProjectExplorer::PersistentSettingsReader &reader);
 private:
     RunMode m_runMode;
     QString m_target;
     QString m_workingDirectory;
+    QString m_title;
 };
 
 /* The run configuration factory is used for restoring run configurations from
diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp
index 3549df23a6e..3018da15cce 100644
--- a/src/plugins/qt4projectmanager/qt4project.cpp
+++ b/src/plugins/qt4projectmanager/qt4project.cpp
@@ -256,6 +256,7 @@ Qt4Project::~Qt4Project()
     m_manager->unregisterProject(this);
     delete m_projectFiles;
     delete m_toolChain;
+    m_toolChain = 0;
 }
 
 void Qt4Project::defaultQtVersionChanged()
-- 
GitLab