cmakebuilddirchooser.cpp 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* KDevelop CMake Support
 *
 * Copyright 2007 Aleix Pol <aleixpol@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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 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 "cmakebuilddirchooser.h"
Kevin Funk's avatar
Kevin Funk committed
22
#include "ui_cmakebuilddirchooser.h"
23
#include "cmakeutils.h"
Kevin Funk's avatar
Kevin Funk committed
24 25
#include "debug.h"

26
#include <project/helper.h>
27
#include <interfaces/iproject.h>
28 29 30
#include <interfaces/icore.h>
#include <interfaces/iruntime.h>
#include <interfaces/iruntimecontroller.h>
31

Kevin Funk's avatar
Kevin Funk committed
32
#include <KColorScheme>
33
#include <KLocalizedString>
34

Kevin Funk's avatar
Kevin Funk committed
35
#include <QDir>
36
#include <QDialogButtonBox>
Kevin Funk's avatar
Kevin Funk committed
37
#include <QVBoxLayout>
38

Milian Wolff's avatar
Milian Wolff committed
39 40
using namespace KDevelop;

41
CMakeBuildDirChooser::CMakeBuildDirChooser(QWidget* parent)
Kevin Funk's avatar
Kevin Funk committed
42
    : QDialog(parent)
43
{
44
    setWindowTitle(i18n("Configure a build directory - %1", ICore::self()->runtimeController()->currentRuntime()->name()));
Kevin Funk's avatar
Kevin Funk committed
45 46 47 48 49 50 51 52 53 54

    m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
    connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);

    auto mainWidget = new QWidget(this);
    auto mainLayout = new QVBoxLayout;
    setLayout(mainLayout);
    mainLayout->addWidget(mainWidget);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
55

56
    m_chooserUi = new Ui::CMakeBuildDirChooser;
Kevin Funk's avatar
Kevin Funk committed
57
    m_chooserUi->setupUi(mainWidget);
58
    setShowAvailableBuildDirs(false);
Kevin Funk's avatar
Kevin Funk committed
59 60
    mainLayout->addWidget(m_buttonBox);

61
    m_chooserUi->buildFolder->setMode(KFile::Directory|KFile::ExistingOnly);
62
    m_chooserUi->installPrefix->setMode(KFile::Directory|KFile::ExistingOnly);
63

64
    m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_chooserUi->extraArguments);
65

66 67 68
    connect(m_chooserUi->buildFolder, &KUrlRequester::textChanged, this, &CMakeBuildDirChooser::updated);
    connect(m_chooserUi->buildType, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::updated);
    connect(m_chooserUi->extraArguments, &KComboBox::editTextChanged, this, &CMakeBuildDirChooser::updated);
Alex Richardson's avatar
Alex Richardson committed
69
    connect(m_chooserUi->availableBuildDirs, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::adoptPreviousBuildDirectory);
Kevin Funk's avatar
Kevin Funk committed
70

71 72 73 74 75
    const auto defaultInstallPrefix = ICore::self()->runtimeController()->currentRuntime()->getenv("KDEV_DEFAULT_INSTALL_PREFIX");
    if (!defaultInstallPrefix.isEmpty()) {
        m_chooserUi->installPrefix->setPath(QFile::decodeName(defaultInstallPrefix));
    }

76 77 78
    updated();
}

79 80
CMakeBuildDirChooser::~CMakeBuildDirChooser()
{
81
    delete m_extraArgumentsHistory;
Milian Wolff's avatar
Milian Wolff committed
82 83

    delete m_chooserUi;
84 85
}

86
void CMakeBuildDirChooser::setProject( IProject* project )
87
{
88
    m_project = project;
89

90 91 92 93 94 95 96
    KDevelop::Path folder = m_project->path();
    QString relative=CMake::projectRootRelative(m_project);
    folder.cd(relative);
    m_srcFolder = folder;

    m_chooserUi->buildFolder->setUrl(KDevelop::proposedBuildFolder(m_srcFolder).toUrl());
    setWindowTitle(i18n("Configure a build directory for %1", project->name()));
97 98 99
    update();
}

100 101 102 103 104
void CMakeBuildDirChooser::buildDirSettings(
    const KDevelop::Path& buildDir,
    QString& srcDir,
    QString& installDir,
    QString& buildType)
105
{
106 107 108 109
    static const QString srcLine = QStringLiteral("CMAKE_HOME_DIRECTORY:INTERNAL=");
    static const QString installLine = QStringLiteral("CMAKE_INSTALL_PREFIX:PATH=");
    static const QString buildLine = QStringLiteral("CMAKE_BUILD_TYPE:STRING=");

110
    const Path cachePath(buildDir, QStringLiteral("CMakeCache.txt"));
Milian Wolff's avatar
Milian Wolff committed
111
    QFile file(cachePath.toLocalFile());
112

113 114 115 116
    srcDir.clear();
    installDir.clear();
    buildType.clear();

117 118
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
Dāvis Mosāns's avatar
Dāvis Mosāns committed
119
        qCWarning(CMAKE) << "Something really strange happened reading" << cachePath;
120
        return;
121 122
    }

123 124
    int cnt = 0;
    while (cnt != 3 && !file.atEnd())
125
    {
126 127
        // note: CMakeCache.txt is UTF8-encoded, also see bug 329305
        QString line = QString::fromUtf8(file.readLine().trimmed());
128 129

        if (line.startsWith(srcLine))
130
        {
131 132 133 134 135 136 137 138 139 140 141 142 143 144
            srcDir = line.mid(srcLine.count());
            ++cnt;
        }

        if (line.startsWith(installLine))
        {
            installDir = line.mid(installLine.count());
            ++cnt;
        }

        if (line.startsWith(buildLine))
        {
            buildType = line.mid(buildLine.count());
            ++cnt;
145 146
        }
    }
147 148 149 150

    qCDebug(CMAKE) << "The source directory for " << file.fileName() << "is" << srcDir;
    qCDebug(CMAKE) << "The install directory for " << file.fileName() << "is" << installDir;
    qCDebug(CMAKE) << "The build type for " << file.fileName() << "is" << buildType;
151 152 153 154 155
}

void CMakeBuildDirChooser::updated()
{
    StatusTypes st;
Milian Wolff's avatar
Milian Wolff committed
156
    Path chosenBuildFolder(m_chooserUi->buildFolder->url());
157
    bool emptyUrl = chosenBuildFolder.isEmpty();
158 159
    if( emptyUrl ) st |= BuildFolderEmpty;

160
    bool dirEmpty = false, dirExists= false, dirRelative = false;
161
    QString srcDir;
162 163
    QString installDir;
    QString buildType;
164 165
    if(!emptyUrl)
    {
Milian Wolff's avatar
Milian Wolff committed
166
        QDir d(chosenBuildFolder.toLocalFile());
167
        dirExists = d.exists();
168 169 170
        dirEmpty = dirExists && d.count()<=2;
        dirRelative = d.isRelative();
        if(!dirEmpty && dirExists && !dirRelative)
171
        {
172
            bool hasCache=QFile::exists(Path(chosenBuildFolder, QStringLiteral("CMakeCache.txt")).toLocalFile());
173
            if(hasCache)
174
            {
Milian Wolff's avatar
Milian Wolff committed
175
                QString proposed=m_srcFolder.toLocalFile();
176

177
                buildDirSettings(chosenBuildFolder, srcDir, installDir, buildType);
178 179 180
                if(!srcDir.isEmpty())
                {
                    if(QDir(srcDir).canonicalPath()==QDir(proposed).canonicalPath())
181 182 183
                    {
                            st |= CorrectBuildDir | BuildDirCreated;
                    }
184 185 186
                }
                else
                {
Dāvis Mosāns's avatar
Dāvis Mosāns committed
187
                    qCWarning(CMAKE) << "maybe you are trying a damaged CMakeCache.txt file. Proper: ";
188
                }
189 190 191 192 193 194 195

                if(!installDir.isEmpty() && QDir(installDir).exists())
                {
                    m_chooserUi->installPrefix->setUrl(QUrl::fromLocalFile(installDir));
                }

                m_chooserUi->buildType->setCurrentText(buildType);
196 197
            }
        }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
198

199
        if(m_alreadyUsed.contains(chosenBuildFolder.toLocalFile()) && !m_chooserUi->availableBuildDirs->isEnabled()) {
200 201
            st=DirAlreadyCreated;
        }
202 203
    }
    else
204
    {
Milian Wolff's avatar
Milian Wolff committed
205
        setStatus(i18n("You need to specify a build directory."), false);
206 207
        return;
    }
Dāvis Mosāns's avatar
Dāvis Mosāns committed
208 209


210
    if(st & (BuildDirCreated | CorrectBuildDir))
211
    {
Milian Wolff's avatar
Milian Wolff committed
212
        setStatus(i18n("Using an already created build directory."), true);
213 214
        m_chooserUi->installPrefix->setEnabled(false);
        m_chooserUi->buildType->setEnabled(false);
215 216 217
    }
    else
    {
218
        bool correct = (dirEmpty || !dirExists) && !(st & DirAlreadyCreated) && !dirRelative;
Dāvis Mosāns's avatar
Dāvis Mosāns committed
219

220 221 222
        if(correct)
        {
            st |= CorrectBuildDir;
223
            setStatus(i18n("Creating a new build directory."), true);
224 225 226
        }
        else
        {
227
            //Useful to explain what's going wrong
228
            if(st & DirAlreadyCreated)
229
                setStatus(i18n("Build directory already configured."), false);
230
            else if (!srcDir.isEmpty())
231
                setStatus(i18n("This build directory is for %1, "
Milian Wolff's avatar
Milian Wolff committed
232
                               "but the project directory is %2.", srcDir, m_srcFolder.toLocalFile()), false);
233
            else if(dirRelative)
Milian Wolff's avatar
Milian Wolff committed
234
                setStatus(i18n("You may not select a relative build directory."), false);
235
            else if(!dirEmpty)
Milian Wolff's avatar
Milian Wolff committed
236
                setStatus(i18n("The selected build directory is not empty."), false);
237 238 239 240 241 242 243
        }

        m_chooserUi->installPrefix->setEnabled(correct);
        m_chooserUi->buildType->setEnabled(correct);
    }
}

244 245 246 247 248 249
void CMakeBuildDirChooser::setCMakeExecutable(const Path& path)
{
    m_chooserUi->cmakeExecutable->setUrl(path.toUrl());
    updated();
}

Milian Wolff's avatar
Milian Wolff committed
250
void CMakeBuildDirChooser::setInstallPrefix(const Path& path)
Dāvis Mosāns's avatar
Dāvis Mosāns committed
251
{
Milian Wolff's avatar
Milian Wolff committed
252
    m_chooserUi->installPrefix->setUrl(path.toUrl());
253
    updated();
254 255
}

Milian Wolff's avatar
Milian Wolff committed
256
void CMakeBuildDirChooser::setBuildFolder(const Path& path)
Dāvis Mosāns's avatar
Dāvis Mosāns committed
257
{
Milian Wolff's avatar
Milian Wolff committed
258
    m_chooserUi->buildFolder->setUrl(path.toUrl());
259
    updated();
260 261
}

Dāvis Mosāns's avatar
Dāvis Mosāns committed
262
void CMakeBuildDirChooser::setBuildType(const QString& s)
263 264 265
{
    m_chooserUi->buildType->addItem(s);
    m_chooserUi->buildType->setCurrentIndex(m_chooserUi->buildType->findText(s));
266 267 268 269 270
    updated();
}

void CMakeBuildDirChooser::setAlreadyUsed (const QStringList & used)
{
271
    m_chooserUi->availableBuildDirs->addItems(used);
272 273
    m_alreadyUsed = used;
    updated();
274 275
}

276 277
void CMakeBuildDirChooser::setExtraArguments(const QString& args)
{
278
    m_chooserUi->extraArguments->setEditText(args);
279 280 281
    updated();
}

282 283 284 285 286 287 288 289 290
void CMakeBuildDirChooser::setStatus(const QString& message, bool canApply)
{
    KColorScheme scheme(QPalette::Normal);
    KColorScheme::ForegroundRole role;
    if (canApply) {
        role = KColorScheme::PositiveText;
    } else {
        role = KColorScheme::NegativeText;
    }
291
    m_chooserUi->status->setText(QStringLiteral("<i><font color='%1'>%2</font></i>").arg(scheme.foreground(role).color().name()).arg(message));
292

Kevin Funk's avatar
Kevin Funk committed
293 294
    auto okButton = m_buttonBox->button(QDialogButtonBox::Ok);
    okButton->setEnabled(canApply);
295
    if (canApply) {
Kevin Funk's avatar
Kevin Funk committed
296 297
        auto cancelButton = m_buttonBox->button(QDialogButtonBox::Cancel);
        cancelButton->clearFocus();
298
    }
299 300
}

301 302 303 304
void CMakeBuildDirChooser::adoptPreviousBuildDirectory(int index)
{
    if (index > 0) {
        Q_ASSERT(m_project);
305
        m_chooserUi->cmakeExecutable->setUrl(CMake::currentCMakeExecutable(m_project, index -1).toUrl());
306 307 308 309 310 311
        m_chooserUi->buildFolder->setUrl(CMake::currentBuildDir(m_project, index -1).toUrl());
        m_chooserUi->installPrefix->setUrl(CMake::currentInstallDir(m_project, index -1).toUrl());
        m_chooserUi->buildType->setCurrentText(CMake::currentBuildType(m_project, index -1));
        m_chooserUi->extraArguments->setCurrentText(CMake::currentExtraArguments(m_project, index -1));
    }

312 313
    m_chooserUi->label_5->setEnabled(index == 0);
    m_chooserUi->cmakeExecutable->setEnabled(index == 0);
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
    m_chooserUi->label_3->setEnabled(index == 0);
    m_chooserUi->buildFolder->setEnabled(index == 0);
    m_chooserUi->label->setEnabled(index == 0);
    m_chooserUi->installPrefix->setEnabled(index == 0);
    m_chooserUi->label_2->setEnabled(index == 0);
    m_chooserUi->buildType->setEnabled(index == 0);
    m_chooserUi->status->setEnabled(index == 0);
    m_chooserUi->extraArguments->setEnabled(index == 0);
    m_chooserUi->label_4->setEnabled(index == 0);
}

bool CMakeBuildDirChooser::reuseBuilddir()
{
    return m_chooserUi->availableBuildDirs->currentIndex() > 0;
}

int CMakeBuildDirChooser::alreadyUsedIndex() const
{
    return m_chooserUi->availableBuildDirs->currentIndex() - 1;
}

void CMakeBuildDirChooser::setShowAvailableBuildDirs(bool show)
{
    m_chooserUi->availableLabel->setVisible(show);
    m_chooserUi->availableBuildDirs->setVisible(show);
}

341 342
Path CMakeBuildDirChooser::cmakeExecutable() const { return Path(m_chooserUi->cmakeExecutable->url()); }

Milian Wolff's avatar
Milian Wolff committed
343
Path CMakeBuildDirChooser::installPrefix() const { return Path(m_chooserUi->installPrefix->url()); }
344

Milian Wolff's avatar
Milian Wolff committed
345
Path CMakeBuildDirChooser::buildFolder() const { return Path(m_chooserUi->buildFolder->url()); }
346 347 348

QString CMakeBuildDirChooser::buildType() const { return m_chooserUi->buildType->currentText(); }

349
QString CMakeBuildDirChooser::extraArguments() const { return m_chooserUi->extraArguments->currentText(); }