qbsbuildstep.cpp 26.3 KB
Newer Older
Tobias Hunger's avatar
Tobias Hunger committed
1
2
/****************************************************************************
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
Tobias Hunger's avatar
Tobias Hunger committed
5
6
7
8
9
10
11
**
** This file is part of Qt Creator.
**
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
Tobias Hunger's avatar
Tobias Hunger committed
15
16
17
**
** GNU Lesser General Public License Usage
** 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.
Tobias Hunger's avatar
Tobias Hunger committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
Tobias Hunger's avatar
Tobias Hunger committed
27
28
29
30
31
32
33
34
35
36
37
38
39
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qbsbuildstep.h"

#include "qbsbuildconfiguration.h"
#include "qbsparser.h"
#include "qbsproject.h"
#include "qbsprojectmanagerconstants.h"

#include "ui_qbsbuildstepconfigwidget.h"

40
#include <coreplugin/icore.h>
Tobias Hunger's avatar
Tobias Hunger committed
41
42
43
44
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
45
46
#include <qtsupport/debugginghelperbuildtask.h>
#include <qtsupport/qtversionmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
47
#include <utils/qtcassert.h>
48
#include <utils/qtcprocess.h>
Tobias Hunger's avatar
Tobias Hunger committed
49
50
51
52
53
54
55

#include <qbs.h>

static const char QBS_CONFIG[] = "Qbs.Configuration";
static const char QBS_DRY_RUN[] = "Qbs.DryRun";
static const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing";
static const char QBS_MAXJOBCOUNT[] = "Qbs.MaxJobs";
56
static const char QBS_SHOWCOMMANDLINES[] = "Qbs.ShowCommandLines";
57
58
static const char QBS_INSTALL[] = "Qbs.Install";
static const char QBS_CLEAN_INSTALL_ROOT[] = "Qbs.CleanInstallRoot";
Tobias Hunger's avatar
Tobias Hunger committed
59
60
61
62
63
64
65
66
67
68
69
70
71
72

// --------------------------------------------------------------------
// Constants:
// --------------------------------------------------------------------

namespace QbsProjectManager {
namespace Internal {

// --------------------------------------------------------------------
// QbsBuildStep:
// --------------------------------------------------------------------

QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl) :
    ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_BUILDSTEP_ID)),
73
    m_job(0), m_parser(0), m_parsingProject(false)
Tobias Hunger's avatar
Tobias Hunger committed
74
{
Tobias Hunger's avatar
Tobias Hunger committed
75
    setDisplayName(tr("Qbs Build"));
76
    setQbsConfiguration(QVariantMap());
Tobias Hunger's avatar
Tobias Hunger committed
77
78
79
80
}

QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl, const QbsBuildStep *other) :
    ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_BUILDSTEP_ID)),
81
    m_qbsBuildOptions(other->m_qbsBuildOptions),  m_job(0), m_parser(0), m_parsingProject(false)
82
83
84
{
    setQbsConfiguration(other->qbsConfiguration());
}
Tobias Hunger's avatar
Tobias Hunger committed
85
86
87
88

QbsBuildStep::~QbsBuildStep()
{
    cancel();
89
90
91
92
    if (m_job) {
        m_job->deleteLater();
        m_job = 0;
    }
Tobias Hunger's avatar
Tobias Hunger committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    delete m_parser;
}

bool QbsBuildStep::init()
{
    if (static_cast<QbsProject *>(project())->isParsing() || m_job)
        return false;

    QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
    if (!bc)
        bc = static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration());

    if (!bc)
        return false;

    delete m_parser;
    m_parser = new Internal::QbsParser;
    ProjectExplorer::IOutputParser *parser = target()->kit()->createOutputParser();
    if (parser)
        m_parser->appendOutputParser(parser);

    m_changedFiles = bc->changedFiles();
115
    m_activeFileTags = bc->activeFileTags();
116
    m_products = bc->products();
Tobias Hunger's avatar
Tobias Hunger committed
117
118
119
120
121
122
123
124
125
126
127
128
129

    connect(m_parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
            this, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)));
    connect(m_parser, SIGNAL(addTask(ProjectExplorer::Task)),
            this, SIGNAL(addTask(ProjectExplorer::Task)));

    return true;
}

void QbsBuildStep::run(QFutureInterface<bool> &fi)
{
    m_fi = &fi;

130
131
132
    // We need a pre-build parsing step in order not to lose project file changes done
    // right before building (but before the delay has elapsed).
    parseProject();
Tobias Hunger's avatar
Tobias Hunger committed
133
134
135
136
137
138
139
140
141
142
143
144
145
146
}

ProjectExplorer::BuildStepConfigWidget *QbsBuildStep::createConfigWidget()
{
    return new QbsBuildStepConfigWidget(this);
}

bool QbsBuildStep::runInGuiThread() const
{
    return true;
}

void QbsBuildStep::cancel()
{
147
148
149
    if (m_parsingProject)
        qbsProject()->cancelParsing();
    else if (m_job)
Tobias Hunger's avatar
Tobias Hunger committed
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        m_job->cancel();
}

QVariantMap QbsBuildStep::qbsConfiguration() const
{
    return m_qbsConfiguration;
}

void QbsBuildStep::setQbsConfiguration(const QVariantMap &config)
{
    QbsProject *pro = static_cast<QbsProject *>(project());

    QVariantMap tmp = config;
    tmp.insert(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY), pro->projectManager()->profileForKit(target()->kit()));
    if (!tmp.contains(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)))
        tmp.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
                   QString::fromLatin1(Constants::QBS_VARIANT_DEBUG));

    if (tmp == m_qbsConfiguration)
        return;
    m_qbsConfiguration = tmp;
171
172
173
    QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
    if (bc)
        bc->emitBuildTypeChanged();
Tobias Hunger's avatar
Tobias Hunger committed
174
175
176
177
178
    emit qbsConfigurationChanged();
}

bool QbsBuildStep::dryRun() const
{
Christian Kandeler's avatar
Christian Kandeler committed
179
    return m_qbsBuildOptions.dryRun();
Tobias Hunger's avatar
Tobias Hunger committed
180
181
182
183
}

bool QbsBuildStep::keepGoing() const
{
Christian Kandeler's avatar
Christian Kandeler committed
184
    return m_qbsBuildOptions.keepGoing();
Tobias Hunger's avatar
Tobias Hunger committed
185
186
}

187
188
bool QbsBuildStep::showCommandLines() const
{
189
    return m_qbsBuildOptions.echoMode() == qbs::CommandEchoModeCommandLine;
190
191
}

192
193
194
195
196
197
198
199
200
201
bool QbsBuildStep::install() const
{
    return m_qbsBuildOptions.install();
}

bool QbsBuildStep::cleanInstallRoot() const
{
    return m_qbsBuildOptions.removeExistingInstallation();
}

Tobias Hunger's avatar
Tobias Hunger committed
202
203
int QbsBuildStep::maxJobs() const
{
204
205
    if (m_qbsBuildOptions.maxJobCount() > 0)
        return m_qbsBuildOptions.maxJobCount();
Tobias Hunger's avatar
Tobias Hunger committed
206
    return qbs::BuildOptions::defaultMaxJobCount();
Tobias Hunger's avatar
Tobias Hunger committed
207
208
209
210
211
212
213
214
}

bool QbsBuildStep::fromMap(const QVariantMap &map)
{
    if (!ProjectExplorer::BuildStep::fromMap(map))
        return false;

    setQbsConfiguration(map.value(QLatin1String(QBS_CONFIG)).toMap());
Christian Kandeler's avatar
Christian Kandeler committed
215
216
217
    m_qbsBuildOptions.setDryRun(map.value(QLatin1String(QBS_DRY_RUN)).toBool());
    m_qbsBuildOptions.setKeepGoing(map.value(QLatin1String(QBS_KEEP_GOING)).toBool());
    m_qbsBuildOptions.setMaxJobCount(map.value(QLatin1String(QBS_MAXJOBCOUNT)).toInt());
218
219
220
    const bool showCommandLines = map.value(QLatin1String(QBS_SHOWCOMMANDLINES)).toBool();
    m_qbsBuildOptions.setEchoMode(showCommandLines ? qbs::CommandEchoModeCommandLine
                                                   : qbs::CommandEchoModeSummary);
221
222
223
    m_qbsBuildOptions.setInstall(map.value(QLatin1String(QBS_INSTALL), true).toBool());
    m_qbsBuildOptions.setRemoveExistingInstallation(map.value(QLatin1String(QBS_CLEAN_INSTALL_ROOT))
                                                    .toBool());
Tobias Hunger's avatar
Tobias Hunger committed
224
225
226
227
228
229
230
    return true;
}

QVariantMap QbsBuildStep::toMap() const
{
    QVariantMap map = ProjectExplorer::BuildStep::toMap();
    map.insert(QLatin1String(QBS_CONFIG), m_qbsConfiguration);
Christian Kandeler's avatar
Christian Kandeler committed
231
232
233
    map.insert(QLatin1String(QBS_DRY_RUN), m_qbsBuildOptions.dryRun());
    map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsBuildOptions.keepGoing());
    map.insert(QLatin1String(QBS_MAXJOBCOUNT), m_qbsBuildOptions.maxJobCount());
234
235
    map.insert(QLatin1String(QBS_SHOWCOMMANDLINES),
               m_qbsBuildOptions.echoMode() == qbs::CommandEchoModeCommandLine);
236
237
238
    map.insert(QLatin1String(QBS_INSTALL), m_qbsBuildOptions.install());
    map.insert(QLatin1String(QBS_CLEAN_INSTALL_ROOT),
               m_qbsBuildOptions.removeExistingInstallation());
Tobias Hunger's avatar
Tobias Hunger committed
239
240
241
242
243
    return map;
}

void QbsBuildStep::buildingDone(bool success)
{
244
    m_lastWasSuccess = success;
Tobias Hunger's avatar
Tobias Hunger committed
245
    // Report errors:
246
247
    foreach (const qbs::ErrorItem &item, m_job->error().items())
        createTaskAndOutput(ProjectExplorer::Task::Error, item.description(),
Christian Kandeler's avatar
Christian Kandeler committed
248
                            item.codeLocation().filePath(), item.codeLocation().line());
Tobias Hunger's avatar
Tobias Hunger committed
249

250
251
    QbsProject *pro = static_cast<QbsProject *>(project());

Christian Kandeler's avatar
Christian Kandeler committed
252
    // Building can uncover additional target artifacts.
253
254
255
256
    pro->updateAfterBuild();

    // The reparsing, if it is necessary, has to be done before finished() is emitted, as
    // otherwise a potential additional build step could conflict with the parsing step.
257
258
259
    if (pro->parsingScheduled())
        parseProject();
    else
260
        finish();
261
}
Christian Kandeler's avatar
Christian Kandeler committed
262

263
void QbsBuildStep::reparsingDone(bool success)
264
{
265
266
267
268
269
270
271
272
273
274
    disconnect(qbsProject(), SIGNAL(projectParsingDone(bool)), this, SLOT(reparsingDone(bool)));
    m_parsingProject = false;
    if (m_job) { // This was a scheduled reparsing after building.
        finish();
    } else if (!success) {
        m_lastWasSuccess = false;
        finish();
    } else {
        build();
    }
Tobias Hunger's avatar
Tobias Hunger committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
}

void QbsBuildStep::handleTaskStarted(const QString &desciption, int max)
{
    Q_UNUSED(desciption);
    QTC_ASSERT(m_fi, return);

    m_progressBase = m_fi->progressValue();
    m_fi->setProgressRange(0, m_progressBase + max);
}

void QbsBuildStep::handleProgress(int value)
{
    QTC_ASSERT(m_fi, return);
    m_fi->setProgressValue(m_progressBase + value);
}

void QbsBuildStep::handleCommandDescriptionReport(const QString &highlight, const QString &message)
{
    Q_UNUSED(highlight);
    emit addOutput(message, NormalOutput);
}

void QbsBuildStep::handleProcessResultReport(const qbs::ProcessResult &result)
{
Christian Kandeler's avatar
Christian Kandeler committed
300
    bool hasOutput = !result.stdOut().isEmpty() || !result.stdErr().isEmpty();
Tobias Hunger's avatar
Tobias Hunger committed
301

302
    if (result.success() && !hasOutput)
Tobias Hunger's avatar
Tobias Hunger committed
303
304
        return;

Christian Kandeler's avatar
Christian Kandeler committed
305
    m_parser->setWorkingDirectory(result.workingDirectory());
306

307
308
    QString commandline = result.executableFilePath() + QLatin1Char(' ')
            + Utils::QtcProcess::joinArgs(result.arguments());
309
310
    addOutput(commandline, NormalOutput);

Christian Kandeler's avatar
Christian Kandeler committed
311
    foreach (const QString &line, result.stdErr()) {
Tobias Hunger's avatar
Tobias Hunger committed
312
313
314
        m_parser->stdError(line);
        addOutput(line, ErrorOutput);
    }
Christian Kandeler's avatar
Christian Kandeler committed
315
    foreach (const QString &line, result.stdOut()) {
Tobias Hunger's avatar
Tobias Hunger committed
316
317
318
        m_parser->stdOutput(line);
        addOutput(line, NormalOutput);
    }
319
    m_parser->flush();
Tobias Hunger's avatar
Tobias Hunger committed
320
321
322
323
324
}

void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message,
                                       const QString &file, int line)
{
325
326
327
328
    ProjectExplorer::Task task = ProjectExplorer::Task(type, message,
                                                       Utils::FileName::fromString(file), line,
                                                       ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
    emit addTask(task, 1);
Tobias Hunger's avatar
Tobias Hunger committed
329
330
331
332
333
334
335
336
    emit addOutput(message, NormalOutput);
}

QString QbsBuildStep::buildVariant() const
{
    return qbsConfiguration().value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
}

337
338
339
340
341
342
343
bool QbsBuildStep::isQmlDebuggingEnabled() const
{
    QVariantMap data = qbsConfiguration();
    return data.value(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY), false).toBool()
            || data.value(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY), false).toBool();
}

Tobias Hunger's avatar
Tobias Hunger committed
344
345
346
347
348
349
void QbsBuildStep::setBuildVariant(const QString &variant)
{
    if (m_qbsConfiguration.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString() == variant)
        return;
    m_qbsConfiguration.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY), variant);
    emit qbsConfigurationChanged();
350
351
352
    QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
    if (bc)
        bc->emitBuildTypeChanged();
Tobias Hunger's avatar
Tobias Hunger committed
353
354
355
356
357
358
359
360
361
}

QString QbsBuildStep::profile() const
{
    return qbsConfiguration().value(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY)).toString();
}

void QbsBuildStep::setDryRun(bool dr)
{
Christian Kandeler's avatar
Christian Kandeler committed
362
    if (m_qbsBuildOptions.dryRun() == dr)
Tobias Hunger's avatar
Tobias Hunger committed
363
        return;
Christian Kandeler's avatar
Christian Kandeler committed
364
    m_qbsBuildOptions.setDryRun(dr);
Tobias Hunger's avatar
Tobias Hunger committed
365
366
367
368
369
    emit qbsBuildOptionsChanged();
}

void QbsBuildStep::setKeepGoing(bool kg)
{
Christian Kandeler's avatar
Christian Kandeler committed
370
    if (m_qbsBuildOptions.keepGoing() == kg)
Tobias Hunger's avatar
Tobias Hunger committed
371
        return;
Christian Kandeler's avatar
Christian Kandeler committed
372
    m_qbsBuildOptions.setKeepGoing(kg);
Tobias Hunger's avatar
Tobias Hunger committed
373
374
375
376
377
    emit qbsBuildOptionsChanged();
}

void QbsBuildStep::setMaxJobs(int jobcount)
{
Christian Kandeler's avatar
Christian Kandeler committed
378
    if (m_qbsBuildOptions.maxJobCount() == jobcount)
Tobias Hunger's avatar
Tobias Hunger committed
379
        return;
Christian Kandeler's avatar
Christian Kandeler committed
380
    m_qbsBuildOptions.setMaxJobCount(jobcount);
Tobias Hunger's avatar
Tobias Hunger committed
381
382
383
    emit qbsBuildOptionsChanged();
}

384
385
void QbsBuildStep::setShowCommandLines(bool show)
{
386
    if (showCommandLines() == show)
387
        return;
388
389
    m_qbsBuildOptions.setEchoMode(show ? qbs::CommandEchoModeCommandLine
                                       : qbs::CommandEchoModeSummary);
390
391
392
    emit qbsBuildOptionsChanged();
}

393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
void QbsBuildStep::setInstall(bool install)
{
    if (m_qbsBuildOptions.install() == install)
        return;
    m_qbsBuildOptions.setInstall(install);
    emit qbsBuildOptionsChanged();
}

void QbsBuildStep::setCleanInstallRoot(bool clean)
{
    if (m_qbsBuildOptions.removeExistingInstallation() == clean)
        return;
    m_qbsBuildOptions.setRemoveExistingInstallation(clean);
    emit qbsBuildOptionsChanged();
}

409
410
411
412
void QbsBuildStep::parseProject()
{
    m_parsingProject = true;
    connect(qbsProject(), SIGNAL(projectParsingDone(bool)), SLOT(reparsingDone(bool)));
413
    qbsProject()->parseCurrentBuildConfiguration();
414
415
416
417
418
419
420
421
422
}

void QbsBuildStep::build()
{
    qbs::BuildOptions options(m_qbsBuildOptions);
    options.setChangedFiles(m_changedFiles);
    options.setFilesToConsider(m_changedFiles);
    options.setActiveFileTags(m_activeFileTags);

423
424
    QString error;
    m_job = qbsProject()->build(options, m_products, error);
425
    if (!m_job) {
426
        emit addOutput(error, ErrorMessageOutput);
427
        m_fi->reportResult(false);
428
        emit finished();
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
        return;
    }

    m_progressBase = 0;

    connect(m_job, SIGNAL(finished(bool,qbs::AbstractJob*)), this, SLOT(buildingDone(bool)));
    connect(m_job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)),
            this, SLOT(handleTaskStarted(QString,int)));
    connect(m_job, SIGNAL(taskProgress(int,qbs::AbstractJob*)),
            this, SLOT(handleProgress(int)));
    connect(m_job, SIGNAL(reportCommandDescription(QString,QString)),
            this, SLOT(handleCommandDescriptionReport(QString,QString)));
    connect(m_job, SIGNAL(reportProcessResult(qbs::ProcessResult)),
            this, SLOT(handleProcessResultReport(qbs::ProcessResult)));

}

446
447
448
449
450
void QbsBuildStep::finish()
{
    QTC_ASSERT(m_fi, return);
    m_fi->reportResult(m_lastWasSuccess);
    m_fi = 0; // do not delete, it is not ours
451
452
453
454
    if (m_job) {
        m_job->deleteLater();
        m_job = 0;
    }
455
456
457
458

    emit finished();
}

459
460
461
462
463
QbsProject *QbsBuildStep::qbsProject() const
{
    return static_cast<QbsProject *>(project());
}

Tobias Hunger's avatar
Tobias Hunger committed
464
465
466
467
468
// --------------------------------------------------------------------
// QbsBuildStepConfigWidget:
// --------------------------------------------------------------------

QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) :
469
470
    m_step(step),
    m_ignoreChange(false)
Tobias Hunger's avatar
Tobias Hunger committed
471
472
473
474
475
476
477
478
479
480
{
    connect(m_step, SIGNAL(displayNameChanged()), this, SLOT(updateState()));
    connect(m_step, SIGNAL(qbsConfigurationChanged()), this, SLOT(updateState()));
    connect(m_step, SIGNAL(qbsBuildOptionsChanged()), this, SLOT(updateState()));

    setContentsMargins(0, 0, 0, 0);

    m_ui = new Ui::QbsBuildStepConfigWidget;
    m_ui->setupUi(this);

481
482
483
484
485
    m_ui->propertyEdit->setValidationFunction([this](Utils::FancyLineEdit *edit,
                                                     QString *errorMessage) {
        return validateProperties(edit, errorMessage);
    });

Tobias Hunger's avatar
Tobias Hunger committed
486
487
488
489
490
    connect(m_ui->buildVariantComboBox, SIGNAL(currentIndexChanged(int)),
            this, SLOT(changeBuildVariant(int)));
    connect(m_ui->dryRunCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeDryRun(bool)));
    connect(m_ui->keepGoingCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeKeepGoing(bool)));
    connect(m_ui->jobSpinBox, SIGNAL(valueChanged(int)), this, SLOT(changeJobCount(int)));
491
492
    connect(m_ui->showCommandLinesCheckBox, &QCheckBox::toggled, this,
            &QbsBuildStepConfigWidget::changeShowCommandLines);
493
494
495
496
    connect(m_ui->installCheckBox, &QCheckBox::toggled, this,
            &QbsBuildStepConfigWidget::changeInstall);
    connect(m_ui->cleanInstallRootCheckBox, &QCheckBox::toggled, this,
            &QbsBuildStepConfigWidget::changeCleanInstallRoot);
497
498
499
500
    connect(m_ui->qmlDebuggingLibraryCheckBox, SIGNAL(toggled(bool)),
            this, SLOT(linkQmlDebuggingLibraryChecked(bool)));
    connect(QtSupport::QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
            this, SLOT(updateQmlDebuggingOption()));
Tobias Hunger's avatar
Tobias Hunger committed
501
502
503
    updateState();
}

Orgad Shaneh's avatar
Orgad Shaneh committed
504
505
506
507
508
QbsBuildStepConfigWidget::~QbsBuildStepConfigWidget()
{
    delete m_ui;
}

Tobias Hunger's avatar
Tobias Hunger committed
509
510
511
512
513
514
515
516
517
518
519
520
QString QbsBuildStepConfigWidget::summaryText() const
{
    return m_summary;
}

QString QbsBuildStepConfigWidget::displayName() const
{
    return m_step->displayName();
}

void QbsBuildStepConfigWidget::updateState()
{
521
522
523
524
    if (!m_ignoreChange) {
        m_ui->dryRunCheckBox->setChecked(m_step->dryRun());
        m_ui->keepGoingCheckBox->setChecked(m_step->keepGoing());
        m_ui->jobSpinBox->setValue(m_step->maxJobs());
525
        m_ui->showCommandLinesCheckBox->setChecked(m_step->showCommandLines());
526
527
        m_ui->installCheckBox->setChecked(m_step->install());
        m_ui->cleanInstallRootCheckBox->setChecked(m_step->cleanInstallRoot());
528
        updatePropertyEdit(m_step->qbsConfiguration());
529
        m_ui->qmlDebuggingLibraryCheckBox->setChecked(m_step->isQmlDebuggingEnabled());
530
    }
Tobias Hunger's avatar
Tobias Hunger committed
531

532
533
    updateQmlDebuggingOption();

Tobias Hunger's avatar
Tobias Hunger committed
534
535
536
    const QString buildVariant = m_step->buildVariant();
    const int idx = (buildVariant == QLatin1String(Constants::QBS_VARIANT_DEBUG)) ? 0 : 1;
    m_ui->buildVariantComboBox->setCurrentIndex(idx);
537
    QString command = QbsBuildConfiguration::equivalentCommandLine(m_step);
Tobias Hunger's avatar
Tobias Hunger committed
538

539
540
541
    for (int i = 0; i < m_propertyCache.count(); ++i) {
        command += QLatin1Char(' ') + m_propertyCache.at(i).first
                + QLatin1Char(':') + m_propertyCache.at(i).second;
542
543
    }

544
545
    if (m_step->isQmlDebuggingEnabled())
        command += QLatin1String(" Qt.declarative.qmlDebugging:true Qt.quick.qmlDebugging:true");
546
    m_ui->commandLineTextEdit->setPlainText(command);
547

Tobias Hunger's avatar
Tobias Hunger committed
548
    QString summary = tr("<b>Qbs:</b> %1").arg(command);
549
    if (m_summary != summary) {
Tobias Hunger's avatar
Tobias Hunger committed
550
551
552
553
554
        m_summary = summary;
        emit updateSummary();
    }
}

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
void QbsBuildStepConfigWidget::updateQmlDebuggingOption()
{
    QString warningText;
    bool supported = QtSupport::BaseQtVersion::isQmlDebuggingSupported(m_step->target()->kit(),
                                                                       &warningText);
    m_ui->qmlDebuggingLibraryCheckBox->setEnabled(supported);

    if (supported && m_step->isQmlDebuggingEnabled())
        warningText = tr("Might make your application vulnerable. Only use in a safe environment.");

    m_ui->qmlDebuggingWarningText->setText(warningText);
    m_ui->qmlDebuggingWarningIcon->setVisible(!warningText.isEmpty());
}


570
571
572
573
574
575
576
void QbsBuildStepConfigWidget::updatePropertyEdit(const QVariantMap &data)
{
    QVariantMap editable = data;

    // remove data that is edited with special UIs:
    editable.remove(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY));
    editable.remove(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY));
577
578
    editable.remove(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY));
    editable.remove(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY));
579
580
581
582
583
584
585
586

    QStringList propertyList;
    for (QVariantMap::const_iterator i = editable.constBegin(); i != editable.constEnd(); ++i)
        propertyList.append(i.key() + QLatin1Char(':') + i.value().toString());

    m_ui->propertyEdit->setText(Utils::QtcProcess::joinArgs(propertyList));
}

Tobias Hunger's avatar
Tobias Hunger committed
587
588
589
590
591
592
593
void QbsBuildStepConfigWidget::changeBuildVariant(int idx)
{
    QString variant;
    if (idx == 1)
        variant = QLatin1String(Constants::QBS_VARIANT_RELEASE);
    else
        variant = QLatin1String(Constants::QBS_VARIANT_DEBUG);
594
    m_ignoreChange = true;
Tobias Hunger's avatar
Tobias Hunger committed
595
    m_step->setBuildVariant(variant);
596
    m_ignoreChange = false;
Tobias Hunger's avatar
Tobias Hunger committed
597
598
599
600
}

void QbsBuildStepConfigWidget::changeDryRun(bool dr)
{
601
    m_ignoreChange = true;
Tobias Hunger's avatar
Tobias Hunger committed
602
    m_step->setDryRun(dr);
603
    m_ignoreChange = false;
Tobias Hunger's avatar
Tobias Hunger committed
604
605
}

606
607
608
609
610
611
612
void QbsBuildStepConfigWidget::changeShowCommandLines(bool show)
{
    m_ignoreChange = true;
    m_step->setShowCommandLines(show);
    m_ignoreChange = false;
}

Tobias Hunger's avatar
Tobias Hunger committed
613
614
void QbsBuildStepConfigWidget::changeKeepGoing(bool kg)
{
615
    m_ignoreChange = true;
Tobias Hunger's avatar
Tobias Hunger committed
616
    m_step->setKeepGoing(kg);
617
    m_ignoreChange = false;
Tobias Hunger's avatar
Tobias Hunger committed
618
619
620
621
}

void QbsBuildStepConfigWidget::changeJobCount(int count)
{
622
    m_ignoreChange = true;
Tobias Hunger's avatar
Tobias Hunger committed
623
    m_step->setMaxJobs(count);
624
625
626
    m_ignoreChange = false;
}

627
628
629
630
631
632
633
634
635
636
637
638
639
640
void QbsBuildStepConfigWidget::changeInstall(bool install)
{
    m_ignoreChange = true;
    m_step->setInstall(install);
    m_ignoreChange = false;
}

void QbsBuildStepConfigWidget::changeCleanInstallRoot(bool clean)
{
    m_ignoreChange = true;
    m_step->setCleanInstallRoot(clean);
    m_ignoreChange = false;
}

641
void QbsBuildStepConfigWidget::applyCachedProperties()
642
643
644
645
646
647
648
649
650
{
    QVariantMap data;
    QVariantMap tmp = m_step->qbsConfiguration();

    // Insert values set up with special UIs:
    data.insert(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY),
                tmp.value(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY)));
    data.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
                tmp.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)));
651
652
653
654
655
656
657
    if (tmp.contains(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY)))
        data.insert(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY),
                    tmp.value(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY)));
    if (tmp.contains(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)))
        data.insert(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY),
                    tmp.value(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)));

658
659
    for (int i = 0; i < m_propertyCache.count(); ++i)
        data.insert(m_propertyCache.at(i).first, m_propertyCache.at(i).second);
660
661
662
663

    m_ignoreChange = true;
    m_step->setQbsConfiguration(data);
    m_ignoreChange = false;
Tobias Hunger's avatar
Tobias Hunger committed
664
665
}

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
void QbsBuildStepConfigWidget::linkQmlDebuggingLibraryChecked(bool checked)
{
    QVariantMap data = m_step->qbsConfiguration();
    if (checked) {
        data.insert(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY), checked);
        data.insert(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY), checked);
    } else {
        data.remove(QLatin1String(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY));
        data.remove(QLatin1String(Constants::QBS_CONFIG_QUICK_DEBUG_KEY));
    }

    m_ignoreChange = true;
    m_step->setQbsConfiguration(data);
    m_ignoreChange = false;
}

682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
bool QbsBuildStepConfigWidget::validateProperties(Utils::FancyLineEdit *edit, QString *errorMessage)
{
    Utils::QtcProcess::SplitError err;
    QStringList argList = Utils::QtcProcess::splitArgs(edit->text(), Utils::HostOsInfo::hostOs(),
                                                       false, &err);
    if (err != Utils::QtcProcess::SplitOk) {
        if (errorMessage)
            *errorMessage = tr("Could not split properties.");
        return false;
    }

    QList<QPair<QString, QString> > properties;
    foreach (const QString &arg, argList) {
        int pos = arg.indexOf(QLatin1Char(':'));
        QString key;
        QString value;
        if (pos > 0) {
            key = arg.left(pos);
            value = arg.mid(pos + 1);
            properties.append(qMakePair(key, value));
        } else {
            if (errorMessage)
704
                *errorMessage = tr("No \":\" found in property definition.");
705
706
707
708
709
710
711
712
713
714
715
            return false;
        }
    }

    if (m_propertyCache != properties) {
        m_propertyCache = properties;
        applyCachedProperties();
    }
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
716
717
718
719
720
721
722
723
724
725
726
// --------------------------------------------------------------------
// QbsBuildStepFactory:
// --------------------------------------------------------------------

QbsBuildStepFactory::QbsBuildStepFactory(QObject *parent) :
    ProjectExplorer::IBuildStepFactory(parent)
{ }

QList<Core::Id> QbsBuildStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
{
    if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_BUILD
727
728
            && qobject_cast<QbsBuildConfiguration *>(parent->parent())
            && qobject_cast<QbsProject *>(parent->target()->project()))
Tobias Hunger's avatar
Tobias Hunger committed
729
730
731
732
        return QList<Core::Id>() << Core::Id(Constants::QBS_BUILDSTEP_ID);
    return QList<Core::Id>();
}

733
QString QbsBuildStepFactory::displayNameForId(Core::Id id) const
Tobias Hunger's avatar
Tobias Hunger committed
734
735
{
    if (id == Core::Id(Constants::QBS_BUILDSTEP_ID))
Tobias Hunger's avatar
Tobias Hunger committed
736
        return tr("Qbs Build");
Tobias Hunger's avatar
Tobias Hunger committed
737
738
739
    return QString();
}

740
bool QbsBuildStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, Core::Id id) const
Tobias Hunger's avatar
Tobias Hunger committed
741
742
{
    if (parent->id() != Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD)
743
744
            || !qobject_cast<QbsBuildConfiguration *>(parent->parent())
            || !qobject_cast<QbsProject *>(parent->target()->project()))
Tobias Hunger's avatar
Tobias Hunger committed
745
746
747
748
        return false;
    return id == Core::Id(Constants::QBS_BUILDSTEP_ID);
}

749
ProjectExplorer::BuildStep *QbsBuildStepFactory::create(ProjectExplorer::BuildStepList *parent, Core::Id id)
Tobias Hunger's avatar
Tobias Hunger committed
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
{
    if (!canCreate(parent, id))
        return 0;
    return new QbsBuildStep(parent);
}

bool QbsBuildStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
{
    return canCreate(parent, ProjectExplorer::idFromMap(map));
}

ProjectExplorer::BuildStep *QbsBuildStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
{
    if (!canRestore(parent, map))
        return 0;
    QbsBuildStep *bs = new QbsBuildStep(parent);
    if (!bs->fromMap(map)) {
        delete bs;
        return 0;
    }
    return bs;
}

bool QbsBuildStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const
{
    return canCreate(parent, product->id());
}

ProjectExplorer::BuildStep *QbsBuildStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product)
{
    if (!canClone(parent, product))
        return 0;
    return new QbsBuildStep(parent, static_cast<QbsBuildStep *>(product));
}

} // namespace Internal
} // namespace QbsProjectManager