Newer
Older
/**************************************************************************
**
** This file is part of Qt Creator
**
** Contact: Nokia Corporation (qt-info@nokia.com)
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
/// TODO
/// To check
/// a) with an old cmake
/// => should not show combobox always use mingw generator
/// b) with an new cmake
/// always show combo box, defaulting if there's already a existing build
#include "cmakeopenprojectwizard.h"
#include "cmakeprojectmanager.h"
#include <utils/pathchooser.h>
#include <QtGui/QVBoxLayout>
#include <QtGui/QFormLayout>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QPlainTextEdit>
#include <QtCore/QDateTime>
using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
///////
// Page Flow:
// Start (No .user file)
// |
// |---> In Source Build --> Page: Tell the user about that
// |--> Already existing cbp file (and new enough) --> Page: Ready to load the project
// |--> Page: Ask for cmd options, run generator
// |---> No in source Build --> Page: Ask the user for the build directory
// |--> Already existing cbp file (and new enough) --> Page: Ready to load the project
// |--> Page: Ask for cmd options, run generator
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory, const Utils::Environment &env)
m_creatingCbpFiles(false),
m_environment(env)
{
int startid;
if (hasInSourceBuild()) {
startid = InSourcePageId;
m_buildDirectory = m_sourceDirectory;
} else {
startid = ShadowBuildPageId;
QDir dir(m_sourceDirectory);
dir.cdUp();
m_buildDirectory = dir.absolutePath() + "/qtcreator-build";
}
setPage(InSourcePageId, new InSourceBuildPage(this));
setPage(ShadowBuildPageId, new ShadowBuildPage(this));
setPage(CMakeRunPageId, new CMakeRunPage(this));
Utils::WizardProgress *wp = wizardProgress();
Utils::WizardProgressItem *inSourceItem = wp->item(InSourcePageId);
Utils::WizardProgressItem *shadowBuildItem = wp->item(ShadowBuildPageId);
Utils::WizardProgressItem *cmakeRunItem = wp->item(CMakeRunPageId);
inSourceItem->setNextItems(QList<Utils::WizardProgressItem *>() << cmakeRunItem);
shadowBuildItem->setNextItems(QList<Utils::WizardProgressItem *>() << cmakeRunItem);
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory,
const QString &buildDirectory, CMakeOpenProjectWizard::Mode mode,
const Utils::Environment &env)
: m_cmakeManager(cmakeManager),
m_sourceDirectory(sourceDirectory),
m_creatingCbpFiles(true),
m_environment(env)
CMakeRunPage::Mode rmode;
if (mode == CMakeOpenProjectWizard::NeedToCreate)
rmode = CMakeRunPage::Recreate;
else if (mode == CMakeOpenProjectWizard::WantToUpdate)
rmode = CMakeRunPage::WantToUpdate;
rmode = CMakeRunPage::NeedToUpdate;
addPage(new CMakeRunPage(this, rmode, buildDirectory));
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory,
const QString &oldBuildDirectory,
const Utils::Environment &env)
: m_cmakeManager(cmakeManager),
m_sourceDirectory(sourceDirectory),
m_creatingCbpFiles(true),
m_environment(env)
{
m_buildDirectory = oldBuildDirectory;
addPage(new ShadowBuildPage(this, true));
addPage(new CMakeRunPage(this, CMakeRunPage::ChangeDirectory));
init();
}
void CMakeOpenProjectWizard::init()
{
setOption(QWizard::NoBackButtonOnStartPage);
CMakeManager *CMakeOpenProjectWizard::cmakeManager() const
{
return m_cmakeManager;
}
int CMakeOpenProjectWizard::nextId() const
{
if (m_creatingCbpFiles)
return QWizard::nextId();
int cid = currentId();
if (cid == InSourcePageId) {
return CMakeRunPageId;
return CMakeRunPageId;
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
}
return -1;
}
bool CMakeOpenProjectWizard::hasInSourceBuild() const
{
QFileInfo fi(m_sourceDirectory + "/CMakeCache.txt");
if (fi.exists())
return true;
return false;
}
bool CMakeOpenProjectWizard::existsUpToDateXmlFile() const
{
QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory()));
if (!cbpFile.isEmpty()) {
// We already have a cbp file
QFileInfo cbpFileInfo(cbpFile);
QFileInfo cmakeListsFileInfo(sourceDirectory() + "/CMakeLists.txt");
if (cbpFileInfo.lastModified() > cmakeListsFileInfo.lastModified())
return true;
}
return false;
}
QString CMakeOpenProjectWizard::buildDirectory() const
{
return m_buildDirectory;
}
QString CMakeOpenProjectWizard::sourceDirectory() const
{
return m_sourceDirectory;
}
void CMakeOpenProjectWizard::setBuildDirectory(const QString &directory)
{
m_buildDirectory = directory;
}
QString CMakeOpenProjectWizard::msvcVersion() const
{
return m_msvcVersion;
}
void CMakeOpenProjectWizard::setMsvcVersion(const QString &version)
{
m_msvcVersion = version;
}
QString CMakeOpenProjectWizard::arguments() const
void CMakeOpenProjectWizard::setArguments(const QString &args)
Utils::Environment CMakeOpenProjectWizard::environment() const
{
return m_environment;
}
InSourceBuildPage::InSourceBuildPage(CMakeOpenProjectWizard *cmakeWizard)
: QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
setLayout(new QVBoxLayout);
QLabel *label = new QLabel(this);
label->setWordWrap(true);
label->setText(tr("Qt Creator has detected an <b>in-source-build in %1</b> "
"which prevents shadow builds. Qt Creator will not allow you to change the build directory. "
"If you want a shadow build, clean your source directory and re-open the project.")
.arg(m_cmakeWizard->buildDirectory()));
ShadowBuildPage::ShadowBuildPage(CMakeOpenProjectWizard *cmakeWizard, bool change)
: QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
QFormLayout *fl = new QFormLayout;
this->setLayout(fl);
QLabel *label = new QLabel(this);
label->setWordWrap(true);
if (change)
label->setText(tr("Please enter the directory in which you want to build your project. "));
else
label->setText(tr("Please enter the directory in which you want to build your project. "
"Qt Creator recommends to not use the source directory for building. "
"This ensures that the source directory remains clean and enables multiple builds "
"with different settings."));
m_pc = new Utils::PathChooser(this);
m_pc->setBaseDirectory(m_cmakeWizard->sourceDirectory());
m_pc->setPath(m_cmakeWizard->buildDirectory());
connect(m_pc, SIGNAL(changed(QString)), this, SLOT(buildDirectoryChanged()));
fl->addRow(tr("Build directory:"), m_pc);
}
void ShadowBuildPage::buildDirectoryChanged()
{
m_cmakeWizard->setBuildDirectory(m_pc->path());
}
CMakeRunPage::CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard, Mode mode, const QString &buildDirectory)
: QWizardPage(cmakeWizard),
m_cmakeWizard(cmakeWizard),
m_complete(false),
m_mode(mode),
m_buildDirectory(buildDirectory)
{
initWidgets();
}
void CMakeRunPage::initWidgets()
{
QFormLayout *fl = new QFormLayout;
setLayout(fl);
// Description Label
m_descriptionLabel = new QLabel(this);
m_descriptionLabel->setWordWrap(true);
fl->addRow(m_descriptionLabel);
if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
m_cmakeExecutable = 0;
} else {
QString text = tr("Please specify the path to the CMake executable. No CMake executable was found in the path.");
QString cmakeExecutable = m_cmakeWizard->cmakeManager()->cmakeExecutable();
if (!cmakeExecutable.isEmpty()) {
QFileInfo fi(cmakeExecutable);
if (!fi.exists())
text += tr(" The CMake executable (%1) does not exist.").arg(cmakeExecutable);
else if (!fi.isExecutable())
text += tr(" The path %1 is not a executable.").arg(cmakeExecutable);
else
text += tr(" The path %1 is not a valid CMake.").arg(cmakeExecutable);
}
fl->addRow(new QLabel(text, this));
// Show a field for the user to enter
m_cmakeExecutable = new Utils::PathChooser(this);
m_cmakeExecutable->setExpectedKind(Utils::PathChooser::ExistingCommand);
fl->addRow("CMake Executable", m_cmakeExecutable);
}
// Run CMake Line (with arguments)
m_argumentsLineEdit = new QLineEdit(this);
connect(m_argumentsLineEdit,SIGNAL(returnPressed()), this, SLOT(runCMake()));
m_generatorComboBox = new QComboBox(this);
connect(m_runCMake, SIGNAL(clicked()), this, SLOT(runCMake()));
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(m_argumentsLineEdit);
hbox->addWidget(m_generatorComboBox);
hbox->addWidget(m_runCMake);
fl->addRow(tr("Arguments"), hbox);
// Bottom output window
m_output->setReadOnly(true);
QSizePolicy pl = m_output->sizePolicy();
pl.setVerticalStretch(1);
m_output->setSizePolicy(pl);
m_exitCodeLabel = new QLabel(this);
m_exitCodeLabel->setVisible(false);
fl->addRow(m_exitCodeLabel);
void CMakeRunPage::initializePage()
{
m_complete = m_cmakeWizard->existsUpToDateXmlFile();
m_buildDirectory = m_cmakeWizard->buildDirectory();
if (m_cmakeWizard->existsUpToDateXmlFile()) {
m_descriptionLabel->setText(
tr("The directory %1 already contains a cbp file, which is recent enough. "
"You can pass special arguments or change the used toolchain here and rerun CMake. "
"Or simply finish the wizard directly.").arg(m_buildDirectory));
} else {
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));
} else if (m_mode == CMakeRunPage::NeedToUpdate) {
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 below. Note that CMake remembers command "
"line arguments from the previous runs.").arg(m_buildDirectory));
} else if(m_mode == CMakeRunPage::Recreate) {
m_descriptionLabel->setText(tr("The directory %1 specified in a build-configuration, "
"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. Note that CMake remembers command "
"line arguments from the previous runs.").arg(m_buildDirectory));
} else if(m_mode == CMakeRunPage::ChangeDirectory) {
m_buildDirectory = m_cmakeWizard->buildDirectory();
m_descriptionLabel->setText(tr("Qt Creator needs to run CMake in the new build directory. "
"Some projects require command line arguments to the "
"initial CMake call."));
} else if (m_mode == CMakeRunPage::WantToUpdate) {
m_descriptionLabel->setText(tr("Refreshing cbp file in %1.").arg(m_buildDirectory));
if (m_cmakeWizard->cmakeManager()->hasCodeBlocksMsvcGenerator()) {
m_generatorComboBox->setVisible(true);
// Try to find out generator from CMakeCachhe file, if it exists
QFile fi(m_buildDirectory + "/CMakeCache.txt");
if (fi.exists()) {
// Cache exists, then read it...
if (fi.open(QIODevice::ReadOnly | QIODevice::Text)) {
while (!fi.atEnd()) {
QString line = fi.readLine();
if (line.startsWith("CMAKE_GENERATOR:INTERNAL=")) {
int splitpos = line.indexOf('=');
if (splitpos != -1) {
cachedGenerator = line.mid(splitpos + 1).trimmed();
}
break;
}
}
}
// Find out whether we have multiple msvc versions
QStringList msvcVersions = ProjectExplorer::ToolChain::availableMSVCVersions();
if (msvcVersions.isEmpty()) {
} else if (msvcVersions.count() == 1) {
m_generatorComboBox->addItem(tr("NMake Generator"), msvcVersions.first());
} else {
foreach (const QString &msvcVersion, msvcVersions)
m_generatorComboBox->addItem(tr("NMake Generator (%1)").arg(msvcVersion), msvcVersion);
if (cachedGenerator == "NMake Makefiles" && !msvcVersions.isEmpty()) {
m_cmakeWizard->setMsvcVersion(msvcVersions.first());
}
m_generatorComboBox->addItem(tr("MinGW Generator"), "mingw");
if (cachedGenerator == "MinGW Makefiles") {
m_generatorComboBox->setCurrentIndex(m_generatorComboBox->count() - 1);
m_cmakeWizard->setMsvcVersion("");
}
} else {
// No new enough cmake, simply hide the combo box
m_generatorComboBox->setVisible(false);
}
void CMakeRunPage::runCMake()
{
m_runCMake->setEnabled(false);
m_argumentsLineEdit->setEnabled(false);
CMakeManager *cmakeManager = m_cmakeWizard->cmakeManager();
#ifdef Q_OS_WIN
m_cmakeWizard->setMsvcVersion(QString());
QString generator = QLatin1String("-GCodeBlocks - MinGW Makefiles");
if (m_generatorComboBox->isVisible()) {
// the combobox is shown, check which generator is selected
int index = m_generatorComboBox->currentIndex();
if (index != -1) {
QString version = m_generatorComboBox->itemData(index).toString();
if (version != "mingw") {
generator = "-GCodeBlocks - NMake Makefiles";
m_cmakeWizard->setMsvcVersion(version);
} else {
m_cmakeWizard->setMsvcVersion("");
#else // Q_OS_WIN
QString generator = QLatin1String("-GCodeBlocks - Unix Makefiles");
Utils::Environment env = m_cmakeWizard->environment();
if (!m_cmakeWizard->msvcVersion().isEmpty()) {
// Add the environment of that msvc version to environment
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChain::createMSVCToolChain(m_cmakeWizard->msvcVersion(), false);
tc->addToEnvironment(env);
delete tc;
}
if (m_cmakeExecutable) {
// We asked the user for the cmake executable
m_cmakeWizard->cmakeManager()->setCMakeExecutable(m_cmakeExecutable->path());
}
m_output->clear();
if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
m_cmakeProcess = new Utils::QtcProcess();
connect(m_cmakeProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(cmakeReadyReadStandardOutput()));
connect(m_cmakeProcess, SIGNAL(readyReadStandardError()), this, SLOT(cmakeReadyReadStandardError()));
connect(m_cmakeProcess, SIGNAL(finished(int)), this, SLOT(cmakeFinished()));
cmakeManager->createXmlFile(m_cmakeProcess, m_argumentsLineEdit->text(), m_cmakeWizard->sourceDirectory(), m_buildDirectory, env, generator);
} else {
m_runCMake->setEnabled(true);
m_argumentsLineEdit->setEnabled(true);
m_output->appendPlainText(tr("No valid CMake executable specified."));
static QColor mix_colors(QColor a, QColor b)
{
return QColor((a.red() + 2 * b.red()) / 3, (a.green() + 2 * b.green()) / 3,
(a.blue() + 2* b.blue()) / 3, (a.alpha() + 2 * b.alpha()) / 3);
}
void CMakeRunPage::cmakeReadyReadStandardOutput()
{
QTextCursor cursor(m_output->document());
QTextCharFormat tf;
QFont font = m_output->font();
tf.setFont(font);
tf.setForeground(m_output->palette().color(QPalette::Text));
cursor.insertText(m_cmakeProcess->readAllStandardOutput(), tf);
}
void CMakeRunPage::cmakeReadyReadStandardError()
QTextCursor cursor(m_output->document());
QTextCharFormat tf;
QFont font = m_output->font();
QFont boldFont = font;
boldFont.setBold(true);
tf.setFont(boldFont);
tf.setForeground(mix_colors(m_output->palette().color(QPalette::Text), QColor(Qt::red)));
cursor.insertText(m_cmakeProcess->readAllStandardError(), tf);
}
void CMakeRunPage::cmakeFinished()
{
m_runCMake->setEnabled(true);
m_argumentsLineEdit->setEnabled(true);
if (m_cmakeProcess->exitCode() != 0) {
m_exitCodeLabel->setVisible(true);
m_exitCodeLabel->setText(tr("CMake exited with errors. Please check cmake output."));
m_complete = false;
} else {
m_exitCodeLabel->setVisible(false);
m_complete = true;
}
m_cmakeProcess->deleteLater();
m_cmakeProcess = 0;
m_cmakeWizard->setArguments(m_argumentsLineEdit->text());
//TODO Actually test that running cmake was finished, for setting this bool
emit completeChanged();
}
void CMakeRunPage::cleanupPage()
{
m_output->clear();
m_complete = false;
m_exitCodeLabel->setVisible(false);
emit completeChanged();
}
bool CMakeRunPage::isComplete() const
{
return m_complete;
}