1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "debuggerrunconfigurationaspect.h"
27 
28 #include "debuggerconstants.h"
29 
30 #include <coreplugin/helpmanager.h>
31 #include <coreplugin/icontext.h>
32 #include <coreplugin/icore.h>
33 #include <projectexplorer/buildconfiguration.h>
34 #include <projectexplorer/buildstep.h>
35 #include <projectexplorer/buildsteplist.h>
36 #include <projectexplorer/kitinformation.h>
37 #include <projectexplorer/project.h>
38 #include <projectexplorer/projectexplorerconstants.h>
39 #include <projectexplorer/runconfiguration.h>
40 #include <projectexplorer/target.h>
41 #include <qtsupport/qtbuildaspects.h>
42 
43 #include <utils/layoutbuilder.h>
44 
45 #include <QCheckBox>
46 #include <QDebug>
47 #include <QFormLayout>
48 #include <QLabel>
49 #include <QTextEdit>
50 
51 using namespace Debugger::Internal;
52 using namespace ProjectExplorer;
53 using namespace Utils;
54 
55 namespace Debugger {
56 namespace Internal {
57 
58 enum DebuggerLanguageStatus {
59     DisabledLanguage = 0,
60     EnabledLanguage,
61     AutoEnabledLanguage
62 };
63 
64 class DebuggerLanguageAspect : public BaseAspect
65 {
66 public:
67     DebuggerLanguageAspect() = default;
68 
69     void addToLayout(LayoutBuilder &builder) override;
70 
71     bool value() const;
72     void setValue(bool val);
73 
74     void setAutoSettingsKey(const QString &settingsKey);
75     void setLabel(const QString &label);
setInfoLabelText(const QString & text)76     void setInfoLabelText(const QString &text) { m_infoLabelText = text; }
77 
setClickCallBack(const std::function<void (bool)> & clickCallBack)78     void setClickCallBack(const std::function<void (bool)> &clickCallBack)
79     {
80         m_clickCallBack = clickCallBack;
81     }
82 
83     void fromMap(const QVariantMap &map) override;
84     void toMap(QVariantMap &map) const override;
85 
86 public:
87     DebuggerLanguageStatus m_value = AutoEnabledLanguage;
88     bool m_defaultValue = false;
89     QString m_label;
90     QString m_infoLabelText;
91     QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
92     QPointer<QLabel> m_infoLabel; // Owned by configuration widget
93     QString m_autoSettingsKey;
94 
95     std::function<void(bool)> m_clickCallBack;
96 };
97 
addToLayout(LayoutBuilder & builder)98 void DebuggerLanguageAspect::addToLayout(LayoutBuilder &builder)
99 {
100     QTC_CHECK(!m_checkBox);
101     m_checkBox = new QCheckBox(m_label);
102     m_checkBox->setChecked(m_value);
103 
104     QTC_CHECK(m_clickCallBack);
105     connect(m_checkBox, &QAbstractButton::clicked, this, m_clickCallBack, Qt::QueuedConnection);
106 
107     connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
108         m_value = m_checkBox->isChecked() ? EnabledLanguage : DisabledLanguage;
109         emit changed();
110     });
111     builder.addItem(QString());
112     builder.addItem(m_checkBox.data());
113 
114     if (!m_infoLabelText.isEmpty()) {
115         QTC_CHECK(!m_infoLabel);
116         m_infoLabel = new QLabel(m_infoLabelText);
117         connect(m_infoLabel, &QLabel::linkActivated, [](const QString &link) {
118             Core::HelpManager::showHelpUrl(link);
119         });
120         builder.addItem(m_infoLabel.data());
121     }
122 }
123 
setAutoSettingsKey(const QString & settingsKey)124 void DebuggerLanguageAspect::setAutoSettingsKey(const QString &settingsKey)
125 {
126     m_autoSettingsKey = settingsKey;
127 }
128 
fromMap(const QVariantMap & map)129 void DebuggerLanguageAspect::fromMap(const QVariantMap &map)
130 {
131     const bool val = map.value(settingsKey(), false).toBool();
132     const bool autoVal = map.value(m_autoSettingsKey, false).toBool();
133     m_value = autoVal ? AutoEnabledLanguage : val ? EnabledLanguage : DisabledLanguage;
134 }
135 
toMap(QVariantMap & data) const136 void DebuggerLanguageAspect::toMap(QVariantMap &data) const
137 {
138     data.insert(settingsKey(), m_value == EnabledLanguage);
139     data.insert(m_autoSettingsKey, m_value == AutoEnabledLanguage);
140 }
141 
142 
value() const143 bool DebuggerLanguageAspect::value() const
144 {
145     return m_value;
146 }
147 
setValue(bool value)148 void DebuggerLanguageAspect::setValue(bool value)
149 {
150     m_value = value ? EnabledLanguage : DisabledLanguage;
151     if (m_checkBox)
152         m_checkBox->setChecked(m_value);
153 }
154 
setLabel(const QString & label)155 void DebuggerLanguageAspect::setLabel(const QString &label)
156 {
157     m_label = label;
158 }
159 
160 } // Internal
161 
162 /*!
163     \class Debugger::DebuggerRunConfigurationAspect
164 */
165 
DebuggerRunConfigurationAspect(Target * target)166 DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
167     : m_target(target)
168 {
169     setId("DebuggerAspect");
170     setDisplayName(tr("Debugger settings"));
171 
172     setConfigWidgetCreator([this] {
173         Layouting::Form builder;
174         builder.addRow(m_cppAspect);
175         builder.addRow(m_qmlAspect);
176         builder.addRow(m_overrideStartupAspect);
177 
178         static const QByteArray env = qgetenv("QTC_DEBUGGER_MULTIPROCESS");
179         if (env.toInt())
180             builder.addRow(m_multiProcessAspect);
181         return builder.emerge(false);
182     });
183 
184     m_cppAspect = new DebuggerLanguageAspect;
185     m_cppAspect->setLabel(tr("Enable C++"));
186     m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger");
187     m_cppAspect->setAutoSettingsKey("RunConfiguration.UseCppDebuggerAuto");
188 
189     m_qmlAspect = new DebuggerLanguageAspect;
190     m_qmlAspect->setLabel(tr("Enable QML"));
191     m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger");
192     m_qmlAspect->setAutoSettingsKey("RunConfiguration.UseQmlDebuggerAuto");
193     m_qmlAspect->setInfoLabelText(tr("<a href=\""
194         "qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
195         "\">What are the prerequisites?</a>"));
196 
197     // Make sure at least one of the debuggers is set to be active.
198     m_cppAspect->setClickCallBack([this](bool on) {
199         if (!on && !m_qmlAspect->value())
200             m_qmlAspect->setValue(true);
201     });
202     m_qmlAspect->setClickCallBack([this](bool on) {
203         if (!on && !m_cppAspect->value())
204             m_cppAspect->setValue(true);
205     });
206 
207     m_multiProcessAspect = new BoolAspect;
208     m_multiProcessAspect->setSettingsKey("RunConfiguration.UseMultiProcess");
209     m_multiProcessAspect->setLabel(tr("Enable Debugging of Subprocesses"),
210                                    BoolAspect::LabelPlacement::AtCheckBox);
211 
212     m_overrideStartupAspect = new StringAspect;
213     m_overrideStartupAspect->setSettingsKey("RunConfiguration.OverrideDebuggerStartup");
214     m_overrideStartupAspect->setDisplayStyle(StringAspect::TextEditDisplay);
215     m_overrideStartupAspect->setLabelText(tr("Additional startup commands:"));
216 }
217 
~DebuggerRunConfigurationAspect()218 DebuggerRunConfigurationAspect::~DebuggerRunConfigurationAspect()
219 {
220     delete m_cppAspect;
221     delete m_qmlAspect;
222     delete m_multiProcessAspect;
223     delete m_overrideStartupAspect;
224 }
225 
setUseQmlDebugger(bool value)226 void DebuggerRunConfigurationAspect::setUseQmlDebugger(bool value)
227 {
228     m_qmlAspect->setValue(value);
229 }
230 
useCppDebugger() const231 bool DebuggerRunConfigurationAspect::useCppDebugger() const
232 {
233     if (m_cppAspect->m_value == AutoEnabledLanguage)
234         return m_target->project()->projectLanguages().contains(
235                     ProjectExplorer::Constants::CXX_LANGUAGE_ID);
236     return m_cppAspect->m_value == EnabledLanguage;
237 }
238 
useQmlDebugger() const239 bool DebuggerRunConfigurationAspect::useQmlDebugger() const
240 {
241     if (m_qmlAspect->m_value == AutoEnabledLanguage) {
242         const Core::Context languages = m_target->project()->projectLanguages();
243         if (!languages.contains(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID))
244             return false;
245 
246         //
247         // Try to find a build configuration to check whether qml debugging is enabled there
248         if (BuildConfiguration *bc = m_target->activeBuildConfiguration()) {
249             const auto aspect = bc->aspect<QtSupport::QmlDebuggingAspect>();
250             return aspect && aspect->value() == TriState::Enabled;
251         }
252 
253         return !languages.contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
254     }
255     return m_qmlAspect->m_value == EnabledLanguage;
256 }
257 
useMultiProcess() const258 bool DebuggerRunConfigurationAspect::useMultiProcess() const
259 {
260     return m_multiProcessAspect->value();
261 }
262 
setUseMultiProcess(bool value)263 void DebuggerRunConfigurationAspect::setUseMultiProcess(bool value)
264 {
265     m_multiProcessAspect->setValue(value);
266 }
267 
overrideStartup() const268 QString DebuggerRunConfigurationAspect::overrideStartup() const
269 {
270     return m_overrideStartupAspect->value();
271 }
272 
portsUsedByDebugger() const273 int DebuggerRunConfigurationAspect::portsUsedByDebugger() const
274 {
275     int ports = 0;
276     if (useQmlDebugger())
277         ++ports;
278     if (useCppDebugger())
279         ++ports;
280     return ports;
281 }
282 
toMap(QVariantMap & map) const283 void DebuggerRunConfigurationAspect::toMap(QVariantMap &map) const
284 {
285     m_cppAspect->toMap(map);
286     m_qmlAspect->toMap(map);
287     m_multiProcessAspect->toMap(map);
288     m_overrideStartupAspect->toMap(map);
289 }
290 
fromMap(const QVariantMap & map)291 void DebuggerRunConfigurationAspect::fromMap(const QVariantMap &map)
292 {
293     m_cppAspect->fromMap(map);
294     m_qmlAspect->fromMap(map);
295     m_multiProcessAspect->fromMap(map);
296     m_overrideStartupAspect->fromMap(map);
297 }
298 
299 } // namespace Debugger
300