project.cpp 14.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con 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
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** 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
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "project.h"
hjk's avatar
hjk committed
32

33
#include "buildinfo.h"
Tobias Hunger's avatar
Tobias Hunger committed
34
#include "buildconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
35
#include "editorconfiguration.h"
con's avatar
con committed
36
#include "projectexplorer.h"
Tobias Hunger's avatar
Tobias Hunger committed
37
#include "target.h"
38
#include "settingsaccessor.h"
con's avatar
con committed
39

40
#include <coreplugin/idocument.h>
41
#include <coreplugin/icontext.h>
42
#include <coreplugin/icore.h>
Daniel Teske's avatar
Daniel Teske committed
43
#include <projectexplorer/buildmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
44
#include <projectexplorer/kitmanager.h>
45

46
#include <utils/algorithm.h>
47
48
49
50
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>

#include <limits>
con's avatar
con committed
51

52
53
54
/*!
    \class ProjectExplorer::Project

55
    \brief The Project class implements a project node in the project explorer.
56
57
58
59
60
*/

/*!
   \fn void ProjectExplorer::Project::environmentChanged()

61
62
63
   A convenience signal emitted if activeBuildConfiguration emits
   environmentChanged or if the active build configuration changes
   (including due to the active target changing).
64
65
66
67
68
*/

/*!
   \fn void ProjectExplorer::Project::buildConfigurationEnabledChanged()

69
70
71
   A convenience signal emitted if activeBuildConfiguration emits
   isEnabledChanged() or if the active build configuration changes
   (including due to the active target changing).
72
73
*/

Tobias Hunger's avatar
Tobias Hunger committed
74
namespace {
hjk's avatar
hjk committed
75
76
77
78
79
const char ACTIVE_TARGET_KEY[] = "ProjectExplorer.Project.ActiveTarget";
const char TARGET_KEY_PREFIX[] = "ProjectExplorer.Project.Target.";
const char TARGET_COUNT_KEY[] = "ProjectExplorer.Project.TargetCount";
const char EDITOR_SETTINGS_KEY[] = "ProjectExplorer.Project.EditorSettings";
const char PLUGIN_SETTINGS_KEY[] = "ProjectExplorer.Project.PluginSettings";
Tobias Hunger's avatar
Tobias Hunger committed
80
81
} // namespace

82
namespace ProjectExplorer {
Tobias Hunger's avatar
Tobias Hunger committed
83
84
85
86
// -------------------------------------------------------------------------
// Project
// -------------------------------------------------------------------------

87
88
class ProjectPrivate
{
89
90
public:
    ProjectPrivate();
Tobias Hunger's avatar
Tobias Hunger committed
91
92
    ~ProjectPrivate();

93
    Core::Id m_id;
94
95
    QList<Target *> m_targets;
    Target *m_activeTarget;
96
    EditorConfiguration m_editorConfiguration;
97
    Core::Context m_projectContext;
98
    Core::Context m_projectLanguages;
99
    QVariantMap m_pluginSettings;
100
    Internal::UserFileAccessor *m_accessor;
101
102
103

    KitMatcher m_requiredKitMatcher;
    KitMatcher m_preferredKitMatcher;
104
105

    Utils::MacroExpander m_macroExpander;
106
107
108
};

ProjectPrivate::ProjectPrivate() :
Tobias Hunger's avatar
Tobias Hunger committed
109
    m_activeTarget(0),
110
    m_accessor(0)
Tobias Hunger's avatar
Tobias Hunger committed
111
112
113
114
{ }

ProjectPrivate::~ProjectPrivate()
{ delete m_accessor; }
con's avatar
con committed
115

116
Project::Project() : d(new ProjectPrivate)
117
118
119
120
121
{
    d->m_macroExpander.setDisplayName(tr("Project"));
    d->m_macroExpander.registerVariable("Project:Name", tr("Project Name"),
            [this] { return displayName(); });
}
122

123
124
Project::~Project()
{
125
    qDeleteAll(d->m_targets);
hjk's avatar
hjk committed
126
    delete d;
127
128
}

129
130
131
132
133
134
Core::Id Project::id() const
{
    QTC_CHECK(d->m_id.isValid());
    return d->m_id;
}

135
Utils::FileName Project::projectFilePath() const
136
{
137
    return Utils::FileName::fromString(document()->filePath());
138
139
}

Tobias Hunger's avatar
Tobias Hunger committed
140
141
bool Project::hasActiveBuildSettings() const
{
Tobias Hunger's avatar
Tobias Hunger committed
142
    return activeTarget() && IBuildConfigurationFactory::find(activeTarget());
Tobias Hunger's avatar
Tobias Hunger committed
143
144
}

Tobias Hunger's avatar
Tobias Hunger committed
145
QString Project::makeUnique(const QString &preferredName, const QStringList &usedNames)
dt's avatar
dt committed
146
{
Tobias Hunger's avatar
Tobias Hunger committed
147
148
    if (!usedNames.contains(preferredName))
        return preferredName;
dt's avatar
dt committed
149
    int i = 2;
Tobias Hunger's avatar
Tobias Hunger committed
150
    QString tryName = preferredName + QString::number(i);
dt's avatar
dt committed
151
    while (usedNames.contains(tryName))
Tobias Hunger's avatar
Tobias Hunger committed
152
        tryName = preferredName + QString::number(++i);
dt's avatar
dt committed
153
154
155
    return tryName;
}

Tobias Hunger's avatar
Tobias Hunger committed
156
157
void Project::changeEnvironment()
{
158
    Target *t = qobject_cast<Target *>(sender());
Tobias Hunger's avatar
Tobias Hunger committed
159
160
161
162
    if (t == activeTarget())
        emit environmentChanged();
}

163
164
165
166
167
168
void Project::changeBuildConfigurationEnabled()
{
    Target *t = qobject_cast<Target *>(sender());
    if (t == activeTarget())
        emit buildConfigurationEnabledChanged();
}
Tobias Hunger's avatar
Tobias Hunger committed
169
170
171

void Project::addTarget(Target *t)
{
172
    QTC_ASSERT(t && !d->m_targets.contains(t), return);
Tobias Hunger's avatar
Tobias Hunger committed
173
    QTC_ASSERT(!target(t->kit()), return);
Tobias Hunger's avatar
Tobias Hunger committed
174
    Q_ASSERT(t->project() == this);
175

Tobias Hunger's avatar
Tobias Hunger committed
176
    t->setDefaultDisplayName(t->displayName());
con's avatar
con committed
177

178
    // add it
179
    d->m_targets.push_back(t);
Tobias Hunger's avatar
Tobias Hunger committed
180
181
    connect(t, SIGNAL(environmentChanged()),
            SLOT(changeEnvironment()));
182
183
    connect(t, SIGNAL(buildConfigurationEnabledChanged()),
            this, SLOT(changeBuildConfigurationEnabled()));
Tobias Hunger's avatar
Tobias Hunger committed
184
185
    connect(t, SIGNAL(buildDirectoryChanged()),
            this, SLOT(onBuildDirectoryChanged()));
Tobias Hunger's avatar
Tobias Hunger committed
186
187
188
189
190
    emit addedTarget(t);

    // check activeTarget:
    if (activeTarget() == 0)
        setActiveTarget(t);
con's avatar
con committed
191
192
}

Daniel Teske's avatar
Daniel Teske committed
193
bool Project::removeTarget(Target *target)
con's avatar
con committed
194
{
Daniel Teske's avatar
Daniel Teske committed
195
196
197
    if (!target || !d->m_targets.contains(target))
        return false;

hjk's avatar
hjk committed
198
    if (BuildManager::isBuilding(target))
Daniel Teske's avatar
Daniel Teske committed
199
        return false;
con's avatar
con committed
200

Tobias Hunger's avatar
Tobias Hunger committed
201
    if (target == activeTarget()) {
202
        if (d->m_targets.size() == 1)
Tobias Hunger's avatar
Tobias Hunger committed
203
            setActiveTarget(0);
204
        else if (d->m_targets.first() == target)
Daniel Teske's avatar
Daniel Teske committed
205
            setActiveTarget(d->m_targets.at(1));
206
        else
207
            setActiveTarget(d->m_targets.at(0));
Tobias Hunger's avatar
Tobias Hunger committed
208
    }
Daniel Teske's avatar
Daniel Teske committed
209
210
211
212
213

    emit aboutToRemoveTarget(target);
    d->m_targets.removeOne(target);
    emit removedTarget(target);

Tobias Hunger's avatar
Tobias Hunger committed
214
    delete target;
215
    return true;
con's avatar
con committed
216
217
}

Tobias Hunger's avatar
Tobias Hunger committed
218
QList<Target *> Project::targets() const
con's avatar
con committed
219
{
220
    return d->m_targets;
con's avatar
con committed
221
222
}

Tobias Hunger's avatar
Tobias Hunger committed
223
Target *Project::activeTarget() const
224
{
225
    return d->m_activeTarget;
226
227
}

Tobias Hunger's avatar
Tobias Hunger committed
228
void Project::setActiveTarget(Target *target)
229
{
230
231
232
233
    if ((!target && !d->m_targets.isEmpty()) ||
        (target && d->m_targets.contains(target) && d->m_activeTarget != target)) {
        d->m_activeTarget = target;
        emit activeTargetChanged(d->m_activeTarget);
Tobias Hunger's avatar
Tobias Hunger committed
234
        emit environmentChanged();
235
        emit buildConfigurationEnabledChanged();
Tobias Hunger's avatar
Tobias Hunger committed
236
    }
237
238
}

239
Target *Project::target(Core::Id id) const
240
{
241
    return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::id, id));
242
243
}

Tobias Hunger's avatar
Tobias Hunger committed
244
Target *Project::target(Kit *k) const
Tobias Hunger's avatar
Tobias Hunger committed
245
{
246
    return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::kit, k));
Tobias Hunger's avatar
Tobias Hunger committed
247
248
}

249
bool Project::supportsKit(Kit *k, QString *errorMessage) const
Tobias Hunger's avatar
Tobias Hunger committed
250
{
Tobias Hunger's avatar
Tobias Hunger committed
251
    Q_UNUSED(k);
252
    Q_UNUSED(errorMessage);
Tobias Hunger's avatar
Tobias Hunger committed
253
254
255
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
256
Target *Project::createTarget(Kit *k)
Tobias Hunger's avatar
Tobias Hunger committed
257
{
Tobias Hunger's avatar
Tobias Hunger committed
258
    if (!k || target(k))
Tobias Hunger's avatar
Tobias Hunger committed
259
260
        return 0;

Tobias Hunger's avatar
Tobias Hunger committed
261
    Target *t = new Target(this, k);
262
263
264
265
    if (!setupTarget(t)) {
        delete t;
        return 0;
    }
Tobias Hunger's avatar
Tobias Hunger committed
266
267
268
    return t;
}

269
270
271
272
273
274
275
276
bool Project::setupTarget(Target *t)
{
    t->updateDefaultBuildConfigurations();
    t->updateDefaultDeployConfigurations();
    t->updateDefaultRunConfigurations();
    return true;
}

277
278
279
280
281
void Project::setId(Core::Id id)
{
    d->m_id = id;
}

Tobias Hunger's avatar
Tobias Hunger committed
282
283
284
285
286
287
288
289
290
Target *Project::restoreTarget(const QVariantMap &data)
{
    Core::Id id = idFromMap(data);
    if (target(id)) {
        qWarning("Warning: Duplicated target id found, not restoring second target with id '%s'. Continuing.",
                 qPrintable(id.toString()));
        return 0;
    }

291
    Kit *k = KitManager::find(id);
Tobias Hunger's avatar
Tobias Hunger committed
292
    if (!k) {
293
        qWarning("Warning: No kit '%s' found. Continuing.", qPrintable(id.toString()));
Tobias Hunger's avatar
Tobias Hunger committed
294
295
296
        return 0;
    }

Tobias Hunger's avatar
Tobias Hunger committed
297
    Target *t = new Target(this, k);
Tobias Hunger's avatar
Tobias Hunger committed
298
299
300
301
    if (!t->fromMap(data)) {
        delete t;
        return 0;
    }
302

Tobias Hunger's avatar
Tobias Hunger committed
303
304
305
    return t;
}

con's avatar
con committed
306
307
void Project::saveSettings()
{
308
    emit aboutToSaveSettings();
Tobias Hunger's avatar
Tobias Hunger committed
309
    if (!d->m_accessor)
310
        d->m_accessor = new Internal::UserFileAccessor(this);
311
312
    if (!targets().isEmpty())
        d->m_accessor->saveSettings(toMap(), Core::ICore::mainWindow());
con's avatar
con committed
313
314
}

dt's avatar
dt committed
315
bool Project::restoreSettings()
con's avatar
con committed
316
{
Tobias Hunger's avatar
Tobias Hunger committed
317
    if (!d->m_accessor)
318
        d->m_accessor = new Internal::UserFileAccessor(this);
319
    QVariantMap map(d->m_accessor->restoreSettings(Core::ICore::mainWindow()));
320
321
322
323
    bool ok = fromMap(map);
    if (ok)
        emit settingsLoaded();
    return ok;
con's avatar
con committed
324
325
326
}


327
/*!
328
    Serializes all data into a QVariantMap.
329
330
331
332

    This map is then saved in the .user file of the project.
    Just put all your data into the map.

333
    \note Do not forget to call your base class' toMap function.
334
    \note Do not forget to call setActiveBuildConfiguration when
335
    creating new build configurations.
336
337
*/

338
QVariantMap Project::toMap() const
con's avatar
con committed
339
{
Tobias Hunger's avatar
Tobias Hunger committed
340
    const QList<Target *> ts = targets();
dt's avatar
dt committed
341

342
    QVariantMap map;
343
    map.insert(QLatin1String(ACTIVE_TARGET_KEY), ts.indexOf(d->m_activeTarget));
Tobias Hunger's avatar
Tobias Hunger committed
344
345
346
    map.insert(QLatin1String(TARGET_COUNT_KEY), ts.size());
    for (int i = 0; i < ts.size(); ++i)
        map.insert(QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(i), ts.at(i)->toMap());
con's avatar
con committed
347

348
    map.insert(QLatin1String(EDITOR_SETTINGS_KEY), d->m_editorConfiguration.toMap());
349
    map.insert(QLatin1String(PLUGIN_SETTINGS_KEY), d->m_pluginSettings);
350
351

    return map;
con's avatar
con committed
352
353
}

354
Utils::FileName Project::projectDirectory() const
355
{
356
    return projectDirectory(projectFilePath());
357
358
}

359
Utils::FileName Project::projectDirectory(const Utils::FileName &top)
360
{
Tobias Hunger's avatar
Tobias Hunger committed
361
    if (top.isEmpty())
362
363
        return Utils::FileName();
    return Utils::FileName::fromString(top.toFileInfo().absoluteDir().path());
364
365
}

366

367
bool Project::fromMap(const QVariantMap &map)
con's avatar
con committed
368
{
369
370
    if (map.contains(QLatin1String(EDITOR_SETTINGS_KEY))) {
        QVariantMap values(map.value(QLatin1String(EDITOR_SETTINGS_KEY)).toMap());
371
        d->m_editorConfiguration.fromMap(values);
con's avatar
con committed
372
373
    }

374
375
376
    if (map.contains(QLatin1String(PLUGIN_SETTINGS_KEY)))
        d->m_pluginSettings = map.value(QLatin1String(PLUGIN_SETTINGS_KEY)).toMap();

377
    bool ok;
Tobias Hunger's avatar
Tobias Hunger committed
378
    int maxI(map.value(QLatin1String(TARGET_COUNT_KEY), 0).toInt(&ok));
379
380
    if (!ok || maxI < 0)
        maxI = 0;
Tobias Hunger's avatar
Tobias Hunger committed
381
    int active(map.value(QLatin1String(ACTIVE_TARGET_KEY), 0).toInt(&ok));
Tobias Hunger's avatar
Tobias Hunger committed
382
    if (!ok || active < 0 || active >= maxI)
Tobias Hunger's avatar
Tobias Hunger committed
383
        active = 0;
384
385

    for (int i = 0; i < maxI; ++i) {
Tobias Hunger's avatar
Tobias Hunger committed
386
        const QString key(QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(i));
387
388
        if (!map.contains(key)) {
            qWarning() << key << "was not found in data.";
389
            return false;
390
        }
391
        QVariantMap targetMap = map.value(key).toMap();
392

Tobias Hunger's avatar
Tobias Hunger committed
393
394
395
        Target *t = restoreTarget(targetMap);
        if (!t)
            continue;
396

Tobias Hunger's avatar
Tobias Hunger committed
397
398
399
        addTarget(t);
        if (i == active)
            setActiveTarget(t);
dt's avatar
dt committed
400
    }
Tobias Hunger's avatar
Tobias Hunger committed
401

402
    return true;
con's avatar
con committed
403
404
405
406
}

EditorConfiguration *Project::editorConfiguration() const
{
407
    return &d->m_editorConfiguration;
con's avatar
con committed
408
409
}

410
411
412
413
QString Project::generatedUiHeader(const QString & /* formFile */) const
{
    return QString();
}
414

415
416
void Project::setProjectContext(Core::Context context)
{
417
418
    if (d->m_projectContext == context)
        return;
419
    d->m_projectContext = context;
420
    emit projectContextUpdated();
421
422
}

423
void Project::setProjectLanguages(Core::Context language)
424
{
425
426
    if (d->m_projectLanguages == language)
        return;
427
    d->m_projectLanguages = language;
428
    emit projectLanguagesUpdated();
429
430
}

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
void Project::addProjectLanguage(Core::Id id)
{
    Core::Context lang = projectLanguages();
    int pos = lang.indexOf(id);
    if (pos < 0)
        lang.add(id);
    setProjectLanguages(lang);
}

void Project::removeProjectLanguage(Core::Id id)
{
    Core::Context lang = projectLanguages();
    int pos = lang.indexOf(id);
    if (pos >= 0)
        lang.removeAt(pos);
    setProjectLanguages(lang);
}

void Project::setProjectLanguage(Core::Id id, bool enabled)
{
    if (enabled)
        addProjectLanguage(id);
    else
        removeProjectLanguage(id);
}

457
458
459
460
461
Core::Context Project::projectContext() const
{
    return d->m_projectContext;
}

462
Core::Context Project::projectLanguages() const
463
{
464
    return d->m_projectLanguages;
465
466
}

467
468
469
470
471
QVariant Project::namedSettings(const QString &name) const
{
    return d->m_pluginSettings.value(name);
}

472
void Project::setNamedSettings(const QString &name, const QVariant &value)
473
{
Tobias Hunger's avatar
Tobias Hunger committed
474
475
476
477
    if (value.isNull())
        d->m_pluginSettings.remove(name);
    else
        d->m_pluginSettings.insert(name, value);
478
479
}

480
481
482
483
484
bool Project::needsConfiguration() const
{
    return false;
}

485
486
void Project::configureAsExampleProject(const QStringList &platforms)
{
Tobias Hunger's avatar
Tobias Hunger committed
487
    Q_UNUSED(platforms);
488
489
}

490
bool Project::requiresTargetPanel() const
491
{
492
    return true;
493
494
}

495
496
497
498
499
bool Project::needsSpecialDeployment() const
{
    return false;
}

500
501
502
503
504
505
506
507
508
void Project::setup(QList<const BuildInfo *> infoList)
{
    QList<Target *> toRegister;
    foreach (const BuildInfo *info, infoList) {
        Kit *k = KitManager::find(info->kitId);
        if (!k)
            continue;
        Target *t = target(k);
        if (!t) {
509
            t = Utils::findOrDefault(toRegister, Utils::equal(&Target::kit, k));
510
511
512
513
514
515
516
517
518
519
520
        }
        if (!t) {
            t = new Target(this, k);
            toRegister << t;
        }

        BuildConfiguration *bc = info->factory()->create(t, info);
        if (!bc)
            continue;
        t->addBuildConfiguration(bc);
    }
521
522
523
    foreach (Target *t, toRegister) {
        t->updateDefaultDeployConfigurations();
        t->updateDefaultRunConfigurations();
524
        addTarget(t);
525
526
527
    }
}

528
529
530
531
532
Utils::MacroExpander *Project::macroExpander() const
{
    return &d->m_macroExpander;
}

533
534
535
ProjectImporter *Project::createProjectImporter() const
{
    return 0;
536
537
}

538
539
540
541
542
543
544
KitMatcher Project::requiredKitMatcher() const
{
    return d->m_requiredKitMatcher;
}

void Project::setRequiredKitMatcher(const KitMatcher &matcher)
{
545
    d->m_requiredKitMatcher = matcher;
546
547
548
549
550
551
552
553
554
}

KitMatcher Project::preferredKitMatcher() const
{
    return d->m_preferredKitMatcher;
}

void Project::setPreferredKitMatcher(const KitMatcher &matcher)
{
555
    d->m_preferredKitMatcher = matcher;
556
557
}

Tobias Hunger's avatar
Tobias Hunger committed
558
559
560
561
562
563
564
void Project::onBuildDirectoryChanged()
{
    Target *target = qobject_cast<Target *>(sender());
    if (target && target == activeTarget())
        emit buildDirectoryChanged();
}

565
} // namespace ProjectExplorer