1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "uic.h"
5#include "option.h"
6#include "driver.h"
7#include <language.h>
8
9#include <qfile.h>
10#include <qdir.h>
11#include <qhashfunctions.h>
12#include <qtextstream.h>
13#include <qcoreapplication.h>
14#include <qcommandlineoption.h>
15#include <qcommandlineparser.h>
16#include <qfileinfo.h>
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22static const char pythonPathVar[] = "PYTHONPATH";
23
24// From the Python paths, find the component the UI file is under
25static QString pythonRoot(const QString &pythonPath, const QString &uiFileIn)
26{
27#ifdef Q_OS_WIN
28 static const Qt::CaseSensitivity fsSensitivity = Qt::CaseInsensitive;
29#else
30 static const Qt::CaseSensitivity fsSensitivity = Qt::CaseSensitive;
31#endif
32
33 if (pythonPath.isEmpty() || uiFileIn.isEmpty())
34 return {};
35 const QString uiFile = QFileInfo(uiFileIn).canonicalFilePath();
36 if (uiFile.isEmpty())
37 return {};
38 const auto uiFileSize = uiFile.size();
39 const auto paths = pythonPath.split(sep: QDir::listSeparator(), behavior: Qt::SkipEmptyParts);
40 for (const auto &path : paths) {
41 const QString canonicalPath = QFileInfo(path).canonicalFilePath();
42 const auto canonicalPathSize = canonicalPath.size();
43 if (uiFileSize > canonicalPathSize
44 && uiFile.at(i: canonicalPathSize) == u'/'
45 && uiFile.startsWith(s: canonicalPath, cs: fsSensitivity)) {
46 return canonicalPath;
47 }
48 }
49 return {};
50}
51
52int runUic(int argc, char *argv[])
53{
54 QHashSeed::setDeterministicGlobalSeed();
55
56 QCoreApplication app(argc, argv);
57 const QString version = QString::fromLatin1(ba: qVersion());
58 QCoreApplication::setApplicationVersion(version);
59
60 Driver driver;
61
62 // Note that uic isn't translated.
63 // If you use this code as an example for a translated app, make sure to translate the strings.
64 QCommandLineParser parser;
65 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
66 parser.setApplicationDescription(u"Qt User Interface Compiler version %1"_s.arg(a: version));
67 parser.addHelpOption();
68 parser.addVersionOption();
69
70 QCommandLineOption dependenciesOption(QStringList{u"d"_s, u"dependencies"_s});
71 dependenciesOption.setDescription(u"Display the dependencies."_s);
72 parser.addOption(commandLineOption: dependenciesOption);
73
74 QCommandLineOption outputOption(QStringList{u"o"_s, u"output"_s});
75 outputOption.setDescription(u"Place the output into <file>"_s);
76 outputOption.setValueName(u"file"_s);
77 parser.addOption(commandLineOption: outputOption);
78
79 QCommandLineOption noAutoConnectionOption(QStringList{u"a"_s, u"no-autoconnection"_s});
80 noAutoConnectionOption.setDescription(u"Do not generate a call to QObject::connectSlotsByName()."_s);
81 parser.addOption(commandLineOption: noAutoConnectionOption);
82
83 QCommandLineOption noProtOption(QStringList{u"p"_s, u"no-protection"_s});
84 noProtOption.setDescription(u"Disable header protection."_s);
85 parser.addOption(commandLineOption: noProtOption);
86
87 QCommandLineOption noImplicitIncludesOption(QStringList{u"n"_s, u"no-implicit-includes"_s});
88 noImplicitIncludesOption.setDescription(u"Disable generation of #include-directives."_s);
89 parser.addOption(commandLineOption: noImplicitIncludesOption);
90
91 QCommandLineOption postfixOption(u"postfix"_s);
92 postfixOption.setDescription(u"Postfix to add to all generated classnames."_s);
93 postfixOption.setValueName(u"postfix"_s);
94 parser.addOption(commandLineOption: postfixOption);
95
96 QCommandLineOption noQtNamespaceOption(u"no-qt-namespace"_s);
97 noQtNamespaceOption.setDescription(
98 u"Disable wrapping the definition of the generated class in QT_{BEGIN,END}_NAMESPACE."_s);
99 parser.addOption(commandLineOption: noQtNamespaceOption);
100
101 QCommandLineOption translateOption(QStringList{u"tr"_s, u"translate"_s});
102 translateOption.setDescription(u"Use <function> for i18n."_s);
103 translateOption.setValueName(u"function"_s);
104 parser.addOption(commandLineOption: translateOption);
105
106 QCommandLineOption includeOption(QStringList{u"include"_s});
107 includeOption.setDescription(u"Add #include <include-file> to <file>."_s);
108 includeOption.setValueName(u"include-file"_s);
109 parser.addOption(commandLineOption: includeOption);
110
111 QCommandLineOption generatorOption(QStringList{u"g"_s, u"generator"_s});
112 generatorOption.setDescription(u"Select generator."_s);
113 generatorOption.setValueName(u"python|cpp"_s);
114 parser.addOption(commandLineOption: generatorOption);
115
116 QCommandLineOption connectionsOption(QStringList{u"c"_s, u"connections"_s});
117 connectionsOption.setDescription(u"Connection syntax."_s);
118 connectionsOption.setValueName(u"pmf|string"_s);
119 parser.addOption(commandLineOption: connectionsOption);
120
121 QCommandLineOption idBasedOption(u"idbased"_s);
122 idBasedOption.setDescription(u"Use id based function for i18n"_s);
123 parser.addOption(commandLineOption: idBasedOption);
124
125 QCommandLineOption fromImportsOption(u"from-imports"_s);
126 fromImportsOption.setDescription(u"Python: generate imports relative to '.'"_s);
127 parser.addOption(commandLineOption: fromImportsOption);
128
129 QCommandLineOption absoluteImportsOption(u"absolute-imports"_s);
130 absoluteImportsOption.setDescription(u"Python: generate absolute imports"_s);
131 parser.addOption(commandLineOption: absoluteImportsOption);
132
133 // FIXME Qt 7: Flip the default?
134 QCommandLineOption rcPrefixOption(u"rc-prefix"_s);
135 rcPrefixOption.setDescription(uR"(Python: Generate "rc_file" instead of "file_rc" import)"_s);
136 parser.addOption(commandLineOption: rcPrefixOption);
137
138 // FIXME Qt 7: Remove?
139 QCommandLineOption useStarImportsOption(u"star-imports"_s);
140 useStarImportsOption.setDescription(u"Python: Use * imports"_s);
141 parser.addOption(commandLineOption: useStarImportsOption);
142
143 QCommandLineOption pythonPathOption(u"python-paths"_s);
144 pythonPathOption.setDescription(u"Python paths for --absolute-imports."_s);
145 pythonPathOption.setValueName(u"pathlist"_s);
146 parser.addOption(commandLineOption: pythonPathOption);
147
148 parser.addPositionalArgument(name: u"[uifile]"_s, description: u"Input file (*.ui), otherwise stdin."_s);
149
150 parser.process(app);
151
152 driver.option().dependencies = parser.isSet(option: dependenciesOption);
153 driver.option().outputFile = parser.value(option: outputOption);
154 driver.option().autoConnection = !parser.isSet(option: noAutoConnectionOption);
155 driver.option().headerProtection = !parser.isSet(option: noProtOption);
156 driver.option().implicitIncludes = !parser.isSet(option: noImplicitIncludesOption);
157 driver.option().qtNamespace = !parser.isSet(option: noQtNamespaceOption);
158 driver.option().idBased = parser.isSet(option: idBasedOption);
159 driver.option().postfix = parser.value(option: postfixOption);
160 driver.option().translateFunction = parser.value(option: translateOption);
161 driver.option().includeFile = parser.value(option: includeOption);
162 if (parser.isSet(option: connectionsOption)) {
163 const auto value = parser.value(option: connectionsOption);
164 if (value == "pmf"_L1)
165 driver.option().forceMemberFnPtrConnectionSyntax = 1;
166 else if (value == "string"_L1)
167 driver.option().forceStringConnectionSyntax = 1;
168 }
169
170 const QString inputFile = parser.positionalArguments().value(i: 0);
171
172 Language language = Language::Cpp;
173 if (parser.isSet(option: generatorOption)) {
174 if (parser.value(option: generatorOption).compare(other: "python"_L1) == 0)
175 language = Language::Python;
176 }
177 language::setLanguage(language);
178 if (language == Language::Python) {
179 if (parser.isSet(option: fromImportsOption))
180 driver.option().pythonResourceImport = Option::PythonResourceImport::FromDot;
181 else if (parser.isSet(option: absoluteImportsOption))
182 driver.option().pythonResourceImport = Option::PythonResourceImport::Absolute;
183 driver.option().useStarImports = parser.isSet(option: useStarImportsOption);
184 if (parser.isSet(option: rcPrefixOption))
185 driver.option().rcPrefix = 1;
186 QString pythonPaths;
187 if (parser.isSet(option: pythonPathOption))
188 pythonPaths = parser.value(option: pythonPathOption);
189 else if (qEnvironmentVariableIsSet(varName: pythonPathVar))
190 pythonPaths = QString::fromUtf8(ba: qgetenv(varName: pythonPathVar));
191 driver.option().pythonRoot = pythonRoot(pythonPath: pythonPaths, uiFileIn: inputFile);
192 }
193
194 if (inputFile.isEmpty()) // reading from stdin
195 driver.option().headerProtection = false;
196
197 if (driver.option().dependencies) {
198 return !driver.printDependencies(fileName: inputFile);
199 }
200
201 QTextStream *out = nullptr;
202 QFile f;
203 if (!driver.option().outputFile.isEmpty()) {
204 f.setFileName(driver.option().outputFile);
205 if (!f.open(flags: QIODevice::WriteOnly | QFile::Text)) {
206 fprintf(stderr, format: "Could not create output file\n");
207 return 1;
208 }
209 out = new QTextStream(&f);
210 out->setEncoding(QStringConverter::Utf8);
211 }
212
213 bool rtn = driver.uic(fileName: inputFile, output: out);
214 delete out;
215
216 if (!rtn) {
217 if (driver.option().outputFile.size()) {
218 f.close();
219 f.remove();
220 }
221 fprintf(stderr, format: "File '%s' is not valid\n", inputFile.isEmpty() ? "<stdin>" : inputFile.toLocal8Bit().constData());
222 }
223
224 return !rtn;
225}
226
227QT_END_NAMESPACE
228
229int main(int argc, char *argv[])
230{
231 return QT_PREPEND_NAMESPACE(runUic)(argc, argv);
232}
233

source code of qtbase/src/tools/uic/main.cpp