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