qmakebuildconfiguration.cpp 27.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
dt's avatar
dt committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
dt's avatar
dt committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
dt's avatar
dt committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
dt's avatar
dt committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
dt's avatar
dt committed
29

30
#include "qmakebuildconfiguration.h"
31

32
#include "qmakebuildinfo.h"
33
#include "qmakekitinformation.h"
34 35 36 37
#include "qmakeproject.h"
#include "qmakeprojectconfigwidget.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakenodes.h"
38 39
#include "qmakestep.h"
#include "makestep.h"
dt's avatar
dt committed
40

41
#include <utils/qtcprocess.h>
42
#include <utils/qtcassert.h>
43
#include <limits>
44 45
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
Tobias Hunger's avatar
Tobias Hunger committed
46 47
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
Tobias Hunger's avatar
Tobias Hunger committed
48
#include <projectexplorer/target.h>
dt_'s avatar
dt_ committed
49
#include <projectexplorer/toolchain.h>
50
#include <projectexplorer/toolchainmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
51
#include <qtsupport/qtkitinformation.h>
52
#include <qtsupport/qtversionmanager.h>
53
#include <utils/qtcassert.h>
54
#include <QDebug>
55

56
#include <QInputDialog>
57

58
namespace QmakeProjectManager {
59

60 61 62 63 64 65 66 67 68 69
// --------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------

static Utils::FileName defaultBuildDirectory(bool supportsShadowBuild,
                                             const QString &projectPath,
                                             const ProjectExplorer::Kit *k,
                                             const QString &suffix)
{
    if (supportsShadowBuild)
70
        return Utils::FileName::fromString(QmakeProject::shadowBuildDirectory(projectPath, k, suffix));
71
    return ProjectExplorer::Project::projectDirectory(Utils::FileName::fromString(projectPath));
72 73
}

hjk's avatar
hjk committed
74 75 76 77
using namespace Internal;
using namespace ProjectExplorer;
using namespace QtSupport;
using namespace Utils;
78

79
const char QMAKE_BC_ID[] = "Qt4ProjectManager.Qt4BuildConfiguration";
hjk's avatar
hjk committed
80 81
const char USE_SHADOW_BUILD_KEY[] = "Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild";
const char BUILD_CONFIGURATION_KEY[] = "Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration";
82

83
enum { debug = 0 };
dt's avatar
dt committed
84

85
QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target) :
86
    BuildConfiguration(target, Core::Id(QMAKE_BC_ID)),
87
    m_shadowBuild(true),
88
    m_isEnabled(false),
89
    m_qmakeBuildConfiguration(0),
Orgad Shaneh's avatar
Orgad Shaneh committed
90 91
    m_subNodeBuild(0),
    m_fileNodeBuild(0)
dt's avatar
dt committed
92
{
93
    ctor();
dt's avatar
dt committed
94 95
}

96
QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, const Core::Id id) :
Tobias Hunger's avatar
Tobias Hunger committed
97
    BuildConfiguration(target, id),
98
    m_shadowBuild(true),
99
    m_isEnabled(false),
100
    m_qmakeBuildConfiguration(0),
Orgad Shaneh's avatar
Orgad Shaneh committed
101 102
    m_subNodeBuild(0),
    m_fileNodeBuild(0)
dt's avatar
dt committed
103
{
104
    ctor();
dt's avatar
dt committed
105 106
}

107
QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, QmakeBuildConfiguration *source) :
Tobias Hunger's avatar
Tobias Hunger committed
108
    BuildConfiguration(target, source),
dt's avatar
dt committed
109
    m_shadowBuild(source->m_shadowBuild),
110
    m_isEnabled(false),
111
    m_qmakeBuildConfiguration(source->m_qmakeBuildConfiguration),
Orgad Shaneh's avatar
Orgad Shaneh committed
112 113
    m_subNodeBuild(0), // temporary value, so not copied
    m_fileNodeBuild(0)
dt's avatar
dt committed
114
{
Tobias Hunger's avatar
Tobias Hunger committed
115
    cloneSteps(source);
116
    ctor();
dt's avatar
dt committed
117 118
}

119
QmakeBuildConfiguration::~QmakeBuildConfiguration()
dt's avatar
dt committed
120
{
121
}
dt's avatar
dt committed
122

123
QVariantMap QmakeBuildConfiguration::toMap() const
124 125 126 127 128
{
    QVariantMap map(BuildConfiguration::toMap());
    map.insert(QLatin1String(USE_SHADOW_BUILD_KEY), m_shadowBuild);
    map.insert(QLatin1String(BUILD_CONFIGURATION_KEY), int(m_qmakeBuildConfiguration));
    return map;
dt's avatar
dt committed
129
}
130

131
bool QmakeBuildConfiguration::fromMap(const QVariantMap &map)
dt's avatar
dt committed
132
{
Tobias Hunger's avatar
Tobias Hunger committed
133 134 135
    if (!BuildConfiguration::fromMap(map))
        return false;

136
    m_shadowBuild = map.value(QLatin1String(USE_SHADOW_BUILD_KEY), true).toBool();
hjk's avatar
hjk committed
137
    m_qmakeBuildConfiguration = BaseQtVersion::QmakeBuildConfigs(map.value(QLatin1String(BUILD_CONFIGURATION_KEY)).toInt());
138

Tobias Hunger's avatar
Tobias Hunger committed
139
    m_qtVersionSupportsShadowBuilds =  supportsShadowBuilds();
140

141 142
    m_lastKitState = LastKitState(target()->kit());

Robert Loehning's avatar
Robert Loehning committed
143 144
    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainUpdated(ProjectExplorer::ToolChain*)),
            this, SLOT(toolChainUpdated(ProjectExplorer::ToolChain*)));
145 146
    connect(QtSupport::QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
            this, SLOT(qtVersionsChanged(QList<int>,QList<int>,QList<int>)));
Tobias Hunger's avatar
Tobias Hunger committed
147
    return true;
dt's avatar
dt committed
148 149
}

150
void QmakeBuildConfiguration::ctor()
151
{
152
    connect(this, SIGNAL(environmentChanged()),
153
            this, SLOT(emitProFileEvaluateNeeded()));
Tobias Hunger's avatar
Tobias Hunger committed
154 155
    connect(target(), SIGNAL(kitChanged()),
            this, SLOT(kitChanged()));
Tobias Hunger's avatar
Tobias Hunger committed
156
}
157

158
void QmakeBuildConfiguration::kitChanged()
Tobias Hunger's avatar
Tobias Hunger committed
159
{
160 161 162
    LastKitState newState = LastKitState(target()->kit());
    if (newState != m_lastKitState) {
        // This only checks if the ids have changed!
163
        // For that reason the QmakeBuildConfiguration is also connected
164 165
        // to the toolchain and qtversion managers
        emitProFileEvaluateNeeded();
166
        updateShadowBuild();
167 168 169 170
        m_lastKitState = newState;
    }
}

171
void QmakeBuildConfiguration::toolChainUpdated(ProjectExplorer::ToolChain *tc)
172 173 174 175 176
{
    if (ToolChainKitInformation::toolChain(target()->kit()) == tc)
        emitProFileEvaluateNeeded();
}

177
void QmakeBuildConfiguration::qtVersionsChanged(const QList<int> &,const QList<int> &, const QList<int> &changed)
178 179 180
{
    if (changed.contains(QtKitInformation::qtVersionId(target()->kit())))
        emitProFileEvaluateNeeded();
181 182
}

183
void QmakeBuildConfiguration::updateShadowBuild()
184
{
Sergio Ahumada's avatar
Sergio Ahumada committed
185
    // We also emit buildDirectoryChanged if the Qt version's supportShadowBuild changed
186 187 188
    bool currentShadowBuild = supportsShadowBuilds();
    if (currentShadowBuild != m_qtVersionSupportsShadowBuilds) {
        if (!currentShadowBuild)
189
            setBuildDirectory(target()->project()->projectDirectory());
190
        m_qtVersionSupportsShadowBuilds = currentShadowBuild;
191 192 193
    }
}

194
NamedWidget *QmakeBuildConfiguration::createConfigWidget()
Tobias Hunger's avatar
Tobias Hunger committed
195
{
196
    return new QmakeProjectConfigWidget(this);
Tobias Hunger's avatar
Tobias Hunger committed
197 198
}

199
QString QmakeBuildConfiguration::defaultShadowBuildDirectory() const
200 201
{
    // todo displayName isn't ideal
202 203
    return QmakeProject::shadowBuildDirectory(target()->project()->projectFilePath().toString(),
                                              target()->kit(), displayName());
204 205
}

206
bool QmakeBuildConfiguration::supportsShadowBuilds()
Tobias Hunger's avatar
Tobias Hunger committed
207
{
208
    BaseQtVersion *version = QtKitInformation::qtVersion(target()->kit());
Tobias Hunger's avatar
Tobias Hunger committed
209 210 211
    return !version || version->supportsShadowBuilds();
}

212 213
/// If only a sub tree should be build this function returns which sub node
/// should be build
214
/// \see QMakeBuildConfiguration::setSubNodeBuild
215
QmakeProFileNode *QmakeBuildConfiguration::subNodeBuild() const
216 217 218 219 220 221 222 223 224 225
{
    return m_subNodeBuild;
}

/// A sub node build on builds a sub node of the project
/// That is triggered by a right click in the project explorer tree
/// The sub node to be build is set via this function immediately before
/// calling BuildManager::buildProject( BuildConfiguration * )
/// and reset immediately afterwards
/// That is m_subNodesBuild is set only temporarly
226
void QmakeBuildConfiguration::setSubNodeBuild(QmakeProFileNode *node)
227 228 229 230
{
    m_subNodeBuild = node;
}

231
FileNode *QmakeBuildConfiguration::fileNodeBuild() const
Orgad Shaneh's avatar
Orgad Shaneh committed
232 233 234 235
{
    return m_fileNodeBuild;
}

236
void QmakeBuildConfiguration::setFileNodeBuild(FileNode *node)
Orgad Shaneh's avatar
Orgad Shaneh committed
237 238 239 240
{
    m_fileNodeBuild = node;
}

241 242
/// returns whether this is a shadow build configuration or not
/// note, even if shadowBuild() returns true, it might be using the
Tobias Hunger's avatar
Tobias Hunger committed
243 244
/// source directory as the shadow build directory, thus it
/// still is a in-source build
245
bool QmakeBuildConfiguration::isShadowBuild() const
246
{
247
    return buildDirectory() != target()->project()->projectDirectory();
248 249
}

250
void QmakeBuildConfiguration::setBuildDirectory(const FileName &directory)
251
{
252 253
    if (directory == buildDirectory())
        return;
254 255 256
    BuildConfiguration::setBuildDirectory(directory);
    QTC_CHECK(supportsShadowBuilds()
              || (!supportsShadowBuilds()
257
                  && buildDirectory() == target()->project()->projectDirectory()));
258
    emitProFileEvaluateNeeded();
259 260
}

261
QString QmakeBuildConfiguration::makefile() const
262
{
263
    return static_cast<QmakeProject *>(target()->project())->rootQmakeProjectNode()->makefile();
264 265
}

266
BaseQtVersion::QmakeBuildConfigs QmakeBuildConfiguration::qmakeBuildConfiguration() const
267
{
dt's avatar
dt committed
268
    return m_qmakeBuildConfiguration;
269 270
}

271
void QmakeBuildConfiguration::setQMakeBuildConfiguration(BaseQtVersion::QmakeBuildConfigs config)
272
{
dt's avatar
dt committed
273
    if (m_qmakeBuildConfiguration == config)
274
        return;
dt's avatar
dt committed
275
    m_qmakeBuildConfiguration = config;
dt's avatar
dt committed
276

277
    emit qmakeBuildConfigurationChanged();
278
    emitProFileEvaluateNeeded();
279 280
}

281
void QmakeBuildConfiguration::emitProFileEvaluateNeeded()
282
{
283 284 285
    Target *t = target();
    Project *p = t->project();
    if (t->activeBuildConfiguration() == this && p->activeTarget() == t)
286
        static_cast<QmakeProject *>(p)->scheduleAsyncUpdate();
287 288
}

289
void QmakeBuildConfiguration::emitQMakeBuildConfigurationChanged()
290 291 292 293
{
    emit qmakeBuildConfigurationChanged();
}

294
QStringList QmakeBuildConfiguration::configCommandLineArguments() const
295
{
296
    QStringList result;
297
    BaseQtVersion *version = QtKitInformation::qtVersion(target()->kit());
hjk's avatar
hjk committed
298
    BaseQtVersion::QmakeBuildConfigs defaultBuildConfiguration =
299
            version ? version->defaultBuildConfig() : BaseQtVersion::QmakeBuildConfigs(BaseQtVersion::DebugBuild | BaseQtVersion::BuildAll);
hjk's avatar
hjk committed
300 301
    BaseQtVersion::QmakeBuildConfigs userBuildConfiguration = m_qmakeBuildConfiguration;
    if ((defaultBuildConfiguration & BaseQtVersion::BuildAll) && !(userBuildConfiguration & BaseQtVersion::BuildAll))
302
        result << QLatin1String("CONFIG-=debug_and_release");
303

hjk's avatar
hjk committed
304
    if (!(defaultBuildConfiguration & BaseQtVersion::BuildAll) && (userBuildConfiguration & BaseQtVersion::BuildAll))
305
        result << QLatin1String("CONFIG+=debug_and_release");
hjk's avatar
hjk committed
306
    if ((defaultBuildConfiguration & BaseQtVersion::DebugBuild) && !(userBuildConfiguration & BaseQtVersion::DebugBuild))
307
        result << QLatin1String("CONFIG+=release");
hjk's avatar
hjk committed
308
    if (!(defaultBuildConfiguration & BaseQtVersion::DebugBuild) && (userBuildConfiguration & BaseQtVersion::DebugBuild))
309
        result << QLatin1String("CONFIG+=debug");
310
    return result;
311
}
312

313
QMakeStep *QmakeBuildConfiguration::qmakeStep() const
314 315
{
    QMakeStep *qs = 0;
316
    BuildStepList *bsl = stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
Tobias Hunger's avatar
Tobias Hunger committed
317 318 319
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((qs = qobject_cast<QMakeStep *>(bsl->at(i))) != 0)
320 321 322 323
            return qs;
    return 0;
}

324
MakeStep *QmakeBuildConfiguration::makeStep() const
325
{
Tobias Hunger's avatar
Tobias Hunger committed
326
    MakeStep *ms = 0;
327
    BuildStepList *bsl = stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
Tobias Hunger's avatar
Tobias Hunger committed
328 329 330 331
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((ms = qobject_cast<MakeStep *>(bsl->at(i))) != 0)
            return ms;
332 333 334
    return 0;
}

hjk's avatar
hjk committed
335
// Returns true if both are equal.
336
QmakeBuildConfiguration::MakefileState QmakeBuildConfiguration::compareToImportFrom(const QString &makefile)
337 338
{
    QMakeStep *qs = qmakeStep();
339
    if (QFileInfo(makefile).exists() && qs) {
hjk's avatar
hjk committed
340
        FileName qmakePath = QtVersionManager::findQMakeBinaryFromMakefile(makefile);
341
        BaseQtVersion *version = QtKitInformation::qtVersion(target()->kit());
dt's avatar
dt committed
342
        if (!version)
Tobias Hunger's avatar
Tobias Hunger committed
343
            return MakefileForWrongProject;
344
        if (QtSupport::QtVersionManager::makefileIsFor(makefile, qs->project()->projectFilePath().toString())
345 346 347 348 349 350 351
                != QtSupport::QtVersionManager::SameProject) {
            if (debug) {
                qDebug() << "different profile used to generate the Makefile:"
                         << makefile << " expected profile:" << qs->project()->projectFilePath();
            }
            return MakefileIncompatible;
        }
352 353
        if (version->qmakeCommand() == qmakePath) {
            // same qtversion
hjk's avatar
hjk committed
354 355
            QPair<BaseQtVersion::QmakeBuildConfigs, QString> result =
                    QtVersionManager::scanMakeFile(makefile, version->defaultBuildConfig());
356
            if (qmakeBuildConfiguration() == result.first) {
357
                // The qmake Build Configuration are the same,
358 359 360
                // now compare arguments lists
                // we have to compare without the spec/platform cmd argument
                // and compare that on its own
361
                QString workingDirectory = QFileInfo(makefile).absolutePath();
362 363 364 365
                QStringList actualArgs;
                QString userArgs = qs->userArguments();
                // This copies the settings from userArgs to actualArgs (minus some we
                // are not interested in), splitting them up into individual strings:
Tobias Hunger's avatar
Tobias Hunger committed
366
                extractSpecFromArguments(&userArgs, workingDirectory, version, &actualArgs);
367
                actualArgs = qs->deducedArguments() + actualArgs + qs->deducedArgumentsAfter();
hjk's avatar
hjk committed
368
                FileName actualSpec = qs->mkspec();
369

370 371
                QString qmakeArgs = result.second;
                QStringList parsedArgs;
hjk's avatar
hjk committed
372
                FileName parsedSpec = extractSpecFromArguments(&qmakeArgs, workingDirectory, version, &parsedArgs);
373 374

                if (debug) {
hjk's avatar
hjk committed
375 376 377 378
                    qDebug() << "Actual args:" << actualArgs;
                    qDebug() << "Parsed args:" << parsedArgs;
                    qDebug() << "Actual spec:" << actualSpec.toString();
                    qDebug() << "Parsed spec:" << parsedSpec.toString();
379 380
                }

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
                // Comparing the sorted list is obviously wrong
                // Though haven written a more complete version
                // that managed had around 200 lines and yet faild
                // to be actually foolproof at all, I think it's
                // not feasible without actually taking the qmake
                // command line parsing code

                // Things, sorting gets wrong:
                // parameters to positional parameters matter
                //  e.g. -o -spec is different from -spec -o
                //       -o 1 -spec 2 is diffrent from -spec 1 -o 2
                // variable assignment order matters
                // variable assignment vs -after
                // -norecursive vs. recursive
                actualArgs.sort();
                parsedArgs.sort();
397 398 399
                if (actualArgs == parsedArgs) {
                    // Specs match exactly
                    if (actualSpec == parsedSpec)
Tobias Hunger's avatar
Tobias Hunger committed
400
                        return MakefileMatches;
401
                    // Actual spec is the default one
hjk's avatar
hjk committed
402
//                    qDebug() << "AS vs VS" << actualSpec << version->mkspec();
403 404
                    if ((actualSpec == version->mkspec() || actualSpec == FileName::fromLatin1("default"))
                        && (parsedSpec == version->mkspec() || parsedSpec == FileName::fromLatin1("default") || parsedSpec.isEmpty()))
Tobias Hunger's avatar
Tobias Hunger committed
405
                        return MakefileMatches;
406
                }
Tobias Hunger's avatar
Tobias Hunger committed
407 408 409
                return MakefileIncompatible;
            } else {
                if (debug)
hjk's avatar
hjk committed
410 411
                    qDebug() << "different qmake buildconfigurations buildconfiguration:"
                             << qmakeBuildConfiguration() << " Makefile:" << result.first;
Tobias Hunger's avatar
Tobias Hunger committed
412
                return MakefileIncompatible;
413
            }
Tobias Hunger's avatar
Tobias Hunger committed
414 415
        } else {
            if (debug)
hjk's avatar
hjk committed
416 417
                qDebug() << "different Qt versions, buildconfiguration:" << version->qmakeCommand().toString()
                         << " Makefile:"<< qmakePath.toString();
Tobias Hunger's avatar
Tobias Hunger committed
418
            return MakefileForWrongProject;
419 420
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
421
    return MakefileMissing;
422
}
423

424
bool QmakeBuildConfiguration::removeQMLInspectorFromArguments(QString *args)
425
{
426
    bool removedArgument = false;
hjk's avatar
hjk committed
427
    for (QtcProcess::ArgIterator ait(args); ait.next(); ) {
428 429
        const QString arg = ait.value();
        if (arg.contains(QLatin1String(Constants::QMAKEVAR_QMLJSDEBUGGER_PATH))
430 431
            || arg.contains(QLatin1String(Constants::QMAKEVAR_QUICK1_DEBUG))
            || arg.contains(QLatin1String(Constants::QMAKEVAR_QUICK2_DEBUG))) {
432
            ait.deleteArg();
433 434 435 436
            removedArgument = true;
        }
    }
    return removedArgument;
437 438
}

439
FileName QmakeBuildConfiguration::extractSpecFromArguments(QString *args,
hjk's avatar
hjk committed
440 441
                                                         const QString &directory, const BaseQtVersion *version,
                                                         QStringList *outArgs)
442
{
hjk's avatar
hjk committed
443
    FileName parsedSpec;
444

445
    bool ignoreNext = false;
446
    bool nextIsSpec = false;
hjk's avatar
hjk committed
447
    for (QtcProcess::ArgIterator ait(args); ait.next(); ) {
448 449
        if (ignoreNext) {
            ignoreNext = false;
450 451 452
            ait.deleteArg();
        } else if (nextIsSpec) {
            nextIsSpec = false;
hjk's avatar
hjk committed
453
            parsedSpec = FileName::fromUserInput(ait.value());
454 455 456 457 458 459 460 461 462 463 464
            ait.deleteArg();
        } else if (ait.value() == QLatin1String("-spec") || ait.value() == QLatin1String("-platform")) {
            nextIsSpec = true;
            ait.deleteArg();
        } else if (ait.value() == QLatin1String("-cache")) {
            // We ignore -cache, because qmake contained a bug that it didn't
            // mention the -cache in the Makefile.
            // That means changing the -cache option in the additional arguments
            // does not automatically rerun qmake. Alas, we could try more
            // intelligent matching for -cache, but i guess people rarely
            // do use that.
465
            ignoreNext = true;
466 467 468
            ait.deleteArg();
        } else if (outArgs && ait.isSimple()) {
            outArgs->append(ait.value());
469 470 471
        }
    }

472
    if (parsedSpec.isEmpty())
hjk's avatar
hjk committed
473
        return FileName();
474

hjk's avatar
hjk committed
475
    FileName baseMkspecDir = FileName::fromUserInput(
476
            version->qmakeProperty("QT_HOST_DATA") + QLatin1String("/mkspecs"));
477
    baseMkspecDir = Utils::FileName::fromString(baseMkspecDir.toFileInfo().canonicalFilePath());
478 479 480 481 482 483

    // if the path is relative it can be
    // relative to the working directory (as found in the Makefiles)
    // or relatively to the mkspec directory
    // if it is the former we need to get the canonical form
    // for the other one we don't need to do anything
484
    if (parsedSpec.toFileInfo().isRelative()) {
485
        if (QFileInfo(directory + QLatin1Char('/') + parsedSpec.toString()).exists())
hjk's avatar
hjk committed
486
            parsedSpec = FileName::fromUserInput(directory + QLatin1Char('/') + parsedSpec.toString());
487
        else
hjk's avatar
hjk committed
488
            parsedSpec = FileName::fromUserInput(baseMkspecDir.toString() + QLatin1Char('/') + parsedSpec.toString());
489 490
    }

491
    QFileInfo f2 = parsedSpec.toFileInfo();
492
    while (f2.isSymLink()) {
hjk's avatar
hjk committed
493
        parsedSpec = FileName::fromString(f2.symLinkTarget());
494
        f2.setFile(parsedSpec.toString());
495 496
    }

497 498
    if (parsedSpec.isChildOf(baseMkspecDir)) {
        parsedSpec = parsedSpec.relativeChildPath(baseMkspecDir);
499
    } else {
hjk's avatar
hjk committed
500 501
        FileName sourceMkSpecPath = FileName::fromString(version->sourcePath().toString()
                                                         + QLatin1String("/mkspecs"));
502
        if (parsedSpec.isChildOf(sourceMkSpecPath))
503
            parsedSpec = parsedSpec.relativeChildPath(sourceMkSpecPath);
504 505
    }
    return parsedSpec;
506
}
507

508
bool QmakeBuildConfiguration::isEnabled() const
509 510 511 512
{
    return m_isEnabled;
}

513
QString QmakeBuildConfiguration::disabledReason() const
dt_'s avatar
dt_ committed
514 515 516 517 518 519
{
    if (!m_isEnabled)
        return tr("Parsing the .pro file");
    return QString();
}

520
void QmakeBuildConfiguration::setEnabled(bool enabled)
521 522 523 524 525 526 527
{
    if (m_isEnabled == enabled)
        return;
    m_isEnabled = enabled;
    emit enabledChanged();
}

528
/*!
529
  \class QmakeBuildConfigurationFactory
530 531
*/

532
QmakeBuildConfigurationFactory::QmakeBuildConfigurationFactory(QObject *parent) :
hjk's avatar
hjk committed
533
    IBuildConfigurationFactory(parent)
534 535 536
{
    update();

hjk's avatar
hjk committed
537 538
    connect(QtVersionManager::instance(),
            SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
539 540 541
            this, SLOT(update()));
}

542
QmakeBuildConfigurationFactory::~QmakeBuildConfigurationFactory()
543 544 545
{
}

546
void QmakeBuildConfigurationFactory::update()
547 548 549 550
{
    emit availableCreationIdsChanged();
}

551
bool QmakeBuildConfigurationFactory::canHandle(const Target *t) const
552
{
Tobias Hunger's avatar
Tobias Hunger committed
553
    if (!t->project()->supportsKit(t->kit()))
Tobias Hunger's avatar
Tobias Hunger committed
554
        return false;
555
    return qobject_cast<QmakeProject *>(t->project());
556 557
}

558
QmakeBuildInfo *QmakeBuildConfigurationFactory::createBuildInfo(const Kit *k,
559 560
                                                              const QString &projectPath,
                                                              BuildConfiguration::BuildType type) const
561
{
562 563
    QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
    QmakeBuildInfo *info = new QmakeBuildInfo(this);
564 565
    QString suffix;
    if (type == BuildConfiguration::Release) {
566 567
        //: The name of the release build configuration created by default for a qmake project.
        info->displayName = tr("Release");
568 569 570
        //: Non-ASCII characters in directory suffix may cause build issues.
        suffix = tr("Release", "Shadow build directory suffix");
    } else {
571 572
        //: The name of the debug build configuration created by default for a qmake project.
        info->displayName = tr("Debug");
573 574 575
        //: Non-ASCII characters in directory suffix may cause build issues.
        suffix = tr("Debug", "Shadow build directory suffix");
    }
576 577 578 579
    info->typeName = tr("Build");
    // Leave info->buildDirectory unset;
    info->kitId = k->id();
    info->supportsShadowBuild = (version && version->supportsShadowBuilds());
580
    info->buildDirectory
581
            = defaultBuildDirectory(info->supportsShadowBuild, projectPath, k, suffix);
582 583
    info->type = type;
    return info;
584 585
}

586
int QmakeBuildConfigurationFactory::priority(const Target *parent) const
587
{
588
    return canHandle(parent) ? 0 : -1;
589 590
}

591
QList<BuildInfo *> QmakeBuildConfigurationFactory::availableBuilds(const Target *parent) const
592
{
593
    QList<ProjectExplorer::BuildInfo *> result;
594
    QmakeBuildInfo *info = createBuildInfo(parent->kit(), parent->project()->projectFilePath().toString(),
595 596
                                           BuildConfiguration::Debug);
    info->displayName.clear(); // ask for a name
597
    info->buildDirectory.clear(); // This depends on the displayName
598 599 600
    result << info;

    return result;
601 602
}

603
int QmakeBuildConfigurationFactory::priority(const Kit *k, const QString &projectPath) const
604
{
605 606
    return (k && Core::MimeDatabase::findByFile(QFileInfo(projectPath))
            .matchesType(QLatin1String(Constants::PROFILE_MIMETYPE))) ? 0 : -1;
607 608
}

609
QList<BuildInfo *> QmakeBuildConfigurationFactory::availableSetups(const Kit *k, const QString &projectPath) const
610 611 612 613 614 615 616 617
{
    QList<ProjectExplorer::BuildInfo *> result;
    result << createBuildInfo(k, projectPath, ProjectExplorer::BuildConfiguration::Debug);
    result << createBuildInfo(k, projectPath, ProjectExplorer::BuildConfiguration::Release);

    return result;
}

618
BuildConfiguration *QmakeBuildConfigurationFactory::create(Target *parent, const BuildInfo *info) const
619
{
620 621 622
    QTC_ASSERT(info->factory() == this, return 0);
    QTC_ASSERT(info->kitId == parent->kit()->id(), return 0);
    QTC_ASSERT(!info->displayName.isEmpty(), return 0);
623

624
    const QmakeBuildInfo *qmakeInfo = static_cast<const QmakeBuildInfo *>(info);
625

626 627
    BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(parent->kit());
    QTC_ASSERT(version, return 0);
dt's avatar
dt committed
628

629 630 631 632 633
    BaseQtVersion::QmakeBuildConfigs config = version->defaultBuildConfig();
    if (qmakeInfo->type == BuildConfiguration::Release)
        config &= ~QtSupport::BaseQtVersion::DebugBuild;
    else
        config |= QtSupport::BaseQtVersion::DebugBuild;
634

635
    QmakeBuildConfiguration *bc = new QmakeBuildConfiguration(parent);
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    bc->setDefaultDisplayName(info->displayName);
    bc->setDisplayName(info->displayName);

    BuildStepList *buildSteps = bc->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
    BuildStepList *cleanSteps = bc->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_CLEAN));
    Q_ASSERT(buildSteps);
    Q_ASSERT(cleanSteps);

    QMakeStep *qmakeStep = new QMakeStep(buildSteps);
    buildSteps->insertStep(0, qmakeStep);

    MakeStep *makeStep = new MakeStep(buildSteps);
    buildSteps->insertStep(1, makeStep);

    MakeStep *cleanStep = new MakeStep(cleanSteps);
    cleanStep->setClean(true);
    cleanStep->setUserArguments(QLatin1String("clean"));
    cleanSteps->insertStep(0, cleanStep);

    QString additionalArguments = qmakeInfo->additionalArguments;

    bool enableQmlDebugger
658
            = QmakeBuildConfiguration::removeQMLInspectorFromArguments(&additionalArguments);
659 660
    if (!additionalArguments.isEmpty())
        qmakeStep->setUserArguments(additionalArguments);
661 662
    if (!qmakeInfo->makefile.isEmpty())
        qmakeStep->setLinkQmlDebuggingLibrary(enableQmlDebugger);
663 664 665 666 667 668

    bc->setQMakeBuildConfiguration(config);

    Utils::FileName directory = qmakeInfo->buildDirectory;
    if (directory.isEmpty()) {
        directory = defaultBuildDirectory(qmakeInfo->supportsShadowBuild,
669
                                          parent->project()->projectFilePath().toString(),
670 671
                                          parent->kit(), info->displayName);
    }
672

673
    bc->setBuildDirectory(directory);
674 675 676
    return bc;
}

677
bool QmakeBuildConfigurationFactory::canClone(const Target *parent, BuildConfiguration *source) const
678
{
679
    return canHandle(parent) && qobject_cast<QmakeBuildConfiguration *>(source);
680 681
}

682
BuildConfiguration *QmakeBuildConfigurationFactory::clone(Target *parent, BuildConfiguration *source)
683
{
684 685
    if (!canClone(parent, source))
        return 0;
686 687
    QmakeBuildConfiguration *oldbc(static_cast<QmakeBuildConfiguration *>(source));
    return new QmakeBuildConfiguration(parent, oldbc);
688 689
}

690
bool QmakeBuildConfigurationFactory::canRestore(const Target *parent, const QVariantMap &map) const
691
{
Tobias Hunger's avatar
Tobias Hunger committed
692
    if (!canHandle(parent))
693
        return false;
694
    return ProjectExplorer::idFromMap(map) == QMAKE_BC_ID;
695 696
}

697
BuildConfiguration *QmakeBuildConfigurationFactory::restore(Target *parent, const QVariantMap &map)
698 699 700
{
    if (!canRestore(parent, map))
        return 0;
701
    QmakeBuildConfiguration *bc = new QmakeBuildConfiguration(parent);
702 703 704 705 706
    if (bc->fromMap(map))
        return bc;
    delete bc;
    return 0;
}
707

708
BuildConfiguration::BuildType QmakeBuildConfiguration::buildType() const
709
{
hjk's avatar
hjk committed
710
    if (qmakeBuildConfiguration() & BaseQtVersion::DebugBuild)
711 712 713 714
        return Debug;
    else
        return Release;
}
Tobias Hunger's avatar
Tobias Hunger committed
715

716
QmakeBuildConfiguration::LastKitState::LastKitState()
717 718 719 720
{

}

721
QmakeBuildConfiguration::LastKitState::LastKitState(Kit *k)
722 723 724 725 726 727 728 729
    : m_qtVersion(QtKitInformation::qtVersionId(k)),
      m_sysroot(SysRootKitInformation::sysRoot(k).toString()),
      m_mkspec(QmakeKitInformation::mkspec(k).toString())
{
    ToolChain *tc = ToolChainKitInformation::toolChain(k);
    m_toolchain = tc ? tc->id() : QString();
}

730
bool QmakeBuildConfiguration::LastKitState::operator ==(const LastKitState &other) const
731 732 733 734 735 736 737
{
    return m_qtVersion == other.m_qtVersion
            && m_toolchain == other.m_toolchain
            && m_sysroot == other.m_sysroot
            && m_mkspec == other.m_mkspec;
}

738
bool QmakeBuildConfiguration::LastKitState::operator !=(const LastKitState &other) const
739 740 741 742
{
    return !operator ==(other);
}

743
} // namespace QmakeProjectManager