1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "qtquickapp.h"
35 #include <QtCore/QDir>
36 #include <QtCore/QFile>
37 #include <QtCore/QRegExp>
38 #include <QtCore/QTextStream>
39 #include <QtCore/QCoreApplication>
41 #ifndef CREATORLESSTEST
42 #include <coreplugin/icore.h>
43 #endif // CREATORLESSTEST
45 namespace Qt4ProjectManager {
48 const QString qmldir(QLatin1String("qmldir"));
49 const QString qmldir_plugin(QLatin1String("plugin"));
50 const QString appViewerBaseName(QLatin1String("qmlapplicationviewer"));
51 const QString appViewerPriFileName(appViewerBaseName + QLatin1String(".pri"));
52 const QString appViewerCppFileName(appViewerBaseName + QLatin1String(".cpp"));
53 const QString appViewerHFileName(appViewerBaseName + QLatin1String(".h"));
54 const QString appViewerOriginsSubDir(appViewerBaseName + QLatin1Char('/'));
56 QmlModule::QmlModule(const QString &uri, const QFileInfo &rootDir, const QFileInfo &qmldir,
57 bool isExternal, QtQuickApp *qtQuickApp)
61 , isExternal(isExternal)
62 , qtQuickApp(qtQuickApp)
65 QString QmlModule::path(Path path) const
69 return rootDir.canonicalFilePath();
72 const QDir proFile(qtQuickApp->path(QtQuickApp::AppProPath));
73 return proFile.relativeFilePath(qmldir.canonicalPath());
76 const QString localRoot = rootDir.canonicalFilePath() + QLatin1Char('/');
77 QDir contentDir = qmldir.dir();
79 const QString localContentDir = contentDir.canonicalPath();
80 return localContentDir.right(localContentDir.length() - localRoot.length());
82 case DeployedContentBase: {
83 const QString modulesDir = qtQuickApp->path(QtQuickApp::ModulesDir);
84 return modulesDir + QLatin1Char('/') + this->path(ContentBase);
86 default: qFatal("QmlModule::path() needs more work");
91 QmlCppPlugin::QmlCppPlugin(const QString &name, const QFileInfo &path,
92 const QmlModule *module, const QFileInfo &proFile)
100 QtQuickApp::QtQuickApp()
101 : AbstractMobileApp()
102 , m_mainQmlMode(ModeGenerate)
106 QtQuickApp::~QtQuickApp()
108 clearModulesAndPlugins();
111 void QtQuickApp::setMainQml(Mode mode, const QString &file)
113 Q_ASSERT(mode != ModeGenerate || file.isEmpty());
114 m_mainQmlMode = mode;
115 m_mainQmlFile.setFile(file);
118 QtQuickApp::Mode QtQuickApp::mainQmlMode() const
120 return m_mainQmlMode;
123 bool QtQuickApp::setExternalModules(const QStringList &uris,
124 const QStringList &importPaths)
126 clearModulesAndPlugins();
127 m_importPaths.clear();
128 foreach (const QFileInfo &importPath, importPaths) {
129 if (!importPath.exists()) {
130 m_error = QCoreApplication::translate(
131 "Qt4ProjectManager::Internal::QtQuickApp",
132 "The QML import path '%1' cannot be found.")
133 .arg(QDir::toNativeSeparators(importPath.filePath()));
136 m_importPaths.append(importPath.canonicalFilePath());
139 foreach (const QString &uri, uris) {
140 QString uriPath = uri;
141 uriPath.replace(QLatin1Char('.'), QLatin1Char('/'));
142 const int modulesCount = m_modules.count();
143 foreach (const QFileInfo &importPath, m_importPaths) {
144 const QFileInfo qmlDirFile(
145 importPath.absoluteFilePath() + QLatin1Char('/')
146 + uriPath + QLatin1Char('/') + qmldir);
147 if (qmlDirFile.exists()) {
148 if (!addExternalModule(uri, importPath, qmlDirFile))
153 if (modulesCount == m_modules.count()) { // no module was added
154 m_error = QCoreApplication::translate(
155 "Qt4ProjectManager::Internal::QtQuickApp",
156 "The QML module '%1' cannot be found.").arg(uri);
164 QString QtQuickApp::pathExtended(int fileType) const
166 QString cleanProjectName = projectName().replace(QLatin1Char('-'), QString());
167 const bool importQmlFile = m_mainQmlMode == ModeImport;
168 const QString qmlSubDir = QLatin1String("qml/")
169 + (importQmlFile ? m_mainQmlFile.dir().dirName() : cleanProjectName)
171 const QString appViewerTargetSubDir = appViewerOriginsSubDir;
172 const QString mainQml = QLatin1String("main.qml");
173 const QString pathBase = outputPathBase();
174 const QDir appProFilePath(pathBase);
177 case MainQml: return importQmlFile ? m_mainQmlFile.canonicalFilePath()
178 : pathBase + qmlSubDir + mainQml;
179 case MainQmlDeployed: return importQmlFile ? qmlSubDir + m_mainQmlFile.fileName()
180 : QString(qmlSubDir + mainQml);
181 case MainQmlOrigin: return originsRoot() + QLatin1String("qml/app/") + mainQml;
182 case AppViewerPri: return pathBase + appViewerTargetSubDir + appViewerPriFileName;
183 case AppViewerPriOrigin: return originsRoot() + appViewerOriginsSubDir + appViewerPriFileName;
184 case AppViewerCpp: return pathBase + appViewerTargetSubDir + appViewerCppFileName;
185 case AppViewerCppOrigin: return originsRoot() + appViewerOriginsSubDir + appViewerCppFileName;
186 case AppViewerH: return pathBase + appViewerTargetSubDir + appViewerHFileName;
187 case AppViewerHOrigin: return originsRoot() + appViewerOriginsSubDir + appViewerHFileName;
188 case QmlDir: return pathBase + qmlSubDir;
189 case QmlDirProFileRelative: return importQmlFile ? appProFilePath.relativeFilePath(m_mainQmlFile.canonicalPath())
190 : QString(qmlSubDir).remove(qmlSubDir.length() - 1, 1);
191 case ModulesDir: return QLatin1String("modules");
192 default: qFatal("QtQuickApp::pathExtended() needs more work");
197 QString QtQuickApp::originsRoot() const
199 return templatesRoot() + QLatin1String("qtquickapp/");
202 QString QtQuickApp::mainWindowClassName() const
204 return QLatin1String("QmlApplicationViewer");
207 bool QtQuickApp::adaptCurrentMainCppTemplateLine(QString &line) const
209 const QLatin1Char quote('"');
210 bool adaptLine = true;
211 if (line.contains(QLatin1String("// MAINQML"))) {
212 insertParameter(line, quote + path(MainQmlDeployed) + quote);
213 } else if (line.contains(QLatin1String("// ADDIMPORTPATH"))) {
214 if (m_modules.isEmpty())
217 insertParameter(line, quote + path(ModulesDir) + quote);
222 void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line,
223 QTextStream &proFileTemplate, QTextStream &proFile,
224 bool &commentOutNextLine) const
226 Q_UNUSED(commentOutNextLine)
227 if (line.contains(QLatin1String("# QML_IMPORT_PATH"))) {
228 QString nextLine = proFileTemplate.readLine(); // eats 'QML_IMPORT_PATH ='
229 if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH =")))
234 const QLatin1String separator(" \\\n ");
235 const QDir proPath(path(AppProPath));
236 foreach (const QString &importPath, m_importPaths) {
237 const QString relativePath = proPath.relativeFilePath(importPath);
238 proFile << separator << relativePath;
245 void QtQuickApp::clearModulesAndPlugins()
247 qDeleteAll(m_modules);
249 qDeleteAll(m_cppPlugins);
250 m_cppPlugins.clear();
253 bool QtQuickApp::addCppPlugin(const QString &qmldirLine, QmlModule *module)
255 const QStringList qmldirLineElements =
256 qmldirLine.split(QLatin1Char(' '), QString::SkipEmptyParts);
257 if (qmldirLineElements.count() < 2) {
258 m_error = QCoreApplication::translate(
259 "Qt4ProjectManager::Internal::QtQuickApp",
260 "Invalid '%1' entry in '%2' of module '%3'.")
261 .arg(qmldir_plugin).arg(qmldir).arg(module->uri);
264 const QString name = qmldirLineElements.at(1);
265 const QFileInfo path(module->qmldir.dir(), qmldirLineElements.value(2, QString()));
267 // TODO: Add more magic to find a good .pro file..
268 const QString proFileName = name + QLatin1String(".pro");
269 const QFileInfo proFile_guess1(module->qmldir.dir(), proFileName);
270 const QFileInfo proFile_guess2(QString(module->qmldir.dir().absolutePath() + QLatin1String("/../")),
272 const QFileInfo proFile_guess3(module->qmldir.dir(),
273 QFileInfo(module->qmldir.path()).fileName() + QLatin1String(".pro"));
274 const QFileInfo proFile_guess4(proFile_guess3.absolutePath() + QLatin1String("/../")
275 + proFile_guess3.fileName());
277 QFileInfo foundProFile;
278 if (proFile_guess1.exists()) {
279 foundProFile = proFile_guess1.canonicalFilePath();
280 } else if (proFile_guess2.exists()) {
281 foundProFile = proFile_guess2.canonicalFilePath();
282 } else if (proFile_guess3.exists()) {
283 foundProFile = proFile_guess3.canonicalFilePath();
284 } else if (proFile_guess4.exists()) {
285 foundProFile = proFile_guess4.canonicalFilePath();
287 m_error = QCoreApplication::translate(
288 "Qt4ProjectManager::Internal::QtQuickApp",
289 "No .pro file for plugin '%1' cannot be found.").arg(name);
292 QmlCppPlugin *plugin =
293 new QmlCppPlugin(name, path, module, foundProFile);
294 m_cppPlugins.append(plugin);
295 module->cppPlugins.insert(name, plugin);
299 bool QtQuickApp::addCppPlugins(QmlModule *module)
301 QFile qmlDirFile(module->qmldir.absoluteFilePath());
302 if (qmlDirFile.open(QIODevice::ReadOnly)) {
303 QTextStream in(&qmlDirFile);
305 while (!(line = in.readLine()).isNull()) {
306 line = line.trimmed();
307 if (line.startsWith(qmldir_plugin) && !addCppPlugin(line, module))
314 bool QtQuickApp::addExternalModule(const QString &name, const QFileInfo &dir,
315 const QFileInfo &contentDir)
317 QmlModule *module = new QmlModule(name, dir, contentDir, true, this);
318 m_modules.append(module);
319 return addCppPlugins(module);
322 #ifndef CREATORLESSTEST
323 Core::GeneratedFiles QtQuickApp::generateFiles(QString *errorMessage) const
325 Core::GeneratedFiles files = AbstractMobileApp::generateFiles(errorMessage);
326 if (!useExistingMainQml()) {
327 files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQmlFile, errorMessage), path(MainQml)));
328 files.last().setAttributes(Core::GeneratedFile::OpenEditorAttribute);
331 files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerPriFile, errorMessage), path(AppViewerPri)));
332 files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerCppFile, errorMessage), path(AppViewerCpp)));
333 files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerHFile, errorMessage), path(AppViewerH)));
337 #endif // CREATORLESSTEST
339 bool QtQuickApp::useExistingMainQml() const
341 return !m_mainQmlFile.filePath().isEmpty();
344 const QList<QmlModule*> QtQuickApp::modules() const
349 QByteArray QtQuickApp::generateFileExtended(int fileType,
350 bool *versionAndCheckSum, QString *comment, QString *errorMessage) const
354 case QtQuickAppGeneratedFileInfo::MainQmlFile:
355 data = readBlob(path(MainQmlOrigin), errorMessage);
357 case QtQuickAppGeneratedFileInfo::AppViewerPriFile:
358 data = readBlob(path(AppViewerPriOrigin), errorMessage);
359 data.append(readBlob(path(DeploymentPriOrigin), errorMessage));
360 *comment = ProFileComment;
361 *versionAndCheckSum = true;
363 case QtQuickAppGeneratedFileInfo::AppViewerCppFile:
364 data = readBlob(path(AppViewerCppOrigin), errorMessage);
365 *versionAndCheckSum = true;
367 case QtQuickAppGeneratedFileInfo::AppViewerHFile:
369 data = readBlob(path(AppViewerHOrigin), errorMessage);
370 *versionAndCheckSum = true;
376 int QtQuickApp::stubVersionMinor() const
381 QList<AbstractGeneratedFileInfo> QtQuickApp::updateableFiles(const QString &mainProFile) const
383 QList<AbstractGeneratedFileInfo> result;
384 static const struct {
388 {QtQuickAppGeneratedFileInfo::AppViewerPriFile, appViewerPriFileName},
389 {QtQuickAppGeneratedFileInfo::AppViewerHFile, appViewerHFileName},
390 {QtQuickAppGeneratedFileInfo::AppViewerCppFile, appViewerCppFileName}
392 const QFileInfo mainProFileInfo(mainProFile);
393 const int size = sizeof(files) / sizeof(files[0]);
394 for (int i = 0; i < size; ++i) {
395 const QString fileName = mainProFileInfo.dir().absolutePath()
396 + QLatin1Char('/') + appViewerOriginsSubDir + files[i].fileName;
397 if (!QFile::exists(fileName))
399 QtQuickAppGeneratedFileInfo file;
400 file.fileType = files[i].fileType;
401 file.fileInfo = QFileInfo(fileName);
402 file.currentVersion = AbstractMobileApp::makeStubVersion(QtQuickApp::StubVersion);
405 if (result.count() != size)
406 result.clear(); // All files must be found. No wrong/partial updates, please.
410 QList<DeploymentFolder> QtQuickApp::deploymentFolders() const
412 QList<DeploymentFolder> result;
413 result.append(DeploymentFolder(path(QmlDirProFileRelative), QLatin1String("qml")));
414 foreach (const QmlModule *module, m_modules)
415 if (module->isExternal)
416 result.append(DeploymentFolder(module->path(QmlModule::ContentDir), module->path(QmlModule::DeployedContentBase)));
420 const int QtQuickApp::StubVersion = 11;
422 } // namespace Internal
423 } // namespace Qt4ProjectManager