3b5dc042f137da0f1d9dd55854a0a63307fce5e6
[platform/upstream/cmake.git] / Source / QtDialog / QCMake.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "QCMake.h"
4
5 #include <QCoreApplication>
6 #include <QDir>
7
8 #include "cmExternalMakefileProjectGenerator.h"
9 #include "cmGlobalGenerator.h"
10 #include "cmState.h"
11 #include "cmStringAlgorithms.h"
12 #include "cmSystemTools.h"
13
14 #ifdef Q_OS_WIN
15 #  include "qt_windows.h" // For SetErrorMode
16 #endif
17
18 QCMake::QCMake(QObject* p)
19   : QObject(p)
20 {
21   this->WarnUninitializedMode = false;
22   this->WarnUnusedMode = false;
23   qRegisterMetaType<QCMakeProperty>();
24   qRegisterMetaType<QCMakePropertyList>();
25
26   cmSystemTools::DisableRunCommandOutput();
27   cmSystemTools::SetRunCommandHideConsole(true);
28
29   cmSystemTools::SetMessageCallback(
30     [this](std::string const& msg, const char* title) {
31       this->messageCallback(msg, title);
32     });
33   cmSystemTools::SetStdoutCallback(
34     [this](std::string const& msg) { this->stdoutCallback(msg); });
35   cmSystemTools::SetStderrCallback(
36     [this](std::string const& msg) { this->stderrCallback(msg); });
37
38   this->CMakeInstance = new cmake(cmake::RoleProject, cmState::Project);
39   this->CMakeInstance->SetCMakeEditCommand(
40     cmSystemTools::GetCMakeGUICommand());
41   this->CMakeInstance->SetProgressCallback(
42     [this](const std::string& msg, float percent) {
43       this->progressCallback(msg, percent);
44     });
45
46   cmSystemTools::SetInterruptCallback(
47     [this] { return this->interruptCallback(); });
48
49   std::vector<cmake::GeneratorInfo> generators;
50   this->CMakeInstance->GetRegisteredGenerators(
51     generators, /*includeNamesWithPlatform=*/false);
52
53   for (cmake::GeneratorInfo const& gen : generators) {
54     this->AvailableGenerators.push_back(gen);
55   }
56 }
57
58 QCMake::~QCMake()
59 {
60   delete this->CMakeInstance;
61   // cmDynamicLoader::FlushCache();
62 }
63
64 void QCMake::loadCache(const QString& dir)
65 {
66   this->setBinaryDirectory(dir);
67 }
68
69 void QCMake::setSourceDirectory(const QString& _dir)
70 {
71   QString dir = QString::fromLocal8Bit(
72     cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().data()).c_str());
73   if (this->SourceDirectory != dir) {
74     this->SourceDirectory = QDir::fromNativeSeparators(dir);
75     emit this->sourceDirChanged(this->SourceDirectory);
76   }
77 }
78
79 void QCMake::setBinaryDirectory(const QString& _dir)
80 {
81   QString dir = QString::fromLocal8Bit(
82     cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().data()).c_str());
83   if (this->BinaryDirectory != dir) {
84     this->BinaryDirectory = QDir::fromNativeSeparators(dir);
85     emit this->binaryDirChanged(this->BinaryDirectory);
86     cmState* state = this->CMakeInstance->GetState();
87     this->setGenerator(QString());
88     this->setToolset(QString());
89     this->setPlatform(QString());
90     if (!this->CMakeInstance->LoadCache(
91           this->BinaryDirectory.toLocal8Bit().data())) {
92       QDir testDir(this->BinaryDirectory);
93       if (testDir.exists("CMakeCache.txt")) {
94         cmSystemTools::Error(
95           "There is a CMakeCache.txt file for the current binary "
96           "tree but cmake does not have permission to read it.  "
97           "Please check the permissions of the directory you are trying to "
98           "run CMake on.");
99       }
100     }
101
102     QCMakePropertyList props = this->properties();
103     emit this->propertiesChanged(props);
104     const char* homeDir = state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
105     if (homeDir) {
106       setSourceDirectory(QString::fromLocal8Bit(homeDir));
107     }
108     const char* gen = state->GetCacheEntryValue("CMAKE_GENERATOR");
109     if (gen) {
110       const std::string* extraGen =
111         state->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
112       std::string curGen =
113         cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
114           gen, extraGen ? *extraGen : "");
115       this->setGenerator(QString::fromLocal8Bit(curGen.c_str()));
116     }
117
118     const char* platform =
119       state->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
120     if (platform) {
121       this->setPlatform(QString::fromLocal8Bit(platform));
122     }
123
124     const char* toolset = state->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
125     if (toolset) {
126       this->setToolset(QString::fromLocal8Bit(toolset));
127     }
128
129     checkOpenPossible();
130   }
131 }
132
133 void QCMake::setGenerator(const QString& gen)
134 {
135   if (this->Generator != gen) {
136     this->Generator = gen;
137     emit this->generatorChanged(this->Generator);
138   }
139 }
140
141 void QCMake::setPlatform(const QString& platform)
142 {
143   if (this->Platform != platform) {
144     this->Platform = platform;
145     emit this->platformChanged(this->Platform);
146   }
147 }
148
149 void QCMake::setToolset(const QString& toolset)
150 {
151   if (this->Toolset != toolset) {
152     this->Toolset = toolset;
153     emit this->toolsetChanged(this->Toolset);
154   }
155 }
156
157 void QCMake::configure()
158 {
159 #ifdef Q_OS_WIN
160   UINT lastErrorMode = SetErrorMode(0);
161 #endif
162
163   this->CMakeInstance->SetHomeDirectory(
164     this->SourceDirectory.toLocal8Bit().data());
165   this->CMakeInstance->SetHomeOutputDirectory(
166     this->BinaryDirectory.toLocal8Bit().data());
167   this->CMakeInstance->SetGlobalGenerator(
168     this->CMakeInstance->CreateGlobalGenerator(
169       this->Generator.toLocal8Bit().data()));
170   this->CMakeInstance->SetGeneratorPlatform(
171     this->Platform.toLocal8Bit().data());
172   this->CMakeInstance->SetGeneratorToolset(this->Toolset.toLocal8Bit().data());
173   this->CMakeInstance->LoadCache();
174   this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode);
175   this->CMakeInstance->SetWarnUnused(this->WarnUnusedMode);
176   this->CMakeInstance->PreLoadCMakeFiles();
177
178   InterruptFlag = 0;
179   cmSystemTools::ResetErrorOccuredFlag();
180
181   int err = this->CMakeInstance->Configure();
182
183 #ifdef Q_OS_WIN
184   SetErrorMode(lastErrorMode);
185 #endif
186
187   emit this->propertiesChanged(this->properties());
188   emit this->configureDone(err);
189 }
190
191 void QCMake::generate()
192 {
193 #ifdef Q_OS_WIN
194   UINT lastErrorMode = SetErrorMode(0);
195 #endif
196
197   InterruptFlag = 0;
198   cmSystemTools::ResetErrorOccuredFlag();
199
200   int err = this->CMakeInstance->Generate();
201
202 #ifdef Q_OS_WIN
203   SetErrorMode(lastErrorMode);
204 #endif
205
206   emit this->generateDone(err);
207   checkOpenPossible();
208 }
209
210 void QCMake::open()
211 {
212 #ifdef Q_OS_WIN
213   UINT lastErrorMode = SetErrorMode(0);
214 #endif
215
216   InterruptFlag = 0;
217   cmSystemTools::ResetErrorOccuredFlag();
218
219   auto successful = this->CMakeInstance->Open(
220     this->BinaryDirectory.toLocal8Bit().data(), false);
221
222 #ifdef Q_OS_WIN
223   SetErrorMode(lastErrorMode);
224 #endif
225
226   emit this->openDone(successful);
227 }
228
229 void QCMake::setProperties(const QCMakePropertyList& newProps)
230 {
231   QCMakePropertyList props = newProps;
232
233   QStringList toremove;
234
235   // set the value of properties
236   cmState* state = this->CMakeInstance->GetState();
237   std::vector<std::string> cacheKeys = state->GetCacheEntryKeys();
238   for (std::string const& key : cacheKeys) {
239     cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key);
240     if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC) {
241       continue;
242     }
243
244     QCMakeProperty prop;
245     prop.Key = QString::fromLocal8Bit(key.c_str());
246     int idx = props.indexOf(prop);
247     if (idx == -1) {
248       toremove.append(QString::fromLocal8Bit(key.c_str()));
249     } else {
250       prop = props[idx];
251       if (prop.Value.type() == QVariant::Bool) {
252         state->SetCacheEntryValue(key, prop.Value.toBool() ? "ON" : "OFF");
253       } else {
254         state->SetCacheEntryValue(key,
255                                   prop.Value.toString().toLocal8Bit().data());
256       }
257       props.removeAt(idx);
258     }
259   }
260
261   // remove some properties
262   foreach (QString const& s, toremove) {
263     this->CMakeInstance->UnwatchUnusedCli(s.toLocal8Bit().data());
264
265     state->RemoveCacheEntry(s.toLocal8Bit().data());
266   }
267
268   // add some new properties
269   foreach (QCMakeProperty const& s, props) {
270     this->CMakeInstance->WatchUnusedCli(s.Key.toLocal8Bit().data());
271
272     if (s.Type == QCMakeProperty::BOOL) {
273       this->CMakeInstance->AddCacheEntry(
274         s.Key.toLocal8Bit().data(), s.Value.toBool() ? "ON" : "OFF",
275         s.Help.toLocal8Bit().data(), cmStateEnums::BOOL);
276     } else if (s.Type == QCMakeProperty::STRING) {
277       this->CMakeInstance->AddCacheEntry(
278         s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(),
279         s.Help.toLocal8Bit().data(), cmStateEnums::STRING);
280     } else if (s.Type == QCMakeProperty::PATH) {
281       this->CMakeInstance->AddCacheEntry(
282         s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(),
283         s.Help.toLocal8Bit().data(), cmStateEnums::PATH);
284     } else if (s.Type == QCMakeProperty::FILEPATH) {
285       this->CMakeInstance->AddCacheEntry(
286         s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(),
287         s.Help.toLocal8Bit().data(), cmStateEnums::FILEPATH);
288     }
289   }
290
291   this->CMakeInstance->SaveCache(this->BinaryDirectory.toLocal8Bit().data());
292 }
293
294 QCMakePropertyList QCMake::properties() const
295 {
296   QCMakePropertyList ret;
297
298   cmState* state = this->CMakeInstance->GetState();
299   std::vector<std::string> cacheKeys = state->GetCacheEntryKeys();
300   for (std::string const& key : cacheKeys) {
301     cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key);
302     if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC ||
303         t == cmStateEnums::UNINITIALIZED) {
304       continue;
305     }
306
307     const char* cachedValue = state->GetCacheEntryValue(key);
308
309     QCMakeProperty prop;
310     prop.Key = QString::fromLocal8Bit(key.c_str());
311     prop.Help =
312       QString::fromLocal8Bit(state->GetCacheEntryProperty(key, "HELPSTRING"));
313     prop.Value = QString::fromLocal8Bit(cachedValue);
314     prop.Advanced = state->GetCacheEntryPropertyAsBool(key, "ADVANCED");
315     if (t == cmStateEnums::BOOL) {
316       prop.Type = QCMakeProperty::BOOL;
317       prop.Value = cmIsOn(cachedValue);
318     } else if (t == cmStateEnums::PATH) {
319       prop.Type = QCMakeProperty::PATH;
320     } else if (t == cmStateEnums::FILEPATH) {
321       prop.Type = QCMakeProperty::FILEPATH;
322     } else if (t == cmStateEnums::STRING) {
323       prop.Type = QCMakeProperty::STRING;
324       const char* stringsProperty =
325         state->GetCacheEntryProperty(key, "STRINGS");
326       if (stringsProperty) {
327         prop.Strings = QString::fromLocal8Bit(stringsProperty).split(";");
328       }
329     }
330
331     ret.append(prop);
332   }
333
334   return ret;
335 }
336
337 void QCMake::interrupt()
338 {
339   this->InterruptFlag.ref();
340 }
341
342 bool QCMake::interruptCallback()
343 {
344 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
345   return this->InterruptFlag;
346 #else
347   return this->InterruptFlag.load();
348 #endif
349 }
350
351 void QCMake::progressCallback(const std::string& msg, float percent)
352 {
353   if (percent >= 0) {
354     emit this->progressChanged(QString::fromStdString(msg), percent);
355   } else {
356     emit this->outputMessage(QString::fromStdString(msg));
357   }
358   QCoreApplication::processEvents();
359 }
360
361 void QCMake::messageCallback(std::string const& msg, const char* /*title*/)
362 {
363   emit this->errorMessage(QString::fromStdString(msg));
364   QCoreApplication::processEvents();
365 }
366
367 void QCMake::stdoutCallback(std::string const& msg)
368 {
369   emit this->outputMessage(QString::fromStdString(msg));
370   QCoreApplication::processEvents();
371 }
372
373 void QCMake::stderrCallback(std::string const& msg)
374 {
375   emit this->outputMessage(QString::fromStdString(msg));
376   QCoreApplication::processEvents();
377 }
378
379 QString QCMake::binaryDirectory() const
380 {
381   return this->BinaryDirectory;
382 }
383
384 QString QCMake::sourceDirectory() const
385 {
386   return this->SourceDirectory;
387 }
388
389 QString QCMake::generator() const
390 {
391   return this->Generator;
392 }
393
394 std::vector<cmake::GeneratorInfo> const& QCMake::availableGenerators() const
395 {
396   return AvailableGenerators;
397 }
398
399 void QCMake::deleteCache()
400 {
401   // delete cache
402   this->CMakeInstance->DeleteCache(this->BinaryDirectory.toLocal8Bit().data());
403   // reload to make our cache empty
404   this->CMakeInstance->LoadCache(this->BinaryDirectory.toLocal8Bit().data());
405   // emit no generator and no properties
406   this->setGenerator(QString());
407   this->setToolset(QString());
408   QCMakePropertyList props = this->properties();
409   emit this->propertiesChanged(props);
410 }
411
412 void QCMake::reloadCache()
413 {
414   // emit that the cache was cleaned out
415   QCMakePropertyList props;
416   emit this->propertiesChanged(props);
417   // reload
418   this->CMakeInstance->LoadCache(this->BinaryDirectory.toLocal8Bit().data());
419   // emit new cache properties
420   props = this->properties();
421   emit this->propertiesChanged(props);
422 }
423
424 void QCMake::setDebugOutput(bool flag)
425 {
426   if (flag != this->CMakeInstance->GetDebugOutput()) {
427     this->CMakeInstance->SetDebugOutputOn(flag);
428     emit this->debugOutputChanged(flag);
429   }
430 }
431
432 bool QCMake::getDebugOutput() const
433 {
434   return this->CMakeInstance->GetDebugOutput();
435 }
436
437 bool QCMake::getSuppressDevWarnings()
438 {
439   return this->CMakeInstance->GetSuppressDevWarnings();
440 }
441
442 void QCMake::setSuppressDevWarnings(bool value)
443 {
444   this->CMakeInstance->SetSuppressDevWarnings(value);
445 }
446
447 bool QCMake::getSuppressDeprecatedWarnings()
448 {
449   return this->CMakeInstance->GetSuppressDeprecatedWarnings();
450 }
451
452 void QCMake::setSuppressDeprecatedWarnings(bool value)
453 {
454   this->CMakeInstance->SetSuppressDeprecatedWarnings(value);
455 }
456
457 bool QCMake::getDevWarningsAsErrors()
458 {
459   return this->CMakeInstance->GetDevWarningsAsErrors();
460 }
461
462 void QCMake::setDevWarningsAsErrors(bool value)
463 {
464   this->CMakeInstance->SetDevWarningsAsErrors(value);
465 }
466
467 bool QCMake::getDeprecatedWarningsAsErrors()
468 {
469   return this->CMakeInstance->GetDeprecatedWarningsAsErrors();
470 }
471
472 void QCMake::setDeprecatedWarningsAsErrors(bool value)
473 {
474   this->CMakeInstance->SetDeprecatedWarningsAsErrors(value);
475 }
476
477 void QCMake::setWarnUninitializedMode(bool value)
478 {
479   this->WarnUninitializedMode = value;
480 }
481
482 void QCMake::setWarnUnusedMode(bool value)
483 {
484   this->WarnUnusedMode = value;
485 }
486
487 void QCMake::checkOpenPossible()
488 {
489   auto data = this->BinaryDirectory.toLocal8Bit().data();
490   auto possible = this->CMakeInstance->Open(data, true);
491   emit openPossible(possible);
492 }