qt4buildconfiguration.cpp 33.5 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) 2012 Nokia Corporation and/or its subsidiary(-ies).
dt's avatar
dt committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
dt's avatar
dt committed
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
dt's avatar
dt committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
dt's avatar
dt committed
30
31
32
33
**
**************************************************************************/

#include "qt4buildconfiguration.h"
34

35
#include "qt4project.h"
Tobias Hunger's avatar
Tobias Hunger committed
36
#include "qt4target.h"
37
#include "qt4projectmanagerconstants.h"
38
39
40
#include "qt4nodes.h"
#include "qmakestep.h"
#include "makestep.h"
dt's avatar
dt committed
41
#include "qt4basetargetfactory.h"
dt's avatar
dt committed
42

43
#include <utils/qtcassert.h>
44
#include <utils/qtcprocess.h>
45
#include <limits>
Tobias Hunger's avatar
Tobias Hunger committed
46
47
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
dt_'s avatar
dt_ committed
48
#include <projectexplorer/toolchain.h>
49
#include <projectexplorer/toolchainmanager.h>
50
51
#include <qtsupport/qtversionfactory.h>
#include <qtsupport/baseqtversion.h>
52
#include <qtsupport/qtversionmanager.h>
53
#include <QDebug>
54

55
#include <QInputDialog>
56

dt's avatar
dt committed
57
58
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
59
60
61
using namespace ProjectExplorer;

namespace {
62
63
const char * const QT4_BC_ID_PREFIX("Qt4ProjectManager.Qt4BuildConfiguration.");
const char * const QT4_BC_ID("Qt4ProjectManager.Qt4BuildConfiguration");
64

65
66
67
68
const char * const USE_SHADOW_BUILD_KEY("Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild");
const char * const BUILD_DIRECTORY_KEY("Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory");
const char * const BUILD_CONFIGURATION_KEY("Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration");
const char * const QT_VERSION_ID_KEY("Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId");
69

70
enum { debug = 0 };
71
}
dt's avatar
dt committed
72

73
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4BaseTarget *target) :
Tobias Hunger's avatar
Tobias Hunger committed
74
    BuildConfiguration(target, QLatin1String(QT4_BC_ID)),
75
    m_shadowBuild(true),
76
    m_isEnabled(false),
Tobias Hunger's avatar
Tobias Hunger committed
77
    m_qtVersionId(-1),
78
79
    m_qmakeBuildConfiguration(0),
    m_subNodeBuild(0)
dt's avatar
dt committed
80
{
81
    ctor();
dt's avatar
dt committed
82
83
}

84
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4BaseTarget *target, const QString &id) :
Tobias Hunger's avatar
Tobias Hunger committed
85
    BuildConfiguration(target, id),
86
    m_shadowBuild(true),
87
    m_isEnabled(false),
Tobias Hunger's avatar
Tobias Hunger committed
88
    m_qtVersionId(-1),
89
90
    m_qmakeBuildConfiguration(0),
    m_subNodeBuild(0)
dt's avatar
dt committed
91
{
92
    ctor();
dt's avatar
dt committed
93
94
}

95
Qt4BuildConfiguration::Qt4BuildConfiguration(Qt4BaseTarget *target, Qt4BuildConfiguration *source) :
Tobias Hunger's avatar
Tobias Hunger committed
96
    BuildConfiguration(target, source),
dt's avatar
dt committed
97
    m_shadowBuild(source->m_shadowBuild),
98
    m_isEnabled(false),
dt's avatar
dt committed
99
    m_buildDirectory(source->m_buildDirectory),
Tobias Hunger's avatar
Tobias Hunger committed
100
    m_qtVersionId(source->m_qtVersionId),
101
102
    m_qmakeBuildConfiguration(source->m_qmakeBuildConfiguration),
    m_subNodeBuild(0) // temporary value, so not copied
dt's avatar
dt committed
103
{
Tobias Hunger's avatar
Tobias Hunger committed
104
    cloneSteps(source);
105
    ctor();
dt's avatar
dt committed
106
107
108
109
}

Qt4BuildConfiguration::~Qt4BuildConfiguration()
{
110
}
dt's avatar
dt committed
111

112
113
114
115
116
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
117
    map.insert(QLatin1String(QT_VERSION_ID_KEY), m_qtVersionId);
118
119
    map.insert(QLatin1String(BUILD_CONFIGURATION_KEY), int(m_qmakeBuildConfiguration));
    return map;
dt's avatar
dt committed
120
}
121

122
bool Qt4BuildConfiguration::fromMap(const QVariantMap &map)
dt's avatar
dt committed
123
{
Tobias Hunger's avatar
Tobias Hunger committed
124
125
126
    if (!BuildConfiguration::fromMap(map))
        return false;

127
    m_shadowBuild = map.value(QLatin1String(USE_SHADOW_BUILD_KEY), true).toBool();
Tobias Hunger's avatar
Tobias Hunger committed
128
    m_qtVersionId = map.value(QLatin1String(QT_VERSION_ID_KEY)).toInt();
129
    m_qmakeBuildConfiguration = QtSupport::BaseQtVersion::QmakeBuildConfigs(map.value(QLatin1String(BUILD_CONFIGURATION_KEY)).toInt());
130
131
    m_buildDirectory = map.value(QLatin1String(BUILD_DIRECTORY_KEY), defaultShadowBuildDirectory()).toString();

132
    // Pick a Qt version if the default version is used:
133
    // We assume that the default Qt version was used in earlier versions of Qt creator.
dt's avatar
dt committed
134
    // Pick a Qt version that supports this target (usually desktop)
Tobias Hunger's avatar
Tobias Hunger committed
135
    if (m_qtVersionId == 0) {
136
137
        QList<QtSupport::BaseQtVersion *> versions = QtSupport::QtVersionManager::instance()->versionsForTargetId(target()->id());
        foreach (QtSupport::BaseQtVersion *v, versions) {
dt's avatar
dt committed
138
            if (v->isValid()) {
Tobias Hunger's avatar
Tobias Hunger committed
139
140
                m_qtVersionId = v->uniqueId();
                break;
141
            }
Tobias Hunger's avatar
Tobias Hunger committed
142
143
        }
        if (m_qtVersionId == 0)
dt's avatar
dt committed
144
            m_qtVersionId = -1;
Tobias Hunger's avatar
Tobias Hunger committed
145
146
    }

147
    QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(m_qtVersionId);
dt's avatar
dt committed
148
149
150
    if (!version || !version->supportsTargetId(target()->id())) {
        m_qtVersionId = -1;
        version = 0;
151
    }
Tobias Hunger's avatar
Tobias Hunger committed
152

dt's avatar
dt committed
153
154
    m_lastEmmitedBuildDirectory = buildDirectory();

155
    ProjectExplorer::ToolChain *tc = toolChain();
dt's avatar
dt committed
156
157
158
159
    if (version && version->isValid()) {
        if (tc && !qt4Target()->possibleToolChains(this).contains(tc))
            setToolChain(0);
        if (!toolChain())
160
            setToolChain(qt4Target()->preferredToolChain(this));
161
        m_shadowBuild = (m_shadowBuild && version->supportsShadowBuilds());
162
    }
163

164
    if (!toolChain()) {
dt's avatar
dt committed
165
        if (version && version->isValid()) {
166
            qWarning("Warning: No tool chain available for '%s' from %s used in '%s'.",
167
                    qPrintable(version->displayName()), qPrintable(version->qmakeCommand().toUserOutput()),
168
169
                    qPrintable(target()->id()));
        } else {
170
            qWarning("Warning: No tool chain available for invalid Qt version used in '%s'.",
171
172
                     qPrintable(target()->id()));
        }
173
    }
174

Tobias Hunger's avatar
Tobias Hunger committed
175
    return true;
dt's avatar
dt committed
176
177
}

178
void Qt4BuildConfiguration::ctor()
179
{
180
181
    connect(this, SIGNAL(environmentChanged()),
            this, SLOT(emitBuildDirectoryChanged()));
182
    connect(this, SIGNAL(environmentChanged()),
183
            this, SLOT(emitProFileEvaluateNeeded()));
184
    connect(qt4Target()->qt4Project(), SIGNAL(proFileUpdated(Qt4ProjectManager::Qt4ProFileNode*,bool,bool)),
185
            this, SLOT(proFileUpdated(Qt4ProjectManager::Qt4ProFileNode*,bool,bool)));
186

187
    QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
188
189
190
191
    connect(vm, SIGNAL(qtVersionsChanged(QList<int>)),
            this, SLOT(qtVersionsChanged(QList<int>)));
}

192
193
194
195
196
197
198
199
void Qt4BuildConfiguration::emitBuildDirectoryChanged()
{
    if (buildDirectory() != m_lastEmmitedBuildDirectory) {
        m_lastEmmitedBuildDirectory = buildDirectory();
        emit buildDirectoryChanged();
    }
}

200
void Qt4BuildConfiguration::proFileUpdated(Qt4ProjectManager::Qt4ProFileNode *, bool success, bool parseInProgress)
201
202
203
{
    // Changing the included Qt modules from 0 to at least one might have caused the
    // tool chain to become invalid.
204
205
    if (!success || parseInProgress)
        return;
206
207
208
209
    if (!qt4Target()->possibleToolChains(this).contains(toolChain()))
        setToolChain(qt4Target()->preferredToolChain(this));
}

210
Qt4BaseTarget *Qt4BuildConfiguration::qt4Target() const
dt's avatar
dt committed
211
{
212
    return static_cast<Qt4BaseTarget *>(target());
dt's avatar
dt committed
213
214
}

215
Utils::Environment Qt4BuildConfiguration::baseEnvironment() const
216
{
217
    Utils::Environment env = BuildConfiguration::baseEnvironment();
dt's avatar
dt committed
218
219
    if (qtVersion())
        qtVersion()->addToEnvironment(env);
220

221
222
223
224
225
226
    ToolChain *tc = toolChain();
    if (tc)
        tc->addToEnvironment(env);
    return env;
}

227
228
229
230
QString Qt4BuildConfiguration::defaultShadowBuildDirectory() const
{
    Qt4BaseTargetFactory *factory = Qt4BaseTargetFactory::qt4BaseTargetFactoryForId(qt4Target()->id());
    // todo displayName isn't ideal
231
    return factory->shadowBuildDirectory(qt4Target()->qt4Project()->document()->fileName(), qt4Target()->id(), displayName());
232
233
}

234
235
/// returns the unexpanded build directory
QString Qt4BuildConfiguration::rawBuildDirectory() const
236
237
{
    QString workingDirectory;
238
239
240
241
    if (m_shadowBuild) {
        if (!m_buildDirectory.isEmpty())
            workingDirectory = m_buildDirectory;
        else
242
            workingDirectory = defaultShadowBuildDirectory();
243
    }
244
    if (workingDirectory.isEmpty())
245
        workingDirectory = target()->project()->projectDirectory();
246
247
248
    return workingDirectory;
}

249
250
251
252
253
254
/// returns the build directory
QString Qt4BuildConfiguration::buildDirectory() const
{
    return QDir::cleanPath(environment().expandVariables(rawBuildDirectory()));
}

255
256
257
/// If only a sub tree should be build this function returns which sub node
/// should be build
/// \see Qt4BuildConfiguration::setSubNodeBuild
258
Qt4ProjectManager::Qt4ProFileNode *Qt4BuildConfiguration::subNodeBuild() const
259
260
261
262
263
264
265
266
267
268
{
    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
269
void Qt4BuildConfiguration::setSubNodeBuild(Qt4ProjectManager::Qt4ProFileNode *node)
270
271
272
273
{
    m_subNodeBuild = node;
}

274
275
/// 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
276
277
/// source directory as the shadow build directory, thus it
/// still is a in-source build
278
279
bool Qt4BuildConfiguration::shadowBuild() const
{
dt's avatar
dt committed
280
    return m_shadowBuild;
281
282
283
284
285
286
}

/// returns the shadow build directory if set
/// \note buildDirectory() is probably the function you want to call
QString Qt4BuildConfiguration::shadowBuildDirectory() const
{
287
    if (m_buildDirectory.isEmpty())
288
        return defaultShadowBuildDirectory();
dt's avatar
dt committed
289
    return m_buildDirectory;
290
291
}

292
293
void Qt4BuildConfiguration::setShadowBuildAndDirectory(bool shadowBuild, const QString &buildDirectory)
{
294
    QtSupport::BaseQtVersion *version = qtVersion();
295
    QString directoryToSet = buildDirectory;
dt's avatar
dt committed
296
    bool toSet = (shadowBuild && version && version->isValid() && version->supportsShadowBuilds());
297
    if (m_shadowBuild == toSet && m_buildDirectory == directoryToSet)
298
        return;
dt's avatar
dt committed
299

300
301
    m_shadowBuild = toSet;
    m_buildDirectory = directoryToSet;
302
303

    emit environmentChanged();
304
    emitBuildDirectoryChanged();
dt's avatar
dt committed
305
    emit proFileEvaluateNeeded(this);
306
307
}

308
309
310
QString Qt4BuildConfiguration::makeCommand() const
{
    ToolChain *tc = toolChain();
311
    return tc ? tc->makeCommand() : QLatin1String("make");
312
313
}

314
static inline QString symbianMakeTarget(QtSupport::BaseQtVersion::QmakeBuildConfigs buildConfig,
315
316
                                        const QString &type)
{
317
    QString rc = (buildConfig & QtSupport::BaseQtVersion::DebugBuild) ?
318
319
320
321
322
323
324
325
                 QLatin1String("debug-") : QLatin1String("release-");
    rc += type;
    return rc;
}

QString Qt4BuildConfiguration::defaultMakeTarget() const
{
    ToolChain *tc = toolChain();
326
    if (!tc || target()->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
327
        return QString();
328
    const QtSupport::BaseQtVersion::QmakeBuildConfigs buildConfig = qmakeBuildConfiguration();
329

330
    return symbianMakeTarget(buildConfig, tc->defaultMakeTarget());
331
332
}

333
334
QString Qt4BuildConfiguration::makefile() const
{
335
    return qt4Target()->qt4Project()->rootQt4ProjectNode()->makefile();
336
337
}

338
QtSupport::BaseQtVersion *Qt4BuildConfiguration::qtVersion() const
339
{
340
    QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
Tobias Hunger's avatar
Tobias Hunger committed
341
    return vm->version(m_qtVersionId);
342
343
}

344
void Qt4BuildConfiguration::setQtVersion(QtSupport::BaseQtVersion *version)
345
{
dt's avatar
dt committed
346
347
348
349
350
351
352
353
    if (version == 0) {
        m_qtVersionId = -1;
        m_shadowBuild = false;
        setToolChain(0);
        emit proFileEvaluateNeeded(this);
        emit qtVersionChanged();
        emit environmentChanged();
        emitBuildDirectoryChanged();
354
        return;
dt's avatar
dt committed
355
356
357
    }
    if (m_qtVersionId == version->uniqueId())
            return;
358

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

361
362
    if (!qt4Target()->possibleToolChains(this).contains(toolChain()))
        setToolChain(qt4Target()->preferredToolChain(this));
363
364
    m_shadowBuild = m_shadowBuild && qtVersion()->supportsShadowBuilds();

dt's avatar
dt committed
365
    emit proFileEvaluateNeeded(this);
366
    emit qtVersionChanged();
367
    emit environmentChanged();
368
    emitBuildDirectoryChanged();
369
370
}

371
void Qt4BuildConfiguration::setToolChain(ProjectExplorer::ToolChain *tc)
372
{
373
    if (tc != 0 && m_qtVersionId > 0 && !qt4Target()->possibleToolChains(this).contains(tc))
374
        return;
dt's avatar
dt committed
375

Daniel Teske's avatar
Daniel Teske committed
376
    if (toolChain() == tc)
377
378
        return;

379
    BuildConfiguration::setToolChain(tc);
dt's avatar
dt committed
380
381

    emit proFileEvaluateNeeded(this);
382
    emitBuildDirectoryChanged();
383
384
}

385
QtSupport::BaseQtVersion::QmakeBuildConfigs Qt4BuildConfiguration::qmakeBuildConfiguration() const
386
{
dt's avatar
dt committed
387
    return m_qmakeBuildConfiguration;
388
389
}

390
void Qt4BuildConfiguration::setQMakeBuildConfiguration(QtSupport::BaseQtVersion::QmakeBuildConfigs config)
391
{
dt's avatar
dt committed
392
    if (m_qmakeBuildConfiguration == config)
393
        return;
dt's avatar
dt committed
394
    m_qmakeBuildConfiguration = config;
dt's avatar
dt committed
395
396

    emit proFileEvaluateNeeded(this);
397
    emit qmakeBuildConfigurationChanged();
398
    emitBuildDirectoryChanged();
399
400
}

401
void Qt4BuildConfiguration::emitProFileEvaluateNeeded()
402
403
404
405
{
    emit proFileEvaluateNeeded(this);
}

406
407
408
409
410
void Qt4BuildConfiguration::emitQMakeBuildConfigurationChanged()
{
    emit qmakeBuildConfigurationChanged();
}

411
412
413
414
415
void Qt4BuildConfiguration::emitBuildDirectoryInitialized()
{
    emit buildDirectoryInitialized();
}

416
417
418
419
420
421
void Qt4BuildConfiguration::emitS60CreatesSmartInstallerChanged()
{
    emit s60CreatesSmartInstallerChanged();
}


422
QStringList Qt4BuildConfiguration::configCommandLineArguments() const
423
{
424
    QStringList result;
dt_'s avatar
dt_ committed
425
    QtSupport::BaseQtVersion::QmakeBuildConfigs defaultBuildConfiguration =  qtVersion() ? qtVersion()->defaultBuildConfig() : (QtSupport::BaseQtVersion::DebugBuild | QtSupport::BaseQtVersion::BuildAll);
426
427
    QtSupport::BaseQtVersion::QmakeBuildConfigs userBuildConfiguration = m_qmakeBuildConfiguration;
    if ((defaultBuildConfiguration & QtSupport::BaseQtVersion::BuildAll) && !(userBuildConfiguration & QtSupport::BaseQtVersion::BuildAll))
428
        result << QLatin1String("CONFIG-=debug_and_release");
429

430
    if (!(defaultBuildConfiguration & QtSupport::BaseQtVersion::BuildAll) && (userBuildConfiguration & QtSupport::BaseQtVersion::BuildAll))
431
        result << QLatin1String("CONFIG+=debug_and_release");
dt_'s avatar
dt_ committed
432
    if ((defaultBuildConfiguration & QtSupport::BaseQtVersion::DebugBuild) && !(userBuildConfiguration & QtSupport::BaseQtVersion::DebugBuild))
433
        result << QLatin1String("CONFIG+=release");
dt_'s avatar
dt_ committed
434
    if (!(defaultBuildConfiguration & QtSupport::BaseQtVersion::DebugBuild) && (userBuildConfiguration & QtSupport::BaseQtVersion::DebugBuild))
435
        result << QLatin1String("CONFIG+=debug");
436
    return result;
437
}
438
439
440
441

QMakeStep *Qt4BuildConfiguration::qmakeStep() const
{
    QMakeStep *qs = 0;
442
    BuildStepList *bsl = stepList(QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
Tobias Hunger's avatar
Tobias Hunger committed
443
444
445
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((qs = qobject_cast<QMakeStep *>(bsl->at(i))) != 0)
446
447
448
449
450
451
            return qs;
    return 0;
}

MakeStep *Qt4BuildConfiguration::makeStep() const
{
Tobias Hunger's avatar
Tobias Hunger committed
452
    MakeStep *ms = 0;
453
    BuildStepList *bsl = stepList(QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
Tobias Hunger's avatar
Tobias Hunger committed
454
455
456
457
    Q_ASSERT(bsl);
    for (int i = 0; i < bsl->count(); ++i)
        if ((ms = qobject_cast<MakeStep *>(bsl->at(i))) != 0)
            return ms;
458
459
460
    return 0;
}

461
462
void Qt4BuildConfiguration::qtVersionsChanged(const QList<int> &changedVersions)
{
463
    if (!changedVersions.contains(m_qtVersionId))
Tobias Hunger's avatar
Tobias Hunger committed
464
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
465
    emit environmentChanged(); // Our qt version changed, that might have changed the environment
466
467
}

468
// returns true if both are equal
469
bool Qt4BuildConfiguration::compareToImportFrom(const QString &makefile)
470
471
{
    QMakeStep *qs = qmakeStep();
472
    if (QFileInfo(makefile).exists() && qs) {
473
        Utils::FileName qmakePath = QtSupport::QtVersionManager::findQMakeBinaryFromMakefile(makefile);
474
        QtSupport::BaseQtVersion *version = qtVersion();
dt's avatar
dt committed
475
476
        if (!version)
            return false;
477
478
        if (version->qmakeCommand() == qmakePath) {
            // same qtversion
479
480
            QPair<QtSupport::BaseQtVersion::QmakeBuildConfigs, QString> result =
                    QtSupport::QtVersionManager::scanMakeFile(makefile, version->defaultBuildConfig());
481
            if (qmakeBuildConfiguration() == result.first) {
482
                // The qmake Build Configuration are the same,
483
484
485
                // now compare arguments lists
                // we have to compare without the spec/platform cmd argument
                // and compare that on its own
486
                QString workingDirectory = QFileInfo(makefile).absolutePath();
487
488
489
490
491
                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:
                extractSpecFromArguments(&userArgs, workingDirectory, version, &actualArgs),
492
                actualArgs = qs->moreArguments() + actualArgs + qs->moreArgumentsAfter();
493
                Utils::FileName actualSpec = qs->mkspec();
494

495
496
                QString qmakeArgs = result.second;
                QStringList parsedArgs;
497
                Utils::FileName parsedSpec = extractSpecFromArguments(&qmakeArgs, workingDirectory, version, &parsedArgs);
498
499
500
501

                if (debug) {
                    qDebug()<<"Actual args:"<<actualArgs;
                    qDebug()<<"Parsed args:"<<parsedArgs;
502
503
                    qDebug()<<"Actual spec:"<<actualSpec.toString();
                    qDebug()<<"Parsed spec:"<<parsedSpec.toString();
504
505
                }

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
                // 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();
522
523
524
525
526
527
                if (actualArgs == parsedArgs) {
                    // Specs match exactly
                    if (actualSpec == parsedSpec)
                        return true;
                    // Actual spec is the default one
//                    qDebug()<<"AS vs VS"<<actualSpec<<version->mkspec();
528
529
                    if ((actualSpec == version->mkspec() || actualSpec == Utils::FileName::fromString(QLatin1String("default")))
                        && (parsedSpec == version->mkspec() || parsedSpec == Utils::FileName::fromString(QLatin1String("default")) || parsedSpec.isEmpty()))
530
531
                        return true;
                }
dt's avatar
dt committed
532
533
            } else if (debug) {
                qDebug()<<"different qmake buildconfigurations buildconfiguration:"<<qmakeBuildConfiguration()<<" Makefile:"<<result.first;
534
            }
dt's avatar
dt committed
535
        } else if (debug) {
536
            qDebug()<<"diffrent qt versions, buildconfiguration:"<<version->qmakeCommand().toString()<<" Makefile:"<<qmakePath.toString();
537
538
539
540
        }
    }
    return false;
}
541

542
bool Qt4BuildConfiguration::removeQMLInspectorFromArguments(QString *args)
543
{
544
545
    bool removedArgument = false;
    for (Utils::QtcProcess::ArgIterator ait(args); ait.next(); ) {
546
547
        const QString arg = ait.value();
        if (arg.contains(QLatin1String(Constants::QMAKEVAR_QMLJSDEBUGGER_PATH))
548
                || arg.contains(QLatin1String(Constants::QMAKEVAR_DECLARATIVE_DEBUG))) {
549
            ait.deleteArg();
550
551
552
553
            removedArgument = true;
        }
    }
    return removedArgument;
554
555
}

556
557
558
Utils::FileName Qt4BuildConfiguration::extractSpecFromArguments(QString *args,
                                                                const QString &directory, const QtSupport::BaseQtVersion *version,
                                                                QStringList *outArgs)
559
{
560
    Utils::FileName parsedSpec;
561

562
    bool ignoreNext = false;
563
564
    bool nextIsSpec = false;
    for (Utils::QtcProcess::ArgIterator ait(args); ait.next(); ) {
565
566
        if (ignoreNext) {
            ignoreNext = false;
567
568
569
            ait.deleteArg();
        } else if (nextIsSpec) {
            nextIsSpec = false;
570
            parsedSpec = Utils::FileName::fromUserInput(ait.value());
571
572
573
574
575
576
577
578
579
580
581
            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.
582
            ignoreNext = true;
583
584
585
            ait.deleteArg();
        } else if (outArgs && ait.isSimple()) {
            outArgs->append(ait.value());
586
587
588
        }
    }

589
    if (parsedSpec.isEmpty())
590
        return Utils::FileName();
591

592
    Utils::FileName baseMkspecDir = Utils::FileName::fromUserInput(version->versionInfo().value(QLatin1String("QMAKE_MKSPECS")));
593
    if (baseMkspecDir.isEmpty())
594
595
        baseMkspecDir = Utils::FileName::fromUserInput(version->versionInfo().value(QLatin1String("QT_INSTALL_DATA"))
                                                       + QLatin1String("/mkspecs"));
596
597
598
599
600
601

    // 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
602
603
604
    if (parsedSpec.toFileInfo().isRelative()) {
        if (QFileInfo(directory + QLatin1Char('/') + parsedSpec.toString()).exists()) {
            parsedSpec = Utils::FileName::fromUserInput(directory + QLatin1Char('/') + parsedSpec.toString());
605
        } else {
606
            parsedSpec = Utils::FileName::fromUserInput(baseMkspecDir.toString() + QLatin1Char('/') + parsedSpec.toString());
607
608
609
        }
    }

610
    QFileInfo f2 = parsedSpec.toFileInfo();
611
    while (f2.isSymLink()) {
612
613
        parsedSpec = Utils::FileName::fromString(f2.symLinkTarget());
        f2.setFile(parsedSpec.toString());
614
615
    }

616
617
    if (parsedSpec.isChildOf(baseMkspecDir)) {
        parsedSpec = parsedSpec.relativeChildPath(baseMkspecDir);
618
    } else {
619
620
        Utils::FileName sourceMkSpecPath = Utils::FileName::fromString(version->sourcePath().toString()
                                                                       + QLatin1String("/mkspecs"));
621
622
        if (parsedSpec.isChildOf(sourceMkSpecPath)) {
            parsedSpec = parsedSpec.relativeChildPath(sourceMkSpecPath);
623
624
625
        }
    }
    return parsedSpec;
626
}
627

628
629
630
631
632
633
634
635
ProjectExplorer::IOutputParser *Qt4BuildConfiguration::createOutputParser() const
{
    ToolChain *tc = toolChain();
    if (tc)
        return toolChain()->outputParser();
    return 0;
}

636
637
638
639
640
bool Qt4BuildConfiguration::isEnabled() const
{
    return m_isEnabled;
}

dt_'s avatar
dt_ committed
641
642
643
644
645
646
647
QString Qt4BuildConfiguration::disabledReason() const
{
    if (!m_isEnabled)
        return tr("Parsing the .pro file");
    return QString();
}

648
649
650
651
652
653
654
655
void Qt4BuildConfiguration::setEnabled(bool enabled)
{
    if (m_isEnabled == enabled)
        return;
    m_isEnabled = enabled;
    emit enabledChanged();
}

656
657
658
659
/*!
  \class Qt4BuildConfigurationFactory
*/

660
661
Qt4BuildConfigurationFactory::Qt4BuildConfigurationFactory(QObject *parent) :
    ProjectExplorer::IBuildConfigurationFactory(parent)
662
663
664
{
    update();

665
    QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
666
667
668
669
670
671
672
673
674
675
676
    connect(vm, SIGNAL(qtVersionsChanged(QList<int>)),
            this, SLOT(update()));
}

Qt4BuildConfigurationFactory::~Qt4BuildConfigurationFactory()
{
}

void Qt4BuildConfigurationFactory::update()
{
    m_versions.clear();
677
678
    QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
    foreach (QtSupport::BaseQtVersion *version, vm->validVersions()) {
dt's avatar
dt committed
679
680
681
682
        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);
683
684
685
686
    }
    emit availableCreationIdsChanged();
}

Tobias Hunger's avatar
Tobias Hunger committed
687
QStringList Qt4BuildConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent) const
688
{
689
    if (!qobject_cast<Qt4BaseTarget *>(parent))
690
        return QStringList();
Tobias Hunger's avatar
Tobias Hunger committed
691
692

    QStringList results;
693
    QtSupport::QtVersionManager *vm = QtSupport::QtVersionManager::instance();
Tobias Hunger's avatar
Tobias Hunger committed
694
695
    for (QMap<QString, VersionInfo>::const_iterator i = m_versions.constBegin();
         i != m_versions.constEnd(); ++i) {
696
697
        if (vm->version(i.value().versionId)->supportsTargetId(parent->id())
                && vm->version(i.value().versionId)->toolChainAvailable(parent->id()))
Tobias Hunger's avatar
Tobias Hunger committed
698
699
700
            results.append(i.key());
    }
    return results;
701
702
703
704
}

QString Qt4BuildConfigurationFactory::displayNameForId(const QString &id) const
{
705
706
707
    if (!m_versions.contains(id))
        return QString();
    return m_versions.value(id).displayName;
708
709
}

Tobias Hunger's avatar
Tobias Hunger committed
710
bool Qt4BuildConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const QString &id) const
711
{
712
    if (!qobject_cast<Qt4BaseTarget *>(parent))
713
714
715
        return false;
    if (!m_versions.contains(id))
        return false;
716
    const VersionInfo &info = m_versions.value(id);
717
    QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(info.versionId);
Tobias Hunger's avatar
Tobias Hunger committed
718
719
    if (!version ||
        !version->supportsTargetId(parent->id()))
720
        return false;
721
722
723
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
724
BuildConfiguration *Qt4BuildConfigurationFactory::create(ProjectExplorer::Target *parent, const QString &id)
725
726
727
728
729
{
    if (!canCreate(parent, id))
        return 0;

    const VersionInfo &info = m_versions.value(id);
730
    QtSupport::BaseQtVersion *version = QtSupport::QtVersionManager::instance()->version(info.versionId);
731
732
    Q_ASSERT(version);

733
    Qt4BaseTarget *qt4Target = static_cast<Qt4BaseTarget *>(parent);
734

735
736
    bool ok;
    QString buildConfigurationName = QInputDialog::getText(0,
737
738
                          tr("New Configuration"),
                          tr("New configuration name:"),
739
740
741
                          QLineEdit::Normal,
                          version->displayName(),
                          &ok);
742
    buildConfigurationName = buildConfigurationName.trimmed();
743
    if (!ok || buildConfigurationName.isEmpty())
Tobias Hunger's avatar
Tobias Hunger committed
744
        return 0;
745

746
    //: Debug build configuration. We recommend not translating it.
747
748
749
750
751
752
    QString defaultDebugName = tr("%1 Debug").arg(version->displayName());
    QString customDebugName;
    if (buildConfigurationName != version->displayName())
        customDebugName = tr("%1 Debug").arg(buildConfigurationName);

    BuildConfiguration *bc = qt4Target->addQt4BuildConfiguration(defaultDebugName, customDebugName,
Tobias Hunger's avatar
Tobias Hunger committed
753
                                        version,
754
                                        (version->defaultBuildConfig() | QtSupport::BaseQtVersion::DebugBuild),
755
                                        QString(), QString(), false);
dt's avatar
dt committed
756

757
    if (qt4Target->id() != QLatin1String(Constants::S60_EMULATOR_TARGET_ID)) {
758
        //: Release build configuration. We recommend not translating it.
759
760
761
762
763
764
        QString defaultReleaseName = tr("%1 Release").arg(version->displayName());
        QString customReleaseName;
        if (buildConfigurationName != version->displayName())
            customReleaseName = tr("%1 Release").arg(buildConfigurationName);

        bc = qt4Target->addQt4BuildConfiguration(defaultReleaseName, customReleaseName,
765
                                                 version,
766
                                                 (version->defaultBuildConfig() & ~QtSupport::BaseQtVersion::DebugBuild),
767
                                                 QString(), QString(), false);
768
    }
769
770
771
    return bc;
}

Tobias Hunger's avatar
Tobias Hunger committed
772
bool Qt4BuildConfigurationFactory::canClone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *source) const
773
{
774
    if (!qobject_cast<Qt4BaseTarget *>(parent))
Tobias Hunger's avatar
Tobias Hunger committed
775
776
777
778
779
        return false;
    Qt4BuildConfiguration *qt4bc(qobject_cast<Qt4BuildConfiguration *>(source));
    if (!qt4bc)
        return false;

780
    QtSupport::BaseQtVersion *version = qt4bc->qtVersion();
Tobias Hunger's avatar
Tobias Hunger committed
781
782
783
784
    if (!version ||
        !version->supportsTargetId(parent->id()))
        return false;
    return true;
785
786
}

Tobias Hunger's avatar
Tobias Hunger committed
787
BuildConfiguration *Qt4BuildConfigurationFactory::clone(Target *parent, BuildConfiguration *source)
788
{
789
790
    if (!canClone(parent, source))
        return 0;
791
    Qt4BaseTarget *target = static_cast<Qt4BaseTarget *>(parent);
792
    Qt4BuildConfiguration *oldbc(static_cast<Qt4BuildConfiguration *>(source));
Tobias Hunger's avatar
Tobias Hunger committed
793
    return new Qt4BuildConfiguration(target, oldbc);
794
795
}

Tobias Hunger's avatar
Tobias Hunger committed
796
bool Qt4BuildConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
797
{