8ffa3e728dbca162407b361692278bc867cf89c7
[platform/upstream/cmake.git] / Source / QtDialog / CMakeSetup.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 <iostream>
4
5 #include "QCMake.h" // include to disable MS warnings
6 #include <QApplication>
7 #include <QDir>
8 #include <QLocale>
9 #include <QString>
10 #include <QTranslator>
11 #include <QtPlugin>
12
13 #include "cmsys/CommandLineArguments.hxx"
14 #include "cmsys/Encoding.hxx"
15 #include "cmsys/SystemTools.hxx"
16
17 #include "CMakeSetupDialog.h"
18 #include "cmAlgorithms.h"
19 #include "cmDocumentation.h"
20 #include "cmDocumentationEntry.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h" // IWYU pragma: keep
23 #include "cmake.h"
24
25 static const char* cmDocumentationName[][2] = { { nullptr,
26                                                   "  cmake-gui - CMake GUI." },
27                                                 { nullptr, nullptr } };
28
29 static const char* cmDocumentationUsage[][2] = {
30   { nullptr,
31     "  cmake-gui [options]\n"
32     "  cmake-gui [options] <path-to-source>\n"
33     "  cmake-gui [options] <path-to-existing-build>\n"
34     "  cmake-gui [options] -S <path-to-source> -B <path-to-build>\n"
35     "  cmake-gui [options] --browse-manual\n" },
36   { nullptr, nullptr }
37 };
38
39 static const char* cmDocumentationOptions[][2] = {
40   { "-S <path-to-source>", "Explicitly specify a source directory." },
41   { "-B <path-to-build>", "Explicitly specify a build directory." },
42   { "--preset=<preset>", "Specify a configure preset." },
43   { nullptr, nullptr }
44 };
45
46 #if defined(Q_OS_MAC)
47 static int cmOSXInstall(std::string dir);
48 static void cmAddPluginPath();
49 #endif
50
51 #if defined(USE_QXcbIntegrationPlugin)
52 Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
53 #endif
54
55 #if defined(USE_QWindowsIntegrationPlugin)
56 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
57 #  if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
58 Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
59 #  endif
60 #endif
61
62 int CMakeGUIExec(CMakeSetupDialog* window);
63 void SetupDefaultQSettings();
64 void OpenReferenceManual();
65
66 int main(int argc, char** argv)
67 {
68   cmSystemTools::EnsureStdPipes();
69   cmsys::Encoding::CommandLineArguments encoding_args =
70     cmsys::Encoding::CommandLineArguments::Main(argc, argv);
71   int argc2 = encoding_args.argc();
72   char const* const* argv2 = encoding_args.argv();
73
74   cmSystemTools::InitializeLibUV();
75   cmSystemTools::FindCMakeResources(argv2[0]);
76   // check docs first so that X is not need to get docs
77   // do docs, if args were given
78   cmDocumentation doc;
79   doc.addCMakeStandardDocSections();
80   if (argc2 > 1 && doc.CheckOptions(argc2, argv2)) {
81     // Construct and print requested documentation.
82     cmake hcm(cmake::RoleInternal, cmState::Unknown);
83     hcm.SetHomeDirectory("");
84     hcm.SetHomeOutputDirectory("");
85     hcm.AddCMakePaths();
86
87     auto generators = hcm.GetGeneratorsDocumentation();
88     doc.SetName("cmake");
89     doc.SetSection("Name", cmDocumentationName);
90     doc.SetSection("Usage", cmDocumentationUsage);
91     doc.AppendSection("Generators", generators);
92     doc.PrependSection("Options", cmDocumentationOptions);
93
94     return (doc.PrintRequestedDocumentation(std::cout) ? 0 : 1);
95   }
96
97 #if defined(Q_OS_MAC)
98   if (argc2 == 2 && strcmp(argv2[1], "--install") == 0) {
99     return cmOSXInstall("/usr/local/bin");
100   }
101   if (argc2 == 2 && cmHasLiteralPrefix(argv2[1], "--install=")) {
102     return cmOSXInstall(argv2[1] + 10);
103   }
104 #endif
105
106 // When we are on OSX and we are launching cmake-gui from a symlink, the
107 // application will fail to launch as it can't find the qt.conf file which
108 // tells it what the name of the plugin folder is. We need to add this path
109 // BEFORE the application is constructed as that is what triggers the
110 // searching for the platform plugins
111 #if defined(Q_OS_MAC)
112   cmAddPluginPath();
113 #endif
114
115 // HighDpiScaling is always enabled starting with Qt6, but will still issue a
116 // deprecation warning if you try to set the attribute for it
117 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) &&                               \
118      QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
119   QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
120 #endif
121
122   SetupDefaultQSettings();
123   QApplication app(argc, argv);
124
125   setlocale(LC_NUMERIC, "C");
126
127   // tell the cmake library where cmake is
128   QDir cmExecDir(QApplication::applicationDirPath());
129 #if defined(Q_OS_MAC)
130   cmExecDir.cd("../../../");
131 #endif
132
133   // pick up translation files if they exists in the data directory
134   QDir translationsDir = cmExecDir;
135   translationsDir.cd(QString::fromLocal8Bit(".." CMAKE_DATA_DIR));
136   translationsDir.cd("i18n");
137   QTranslator translator;
138   if (translator.load(QLocale(), "cmake", "_", translationsDir.path())) {
139     QApplication::installTranslator(&translator);
140   }
141
142   // app setup
143   QApplication::setApplicationName("CMakeSetup");
144   QApplication::setOrganizationName("Kitware");
145   QIcon appIcon;
146   appIcon.addFile(":/Icons/CMakeSetup32.png");
147   appIcon.addFile(":/Icons/CMakeSetup128.png");
148   QApplication::setWindowIcon(QIcon::fromTheme("cmake-gui", appIcon));
149
150   CMakeSetupDialog dialog;
151   dialog.show();
152
153   QStringList args = QApplication::arguments();
154   std::string binaryDirectory;
155   std::string sourceDirectory;
156   std::string presetName;
157   for (int i = 1; i < args.size(); ++i) {
158     const QString& arg = args[i];
159     if (arg.startsWith("-S")) {
160       QString path = arg.mid(2);
161       if (path.isEmpty()) {
162         ++i;
163         if (i >= args.size()) {
164           std::cerr << "No source directory specified for -S" << std::endl;
165           return 1;
166         }
167         path = args[i];
168         if (path[0] == '-') {
169           std::cerr << "No source directory specified for -S" << std::endl;
170           return 1;
171         }
172       }
173
174       sourceDirectory =
175         cmSystemTools::CollapseFullPath(path.toLocal8Bit().data());
176       cmSystemTools::ConvertToUnixSlashes(sourceDirectory);
177     } else if (arg.startsWith("-B")) {
178       QString path = arg.mid(2);
179       if (path.isEmpty()) {
180         ++i;
181         if (i >= args.size()) {
182           std::cerr << "No build directory specified for -B" << std::endl;
183           return 1;
184         }
185         path = args[i];
186         if (path[0] == '-') {
187           std::cerr << "No build directory specified for -B" << std::endl;
188           return 1;
189         }
190       }
191
192       binaryDirectory =
193         cmSystemTools::CollapseFullPath(path.toLocal8Bit().data());
194       cmSystemTools::ConvertToUnixSlashes(binaryDirectory);
195     } else if (arg.startsWith("--preset=")) {
196       QString preset = arg.mid(cmStrLen("--preset="));
197       if (preset.isEmpty()) {
198         std::cerr << "No preset specified for --preset" << std::endl;
199         return 1;
200       }
201       presetName = preset.toLocal8Bit().data();
202     } else if (arg == "--browse-manual") {
203       OpenReferenceManual();
204       return 0;
205     }
206   }
207   if (!sourceDirectory.empty() &&
208       (!binaryDirectory.empty() || !presetName.empty())) {
209     dialog.setSourceDirectory(QString::fromLocal8Bit(sourceDirectory.c_str()));
210     if (!binaryDirectory.empty()) {
211       dialog.setBinaryDirectory(
212         QString::fromLocal8Bit(binaryDirectory.c_str()));
213       if (!presetName.empty()) {
214         dialog.setStartupBinaryDirectory(true);
215       }
216     }
217     if (!presetName.empty()) {
218       dialog.setDeferredPreset(QString::fromLocal8Bit(presetName.c_str()));
219     }
220   } else {
221     if (args.count() == 2) {
222       std::string filePath =
223         cmSystemTools::CollapseFullPath(args[1].toLocal8Bit().data());
224
225       // check if argument is a directory containing CMakeCache.txt
226       std::string buildFilePath = cmStrCat(filePath, "/CMakeCache.txt");
227
228       // check if argument is a CMakeCache.txt file
229       if (cmSystemTools::GetFilenameName(filePath) == "CMakeCache.txt" &&
230           cmSystemTools::FileExists(filePath.c_str())) {
231         buildFilePath = filePath;
232       }
233
234       // check if argument is a directory containing CMakeLists.txt
235       std::string srcFilePath = cmStrCat(filePath, "/CMakeLists.txt");
236
237       if (cmSystemTools::FileExists(buildFilePath.c_str())) {
238         dialog.setBinaryDirectory(QString::fromLocal8Bit(
239           cmSystemTools::GetFilenamePath(buildFilePath).c_str()));
240       } else if (cmSystemTools::FileExists(srcFilePath.c_str())) {
241         dialog.setSourceDirectory(QString::fromLocal8Bit(filePath.c_str()));
242         dialog.setBinaryDirectory(QString::fromLocal8Bit(
243           cmSystemTools::CollapseFullPath(".").c_str()));
244       }
245     }
246   }
247
248   return CMakeGUIExec(&dialog);
249 }
250
251 #if defined(Q_OS_MAC)
252 #  include <cerrno>
253 #  include <cstring>
254
255 #  include <unistd.h>
256
257 #  include "cm_sys_stat.h"
258 static bool cmOSXInstall(std::string const& dir, std::string const& tool)
259 {
260   if (tool.empty()) {
261     return true;
262   }
263   std::string link = dir + cmSystemTools::GetFilenameName(tool);
264   struct stat st;
265   if (lstat(link.c_str(), &st) == 0 && S_ISLNK(st.st_mode)) {
266     char buf[4096];
267     ssize_t s = readlink(link.c_str(), buf, sizeof(buf) - 1);
268     if (s >= 0 && std::string(buf, s) == tool) {
269       std::cerr << "Exists: '" << link << "' -> '" << tool << "'\n";
270       return true;
271     }
272   }
273   cmSystemTools::MakeDirectory(dir);
274   if (symlink(tool.c_str(), link.c_str()) == 0) {
275     std::cerr << "Linked: '" << link << "' -> '" << tool << "'\n";
276     return true;
277   }
278   int err = errno;
279   std::cerr << "Failed: '" << link << "' -> '" << tool
280             << "': " << strerror(err) << "\n";
281   return false;
282 }
283 static int cmOSXInstall(std::string dir)
284 {
285   if (!cmHasLiteralSuffix(dir, "/")) {
286     dir += "/";
287   }
288   return (cmOSXInstall(dir, cmSystemTools::GetCMakeCommand()) &&
289           cmOSXInstall(dir, cmSystemTools::GetCTestCommand()) &&
290           cmOSXInstall(dir, cmSystemTools::GetCPackCommand()) &&
291           cmOSXInstall(dir, cmSystemTools::GetCMakeGUICommand()) &&
292           cmOSXInstall(dir, cmSystemTools::GetCMakeCursesCommand()))
293     ? 0
294     : 1;
295 }
296
297 // Locate the PlugIns directory and add it to the QApplication library paths.
298 // We need to resolve all symlinks so we have a known relative path between
299 // MacOS/CMake and the PlugIns directory.
300 //
301 // Note we are using cmSystemTools since Qt can't provide the path to the
302 // executable before the QApplication is created, and that is when plugin
303 // searching occurs.
304 static void cmAddPluginPath()
305 {
306   std::string const& path = cmSystemTools::GetCMakeGUICommand();
307   if (path.empty()) {
308     return;
309   }
310   std::string const& realPath = cmSystemTools::GetRealPath(path);
311   QFileInfo appPath(QString::fromLocal8Bit(realPath.c_str()));
312   QDir pluginDir = appPath.dir();
313   bool const foundPluginDir = pluginDir.cd("../PlugIns");
314   if (foundPluginDir) {
315     QApplication::addLibraryPath(pluginDir.path());
316   }
317 }
318
319 #endif