/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Build Suite. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, 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, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "qtprofilesetup.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace qbs { struct QtModuleInfo { QtModuleInfo(const QString &name, const QString &qbsName, const QStringList &deps = QStringList()) : name(name), qbsName(qbsName), dependencies(deps), hasLibrary(!qbsName.endsWith(QLatin1String("-private"))) { const QString coreModule = QLatin1String("core"); if (qbsName != coreModule && !dependencies.contains(coreModule)) dependencies.prepend(coreModule); } QtModuleInfo() : hasLibrary(true) {} QString name; // As in the path to the headers and ".name" in the pri files. QString qbsName; // Lower-case version without "qt" prefix. QStringList dependencies; // qbs names. QStringList includePaths; bool hasLibrary; }; static void copyTemplateFile(const QString &fileName, const QString &targetDirectory, const QString &profileName) { if (!QDir::root().mkpath(targetDirectory)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " "Cannot create directory '%2'.") .arg(profileName, targetDirectory)); } QFile sourceFile(QLatin1String(":/templates/") + fileName); const QString targetPath = targetDirectory + QLatin1Char('/') + fileName; QFile::remove(targetPath); // QFile::copy() cannot overwrite. if (!sourceFile.copy(targetPath)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " "Cannot copy file '%2' into directory '%3' (%4).") .arg(profileName, fileName, targetDirectory, sourceFile.errorString())); } QFile targetFile(targetPath); if (!targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: Cannot set write " "permission on file '%2' (%3).") .arg(profileName, targetPath, targetFile.errorString())); } } static void replaceListPlaceholder(QByteArray &content, const QByteArray &placeHolder, const QStringList &list) { QByteArray listString; foreach (const QString &elem, list) listString += "'" + elem.toUtf8() + "', "; content.replace(placeHolder, listString); } // We erroneously called the "testlib" module "test" for quite a while. Let's not punish users // for that. static void addTestModule(QList &modules) { QtModuleInfo testModule(QLatin1String("QtTest"), QLatin1String("test"), QStringList() << QLatin1String("testlib")); testModule.hasLibrary = false; modules << testModule; } // See above. static void addDesignerComponentsModule(QList &modules) { QtModuleInfo module(QLatin1String("QtDesignerComponents"), QLatin1String("designercomponents"), QStringList() << QLatin1String("designercomponents-private")); module.hasLibrary = false; modules << module; } static void createModules(Profile &profile, Settings *settings, const QtEnvironment &qtEnvironment) { QList modules; if (qtEnvironment.qtMajorVersion < 5) { modules // as per http://qt-project.org/doc/qt-4.8/modules.html + private stuff. << QtModuleInfo(QLatin1String("QtCore"), QLatin1String("core")) << QtModuleInfo(QLatin1String("QtCore"), QLatin1String("core-private"), QStringList() << QLatin1String("core")) << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui-private"), QStringList() << QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia"), QStringList() << QLatin1String("gui") << QLatin1String("network")) << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia-private"), QStringList() << QLatin1String("multimedia")) << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network")) << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network-private"), QStringList() << QLatin1String("network")) << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl"), QStringList() << QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl-private"), QStringList() << QLatin1String("opengl")) << QtModuleInfo(QLatin1String("QtOpenVG"), QLatin1String("openvg"), QStringList() << QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script")) << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script-private"), QStringList() << QLatin1String("script")) << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttols"), QStringList() << QLatin1String("script") << QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttols-private"), QStringList() << QLatin1String("scripttols")) << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql")) << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql-private"), QStringList() << QLatin1String("sql")) << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg"), QStringList() << QLatin1String("gui")) << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg-private"), QStringList() << QLatin1String("svg")) << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit"), QStringList() << QLatin1String("gui") << QLatin1String("network")) << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit-private"), QStringList() << QLatin1String("webkit")) << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml")) << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml-private"), QStringList() << QLatin1String("xml")) << QtModuleInfo(QLatin1String("QtXmlPatterns"), QLatin1String("xmlpatterns"), QStringList() << "network") << QtModuleInfo(QLatin1String("QtXmlPatterns"), QLatin1String("xmlpatterns-private"), QStringList() << QLatin1String("xmlpatterns")) << QtModuleInfo(QLatin1String("QtDeclarative"), QLatin1String("declarative"), QStringList() << QLatin1String("gui") << QLatin1String("script")) << QtModuleInfo(QLatin1String("QtDeclarative"), QLatin1String("declarative-private"), QStringList() << QLatin1String("declarative")) << QtModuleInfo(QLatin1String("Phonon"), QLatin1String("phonon")) << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer"), QStringList() << QLatin1String("gui") << QLatin1String("xml")) << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer-private"), QStringList() << QLatin1String("designer")) << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools")) << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools-private"), QStringList() << QLatin1String("uitools")) << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help"), QStringList() << QLatin1String("network") << QLatin1String("sql")) << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help-private"), QStringList() << QLatin1String("help")) << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib")) << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib-private"), QStringList() << QLatin1String("testlib")) << QtModuleInfo(QLatin1String("QAxContainer"), QLatin1String("axcontainer")) << QtModuleInfo(QLatin1String("QAxServer"), QLatin1String("axserver")) << QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus")) << QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus-private"), QStringList() << QLatin1String("dbus")); QtModuleInfo designerComponentsPrivate(QLatin1String("QtDesignerComponents"), QLatin1String("designercomponents-private"), QStringList() << QLatin1String("gui-private") << QLatin1String("designer-private")); designerComponentsPrivate.hasLibrary = true; modules << designerComponentsPrivate; // These are for the convenience of project file authors. It allows them // to add a dependency to e.g. "Qt.widgets" without a version check. QtModuleInfo virtualModule; virtualModule.hasLibrary = false; virtualModule.qbsName = QLatin1String("widgets"); virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); modules << virtualModule; virtualModule.qbsName = QLatin1String("quick"); virtualModule.dependencies = QStringList() << QLatin1String("declarative"); modules << virtualModule; virtualModule.qbsName = QLatin1String("concurrent"); virtualModule.dependencies = QStringList() << QLatin1String("core"); modules << virtualModule; virtualModule.qbsName = QLatin1String("printsupport"); virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); modules << virtualModule; addTestModule(modules); addDesignerComponentsModule(modules); } else { QDirIterator dit(qtEnvironment.mkspecBasePath + QLatin1String("/modules")); while (dit.hasNext()) { const QString moduleFileNamePrefix = QLatin1String("qt_lib_"); const QString moduleFileNameSuffix = QLatin1String(".pri"); dit.next(); if (!dit.fileName().startsWith(moduleFileNamePrefix) || !dit.fileName().endsWith(moduleFileNameSuffix)) { continue; } QtModuleInfo moduleInfo; moduleInfo.qbsName = dit.fileName().mid(moduleFileNamePrefix.count(), dit.fileName().count() - moduleFileNamePrefix.count() - moduleFileNameSuffix.count()); moduleInfo.qbsName.replace(QLatin1String("_private"), QLatin1String("-private")); QFile priFile(dit.filePath()); if (!priFile.open(QIODevice::ReadOnly)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: Cannot open " "file '%1' (%2).").arg(priFile.fileName(), priFile.errorString())); } const QByteArray priFileContents = priFile.readAll(); foreach (const QByteArray &line, priFileContents.split('\n')) { const QByteArray simplifiedLine = line.simplified(); const QList parts = simplifiedLine.split('='); if (parts.count() != 2 || parts.at(1).isEmpty()) continue; const QByteArray key = parts.first().simplified(); const QByteArray value = parts.last().simplified(); if (key.endsWith(".name")) { moduleInfo.name = QString::fromLocal8Bit(value); } else if (key.endsWith(".depends")) { moduleInfo.dependencies = QString::fromLocal8Bit(value).split(QLatin1Char(' ')); for (int i = 0; i < moduleInfo.dependencies.count(); ++i) { moduleInfo.dependencies[i].replace(QLatin1String("_private"), QLatin1String("-private")); } } else if (key.endsWith(".module_config")) { moduleInfo.hasLibrary = !value.contains("no_link"); } else if (key.endsWith(".includes")) { moduleInfo.includePaths = QString::fromLocal8Bit(value).split(QLatin1Char(' ')); for (int i = 0; i < moduleInfo.includePaths.count(); ++i) { moduleInfo.includePaths[i].replace( QLatin1String("$$QT_MODULE_INCLUDE_BASE"), qtEnvironment.includePath); } } } modules << moduleInfo; if (moduleInfo.qbsName == QLatin1String("testlib")) addTestModule(modules); if (moduleInfo.qbsName == QLatin1String("designercomponents-private")) addDesignerComponentsModule(modules); } } const QString profileBaseDir = QString::fromLocal8Bit("%1/qbs/profiles/%2") .arg(QFileInfo(settings->fileName()).dir().absolutePath(), profile.name()); const QString qbsQtModuleBaseDir = profileBaseDir + QLatin1String("/modules/Qt"); QString removeError; if (!qbs::Internal::removeDirectoryWithContents(qbsQtModuleBaseDir, &removeError)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: Could not remove " "the existing profile of the same name (%2).").arg(profile.name(), removeError)); } copyTemplateFile(QLatin1String("QtModule.qbs"), qbsQtModuleBaseDir, profile.name()); copyTemplateFile(QLatin1String("qtfunctions.js"), qbsQtModuleBaseDir, profile.name()); foreach (const QtModuleInfo &module, modules) { const QString qbsQtModuleDir = qbsQtModuleBaseDir + QLatin1Char('/') + module.qbsName; if (module.qbsName == QLatin1String("core")) { copyTemplateFile(QLatin1String("core.qbs"), qbsQtModuleDir, profile.name()); copyTemplateFile(QLatin1String("moc.js"), qbsQtModuleDir, profile.name()); } else if (module.qbsName == QLatin1String("gui")) { copyTemplateFile(QLatin1String("gui.qbs"), qbsQtModuleDir, profile.name()); } else { copyTemplateFile(QLatin1String("module.qbs"), qbsQtModuleDir, profile.name()); QFile moduleFile(qbsQtModuleDir + QLatin1String("/module.qbs")); if (!moduleFile.open(QIODevice::ReadWrite)) { throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: Cannot adapt " "module file '%2' (%3).") .arg(profile.name(), moduleFile.fileName(), moduleFile.errorString())); } QByteArray content = moduleFile.readAll(); content.replace("### name", '"' + module.name.mid(2).toUtf8() + '"'); // Strip off "Qt". content.replace("### has library", module.hasLibrary ? "true" : "false"); replaceListPlaceholder(content, "### dependencies", module.dependencies); replaceListPlaceholder(content, "### includes", module.includePaths); QByteArray propertiesString; if (module.qbsName == QLatin1String("declarative") || module.qbsName == QLatin1String("quick")) { const QByteArray debugMacro = module.qbsName == QLatin1String("declarative") || qtEnvironment.qtMajorVersion < 5 ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG"; propertiesString = "property bool qmlDebugging: false\n" " cpp.defines: " "qmlDebugging ? base.concat('" + debugMacro + "') : base"; } content.replace("### special properties", propertiesString); moduleFile.resize(0); moduleFile.write(content); } } profile.setValue(QLatin1String("preferences.qbsSearchPaths"), profileBaseDir); } static QString guessMinimumWindowsVersion(const QtEnvironment &qt) { if (qt.mkspecName.startsWith("winrt-")) return QLatin1String("6.2"); if (!qt.mkspecName.startsWith("win32-")) return QString(); if (qt.architecture == QLatin1String("x86_64") || qt.architecture == QLatin1String("ia64")) { return QLatin1String("5.2"); } QRegExp rex(QLatin1String("^win32-msvc(\\d+)$")); if (rex.exactMatch(qt.mkspecName)) { int msvcVersion = rex.cap(1).toInt(); if (msvcVersion < 2012) return QLatin1String("5.0"); else return QLatin1String("5.1"); } return qt.qtMajorVersion < 5 ? QLatin1String("5.0") : QLatin1String("5.1"); } static bool checkForStaticBuild(const QtEnvironment &qt) { if (qt.qtMajorVersion >= 5) return qt.qtConfigItems.contains(QLatin1String("static")); if (qt.frameworkBuild) return false; // there are no Qt4 static frameworks QDir libdir(qt.libraryPath); const QStringList coreLibFiles = libdir.entryList(QStringList(QLatin1String("*Core*")), QDir::Files); if (coreLibFiles.isEmpty()) throw ErrorInfo(Internal::Tr::tr("Could not determine whether Qt is a static build.")); foreach (const QString &fileName, coreLibFiles) { if (QLibrary::isLibrary(qt.libraryPath + QLatin1Char('/') + fileName)) return false; } return true; } void doSetupQtProfile(const QString &profileName, Settings *settings, const QtEnvironment &_qtEnvironment) { QtEnvironment qtEnvironment = _qtEnvironment; const bool staticBuild = checkForStaticBuild(qtEnvironment); // determine whether user apps require C++11 if (qtEnvironment.qtConfigItems.contains(QLatin1String("c++11")) && staticBuild) qtEnvironment.configItems.append(QLatin1String("c++11")); Profile profile(profileName, settings); profile.removeProfile(); const QString settingsTemplate(QLatin1String("Qt.core.%1")); profile.setValue(settingsTemplate.arg("config"), qtEnvironment.configItems); profile.setValue(settingsTemplate.arg("qtConfig"), qtEnvironment.qtConfigItems); profile.setValue(settingsTemplate.arg("binPath"), qtEnvironment.binaryPath); profile.setValue(settingsTemplate.arg("libPath"), qtEnvironment.libraryPath); profile.setValue(settingsTemplate.arg("pluginPath"), qtEnvironment.pluginPath); profile.setValue(settingsTemplate.arg("incPath"), qtEnvironment.includePath); profile.setValue(settingsTemplate.arg("mkspecPath"), qtEnvironment.mkspecPath); profile.setValue(settingsTemplate.arg("docPath"), qtEnvironment.documentationPath); profile.setValue(settingsTemplate.arg("version"), qtEnvironment.qtVersion); profile.setValue(settingsTemplate.arg("namespace"), qtEnvironment.qtNameSpace); profile.setValue(settingsTemplate.arg("libInfix"), qtEnvironment.qtLibInfix); profile.setValue(settingsTemplate.arg("buildVariant"), qtEnvironment.buildVariant); profile.setValue(settingsTemplate.arg(QLatin1String("staticBuild")), staticBuild); // Set the minimum operating system versions appropriate for this Qt version const QString windowsVersion = guessMinimumWindowsVersion(qtEnvironment); QString osxVersion, iosVersion, androidVersion; if (qtEnvironment.mkspecPath.contains("macx")) { profile.setValue(settingsTemplate.arg("frameworkBuild"), qtEnvironment.frameworkBuild); if (qtEnvironment.qtMajorVersion >= 5) { osxVersion = QLatin1String("10.6"); } else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 6) { QDir qconfigDir; if (qtEnvironment.frameworkBuild) { qconfigDir.setPath(qtEnvironment.libraryPath); qconfigDir.cd("QtCore.framework/Headers"); } else { qconfigDir.setPath(qtEnvironment.includePath); qconfigDir.cd("Qt"); } QFile qconfig(qconfigDir.absoluteFilePath("qconfig.h")); if (qconfig.open(QIODevice::ReadOnly)) { bool qtCocoaBuild = false; QTextStream ts(&qconfig); QString line; do { line = ts.readLine(); if (QRegExp(QLatin1String("\\s*#define\\s+QT_MAC_USE_COCOA\\s+1\\s*"), Qt::CaseSensitive).exactMatch(line)) { qtCocoaBuild = true; break; } } while (!line.isNull()); if (ts.status() == QTextStream::Ok) osxVersion = qtCocoaBuild ? QLatin1String("10.5") : QLatin1String("10.4"); } if (osxVersion.isEmpty()) { throw ErrorInfo(Internal::Tr::tr("Error reading qconfig.h; could not determine " "whether Qt is using Cocoa or Carbon")); } } if (qtEnvironment.configItems.contains("c++11")) osxVersion = QLatin1String("10.7"); } if (qtEnvironment.mkspecPath.contains("ios") && qtEnvironment.qtMajorVersion >= 5) iosVersion = QLatin1String("5.0"); if (qtEnvironment.mkspecPath.contains("android")) { if (qtEnvironment.qtMajorVersion >= 5) androidVersion = QLatin1String("2.3"); else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 8) androidVersion = QLatin1String("1.6"); // Necessitas } // ### TODO: wince, winphone, blackberry if (!windowsVersion.isEmpty()) profile.setValue(QLatin1String("cpp.minimumWindowsVersion"), windowsVersion); if (!osxVersion.isEmpty()) profile.setValue(QLatin1String("cpp.minimumOsxVersion"), osxVersion); if (!iosVersion.isEmpty()) profile.setValue(QLatin1String("cpp.minimumIosVersion"), iosVersion); if (!androidVersion.isEmpty()) profile.setValue(QLatin1String("cpp.minimumAndroidVersion"), androidVersion); createModules(profile, settings, qtEnvironment); } ErrorInfo setupQtProfile(const QString &profileName, Settings *settings, const QtEnvironment &qtEnvironment) { try { doSetupQtProfile(profileName, settings, qtEnvironment); return ErrorInfo(); } catch (const ErrorInfo &e) { return e; } } } // namespace qbs