qtquickapp.cpp 17.5 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
**
**************************************************************************/

33
#include "qtquickapp.h"
34
35
36
37
38

#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QRegExp>
#include <QtCore/QTextStream>
39
#include <QtCore/QCoreApplication>
40

41
42
43
44
#ifndef CREATORLESSTEST
#include <coreplugin/icore.h>
#endif // CREATORLESSTEST

45
namespace Qt4ProjectManager {
46
47
namespace Internal {

48
49
50
51
52
53
54
const QString qmldir(QLatin1String("qmldir"));
const QString qmldir_plugin(QLatin1String("plugin"));
const QString appViewerBaseName(QLatin1String("qmlapplicationviewer"));
const QString appViewerPriFileName(appViewerBaseName + QLatin1String(".pri"));
const QString appViewerCppFileName(appViewerBaseName + QLatin1String(".cpp"));
const QString appViewerHFileName(appViewerBaseName + QLatin1String(".h"));
const QString appViewerOriginsSubDir(appViewerBaseName + QLatin1Char('/'));
55

56
QmlModule::QmlModule(const QString &uri, const QFileInfo &rootDir, const QFileInfo &qmldir,
57
                     bool isExternal, QtQuickApp *qtQuickApp)
58
    : uri(uri)
59
60
61
    , rootDir(rootDir)
    , qmldir(qmldir)
    , isExternal(isExternal)
62
    , qtQuickApp(qtQuickApp)
63
64
{}

65
QString QmlModule::path(Path path) const
66
{
67
    switch (path) {
68
69
70
71
        case Root: {
            return rootDir.canonicalFilePath();
        }
        case ContentDir: {
72
            const QDir proFile(qtQuickApp->path(QtQuickApp::AppProPath));
73
74
75
76
77
78
79
80
81
82
            return proFile.relativeFilePath(qmldir.canonicalPath());
        }
        case ContentBase: {
            const QString localRoot = rootDir.canonicalFilePath() + QLatin1Char('/');
            QDir contentDir = qmldir.dir();
            contentDir.cdUp();
            const QString localContentDir = contentDir.canonicalPath();
            return localContentDir.right(localContentDir.length() - localRoot.length());
        }
        case DeployedContentBase: {
83
            const QString modulesDir = qtQuickApp->path(QtQuickApp::ModulesDir);
84
            return modulesDir + QLatin1Char('/') + this->path(ContentBase);
85
86
87
88
89
90
91
92
93
94
95
96
        }
        default: qFatal("QmlModule::path() needs more work");
    }
    return QString();
}

QmlCppPlugin::QmlCppPlugin(const QString &name, const QFileInfo &path,
                           const QmlModule *module, const QFileInfo &proFile)
    : name(name)
    , path(path)
    , module(module)
    , proFile(proFile)
97
98
99
{
}

100
101
102
QtQuickApp::QtQuickApp()
    : AbstractMobileApp()
    , m_mainQmlMode(ModeGenerate)
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
103
    , m_componentSet(QtQuick10Components)
104
105
106
{
}

107
QtQuickApp::~QtQuickApp()
108
109
110
111
{
    clearModulesAndPlugins();
}

Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
112
113
114
115
116
117
118
119
120
121
void QtQuickApp::setComponentSet(ComponentSet componentSet)
{
    m_componentSet = componentSet;
}

QtQuickApp::ComponentSet QtQuickApp::componentSet() const
{
    return m_componentSet;
}

122
void QtQuickApp::setMainQml(Mode mode, const QString &file)
123
{
124
125
126
    Q_ASSERT(mode != ModeGenerate || file.isEmpty());
    m_mainQmlMode = mode;
    m_mainQmlFile.setFile(file);
127
128
}

129
QtQuickApp::Mode QtQuickApp::mainQmlMode() const
130
{
131
    return m_mainQmlMode;
132
133
}

134
bool QtQuickApp::setExternalModules(const QStringList &uris,
135
                                    const QStringList &importPaths)
136
137
138
139
140
{
    clearModulesAndPlugins();
    m_importPaths.clear();
    foreach (const QFileInfo &importPath, importPaths) {
        if (!importPath.exists()) {
141
            m_error = QCoreApplication::translate(
142
                        "Qt4ProjectManager::Internal::QtQuickApp",
143
                        "The QML import path '%1' cannot be found.")
144
145
146
147
148
149
                      .arg(QDir::toNativeSeparators(importPath.filePath()));
            return false;
        } else {
            m_importPaths.append(importPath.canonicalFilePath());
        }
    }
150
151
152
    foreach (const QString &uri, uris) {
        QString uriPath = uri;
        uriPath.replace(QLatin1Char('.'), QLatin1Char('/'));
153
154
155
156
        const int modulesCount = m_modules.count();
        foreach (const QFileInfo &importPath, m_importPaths) {
            const QFileInfo qmlDirFile(
                    importPath.absoluteFilePath() + QLatin1Char('/')
157
                    + uriPath + QLatin1Char('/') + qmldir);
158
            if (qmlDirFile.exists()) {
159
                if (!addExternalModule(uri, importPath, qmlDirFile))
160
161
162
163
164
                    return false;
                break;
            }
        }
        if (modulesCount == m_modules.count()) { // no module was added
165
            m_error = QCoreApplication::translate(
166
                      "Qt4ProjectManager::Internal::QtQuickApp",
167
                      "The QML module '%1' cannot be found.").arg(uri);
168
169
170
171
172
173
174
            return false;
        }
    }
    m_error.clear();
    return true;
}

175
QString QtQuickApp::pathExtended(int fileType) const
176
{
177
    QString cleanProjectName = projectName().replace(QLatin1Char('-'), QString());
178
    const bool importQmlFile = m_mainQmlMode == ModeImport;
179
    const QString qmlSubDir = QLatin1String("qml/")
180
181
                              + (importQmlFile ? m_mainQmlFile.dir().dirName() : cleanProjectName)
                              + QLatin1Char('/');
182
    const QString appViewerTargetSubDir = appViewerOriginsSubDir;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
183
184
185
186
187
188
189

    const QString mainQmlFile = QLatin1String("main.qml");
    const QString mainPageQmlFile = QLatin1String("MainPage.qml");

    const QString qmlOriginDir = originsRoot() + QLatin1String("qml/app/")
                        + componentSetDir(componentSet()) + QLatin1Char('/');

190
    const QString pathBase = outputPathBase();
191
192
    const QDir appProFilePath(pathBase);

193
    switch (fileType) {
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
194
195
        case MainQml:
            return importQmlFile ? m_mainQmlFile.canonicalFilePath() : pathBase + qmlSubDir + mainQmlFile;
196
        case MainQmlDeployed:               return importQmlFile ? qmlSubDir + m_mainQmlFile.fileName()
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
197
198
199
200
                                                                 : QString(qmlSubDir + mainQmlFile);
        case MainQmlOrigin:                 return qmlOriginDir + mainQmlFile;
        case MainPageQml:                   return pathBase + qmlSubDir + mainPageQmlFile;
        case MainPageQmlOrigin:             return qmlOriginDir + mainPageQmlFile;
201
        case AppViewerPri:                  return pathBase + appViewerTargetSubDir + appViewerPriFileName;
202
        case AppViewerPriOrigin:            return originsRoot() + appViewerOriginsSubDir + appViewerPriFileName;
203
        case AppViewerCpp:                  return pathBase + appViewerTargetSubDir + appViewerCppFileName;
204
        case AppViewerCppOrigin:            return originsRoot() + appViewerOriginsSubDir + appViewerCppFileName;
205
        case AppViewerH:                    return pathBase + appViewerTargetSubDir + appViewerHFileName;
206
        case AppViewerHOrigin:              return originsRoot() + appViewerOriginsSubDir + appViewerHFileName;
207
        case QmlDir:                        return pathBase + qmlSubDir;
208
209
        case QmlDirProFileRelative:         return importQmlFile ? appProFilePath.relativeFilePath(m_mainQmlFile.canonicalPath())
                                                                 : QString(qmlSubDir).remove(qmlSubDir.length() - 1, 1);
210
        case ModulesDir:                    return QLatin1String("modules");
211
        default:                            qFatal("QtQuickApp::pathExtended() needs more work");
212
213
214
215
    }
    return QString();
}

216
QString QtQuickApp::originsRoot() const
217
{
218
    return templatesRoot() + QLatin1String("qtquickapp/");
219
220
}

221
QString QtQuickApp::mainWindowClassName() const
222
{
223
    return QLatin1String("QmlApplicationViewer");
224
225
}

226
bool QtQuickApp::adaptCurrentMainCppTemplateLine(QString &line) const
227
{
228
229
230
231
232
233
234
235
236
237
238
239
240
    const QLatin1Char quote('"');
    bool adaptLine = true;
    if (line.contains(QLatin1String("// MAINQML"))) {
        insertParameter(line, quote + path(MainQmlDeployed) + quote);
    } else if (line.contains(QLatin1String("// ADDIMPORTPATH"))) {
        if (m_modules.isEmpty())
            adaptLine = false;
        else
            insertParameter(line, quote + path(ModulesDir) + quote);
    }
    return adaptLine;
}

241
void QtQuickApp::handleCurrentProFileTemplateLine(const QString &line,
242
    QTextStream &proFileTemplate, QTextStream &proFile,
243
    bool &commentOutNextLine) const
244
{
Alessandro Portale's avatar
Alessandro Portale committed
245
246
    Q_UNUSED(commentOutNextLine)
    if (line.contains(QLatin1String("# QML_IMPORT_PATH"))) {
247
248
249
250
251
252
253
254
255
256
257
258
259
260
        QString nextLine = proFileTemplate.readLine(); // eats 'QML_IMPORT_PATH ='
        if (!nextLine.startsWith(QLatin1String("QML_IMPORT_PATH =")))
            return;

        proFile << nextLine;

        const QLatin1String separator(" \\\n    ");
        const QDir proPath(path(AppProPath));
        foreach (const QString &importPath, m_importPaths) {
            const QString relativePath = proPath.relativeFilePath(importPath);
            proFile << separator << relativePath;
        }

        proFile << endl;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
261
262
263
264
265
    } else if (line.contains(QLatin1String("# QTQUICKCOMPONENTS"))) {
        QString nextLine = proFileTemplate.readLine(); // eats '# CONFIG += qtquickcomponents'
        if (componentSet() == Symbian10Components)
            nextLine.remove(0, 2); // remove comment
        proFile << nextLine << endl;
266
    }
267
268
}

269
void QtQuickApp::clearModulesAndPlugins()
270
271
272
273
274
275
276
{
    qDeleteAll(m_modules);
    m_modules.clear();
    qDeleteAll(m_cppPlugins);
    m_cppPlugins.clear();
}

277
bool QtQuickApp::addCppPlugin(const QString &qmldirLine, QmlModule *module)
278
279
280
281
{
    const QStringList qmldirLineElements =
            qmldirLine.split(QLatin1Char(' '), QString::SkipEmptyParts);
    if (qmldirLineElements.count() < 2) {
282
        m_error = QCoreApplication::translate(
283
                      "Qt4ProjectManager::Internal::QtQuickApp",
284
                      "Invalid '%1' entry in '%2' of module '%3'.")
285
                  .arg(qmldir_plugin).arg(qmldir).arg(module->uri);
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
        return false;
    }
    const QString name = qmldirLineElements.at(1);
    const QFileInfo path(module->qmldir.dir(), qmldirLineElements.value(2, QString()));

    // TODO: Add more magic to find a good .pro file..
    const QString proFileName = name + QLatin1String(".pro");
    const QFileInfo proFile_guess1(module->qmldir.dir(), proFileName);
    const QFileInfo proFile_guess2(QString(module->qmldir.dir().absolutePath() + QLatin1String("/../")),
                                   proFileName);
    const QFileInfo proFile_guess3(module->qmldir.dir(),
                                   QFileInfo(module->qmldir.path()).fileName() + QLatin1String(".pro"));
    const QFileInfo proFile_guess4(proFile_guess3.absolutePath() + QLatin1String("/../")
                                   + proFile_guess3.fileName());

    QFileInfo foundProFile;
    if (proFile_guess1.exists()) {
        foundProFile = proFile_guess1.canonicalFilePath();
    } else if (proFile_guess2.exists()) {
        foundProFile = proFile_guess2.canonicalFilePath();
    } else if (proFile_guess3.exists()) {
        foundProFile = proFile_guess3.canonicalFilePath();
    } else if (proFile_guess4.exists()) {
        foundProFile = proFile_guess4.canonicalFilePath();
    } else {
311
        m_error = QCoreApplication::translate(
312
                    "Qt4ProjectManager::Internal::QtQuickApp",
313
                    "No .pro file for plugin '%1' cannot be found.").arg(name);
314
315
316
317
318
319
320
321
322
        return false;
    }
    QmlCppPlugin *plugin =
            new QmlCppPlugin(name, path, module, foundProFile);
    m_cppPlugins.append(plugin);
    module->cppPlugins.insert(name, plugin);
    return true;
}

323
bool QtQuickApp::addCppPlugins(QmlModule *module)
324
325
326
{
    QFile qmlDirFile(module->qmldir.absoluteFilePath());
    if (qmlDirFile.open(QIODevice::ReadOnly)) {
327
        QTextStream in(&qmlDirFile);
328
        QString line;
329
330
        while (!(line = in.readLine()).isNull()) {
            line = line.trimmed();
331
332
            if (line.startsWith(qmldir_plugin) && !addCppPlugin(line, module))
                return false;
333
        };
334
335
336
337
    }
    return true;
}

338
bool QtQuickApp::addExternalModule(const QString &name, const QFileInfo &dir,
339
340
341
342
343
344
345
                                         const QFileInfo &contentDir)
{
    QmlModule *module = new QmlModule(name, dir, contentDir, true, this);
    m_modules.append(module);
    return addCppPlugins(module);
}

346
#ifndef CREATORLESSTEST
347
Core::GeneratedFiles QtQuickApp::generateFiles(QString *errorMessage) const
348
{
349
    Core::GeneratedFiles files = AbstractMobileApp::generateFiles(errorMessage);
350
    if (!useExistingMainQml()) {
351
        files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainQmlFile, errorMessage), path(MainQml)));
352
353
        if ((componentSet() == QtQuickApp::Symbian10Components)
                || (componentSet() == QtQuickApp::Meego10Components))
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
354
            files.append(file(generateFile(QtQuickAppGeneratedFileInfo::MainPageQmlFile, errorMessage), path(MainPageQml)));
355
356
        files.last().setAttributes(Core::GeneratedFile::OpenEditorAttribute);
    }
357

358
359
360
    files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerPriFile, errorMessage), path(AppViewerPri)));
    files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerCppFile, errorMessage), path(AppViewerCpp)));
    files.append(file(generateFile(QtQuickAppGeneratedFileInfo::AppViewerHFile, errorMessage), path(AppViewerH)));
361
362
363
364
365

    return files;
}
#endif // CREATORLESSTEST

366
bool QtQuickApp::useExistingMainQml() const
367
368
369
370
{
    return !m_mainQmlFile.filePath().isEmpty();
}

371
const QList<QmlModule*> QtQuickApp::modules() const
372
373
374
375
{
    return m_modules;
}

376
QByteArray QtQuickApp::generateFileExtended(int fileType,
377
    bool *versionAndCheckSum, QString *comment, QString *errorMessage) const
378
379
{
    QByteArray data;
380
    switch (fileType) {
381
        case QtQuickAppGeneratedFileInfo::MainQmlFile:
382
            data = readBlob(path(MainQmlOrigin), errorMessage);
383
            break;
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
384
385
386
        case QtQuickAppGeneratedFileInfo::MainPageQmlFile:
            data = readBlob(path(MainPageQmlOrigin), errorMessage);
            break;
387
        case QtQuickAppGeneratedFileInfo::AppViewerPriFile:
388
            data = readBlob(path(AppViewerPriOrigin), errorMessage);
389
            data.append(readBlob(path(DeploymentPriOrigin), errorMessage));
390
391
            *comment = ProFileComment;
            *versionAndCheckSum = true;
392
            break;
393
        case QtQuickAppGeneratedFileInfo::AppViewerCppFile:
394
395
            data = readBlob(path(AppViewerCppOrigin), errorMessage);
            *versionAndCheckSum = true;
396
            break;
397
        case QtQuickAppGeneratedFileInfo::AppViewerHFile:
398
        default:
399
400
            data = readBlob(path(AppViewerHOrigin), errorMessage);
            *versionAndCheckSum = true;
401
402
            break;
    }
403
    return data;
404
405
}

406
int QtQuickApp::stubVersionMinor() const
407
{
408
    return StubVersion;
409
410
}

411
QList<AbstractGeneratedFileInfo> QtQuickApp::updateableFiles(const QString &mainProFile) const
412
{
413
    QList<AbstractGeneratedFileInfo> result;
414
    static const struct {
415
        int fileType;
416
417
        QString fileName;
    } files[] = {
418
419
420
        {QtQuickAppGeneratedFileInfo::AppViewerPriFile, appViewerPriFileName},
        {QtQuickAppGeneratedFileInfo::AppViewerHFile, appViewerHFileName},
        {QtQuickAppGeneratedFileInfo::AppViewerCppFile, appViewerCppFileName}
421
422
    };
    const QFileInfo mainProFileInfo(mainProFile);
423
424
    const int size = sizeof(files) / sizeof(files[0]);
    for (int i = 0; i < size; ++i) {
425
        const QString fileName = mainProFileInfo.dir().absolutePath()
426
                + QLatin1Char('/') + appViewerOriginsSubDir + files[i].fileName;
427
428
        if (!QFile::exists(fileName))
            continue;
429
        QtQuickAppGeneratedFileInfo file;
430
        file.fileType = files[i].fileType;
431
        file.fileInfo = QFileInfo(fileName);
432
        file.currentVersion = AbstractMobileApp::makeStubVersion(QtQuickApp::StubVersion);
433
434
        result.append(file);
    }
435
436
    if (result.count() != size)
        result.clear(); // All files must be found. No wrong/partial updates, please.
437
438
439
    return result;
}

440
441
442
443
444
445
446
447
448
449
QList<DeploymentFolder> QtQuickApp::deploymentFolders() const
{
    QList<DeploymentFolder> result;
    result.append(DeploymentFolder(path(QmlDirProFileRelative), QLatin1String("qml")));
    foreach (const QmlModule *module, m_modules)
        if (module->isExternal)
            result.append(DeploymentFolder(module->path(QmlModule::ContentDir), module->path(QmlModule::DeployedContentBase)));
    return result;
}

Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
450
451
452
453
454
QString QtQuickApp::componentSetDir(ComponentSet componentSet) const
{
    switch (componentSet) {
    case Symbian10Components:
        return QLatin1String("symbian10");
455
456
    case Meego10Components:
        return QLatin1String("meego10");
Feetu Nyrhinen's avatar
Feetu Nyrhinen committed
457
458
459
460
461
462
    case QtQuick10Components:
    default:
        return QLatin1String("qtquick10");
    }
}

463
const int QtQuickApp::StubVersion = 15;
464

465
} // namespace Internal
466
} // namespace Qt4ProjectManager