/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** 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. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** 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. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "qmldumptool.h" #include "qt4project.h" #include "qt4projectmanagerconstants.h" #include "qtversionmanager.h" #include "debugginghelperbuildtask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { using namespace Qt4ProjectManager; using Qt4ProjectManager::Internal::DebuggingHelperBuildTask; class QmlDumpBuildTask; typedef QHash QmlDumpByVersion; Q_GLOBAL_STATIC(QmlDumpByVersion, qmlDumpBuilds) // A task suitable to be run by QtConcurrent to build qmldump. class QmlDumpBuildTask : public QObject { Q_DISABLE_COPY(QmlDumpBuildTask) Q_OBJECT public: explicit QmlDumpBuildTask(QtVersion *version) : m_buildTask(new DebuggingHelperBuildTask(version, DebuggingHelperBuildTask::QmlDump)) , m_failed(false) { qmlDumpBuilds()->insert(version->uniqueId(), this); connect(m_buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)), this, SLOT(finish(int,QString,DebuggingHelperBuildTask::Tools)), Qt::QueuedConnection); } void run(QFutureInterface &future) { m_buildTask->run(future); } void updateProjectWhenDone(QPointer project, bool preferDebug) { foreach (const ProjectToUpdate &update, m_projectsToUpdate) { if (update.project == project) return; } ProjectToUpdate update; update.project = project; update.preferDebug = preferDebug; m_projectsToUpdate += update; } bool hasFailed() const { return m_failed; } private slots: void finish(int qtId, const QString &output, DebuggingHelperBuildTask::Tools tools) { QtVersion *version = QtVersionManager::instance()->version(qtId); QTC_ASSERT(tools == DebuggingHelperBuildTask::QmlDump, return); QString errorMessage; if (!version) { m_failed = true; errorMessage = QString::fromLatin1("Qt version became invalid"); } else { version->invalidateCache(); if (!version->hasQmlDump()) { m_failed = true; errorMessage = QString::fromLatin1("Could not build QML plugin dumping helper for %1\n" "Output:\n%2"). arg(version->displayName(), output); } } if (m_failed) { qWarning("%s", qPrintable(errorMessage)); } // update qmldump path for all the project QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); if (!modelManager) return; foreach (const ProjectToUpdate &update, m_projectsToUpdate) { if (!update.project) continue; QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(update.project); projectInfo.qmlDumpPath = version->qmlDumpTool(update.preferDebug); if (projectInfo.qmlDumpPath.isEmpty()) projectInfo.qmlDumpPath = version->qmlDumpTool(!update.preferDebug); projectInfo.qmlDumpEnvironment = version->qmlToolsEnvironment(); modelManager->updateProjectInfo(projectInfo); } // clean up qmlDumpBuilds()->remove(qtId); deleteLater(); } private: class ProjectToUpdate { public: QPointer project; bool preferDebug; }; QList m_projectsToUpdate; Internal::DebuggingHelperBuildTask *m_buildTask; // deletes itself after run() bool m_failed; }; } // end of anonymous namespace namespace Qt4ProjectManager { static inline QStringList validBinaryFilenames(bool debugBuild) { QStringList list = QStringList() << QLatin1String("qmldump.exe") << QLatin1String("qmldump") << QLatin1String("qmldump.app/Contents/MacOS/qmldump"); if (debugBuild) list.prepend(QLatin1String("debug/qmldump.exe")); else list.prepend(QLatin1String("release/qmldump.exe")); return list; } bool QmlDumpTool::canBuild(const QtVersion *qtVersion) { const QString installHeaders = qtVersion->versionInfo().value("QT_INSTALL_HEADERS"); const QString header = installHeaders + QLatin1String("/QtDeclarative/private/qdeclarativemetatype_p.h"); return (qtVersion->supportsTargetId(Constants::DESKTOP_TARGET_ID) || (qtVersion->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID) && (qtVersion->qtVersion() > QtVersionNumber(4, 7, 1)))) && QFile::exists(header); } static QtVersion *qtVersionForProject(ProjectExplorer::Project *project) { if (project && project->id() == Qt4ProjectManager::Constants::QT4PROJECT_ID) { Qt4Project *qt4Project = static_cast(project); if (qt4Project && qt4Project->activeTarget() && qt4Project->activeTarget()->activeBuildConfiguration()) { QtVersion *version = qt4Project->activeTarget()->activeBuildConfiguration()->qtVersion(); if (version->isValid()) return version; } return 0; } if (project && project->id() == QLatin1String("QmlProjectManager.QmlProject")) { // We cannot access the QmlProject interfaces here, therefore use the metatype system if (!project->activeTarget() || !project->activeTarget()->activeRunConfiguration()) return 0; QVariant variant = project->activeTarget()->activeRunConfiguration()->property("qtVersionId"); QTC_ASSERT(variant.isValid() && variant.canConvert(QVariant::Int), return 0); QtVersion *version = QtVersionManager::instance()->version(variant.toInt()); if (version && version->isValid()) return version; return 0; } // else, find any desktop or simulator Qt version that has qmldump, or // - if there isn't any - one that could build it QtVersion *canBuildQmlDump = 0; QtVersionManager *qtVersions = QtVersionManager::instance(); foreach (QtVersion *version, qtVersions->validVersions()) { if (version->supportsTargetId(Constants::DESKTOP_TARGET_ID) || version->supportsTargetId(Constants::QT_SIMULATOR_TARGET_ID)) { if (version->hasQmlDump()) return version; if (!canBuildQmlDump && QmlDumpTool::canBuild(version)) { canBuildQmlDump = version; } } } return canBuildQmlDump; } QString QmlDumpTool::toolForProject(ProjectExplorer::Project *project, bool debugDump) { QtVersion *version = qtVersionForProject(project); if (version) { QString qtInstallData = version->versionInfo().value("QT_INSTALL_DATA"); QString toolPath = toolByInstallData(qtInstallData, debugDump); return toolPath; } return QString(); } static QString sourcePath() { return Core::ICore::instance()->resourcePath() + QLatin1String("/qml/qmldump/"); } static QStringList sourceFileNames() { QStringList files; files << QLatin1String("main.cpp") << QLatin1String("qmldump.pro") << QLatin1String("qmlstreamwriter.cpp") << QLatin1String("qmlstreamwriter.h") << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT"); #ifdef Q_OS_MAC files << QLatin1String("Info.plist"); #endif return files; } QString QmlDumpTool::toolByInstallData(const QString &qtInstallData, bool debugDump) { if (!Core::ICore::instance()) return QString(); const QString mainFilename = Core::ICore::instance()->resourcePath() + QLatin1String("/qml/qmldump/main.cpp"); const QStringList directories = installDirectories(qtInstallData); const QStringList binFilenames = validBinaryFilenames(debugDump); return byInstallDataHelper(sourcePath(), sourceFileNames(), directories, binFilenames); } QStringList QmlDumpTool::locationsByInstallData(const QString &qtInstallData, bool debugDump) { QStringList result; QFileInfo fileInfo; const QStringList binFilenames = validBinaryFilenames(debugDump); foreach(const QString &directory, installDirectories(qtInstallData)) { if (getHelperFileInfoFor(binFilenames, directory, &fileInfo)) result << fileInfo.filePath(); } return result; } bool QmlDumpTool::build(const QString &directory, const QString &makeCommand, const QString &qmakeCommand, const QString &mkspec, const Utils::Environment &env, const QString &targetMode, const QStringList &qmakeArguments, QString *output, QString *errorMessage) { return buildHelper(QCoreApplication::translate("Qt4ProjectManager::QmlDumpTool", "qmldump"), QLatin1String("qmldump.pro"), directory, makeCommand, qmakeCommand, mkspec, env, targetMode, qmakeArguments, output, errorMessage); } QString QmlDumpTool::copy(const QString &qtInstallData, QString *errorMessage) { const QStringList directories = QmlDumpTool::installDirectories(qtInstallData); // Try to find a writeable directory. foreach(const QString &directory, directories) { if (copyFiles(sourcePath(), sourceFileNames(), directory, errorMessage)) { return directory; } } *errorMessage = QCoreApplication::translate("ProjectExplorer::QmlDumpTool", "qmldump could not be built in any of the directories:\n- %1\n\nReason: %2") .arg(directories.join(QLatin1String("\n- ")), *errorMessage); return QString(); } QStringList QmlDumpTool::installDirectories(const QString &qtInstallData) { const QChar slash = QLatin1Char('/'); const uint hash = qHash(qtInstallData); QStringList directories; directories << (qtInstallData + QLatin1String("/qtc-qmldump/")) << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-qmldump/") + QString::number(hash))) + slash << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-qmldump/") + QString::number(hash)) + slash; return directories; } void QmlDumpTool::pathAndEnvironment(ProjectExplorer::Project *project, bool preferDebug, QString *dumperPath, Utils::Environment *env) { QString path; QtVersion *version = qtVersionForProject(project); if (version && !version->hasQmlDump() && QmlDumpTool::canBuild(version)) { QmlDumpBuildTask *qmlDumpBuildTask = qmlDumpBuilds()->value(version->uniqueId()); if (qmlDumpBuildTask) { if (!qmlDumpBuildTask->hasFailed()) qmlDumpBuildTask->updateProjectWhenDone(project, preferDebug); } else { QmlDumpBuildTask *buildTask = new QmlDumpBuildTask(version); buildTask->updateProjectWhenDone(project, preferDebug); QFuture task = QtConcurrent::run(&QmlDumpBuildTask::run, buildTask); const QString taskName = QmlDumpBuildTask::tr("Building helper"); Core::ICore::instance()->progressManager()->addTask(task, taskName, QLatin1String("Qt4ProjectManager::BuildHelpers")); } return; } path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, preferDebug); if (path.isEmpty()) path = Qt4ProjectManager::QmlDumpTool::toolForProject(project, !preferDebug); if (!path.isEmpty()) { QFileInfo qmldumpFileInfo(path); if (!qmldumpFileInfo.exists()) { qWarning() << "QmlDumpTool::qmlDumpPath: qmldump executable does not exist at" << path; path.clear(); } else if (!qmldumpFileInfo.isFile()) { qWarning() << "QmlDumpTool::qmlDumpPath: " << path << " is not a file"; path.clear(); } } if (!path.isEmpty() && version && dumperPath && env) { *dumperPath = path; *env = version->qmlToolsEnvironment(); } } } // namespace Qt4ProjectManager #include "qmldumptool.moc"