1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 BlackBerry Limited. All rights reserved.
4 ** Contact: BlackBerry (qt@blackberry.com)
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 "qnxconfiguration.h"
27 #include "qnxqtversion.h"
28 #include "qnxutils.h"
29 #include "qnxtoolchain.h"
30
31 #include "debugger/debuggeritem.h"
32
33 #include <coreplugin/icore.h>
34
35 #include <projectexplorer/toolchainmanager.h>
36 #include <projectexplorer/toolchain.h>
37 #include <projectexplorer/kit.h>
38 #include <projectexplorer/kitmanager.h>
39
40 #include <qtsupport/baseqtversion.h>
41 #include <qtsupport/qtversionmanager.h>
42 #include <qtsupport/qtkitinformation.h>
43
44 #include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
45
46 #include <debugger/debuggeritem.h>
47 #include <debugger/debuggeritemmanager.h>
48 #include <debugger/debuggerkitinformation.h>
49
50 #include <coreplugin/icore.h>
51 #include <utils/algorithm.h>
52
53 #include <QDir>
54 #include <QMessageBox>
55 #include <QFileInfo>
56
57 using namespace ProjectExplorer;
58 using namespace QtSupport;
59 using namespace Utils;
60 using namespace Debugger;
61
62 namespace Qnx {
63 namespace Internal {
64
65 const QLatin1String QNXEnvFileKey("EnvFile");
66 const QLatin1String QNXVersionKey("QNXVersion");
67 // For backward compatibility
68 const QLatin1String SdpEnvFileKey("NDKEnvFile");
69
70 const QLatin1String QNXConfiguration("QNX_CONFIGURATION");
71 const QLatin1String QNXTarget("QNX_TARGET");
72 const QLatin1String QNXHost("QNX_HOST");
73
74 QnxConfiguration::QnxConfiguration() = default;
75
QnxConfiguration(const FilePath & sdpEnvFile)76 QnxConfiguration::QnxConfiguration(const FilePath &sdpEnvFile)
77 {
78 setDefaultConfiguration(sdpEnvFile);
79 readInformation();
80 }
81
QnxConfiguration(const QVariantMap & data)82 QnxConfiguration::QnxConfiguration(const QVariantMap &data)
83 {
84 QString envFilePath = data.value(QNXEnvFileKey).toString();
85 if (envFilePath.isEmpty())
86 envFilePath = data.value(SdpEnvFileKey).toString();
87
88 m_version = QnxVersionNumber(data.value(QNXVersionKey).toString());
89
90 setDefaultConfiguration(FilePath::fromString(envFilePath));
91 readInformation();
92 }
93
envFile() const94 FilePath QnxConfiguration::envFile() const
95 {
96 return m_envFile;
97 }
98
qnxTarget() const99 FilePath QnxConfiguration::qnxTarget() const
100 {
101 return m_qnxTarget;
102 }
103
qnxHost() const104 FilePath QnxConfiguration::qnxHost() const
105 {
106 return m_qnxHost;
107 }
108
qccCompilerPath() const109 FilePath QnxConfiguration::qccCompilerPath() const
110 {
111 return m_qccCompiler;
112 }
113
qnxEnv() const114 EnvironmentItems QnxConfiguration::qnxEnv() const
115 {
116 return m_qnxEnv;
117 }
118
version() const119 QnxVersionNumber QnxConfiguration::version() const
120 {
121 return m_version;
122 }
123
toMap() const124 QVariantMap QnxConfiguration::toMap() const
125 {
126 QVariantMap data;
127 data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString());
128 data.insert(QLatin1String(QNXVersionKey), m_version.toString());
129 return data;
130 }
131
isValid() const132 bool QnxConfiguration::isValid() const
133 {
134 return !m_qccCompiler.isEmpty() && !m_targets.isEmpty();
135 }
136
displayName() const137 QString QnxConfiguration::displayName() const
138 {
139 return m_configName;
140 }
141
activate()142 bool QnxConfiguration::activate()
143 {
144 if (isActive())
145 return true;
146
147 if (!isValid()) {
148 QString errorMessage
149 = QCoreApplication::translate("Qnx::Internal::QnxConfiguration",
150 "The following errors occurred while activating the QNX configuration:");
151 foreach (const QString &error, validationErrors())
152 errorMessage += QLatin1String("\n") + error;
153
154 QMessageBox::warning(Core::ICore::dialogParent(),
155 QCoreApplication::translate("Qnx::Internal::QnxConfiguration",
156 "Cannot Set Up QNX Configuration"),
157 errorMessage,
158 QMessageBox::Ok);
159 return false;
160 }
161
162 foreach (const Target &target, m_targets)
163 createTools(target);
164
165 return true;
166 }
167
deactivate()168 void QnxConfiguration::deactivate()
169 {
170 if (!isActive())
171 return;
172
173 QList<DebuggerItem> debuggersToRemove;
174 const QList<ToolChain *> toolChainsToRemove
175 = ToolChainManager::toolChains(Utils::equal(&ToolChain::compilerCommand, qccCompilerPath()));
176
177 foreach (DebuggerItem debuggerItem,
178 DebuggerItemManager::debuggers()) {
179 if (findTargetByDebuggerPath(debuggerItem.command()))
180 debuggersToRemove.append(debuggerItem);
181 }
182
183 foreach (Kit *kit, KitManager::kits()) {
184 if (kit->isAutoDetected()
185 && DeviceTypeKitAspect::deviceTypeId(kit) == Constants::QNX_QNX_OS_TYPE
186 && toolChainsToRemove.contains(ToolChainKitAspect::cxxToolChain(kit)))
187 KitManager::deregisterKit(kit);
188 }
189
190 foreach (ToolChain *tc, toolChainsToRemove)
191 ToolChainManager::deregisterToolChain(tc);
192
193 foreach (DebuggerItem debuggerItem, debuggersToRemove)
194 DebuggerItemManager::deregisterDebugger(debuggerItem.id());
195 }
196
isActive() const197 bool QnxConfiguration::isActive() const
198 {
199 const bool hasToolChain = ToolChainManager::toolChain(Utils::equal(&ToolChain::compilerCommand,
200 qccCompilerPath()));
201 const bool hasDebugger = Utils::contains(DebuggerItemManager::debuggers(), [this](const DebuggerItem &di) {
202 return findTargetByDebuggerPath(di.command());
203 });
204
205 return hasToolChain && hasDebugger;
206 }
207
canCreateKits() const208 bool QnxConfiguration::canCreateKits() const
209 {
210 if (!isValid())
211 return false;
212
213 return Utils::anyOf(m_targets,
214 [this](const Target &target) -> bool { return qnxQtVersion(target); });
215 }
216
sdpPath() const217 FilePath QnxConfiguration::sdpPath() const
218 {
219 return envFile().parentDir();
220 }
221
qnxQtVersion(const Target & target) const222 QnxQtVersion *QnxConfiguration::qnxQtVersion(const Target &target) const
223 {
224 foreach (BaseQtVersion *version,
225 QtVersionManager::instance()->versions(Utils::equal(&BaseQtVersion::type,
226 QString::fromLatin1(Constants::QNX_QNX_QT)))) {
227 auto qnxQt = dynamic_cast<QnxQtVersion *>(version);
228 if (qnxQt && FilePath::fromString(qnxQt->sdpPath()) == sdpPath()) {
229 foreach (const Abi &qtAbi, version->qtAbis()) {
230 if ((qtAbi == target.m_abi) && (qnxQt->cpuDir() == target.cpuDir()))
231 return qnxQt;
232 }
233 }
234 }
235
236 return nullptr;
237 }
238
autoDetect(const QList<ToolChain * > & alreadyKnown)239 QList<ToolChain *> QnxConfiguration::autoDetect(const QList<ToolChain *> &alreadyKnown)
240 {
241 QList<ToolChain *> result;
242
243 foreach (const Target &target, m_targets)
244 result += findToolChain(alreadyKnown, target.m_abi);
245
246 return result;
247 }
248
createTools(const Target & target)249 void QnxConfiguration::createTools(const Target &target)
250 {
251 QnxToolChainMap toolchainMap = createToolChain(target);
252 QVariant debuggerId = createDebugger(target);
253 createKit(target, toolchainMap, debuggerId);
254 }
255
createDebugger(const Target & target)256 QVariant QnxConfiguration::createDebugger(const Target &target)
257 {
258 Utils::Environment sysEnv = Utils::Environment::systemEnvironment();
259 sysEnv.modify(qnxEnvironmentItems());
260 Debugger::DebuggerItem debugger;
261 debugger.setCommand(target.m_debuggerPath);
262 debugger.reinitializeFromFile(sysEnv);
263 debugger.setAutoDetected(true);
264 debugger.setUnexpandedDisplayName(
265 QCoreApplication::translate(
266 "Qnx::Internal::QnxConfiguration",
267 "Debugger for %1 (%2)")
268 .arg(displayName())
269 .arg(target.shortDescription()));
270 return Debugger::DebuggerItemManager::registerDebugger(debugger);
271 }
272
createToolChain(const Target & target)273 QnxConfiguration::QnxToolChainMap QnxConfiguration::createToolChain(const Target &target)
274 {
275 QnxToolChainMap toolChainMap;
276
277 for (auto language : { ProjectExplorer::Constants::C_LANGUAGE_ID,
278 ProjectExplorer::Constants::CXX_LANGUAGE_ID}) {
279 auto toolChain = new QnxToolChain;
280 toolChain->setDetection(ToolChain::AutoDetection);
281 toolChain->setLanguage(language);
282 toolChain->setTargetAbi(target.m_abi);
283 toolChain->setDisplayName(
284 QCoreApplication::translate(
285 "Qnx::Internal::QnxConfiguration",
286 "QCC for %1 (%2)")
287 .arg(displayName())
288 .arg(target.shortDescription()));
289 toolChain->setSdpPath(sdpPath().toString());
290 toolChain->setCpuDir(target.cpuDir());
291 toolChain->resetToolChain(qccCompilerPath());
292 ToolChainManager::registerToolChain(toolChain);
293
294 toolChainMap.insert(std::make_pair(language, toolChain));
295 }
296
297 return toolChainMap;
298 }
299
findToolChain(const QList<ToolChain * > & alreadyKnown,const Abi & abi)300 QList<ToolChain *> QnxConfiguration::findToolChain(const QList<ToolChain *> &alreadyKnown,
301 const Abi &abi)
302 {
303 return Utils::filtered(alreadyKnown, [this, abi](ToolChain *tc) {
304 return tc->typeId() == Constants::QNX_TOOLCHAIN_ID
305 && tc->targetAbi() == abi
306 && tc->compilerCommand() == m_qccCompiler;
307 });
308 }
309
createKit(const Target & target,const QnxToolChainMap & toolChainMap,const QVariant & debugger)310 void QnxConfiguration::createKit(const Target &target, const QnxToolChainMap &toolChainMap,
311 const QVariant &debugger)
312 {
313 QnxQtVersion *qnxQt = qnxQtVersion(target);
314 // Do not create incomplete kits if no qt qnx version found
315 if (!qnxQt)
316 return;
317
318 const auto init = [&](Kit *k) {
319 QtKitAspect::setQtVersion(k, qnxQt);
320 ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::C_LANGUAGE_ID));
321 ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
322
323 if (debugger.isValid())
324 DebuggerKitAspect::setDebugger(k, debugger);
325
326 DeviceTypeKitAspect::setDeviceTypeId(k, Constants::QNX_QNX_OS_TYPE);
327 // TODO: Add sysroot?
328
329 k->setUnexpandedDisplayName(
330 QCoreApplication::translate(
331 "Qnx::Internal::QnxConfiguration",
332 "Kit for %1 (%2)")
333 .arg(displayName())
334 .arg(target.shortDescription()));
335
336 k->setAutoDetected(true);
337 k->setAutoDetectionSource(envFile().toString());
338 k->setMutable(DeviceKitAspect::id(), true);
339
340 k->setSticky(ToolChainKitAspect::id(), true);
341 k->setSticky(DeviceTypeKitAspect::id(), true);
342 k->setSticky(SysRootKitAspect::id(), true);
343 k->setSticky(DebuggerKitAspect::id(), true);
344 k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
345
346 EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems());
347 };
348
349 // add kit with device and qt version not sticky
350 KitManager::registerKit(init);
351 }
352
validationErrors() const353 QStringList QnxConfiguration::validationErrors() const
354 {
355 QStringList errorStrings;
356 if (m_qccCompiler.isEmpty())
357 errorStrings << QCoreApplication::translate("Qnx::Internal::QnxConfiguration",
358 "- No GCC compiler found.");
359
360 if (m_targets.isEmpty())
361 errorStrings << QCoreApplication::translate("Qnx::Internal::QnxConfiguration",
362 "- No targets found.");
363
364 return errorStrings;
365 }
366
setVersion(const QnxVersionNumber & version)367 void QnxConfiguration::setVersion(const QnxVersionNumber &version)
368 {
369 m_version = version;
370 }
371
readInformation()372 void QnxConfiguration::readInformation()
373 {
374 const QString qConfigPath = m_qnxConfiguration.pathAppended("qconfig").toString();
375 QList <ConfigInstallInformation> installInfoList = QnxUtils::installedConfigs(qConfigPath);
376 if (installInfoList.isEmpty())
377 return;
378
379 foreach (const ConfigInstallInformation &info, installInfoList) {
380 if (m_qnxHost == FilePath::fromString(info.host).canonicalPath()
381 && m_qnxTarget == FilePath::fromString(info.target).canonicalPath()) {
382 m_configName = info.name;
383 setVersion(QnxVersionNumber(info.version));
384 break;
385 }
386 }
387 }
388
setDefaultConfiguration(const Utils::FilePath & envScript)389 void QnxConfiguration::setDefaultConfiguration(const Utils::FilePath &envScript)
390 {
391 QTC_ASSERT(!envScript.isEmpty(), return);
392 m_envFile = envScript;
393 m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile.toString());
394 foreach (const EnvironmentItem &item, m_qnxEnv) {
395 if (item.name == QNXConfiguration)
396 m_qnxConfiguration = FilePath::fromString(item.value).canonicalPath();
397 else if (item.name == QNXTarget)
398 m_qnxTarget = FilePath::fromString(item.value).canonicalPath();
399 else if (item.name == QNXHost)
400 m_qnxHost = FilePath::fromString(item.value).canonicalPath();
401 }
402
403 FilePath qccPath = FilePath::fromString(HostOsInfo::withExecutableSuffix(
404 m_qnxHost.toString() + QLatin1String("/usr/bin/qcc")));
405
406 if (qccPath.exists())
407 m_qccCompiler = qccPath;
408
409 updateTargets();
410 assignDebuggersToTargets();
411
412 // Remove debuggerless targets.
413 Utils::erase(m_targets, [](const Target &target) {
414 if (target.m_debuggerPath.isEmpty())
415 qWarning() << "No debugger found for" << target.m_path << "... discarded";
416 return target.m_debuggerPath.isEmpty();
417 });
418 }
419
qnxEnvironmentItems() const420 EnvironmentItems QnxConfiguration::qnxEnvironmentItems() const
421 {
422 Utils::EnvironmentItems envList;
423 envList.push_back(EnvironmentItem(QNXConfiguration, m_qnxConfiguration.toString()));
424 envList.push_back(EnvironmentItem(QNXTarget, m_qnxTarget.toString()));
425 envList.push_back(EnvironmentItem(QNXHost, m_qnxHost.toString()));
426
427 return envList;
428 }
429
findTargetByDebuggerPath(const FilePath & path) const430 const QnxConfiguration::Target *QnxConfiguration::findTargetByDebuggerPath(
431 const FilePath &path) const
432 {
433 const auto it = std::find_if(m_targets.begin(), m_targets.end(),
434 [path](const Target &target) { return target.m_debuggerPath == path; });
435 return it == m_targets.end() ? nullptr : &(*it);
436 }
437
updateTargets()438 void QnxConfiguration::updateTargets()
439 {
440 m_targets.clear();
441 QList<QnxTarget> targets = QnxUtils::findTargets(m_qnxTarget);
442 for (const auto &target : targets)
443 m_targets.append(Target(target.m_abi, target.m_path));
444 }
445
assignDebuggersToTargets()446 void QnxConfiguration::assignDebuggersToTargets()
447 {
448 const QDir hostUsrBinDir(m_qnxHost.pathAppended("usr/bin").toString());
449 QStringList debuggerNames = hostUsrBinDir.entryList(
450 QStringList(HostOsInfo::withExecutableSuffix(QLatin1String("nto*-gdb"))),
451 QDir::Files);
452 Utils::Environment sysEnv = Utils::Environment::systemEnvironment();
453 sysEnv.modify(qnxEnvironmentItems());
454 foreach (const QString &debuggerName, debuggerNames) {
455 const FilePath debuggerPath = FilePath::fromString(hostUsrBinDir.path())
456 .pathAppended(debuggerName);
457 DebuggerItem item;
458 item.setCommand(debuggerPath);
459 item.reinitializeFromFile(sysEnv);
460 bool found = false;
461 foreach (const Abi &abi, item.abis()) {
462 for (Target &target : m_targets) {
463 if (target.m_abi.isCompatibleWith(abi)) {
464 found = true;
465
466 if (target.m_debuggerPath.isEmpty()) {
467 target.m_debuggerPath = debuggerPath;
468 } else {
469 qWarning() << debuggerPath << "has the same ABI as" << target.m_debuggerPath
470 << "... discarded";
471 break;
472 }
473 }
474 }
475 }
476 if (!found)
477 qWarning() << "No target found for" << debuggerName << "... discarded";
478 }
479 }
480
shortDescription() const481 QString QnxConfiguration::Target::shortDescription() const
482 {
483 return QnxUtils::cpuDirShortDescription(cpuDir());
484 }
485
cpuDir() const486 QString QnxConfiguration::Target::cpuDir() const
487 {
488 return m_path.fileName();
489 }
490
491 } // namespace Internal
492 } // namespace Qnx
493