qt4buildconfiguration.cpp 27.1 KB
Newer Older
dt's avatar
dt committed
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
dt's avatar
dt committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qt4buildconfiguration.h"
31

32
#include "qt4project.h"
Tobias Hunger's avatar
Tobias Hunger committed
33
#include "qt4target.h"
34
#include "qt4projectmanagerconstants.h"
35
36
37
#include "qt4nodes.h"
#include "qmakestep.h"
#include "makestep.h"
dt's avatar
dt committed
38

39
#include <utils/qtcassert.h>
40
#include <limits>
Tobias Hunger's avatar
Tobias Hunger committed
41
42
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
43

44
45
#include <QtCore/QDebug>

46
47
#include <QtGui/QInputDialog>

dt's avatar
dt committed
48
49
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
50
51
52
using namespace ProjectExplorer;

namespace {
53
54
const char * const QT4_BC_ID_PREFIX("Qt4ProjectManager.Qt4BuildConfiguration.");
const char * const QT4_BC_ID("Qt4ProjectManager.Qt4BuildConfiguration");
55

56
57
58
59
60
const char * const USE_SHADOW_BUILD_KEY("Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild");
const char * const BUILD_DIRECTORY_KEY("Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory");
const char * const TOOLCHAIN_KEY("Qt4ProjectManager.Qt4BuildConfiguration.ToolChain");
const char * const BUILD_CONFIGURATION_KEY("Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration");
const char * const QT_VERSION_ID_KEY("Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId");
61

62
enum { debug = 0 };
63
}
dt's avatar
dt committed
64

Tobias Hunger's avatar
Tobias Hunger committed
65
66
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4Target *target) :
    BuildConfiguration(target, QLatin1String(QT4_BC_ID)),
67
    m_shadowBuild(true),
Tobias Hunger's avatar
Tobias Hunger committed
68
    m_qtVersionId(-1),
dt's avatar
dt committed
69
    m_toolChainType(-1), // toolChainType() makes sure to return the default toolchainType
70
71
    m_qmakeBuildConfiguration(0),
    m_subNodeBuild(0)
dt's avatar
dt committed
72
{
73
    ctor();
dt's avatar
dt committed
74
75
}

Tobias Hunger's avatar
Tobias Hunger committed
76
77
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4Target *target, const QString &id) :
    BuildConfiguration(target, id),
78
    m_shadowBuild(true),
Tobias Hunger's avatar
Tobias Hunger committed
79
    m_qtVersionId(-1),
80
81
82
    m_toolChainType(-1), // toolChainType() makes sure to return the default toolchainType
    m_qmakeBuildConfiguration(0),
    m_subNodeBuild(0)
dt's avatar
dt committed
83
{
84
    ctor();
dt's avatar
dt committed
85
86
}

Tobias Hunger's avatar
Tobias Hunger committed
87
88
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4Target *target, Qt4BuildConfiguration *source) :
    BuildConfiguration(target, source),
dt's avatar
dt committed
89
90
    m_shadowBuild(source->m_shadowBuild),
    m_buildDirectory(source->m_buildDirectory),
Tobias Hunger's avatar
Tobias Hunger committed
91
    m_qtVersionId(source->m_qtVersionId),
dt's avatar
dt committed
92
    m_toolChainType(source->m_toolChainType),
93
94
    m_qmakeBuildConfiguration(source->m_qmakeBuildConfiguration),
    m_subNodeBuild(0) // temporary value, so not copied
dt's avatar
dt committed
95
{
Tobias Hunger's avatar
Tobias Hunger committed
96
    cloneSteps(source);
97
    ctor();
dt's avatar
dt committed
98
99
100
101
}

Qt4BuildConfiguration::~Qt4BuildConfiguration()
{
102
}
dt's avatar
dt committed
103

104
105
106
107
108
QVariantMap Qt4BuildConfiguration::toMap() const
{
    QVariantMap map(BuildConfiguration::toMap());
    map.insert(QLatin1String(USE_SHADOW_BUILD_KEY), m_shadowBuild);
    map.insert(QLatin1String(BUILD_DIRECTORY_KEY), m_buildDirectory);
Tobias Hunger's avatar
Tobias Hunger committed
109
    map.insert(QLatin1String(QT_VERSION_ID_KEY), m_qtVersionId);
110
111
112
    map.insert(QLatin1String(TOOLCHAIN_KEY), m_toolChainType);
    map.insert(QLatin1String(BUILD_CONFIGURATION_KEY), int(m_qmakeBuildConfiguration));
    return map;
dt's avatar
dt committed
113
}
114

115
116

bool Qt4BuildConfiguration::fromMap(const QVariantMap &map)
dt's avatar
dt committed
117
{
Tobias Hunger's avatar
Tobias Hunger committed
118
119
120
    if (!BuildConfiguration::fromMap(map))
        return false;

121
122
    int fileVersion = map.value(ProjectExplorer::Constants::USERFILE_PREVIOUS_VERSION_KEY,
                                std::numeric_limits<int>::max()).toInt();
123
124
    m_shadowBuild = map.value(QLatin1String(USE_SHADOW_BUILD_KEY), true).toBool();
    m_buildDirectory = map.value(QLatin1String(BUILD_DIRECTORY_KEY), qt4Target()->defaultBuildDirectory()).toString();
Tobias Hunger's avatar
Tobias Hunger committed
125
    m_qtVersionId = map.value(QLatin1String(QT_VERSION_ID_KEY)).toInt();
126
127
128
    m_toolChainType = map.value(QLatin1String(TOOLCHAIN_KEY)).toInt();
    m_qmakeBuildConfiguration = QtVersion::QmakeBuildConfigs(map.value(QLatin1String(BUILD_CONFIGURATION_KEY)).toInt());

129
    // Pick a Qt version if the default version is used:
130
131
    // We assume that the default Qt version was used in earlier versions of Qt creator.
    // Pick a Qt version that is supporting a desktop.
Tobias Hunger's avatar
Tobias Hunger committed
132
133
134
    if (m_qtVersionId == 0) {
        QList<QtVersion *> versions = QtVersionManager::instance()->versions();
        foreach (QtVersion *v, versions) {
135
            if (v->isValid() && v->supportsTargetId(QLatin1String(Constants::DESKTOP_TARGET_ID))) {
Tobias Hunger's avatar
Tobias Hunger committed
136
137
                m_qtVersionId = v->uniqueId();
                break;
138
            }
Tobias Hunger's avatar
Tobias Hunger committed
139
140
141
142
143
        }
        if (m_qtVersionId == 0)
            m_qtVersionId = versions.at(0)->uniqueId();
    }

144
    QtVersion *version = qtVersion();
145
146
147
148
149
150
151
152
153
154
    if (fileVersion >= 1) { // we are not upgrading from pre-targets!
        if (version->isValid() && !version->supportedTargetIds().contains(target()->id())) {
            qWarning() << "Buildconfiguration" << displayName() << ": Qt" << version->displayName() << "not supported by target" << target()->id();
            return false;
        }
    } else {
        if (!version->isValid() || !version->supportedTargetIds().contains(target()->id())) {
            qWarning() << "Buildconfiguration" << displayName() << ": Qt" << version->displayName() << "not supported by target" << target()->id();
            return false;
        }
155
    }
Tobias Hunger's avatar
Tobias Hunger committed
156

157
158
    if (version->isValid())
        m_shadowBuild = (m_shadowBuild && version->supportsShadowBuilds());
159

Tobias Hunger's avatar
Tobias Hunger committed
160
161
162
163
    QList<ToolChain::ToolChainType> possibleTcs(qt4Target()->filterToolChainTypes(qtVersion()->possibleToolChainTypes()));
    if (!possibleTcs.contains(toolChainType()))
        setToolChainType(qt4Target()->preferredToolChainType(possibleTcs));

164
165
    if (toolChainType() == ToolChain::INVALID) {
        qWarning() << "No toolchain available for" << qtVersion()->displayName() << "used in" << target()->id() << "!";
Tobias Hunger's avatar
Tobias Hunger committed
166
        return false;
167
    }
168

Tobias Hunger's avatar
Tobias Hunger committed
169
    return true;
dt's avatar
dt committed
170
171
}

172
void Qt4BuildConfiguration::ctor()
173
{
174
175
176
177
    m_buildDirectory = qt4Target()->defaultBuildDirectory();
    if (m_buildDirectory == target()->project()->projectDirectory())
        m_shadowBuild = false;

178
179
180
181
182
    QtVersionManager *vm = QtVersionManager::instance();
    connect(vm, SIGNAL(qtVersionsChanged(QList<int>)),
            this, SLOT(qtVersionsChanged(QList<int>)));
}

Tobias Hunger's avatar
Tobias Hunger committed
183
184
185
186
187
188
189
190
191
192
void Qt4BuildConfiguration::pickValidQtVersion()
{
    QList<QtVersion *> versions = QtVersionManager::instance()->versionsForTargetId(qt4Target()->id());
    if (!versions.isEmpty())
        setQtVersion(versions.at(0));
    else
        setQtVersion(QtVersionManager::instance()->emptyVersion());
}

Qt4Target *Qt4BuildConfiguration::qt4Target() const
dt's avatar
dt committed
193
{
Tobias Hunger's avatar
Tobias Hunger committed
194
    return static_cast<Qt4Target *>(target());
dt's avatar
dt committed
195
196
}

197
Utils::Environment Qt4BuildConfiguration::baseEnvironment() const
198
{
199
    Utils::Environment env = BuildConfiguration::baseEnvironment();
200
    qtVersion()->addToEnvironment(env);
201
202
    env.set(QLatin1String("BUILDDIR"), QDir::toNativeSeparators(buildDirectory()));

203
204
205
206
207
208
    ToolChain *tc = toolChain();
    if (tc)
        tc->addToEnvironment(env);
    return env;
}

209
/// returns the build directory
210
211
212
QString Qt4BuildConfiguration::buildDirectory() const
{
    QString workingDirectory;
213
214
215
216
217
218
    if (m_shadowBuild) {
        if (!m_buildDirectory.isEmpty())
            workingDirectory = m_buildDirectory;
        else
            workingDirectory = qt4Target()->defaultBuildDirectory();
    }
219
    if (workingDirectory.isEmpty())
220
        workingDirectory = target()->project()->projectDirectory();
221
222
223
    return workingDirectory;
}

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/// If only a sub tree should be build this function returns which sub node
/// should be build
/// \see Qt4BuildConfiguration::setSubNodeBuild
Qt4ProjectManager::Internal::Qt4ProFileNode *Qt4BuildConfiguration::subNodeBuild() const
{
    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
void Qt4BuildConfiguration::setSubNodeBuild(Qt4ProjectManager::Internal::Qt4ProFileNode *node)
{
    m_subNodeBuild = node;
}

243
244
/// 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
245
246
/// source directory as the shadow build directory, thus it
/// still is a in-source build
247
248
bool Qt4BuildConfiguration::shadowBuild() const
{
dt's avatar
dt committed
249
    return m_shadowBuild;
250
251
252
253
254
255
}

/// returns the shadow build directory if set
/// \note buildDirectory() is probably the function you want to call
QString Qt4BuildConfiguration::shadowBuildDirectory() const
{
256
257
    if (m_buildDirectory.isEmpty())
        return qt4Target()->defaultBuildDirectory();
dt's avatar
dt committed
258
    return m_buildDirectory;
259
260
}

261
262
void Qt4BuildConfiguration::setShadowBuildAndDirectory(bool shadowBuild, const QString &buildDirectory)
{
263
264
265
266
    QtVersion *version = qtVersion();
    QString directoryToSet = QDir::fromNativeSeparators(buildDirectory);
    bool toSet = (shadowBuild && version->isValid() && version->supportsShadowBuilds());
    if (m_shadowBuild == toSet && m_buildDirectory == directoryToSet)
267
        return;
dt's avatar
dt committed
268

269
270
    m_shadowBuild = toSet;
    m_buildDirectory = directoryToSet;
271
272

    emit environmentChanged();
273
    emit buildDirectoryChanged();
dt's avatar
dt committed
274
    emit proFileEvaluateNeeded(this);
275
276
}

277
278
279
280
281
282
283
284
285
286
287
288
ProjectExplorer::ToolChain *Qt4BuildConfiguration::toolChain() const
{
    ToolChain::ToolChainType tct = toolChainType();
    return qtVersion()->toolChain(tct);
}

QString Qt4BuildConfiguration::makeCommand() const
{
    ToolChain *tc = toolChain();
    return tc ? tc->makeCommand() : "make";
}

289
static inline QString symbianMakeTarget(QtVersion::QmakeBuildConfigs buildConfig,
290
291
292
293
294
295
296
297
298
299
300
301
                                        const QString &type)
{
    QString rc = (buildConfig & QtVersion::DebugBuild) ?
                 QLatin1String("debug-") : QLatin1String("release-");
    rc += type;
    return rc;
}

QString Qt4BuildConfiguration::defaultMakeTarget() const
{
    ToolChain *tc = toolChain();
    if (!tc)
302
        return QString();
303
    const QtVersion::QmakeBuildConfigs buildConfig = qmakeBuildConfiguration();
304
305
306
307
308
309
310
311

    switch (tc->type()) {
    case ToolChain::GCCE:
        return symbianMakeTarget(buildConfig, QLatin1String("gcce"));
    case ToolChain::RVCT_ARMV5:
        return symbianMakeTarget(buildConfig, QLatin1String("armv5"));
    case ToolChain::RVCT_ARMV6:
        return symbianMakeTarget(buildConfig, QLatin1String("armv6"));
312
313
    case ToolChain::RVCT_ARMV5_GNUPOC:
    case ToolChain::GCCE_GNUPOC:
314
315
316
    default:
        break;
    }
317
    return QString();
318
319
320
321
}

QtVersion *Qt4BuildConfiguration::qtVersion() const
{
322
    QtVersionManager *vm = QtVersionManager::instance();
Tobias Hunger's avatar
Tobias Hunger committed
323
    return vm->version(m_qtVersionId);
324
325
}

Tobias Hunger's avatar
Tobias Hunger committed
326
void Qt4BuildConfiguration::setQtVersion(QtVersion *version)
327
{
Tobias Hunger's avatar
Tobias Hunger committed
328
    Q_ASSERT(version);
329

Tobias Hunger's avatar
Tobias Hunger committed
330
    if (m_qtVersionId == version->uniqueId())
331
332
        return;

Tobias Hunger's avatar
Tobias Hunger committed
333
    m_qtVersionId = version->uniqueId();
dt's avatar
dt committed
334

335
336
337
338
    if (!version->possibleToolChainTypes().contains(ProjectExplorer::ToolChain::ToolChainType(m_toolChainType))) {
        QList<ToolChain::ToolChainType> candidates =
                qt4Target()->filterToolChainTypes(qtVersion()->possibleToolChainTypes());
        if (candidates.isEmpty())
dt's avatar
dt committed
339
            m_toolChainType = ToolChain::INVALID;
340
341
342
        else
            m_toolChainType = candidates.first();
    }
dt's avatar
dt committed
343

344
345
    m_shadowBuild = m_shadowBuild && qtVersion()->supportsShadowBuilds();

dt's avatar
dt committed
346
    emit proFileEvaluateNeeded(this);
347
    emit qtVersionChanged();
348
    emit environmentChanged();
349
350
351
352
}

void Qt4BuildConfiguration::setToolChainType(ProjectExplorer::ToolChain::ToolChainType type)
{
dt's avatar
dt committed
353
354
    if (!qt4Target()->filterToolChainTypes(qtVersion()->possibleToolChainTypes()).contains(type)
        || m_toolChainType == type)
355
        return;
dt's avatar
dt committed
356

dt's avatar
dt committed
357
    m_toolChainType = type;
dt's avatar
dt committed
358
359

    emit proFileEvaluateNeeded(this);
360
    emit toolChainTypeChanged();
361
    emit environmentChanged();
362
363
364
365
}

ProjectExplorer::ToolChain::ToolChainType Qt4BuildConfiguration::toolChainType() const
{
Tobias Hunger's avatar
Tobias Hunger committed
366
    return ToolChain::ToolChainType(m_toolChainType);
367
368
}

369
370
QtVersion::QmakeBuildConfigs Qt4BuildConfiguration::qmakeBuildConfiguration() const
{
dt's avatar
dt committed
371
    return m_qmakeBuildConfiguration;
372
373
374
375
}

void Qt4BuildConfiguration::setQMakeBuildConfiguration(QtVersion::QmakeBuildConfigs config)
{
dt's avatar
dt committed
376
    if (m_qmakeBuildConfiguration == config)
377
        return;
dt's avatar
dt committed
378
    m_qmakeBuildConfiguration = config;
dt's avatar
dt committed
379
380

    emit proFileEvaluateNeeded(this);
381
382
383
    emit qmakeBuildConfigurationChanged();
}

384
385
386
387
388
void Qt4BuildConfiguration::emitProFileEvaluteNeeded()
{
    emit proFileEvaluateNeeded(this);
}

389
390
391
392
393
void Qt4BuildConfiguration::emitQMakeBuildConfigurationChanged()
{
    emit qmakeBuildConfigurationChanged();
}

394
395
396
397
398
void Qt4BuildConfiguration::emitBuildDirectoryInitialized()
{
    emit buildDirectoryInitialized();
}

399
400
401
402
403
404
void Qt4BuildConfiguration::emitS60CreatesSmartInstallerChanged()
{
    emit s60CreatesSmartInstallerChanged();
}


405
QStringList Qt4BuildConfiguration::configCommandLineArguments() const
406
{
407
    QStringList result;
408
    QtVersion::QmakeBuildConfigs defaultBuildConfiguration = qtVersion()->defaultBuildConfig();
dt's avatar
dt committed
409
    QtVersion::QmakeBuildConfigs userBuildConfiguration = m_qmakeBuildConfiguration;
410
411
412
413
414
415
416
417
418
419
    if ((defaultBuildConfiguration & QtVersion::BuildAll) && !(userBuildConfiguration & QtVersion::BuildAll))
        result << "CONFIG-=debug_and_release";

    if (!(defaultBuildConfiguration & QtVersion::BuildAll) && (userBuildConfiguration & QtVersion::BuildAll))
        result << "CONFIG+=debug_and_release";
    if ((defaultBuildConfiguration & QtVersion::DebugBuild)
            && !(userBuildConfiguration & QtVersion::DebugBuild)
            && !(userBuildConfiguration & QtVersion::BuildAll))
        result << "CONFIG+=release";
    if (!(defaultBuildConfiguration & QtVersion::DebugBuild)
Tobias Hunger's avatar
Tobias Hunger committed
420
421
            && (userBuildConfiguration & QtVersion::DebugBuild)
            && !(userBuildConfiguration & QtVersion::BuildAll))
422
423
        result << "CONFIG+=debug";
    return result;
424
}
425
426
427
428

QMakeStep *Qt4BuildConfiguration::qmakeStep() const
{
    QMakeStep *qs = 0;
Tobias Hunger's avatar
Tobias Hunger committed
429
430
431
432
    BuildStepList *bsl = stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((qs = qobject_cast<QMakeStep *>(bsl->at(i))) != 0)
433
434
435
436
437
438
            return qs;
    return 0;
}

MakeStep *Qt4BuildConfiguration::makeStep() const
{
Tobias Hunger's avatar
Tobias Hunger committed
439
440
441
442
443
444
    MakeStep *ms = 0;
    BuildStepList *bsl = stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((ms = qobject_cast<MakeStep *>(bsl->at(i))) != 0)
            return ms;
445
446
447
    return 0;
}

448
449
void Qt4BuildConfiguration::qtVersionsChanged(const QList<int> &changedVersions)
{
Tobias Hunger's avatar
Tobias Hunger committed
450
451
452
453
454
    if (!changedVersions.contains(m_qtVersionId) ||
        qtVersion()->isValid())
        return;

    pickValidQtVersion();
455
456
}

457
// returns true if both are equal
458
bool Qt4BuildConfiguration::compareToImportFrom(const QString &workingDirectory)
459
460
461
462
463
464
465
466
467
{
    QMakeStep *qs = qmakeStep();
    if (QDir(workingDirectory).exists(QLatin1String("Makefile")) && qs) {
        QString qmakePath = QtVersionManager::findQMakeBinaryFromMakefile(workingDirectory);
        QtVersion *version = qtVersion();
        if (version->qmakeCommand() == qmakePath) {
            // same qtversion
            QPair<QtVersion::QmakeBuildConfigs, QStringList> result =
                    QtVersionManager::scanMakeFile(workingDirectory, version->defaultBuildConfig());
468
            if (qmakeBuildConfiguration() == result.first) {
469
                // The qmake Build Configuration are the same,
470
471
472
                // now compare arguments lists
                // we have to compare without the spec/platform cmd argument
                // and compare that on its own
dt's avatar
dt committed
473
                QString actualSpec = extractSpecFromArgumentList(qs->userArguments(), workingDirectory, version);
474
                if (actualSpec.isEmpty()) {
Tobias Hunger's avatar
Tobias Hunger committed
475
                    // Easy one: the user has chosen not to override the settings
476
477
478
479
                    actualSpec = version->mkspec();
                }


480
                QString parsedSpec = extractSpecFromArgumentList(result.second, workingDirectory, version);
dt's avatar
dt committed
481
482
483
                QStringList actualArgs = qs->moreArguments();
                actualArgs << qs->userArguments();
                actualArgs = removeSpecFromArgumentList(actualArgs);
484
                QStringList parsedArgs = removeSpecFromArgumentList(result.second);
485
486
487
488
489
490
491
492

                if (debug) {
                    qDebug()<<"Actual args:"<<actualArgs;
                    qDebug()<<"Parsed args:"<<parsedArgs;
                    qDebug()<<"Actual spec:"<<actualSpec;
                    qDebug()<<"Parsed spec:"<<parsedSpec;
                }

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
                // 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();
509
510
511
512
513
514
515
516
517
518
                if (actualArgs == parsedArgs) {
                    // Specs match exactly
                    if (actualSpec == parsedSpec)
                        return true;
                    // Actual spec is the default one
//                    qDebug()<<"AS vs VS"<<actualSpec<<version->mkspec();
                    if ((actualSpec == version->mkspec() || actualSpec == "default")
                        && (parsedSpec == version->mkspec() || parsedSpec == "default" || parsedSpec.isEmpty()))
                        return true;
                }
dt's avatar
dt committed
519
520
            } else if (debug) {
                qDebug()<<"different qmake buildconfigurations buildconfiguration:"<<qmakeBuildConfiguration()<<" Makefile:"<<result.first;
521
            }
dt's avatar
dt committed
522
523
        } else if (debug) {
            qDebug()<<"diffrent qt versions, buildconfiguration:"<<version->qmakeCommand()<<" Makefile:"<<qmakePath;
524
525
526
527
        }
    }
    return false;
}
528

529
530
531
532
QStringList Qt4BuildConfiguration::removeQMLInspectorFromArgumentList(const QStringList &old)
{
    QStringList result;
    foreach (const QString &str, old)
533
        if (!str.startsWith(QLatin1String(Constants::QMAKEVAR_QMLJSDEBUGGER_PATH)))
534
535
536
537
            result << str;
    return result;
}

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
// We match -spec and -platfrom separetly
// 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.

QStringList Qt4BuildConfiguration::removeSpecFromArgumentList(const QStringList &old)
{
    if (!old.contains("-spec") && !old.contains("-platform") && !old.contains("-cache"))
        return old;
    QStringList newList;
    bool ignoreNext = false;
    foreach(const QString &item, old) {
        if (ignoreNext) {
            ignoreNext = false;
        } else if (item == "-spec" || item == "-platform" || item == "-cache") {
            ignoreNext = true;
        } else {
            newList << item;
        }
    }
    return newList;
}

QString Qt4BuildConfiguration::extractSpecFromArgumentList(const QStringList &list, QString directory, QtVersion *version)
{
    int index = list.indexOf("-spec");
    if (index == -1)
        index = list.indexOf("-platform");
    if (index == -1)
        return QString();

    ++index;

    if (index >= list.length())
        return QString();

    QString baseMkspecDir = version->versionInfo().value("QMAKE_MKSPECS");
    if (baseMkspecDir.isEmpty())
        baseMkspecDir = version->versionInfo().value("QT_INSTALL_DATA") + "/mkspecs";

    QString parsedSpec = QDir::cleanPath(list.at(index));
#ifdef Q_OS_WIN
    baseMkspecDir = baseMkspecDir.toLower();
    parsedSpec = parsedSpec.toLower();
#endif
    // 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
    if (QFileInfo(parsedSpec).isRelative()) {
592
593
        if(QFileInfo(directory + QLatin1Char('/') + parsedSpec).exists()) {
            parsedSpec = QDir::cleanPath(directory + QLatin1Char('/') + parsedSpec);
594
595
596
597
#ifdef Q_OS_WIN
            parsedSpec = parsedSpec.toLower();
#endif
        } else {
598
            parsedSpec = baseMkspecDir + QLatin1Char('/') + parsedSpec;
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
        }
    }

    QFileInfo f2(parsedSpec);
    while (f2.isSymLink()) {
        parsedSpec = f2.symLinkTarget();
        f2.setFile(parsedSpec);
    }

    if (parsedSpec.startsWith(baseMkspecDir)) {
        parsedSpec = parsedSpec.mid(baseMkspecDir.length() + 1);
    } else {
        QString sourceMkSpecPath = version->sourcePath() + "/mkspecs";
        if (parsedSpec.startsWith(sourceMkSpecPath)) {
            parsedSpec = parsedSpec.mid(sourceMkSpecPath.length() + 1);
        }
    }
#ifdef Q_OS_WIN
    parsedSpec = parsedSpec.toLower();
#endif
    return parsedSpec;
620
}
621

622
623
624
625
626
627
628
629
ProjectExplorer::IOutputParser *Qt4BuildConfiguration::createOutputParser() const
{
    ToolChain *tc = toolChain();
    if (tc)
        return toolChain()->outputParser();
    return 0;
}

630
631
632
633
/*!
  \class Qt4BuildConfigurationFactory
*/

634
635
Qt4BuildConfigurationFactory::Qt4BuildConfigurationFactory(QObject *parent) :
    ProjectExplorer::IBuildConfigurationFactory(parent)
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
{
    update();

    QtVersionManager *vm = QtVersionManager::instance();
    connect(vm, SIGNAL(qtVersionsChanged(QList<int>)),
            this, SLOT(update()));
}

Qt4BuildConfigurationFactory::~Qt4BuildConfigurationFactory()
{
}

void Qt4BuildConfigurationFactory::update()
{
    m_versions.clear();
    QtVersionManager *vm = QtVersionManager::instance();
    foreach (const QtVersion *version, vm->versions()) {
653
654
655
656
657
658
        if (version->isValid()) {
            QString key = QString::fromLatin1(QT4_BC_ID_PREFIX)
                    + QString::fromLatin1("Qt%1").arg(version->uniqueId());
            VersionInfo info(tr("Using Qt Version \"%1\"").arg(version->displayName()), version->uniqueId());
            m_versions.insert(key, info);
        }
659
660
661
662
    }
    emit availableCreationIdsChanged();
}

Tobias Hunger's avatar
Tobias Hunger committed
663
QStringList Qt4BuildConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent) const
664
{
Tobias Hunger's avatar
Tobias Hunger committed
665
    if (!qobject_cast<Qt4Target *>(parent))
666
        return QStringList();
Tobias Hunger's avatar
Tobias Hunger committed
667
668
669
670
671
672
673
674
675

    QStringList results;
    QtVersionManager *vm = QtVersionManager::instance();
    for (QMap<QString, VersionInfo>::const_iterator i = m_versions.constBegin();
         i != m_versions.constEnd(); ++i) {
        if (vm->version(i.value().versionId)->supportsTargetId(parent->id()))
            results.append(i.key());
    }
    return results;
676
677
678
679
}

QString Qt4BuildConfigurationFactory::displayNameForId(const QString &id) const
{
680
681
682
    if (!m_versions.contains(id))
        return QString();
    return m_versions.value(id).displayName;
683
684
}

Tobias Hunger's avatar
Tobias Hunger committed
685
bool Qt4BuildConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const QString &id) const
686
{
Tobias Hunger's avatar
Tobias Hunger committed
687
    if (!qobject_cast<Qt4Target *>(parent))
688
689
690
        return false;
    if (!m_versions.contains(id))
        return false;
691
692
    const VersionInfo &info = m_versions.value(id);
    QtVersion *version = QtVersionManager::instance()->version(info.versionId);
Tobias Hunger's avatar
Tobias Hunger committed
693
694
    if (!version ||
        !version->supportsTargetId(parent->id()))
695
        return false;
696
697
698
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
699
BuildConfiguration *Qt4BuildConfigurationFactory::create(ProjectExplorer::Target *parent, const QString &id)
700
701
702
703
704
705
706
707
{
    if (!canCreate(parent, id))
        return 0;

    const VersionInfo &info = m_versions.value(id);
    QtVersion *version = QtVersionManager::instance()->version(info.versionId);
    Q_ASSERT(version);

Tobias Hunger's avatar
Tobias Hunger committed
708
    Qt4Target *qt4Target(static_cast<Qt4Target *>(parent));
709

710
711
    bool ok;
    QString buildConfigurationName = QInputDialog::getText(0,
712
713
                          tr("New Configuration"),
                          tr("New configuration name:"),
714
715
716
                          QLineEdit::Normal,
                          version->displayName(),
                          &ok);
717
    buildConfigurationName = buildConfigurationName.trimmed();
718
    if (!ok || buildConfigurationName.isEmpty())
Tobias Hunger's avatar
Tobias Hunger committed
719
        return 0;
720

Tobias Hunger's avatar
Tobias Hunger committed
721
722
    qt4Target->addQt4BuildConfiguration(tr("%1 Debug").arg(buildConfigurationName),
                                        version,
723
724
                                        (version->defaultBuildConfig() | QtVersion::DebugBuild),
                                        QStringList(), QString());
725
    BuildConfiguration *bc =
Tobias Hunger's avatar
Tobias Hunger committed
726
727
    qt4Target->addQt4BuildConfiguration(tr("%1 Release").arg(buildConfigurationName),
                                        version,
728
729
                                        (version->defaultBuildConfig() & ~QtVersion::DebugBuild),
                                        QStringList(), QString());
730
731
732
    return bc;
}

Tobias Hunger's avatar
Tobias Hunger committed
733
bool Qt4BuildConfigurationFactory::canClone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *source) const
734
{
Tobias Hunger's avatar
Tobias Hunger committed
735
736
737
738
739
740
741
742
743
744
745
    if (!qobject_cast<Qt4Target *>(parent))
        return false;
    Qt4BuildConfiguration *qt4bc(qobject_cast<Qt4BuildConfiguration *>(source));
    if (!qt4bc)
        return false;

    QtVersion *version = qt4bc->qtVersion();
    if (!version ||
        !version->supportsTargetId(parent->id()))
        return false;
    return true;
746
747
}

Tobias Hunger's avatar
Tobias Hunger committed
748
BuildConfiguration *Qt4BuildConfigurationFactory::clone(Target *parent, BuildConfiguration *source)
749
{
750
751
    if (!canClone(parent, source))
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
752
    Qt4Target *target(static_cast<Qt4Target *>(parent));
753
    Qt4BuildConfiguration *oldbc(static_cast<Qt4BuildConfiguration *>(source));
Tobias Hunger's avatar
Tobias Hunger committed
754
    return new Qt4BuildConfiguration(target, oldbc);
755
756
}

Tobias Hunger's avatar
Tobias Hunger committed
757
bool Qt4BuildConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
758
759
{
    QString id(ProjectExplorer::idFromMap(map));
Tobias Hunger's avatar
Tobias Hunger committed
760
    if (!qobject_cast<Qt4Target *>(parent))
761
762
763
        return false;
    return id.startsWith(QLatin1String(QT4_BC_ID_PREFIX)) ||
           id == QLatin1String(QT4_BC_ID);
764
765
}

Tobias Hunger's avatar
Tobias Hunger committed
766
BuildConfiguration *Qt4BuildConfigurationFactory::restore(Target *parent, const QVariantMap &map)
767
768
769
{
    if (!canRestore(parent, map))
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
770
771
    Qt4Target *target(static_cast<Qt4Target *>(parent));
    Qt4BuildConfiguration *bc(new Qt4BuildConfiguration(target));
772
773
774
775
776
    if (bc->fromMap(map))
        return bc;
    delete bc;
    return 0;
}