1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Canonical 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 "cmaketoolmanager.h"
27 
28 #include "cmaketoolsettingsaccessor.h"
29 
30 #include <extensionsystem/pluginmanager.h>
31 
32 #include <coreplugin/helpmanager.h>
33 #include <coreplugin/icore.h>
34 
35 #include <utils/environment.h>
36 #include <utils/pointeralgorithm.h>
37 #include <utils/qtcassert.h>
38 
39 using namespace Core;
40 using namespace Utils;
41 
42 namespace CMakeProjectManager {
43 
44 // --------------------------------------------------------------------
45 // CMakeToolManagerPrivate:
46 // --------------------------------------------------------------------
47 
48 class CMakeToolManagerPrivate
49 {
50 public:
51     Id m_defaultCMake;
52     std::vector<std::unique_ptr<CMakeTool>> m_cmakeTools;
53     Internal::CMakeToolSettingsAccessor m_accessor;
54 };
55 static CMakeToolManagerPrivate *d = nullptr;
56 
57 // --------------------------------------------------------------------
58 // CMakeToolManager:
59 // --------------------------------------------------------------------
60 
61 CMakeToolManager *CMakeToolManager::m_instance = nullptr;
62 
CMakeToolManager()63 CMakeToolManager::CMakeToolManager()
64 {
65     QTC_ASSERT(!m_instance, return);
66     m_instance = this;
67 
68     qRegisterMetaType<QString *>();
69 
70     d = new CMakeToolManagerPrivate;
71     connect(ICore::instance(), &ICore::saveSettingsRequested,
72             this, &CMakeToolManager::saveCMakeTools);
73 
74     connect(this, &CMakeToolManager::cmakeAdded, this, &CMakeToolManager::cmakeToolsChanged);
75     connect(this, &CMakeToolManager::cmakeRemoved, this, &CMakeToolManager::cmakeToolsChanged);
76     connect(this, &CMakeToolManager::cmakeUpdated, this, &CMakeToolManager::cmakeToolsChanged);
77 
78     setObjectName("CMakeToolManager");
79     ExtensionSystem::PluginManager::addObject(this);
80 }
81 
~CMakeToolManager()82 CMakeToolManager::~CMakeToolManager()
83 {
84     ExtensionSystem::PluginManager::removeObject(this);
85     delete d;
86 }
87 
instance()88 CMakeToolManager *CMakeToolManager::instance()
89 {
90     return m_instance;
91 }
92 
cmakeTools()93 QList<CMakeTool *> CMakeToolManager::cmakeTools()
94 {
95     return Utils::toRawPointer<QList>(d->m_cmakeTools);
96 }
97 
registerCMakeTool(std::unique_ptr<CMakeTool> && tool)98 bool CMakeToolManager::registerCMakeTool(std::unique_ptr<CMakeTool> &&tool)
99 {
100     if (!tool || Utils::contains(d->m_cmakeTools, tool.get()))
101         return true;
102 
103     const Utils::Id toolId = tool->id();
104     QTC_ASSERT(toolId.isValid(),return false);
105 
106     //make sure the same id was not used before
107     QTC_ASSERT(!Utils::contains(d->m_cmakeTools, [toolId](const std::unique_ptr<CMakeTool> &known) {
108         return toolId == known->id();
109     }), return false);
110 
111     d->m_cmakeTools.emplace_back(std::move(tool));
112 
113     emit CMakeToolManager::m_instance->cmakeAdded(toolId);
114 
115     ensureDefaultCMakeToolIsValid();
116 
117     updateDocumentation();
118 
119     return true;
120 }
121 
deregisterCMakeTool(const Id & id)122 void CMakeToolManager::deregisterCMakeTool(const Id &id)
123 {
124     auto toRemove = Utils::take(d->m_cmakeTools, Utils::equal(&CMakeTool::id, id));
125     if (toRemove.has_value()) {
126         ensureDefaultCMakeToolIsValid();
127 
128         updateDocumentation();
129 
130         emit m_instance->cmakeRemoved(id);
131     }
132 }
133 
defaultCMakeTool()134 CMakeTool *CMakeToolManager::defaultCMakeTool()
135 {
136     return findById(d->m_defaultCMake);
137 }
138 
setDefaultCMakeTool(const Id & id)139 void CMakeToolManager::setDefaultCMakeTool(const Id &id)
140 {
141     if (d->m_defaultCMake != id && findById(id)) {
142         d->m_defaultCMake = id;
143         emit m_instance->defaultCMakeChanged();
144         return;
145     }
146 
147     ensureDefaultCMakeToolIsValid();
148 }
149 
findByCommand(const Utils::FilePath & command)150 CMakeTool *CMakeToolManager::findByCommand(const Utils::FilePath &command)
151 {
152     return Utils::findOrDefault(d->m_cmakeTools, Utils::equal(&CMakeTool::cmakeExecutable, command));
153 }
154 
findById(const Id & id)155 CMakeTool *CMakeToolManager::findById(const Id &id)
156 {
157     return Utils::findOrDefault(d->m_cmakeTools, Utils::equal(&CMakeTool::id, id));
158 }
159 
restoreCMakeTools()160 void CMakeToolManager::restoreCMakeTools()
161 {
162     Internal::CMakeToolSettingsAccessor::CMakeTools tools
163             = d->m_accessor.restoreCMakeTools(ICore::dialogParent());
164     d->m_cmakeTools = std::move(tools.cmakeTools);
165     setDefaultCMakeTool(tools.defaultToolId);
166 
167     updateDocumentation();
168 
169     emit m_instance->cmakeToolsLoaded();
170 }
171 
updateDocumentation()172 void CMakeToolManager::updateDocumentation()
173 {
174     const QList<CMakeTool *> tools = cmakeTools();
175     QStringList docs;
176     for (const auto tool : tools) {
177         if (!tool->qchFilePath().isEmpty())
178             docs.append(tool->qchFilePath().toString());
179     }
180     Core::HelpManager::registerDocumentation(docs);
181 }
182 
autoDetectCMakeForDevice(const FilePath & deviceRoot,const QString & detectionSource,QString * logMessage)183 void CMakeToolManager::autoDetectCMakeForDevice(const FilePath &deviceRoot,
184                                                 const QString &detectionSource,
185                                                 QString *logMessage)
186 {
187     QStringList messages{tr("Searching CMake binaries...")};
188     const FilePaths candidates = {deviceRoot.withNewPath("cmake")};
189     const Environment env = deviceRoot.deviceEnvironment();
190     for (const FilePath &candidate : candidates) {
191         const FilePath cmake = candidate.searchOnDevice(env.path());
192         if (!cmake.isEmpty()) {
193             registerCMakeByPath(cmake, detectionSource);
194             messages.append(tr("Found \"%1\"").arg(cmake.toUserOutput()));
195         }
196     }
197     if (logMessage)
198         *logMessage = messages.join('\n');
199 }
200 
201 
registerCMakeByPath(const FilePath & cmakePath,const QString & detectionSource)202 void CMakeToolManager::registerCMakeByPath(const FilePath &cmakePath, const QString &detectionSource)
203 {
204     const Id id = Id::fromString(cmakePath.toUserOutput());
205 
206     CMakeTool *cmakeTool = findById(id);
207     if (cmakeTool)
208         return;
209 
210     auto newTool = std::make_unique<CMakeTool>(CMakeTool::ManualDetection, id);
211     newTool->setFilePath(cmakePath);
212     newTool->setDetectionSource(detectionSource);
213     newTool->setDisplayName(cmakePath.toUserOutput());
214     registerCMakeTool(std::move(newTool));
215 }
216 
removeDetectedCMake(const QString & detectionSource,QString * logMessage)217 void CMakeToolManager::removeDetectedCMake(const QString &detectionSource, QString *logMessage)
218 {
219     QStringList logMessages{tr("Removing CMake entries...")};
220     while (true) {
221         auto toRemove = Utils::take(d->m_cmakeTools, Utils::equal(&CMakeTool::detectionSource, detectionSource));
222         if (!toRemove.has_value())
223             break;
224         logMessages.append(tr("Removed \"%1\"").arg((*toRemove)->displayName()));
225         emit m_instance->cmakeRemoved((*toRemove)->id());
226     }
227 
228     ensureDefaultCMakeToolIsValid();
229     updateDocumentation();
230     if (logMessage)
231         *logMessage = logMessages.join('\n');
232 }
233 
listDetectedCMake(const QString & detectionSource,QString * logMessage)234 void CMakeToolManager::listDetectedCMake(const QString &detectionSource, QString *logMessage)
235 {
236     QTC_ASSERT(logMessage, return);
237     QStringList logMessages{tr("CMake:")};
238     for (const auto &tool : qAsConst(d->m_cmakeTools)) {
239         if (tool->detectionSource() == detectionSource)
240             logMessages.append(tool->displayName());
241     }
242     *logMessage = logMessages.join('\n');
243 }
244 
notifyAboutUpdate(CMakeTool * tool)245 void CMakeToolManager::notifyAboutUpdate(CMakeTool *tool)
246 {
247     if (!tool || !Utils::contains(d->m_cmakeTools, tool))
248         return;
249     emit m_instance->cmakeUpdated(tool->id());
250 }
251 
saveCMakeTools()252 void CMakeToolManager::saveCMakeTools()
253 {
254     d->m_accessor.saveCMakeTools(cmakeTools(), d->m_defaultCMake, ICore::dialogParent());
255 }
256 
ensureDefaultCMakeToolIsValid()257 void CMakeToolManager::ensureDefaultCMakeToolIsValid()
258 {
259     const Utils::Id oldId = d->m_defaultCMake;
260     if (d->m_cmakeTools.size() == 0) {
261         d->m_defaultCMake = Utils::Id();
262     } else {
263         if (findById(d->m_defaultCMake))
264             return;
265         d->m_defaultCMake = d->m_cmakeTools.at(0)->id();
266     }
267 
268     // signaling:
269     if (oldId != d->m_defaultCMake)
270         emit m_instance->defaultCMakeChanged();
271 }
272 
273 } // namespace CMakeProjectManager
274