aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/qtprofilesetup
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2014-01-10 17:16:32 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2014-01-16 16:51:48 +0100
commitf4b6d84baa16440812127099f7b03ec79f87bd2a (patch)
tree2f0e8a7959cc357684e887297faa4792b11facad /src/lib/qtprofilesetup
parent008992106eea60f0a63efc0978399c454fdab2ca (diff)
Move knowledge about Qt modules into libsetupqt.
In addition, we make less hard-coded assumptions than before, instead generating almost all modules dynamically from the respective Qt installation when setup-qt is being run. This way, qbs does not have to know about all the modules beforehand, and only the Qt modules that are actually present will have qbs counterparts, making it possible for project file authors to make use of soft dependencies for Qt modules. Task-number: QBS-479 Change-Id: Ie20d2acf249cd159ce4caff2ede4721ab879fad2 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/lib/qtprofilesetup')
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.cpp194
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.h1
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.pro1
-rw-r--r--src/lib/qtprofilesetup/qtprofilesetup.qbs5
-rw-r--r--src/lib/qtprofilesetup/templates.qrc11
-rw-r--r--src/lib/qtprofilesetup/templates/QtModule.qbs39
-rw-r--r--src/lib/qtprofilesetup/templates/core.qbs351
-rw-r--r--src/lib/qtprofilesetup/templates/gui.qbs45
-rw-r--r--src/lib/qtprofilesetup/templates/moc.js47
-rw-r--r--src/lib/qtprofilesetup/templates/module.qbs10
-rw-r--r--src/lib/qtprofilesetup/templates/qtfunctions.js28
-rw-r--r--src/lib/qtprofilesetup/templates/utils.js214
12 files changed, 945 insertions, 1 deletions
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp
index 0bc19d124..6bf191c36 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.cpp
+++ b/src/lib/qtprofilesetup/qtprofilesetup.cpp
@@ -31,16 +31,205 @@
#include <logging/translator.h>
#include <tools/error.h>
+#include <tools/fileinfo.h>
#include <tools/profile.h>
#include <tools/settings.h>
#include <QDir>
+#include <QDirIterator>
#include <QFile>
+#include <QFileInfo>
#include <QRegExp>
#include <QTextStream>
namespace qbs {
+struct QtModuleInfo
+{
+ QtModuleInfo(const QString &name, const QString &qbsName,
+ const QStringList &deps = QStringList(), bool hasLib = true)
+ : name(name), qbsName(qbsName), dependencies(deps), hasLibrary(hasLib)
+ {
+ 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);
+}
+
+static void createModules(Profile &profile, Settings *settings,
+ const QtEnvironment &qtEnvironment)
+{
+ QList<QtModuleInfo> modules;
+ if (qtEnvironment.qtMajorVersion < 5) {
+ modules // as per http://qt-project.org/doc/qt-4.8/modules.html
+ << QtModuleInfo(QLatin1String("QtCore"), QLatin1String("core"))
+ << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui"))
+ << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia"),
+ QStringList() << QLatin1String("gui") << QLatin1String("network"))
+ << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network"))
+ << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl"),
+ QStringList() << QLatin1String("gui"))
+ << QtModuleInfo(QLatin1String("QtOpenVG"), QLatin1String("openvg"),
+ QStringList() << QLatin1String("gui"))
+ << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script"))
+ << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttols"),
+ QStringList() << QLatin1String("script") << QLatin1String("gui"))
+ << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql"))
+ << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg"),
+ QStringList() << QLatin1String("gui"))
+ << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit"),
+ QStringList() << QLatin1String("gui") << QLatin1String("network"))
+ << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml"))
+ << QtModuleInfo(QLatin1String("QtXmlPatterns"), QLatin1String("xmlpatterns"),
+ QStringList() << "network")
+ << QtModuleInfo(QLatin1String("QtDeclarative"), QLatin1String("qtdeclarative"),
+ QStringList() << QLatin1String("gui") << QLatin1String("script"))
+ << QtModuleInfo(QLatin1String("Phonon"), QLatin1String("phonon"))
+ << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer"),
+ QStringList() << QLatin1String("gui") << QLatin1String("xml"))
+ << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools"))
+ << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help"),
+ QStringList() << QLatin1String("network") << QLatin1String("sql"))
+ << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib"))
+ << QtModuleInfo(QLatin1String("QAxContainer"), QLatin1String("axcontainer"))
+ << QtModuleInfo(QLatin1String("QAxServer"), QLatin1String("axserver"))
+ << QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus"));
+ } 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<QByteArray> parts = simplifiedLine.split('=');
+ if (parts.count() != 2)
+ 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") && value.contains("no_link")) {
+ moduleInfo.hasLibrary = false;
+ } 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;
+ }
+ }
+
+ 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(removeError));
+ }
+ copyTemplateFile(QLatin1String("QtModule.qbs"), qbsQtModuleBaseDir, profile.name());
+ copyTemplateFile(QLatin1String("qtfunctions.js"), qbsQtModuleBaseDir, profile.name());
+ copyTemplateFile(QLatin1String("utils.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")) {
+ propertiesString = "property bool qmlDebugging: false";
+ }
+ 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-"))
@@ -156,6 +345,11 @@ ErrorInfo setupQtProfile(const QString &profileName, Settings *settings,
if (!androidVersion.isEmpty())
profile.setValue(QLatin1String("cpp.minimumAndroidVersion"), androidVersion);
+ try {
+ createModules(profile, settings, qtEnvironment);
+ } catch (const ErrorInfo &e) {
+ return e;
+ }
return ErrorInfo();
}
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.h b/src/lib/qtprofilesetup/qtprofilesetup.h
index 8d2e04568..df0c1fb1c 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.h
+++ b/src/lib/qtprofilesetup/qtprofilesetup.h
@@ -51,6 +51,7 @@ public:
QString qtNameSpace;
QString mkspecPath;
QString mkspecName;
+ QString mkspecBasePath;
QStringList buildVariant;
QStringList configItems;
QStringList qtConfigItems;
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.pro b/src/lib/qtprofilesetup/qtprofilesetup.pro
index cedb66e2a..0856d1ed5 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.pro
+++ b/src/lib/qtprofilesetup/qtprofilesetup.pro
@@ -4,6 +4,7 @@ include(../corelib/use_corelib.pri)
HEADERS = qtprofilesetup.h
SOURCES = qtprofilesetup.cpp
+RESOURCES = templates.qrc
!qbs_no_dev_install {
header.files = qtprofilesetup.h
diff --git a/src/lib/qtprofilesetup/qtprofilesetup.qbs b/src/lib/qtprofilesetup/qtprofilesetup.qbs
index 59d2595c3..00b3fe10e 100644
--- a/src/lib/qtprofilesetup/qtprofilesetup.qbs
+++ b/src/lib/qtprofilesetup/qtprofilesetup.qbs
@@ -15,7 +15,10 @@ QbsLibrary {
qbs.installDir: headerInstallPrefix
}
- files: "qtprofilesetup.cpp"
+ files: [
+ "qtprofilesetup.cpp",
+ "templates.qrc"
+ ]
Export {
Depends { name: "qbscore" }
diff --git a/src/lib/qtprofilesetup/templates.qrc b/src/lib/qtprofilesetup/templates.qrc
new file mode 100644
index 000000000..7cb420320
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates.qrc
@@ -0,0 +1,11 @@
+<RCC>
+ <qresource prefix="/">
+ <file>templates/core.qbs</file>
+ <file>templates/gui.qbs</file>
+ <file>templates/module.qbs</file>
+ <file>templates/QtModule.qbs</file>
+ <file>templates/moc.js</file>
+ <file>templates/qtfunctions.js</file>
+ <file>templates/utils.js</file>
+ </qresource>
+</RCC>
diff --git a/src/lib/qtprofilesetup/templates/QtModule.qbs b/src/lib/qtprofilesetup/templates/QtModule.qbs
new file mode 100644
index 000000000..4694dbcb1
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/QtModule.qbs
@@ -0,0 +1,39 @@
+import qbs 1.0
+import qbs.FileInfo
+import 'qtfunctions.js' as QtFunctions
+
+Module {
+ Depends { name: "cpp" }
+ Depends { name: "Qt.core" }
+
+ property string qtModuleName
+ property path binPath: Qt.core.binPath
+ property path incPath: Qt.core.incPath
+ property path libPath: Qt.core.libPath
+ property string qtLibInfix: Qt.core.libInfix
+ property string repository: Qt.core.versionMajor === 5 ? 'qtbase' : undefined
+ property string includeDirName: 'Qt' + qtModuleName
+ property string internalLibraryName: QtFunctions.getQtLibraryName(qtModuleName + qtLibInfix, Qt.core, qbs)
+ property string qtVersion: Qt.core.version
+ property bool hasLibrary: true
+
+ Properties {
+ condition: qtModuleName != undefined
+
+ cpp.includePaths: {
+ var modulePath = FileInfo.joinPaths(incPath, includeDirName);
+ var paths = [incPath, modulePath];
+ if (Qt.core.versionMajor >= 5)
+ paths.unshift(FileInfo.joinPaths(modulePath, qtVersion, includeDirName));
+ if (Qt.core.frameworkBuild)
+ paths.unshift(libPath + '/' + includeDirName + qtLibInfix + '.framework/Versions/' + Qt.core.versionMajor + '/Headers');
+ return paths;
+ }
+
+ cpp.dynamicLibraries: Qt.core.frameworkBuild || !hasLibrary
+ ? undefined : [internalLibraryName]
+ cpp.frameworks: Qt.core.frameworkBuild && hasLibrary ? [internalLibraryName] : undefined
+ cpp.defines: qtModuleName.contains("private")
+ ? [] : [ "QT_" + qtModuleName.toUpperCase() + "_LIB" ]
+ }
+}
diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs
new file mode 100644
index 000000000..33c2ba856
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/core.qbs
@@ -0,0 +1,351 @@
+import qbs 1.0
+import qbs.FileInfo
+import "moc.js" as Moc
+import '../qtfunctions.js' as QtFunctions
+import '../utils.js' as ModUtils
+
+Module {
+ id: qtcore
+
+ Depends { name: "cpp" }
+
+ property string namespace
+ property string libInfix: ""
+ property string repository: versionMajor === 5 ? "qtbase" : undefined
+ property stringList config
+ property stringList qtConfig
+ property path binPath
+ property path incPath
+ property path libPath
+ property path pluginPath
+ property path mkspecPath
+ property string mocName: "moc"
+ property string lreleaseName: "lrelease"
+ property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3"
+ property stringList qdocEnvironment
+ property string qdocQhpFileName
+ property path docPath
+ property stringList helpGeneratorArgs: versionMajor >= 5 ? ["-platform", "minimal"] : []
+ property string version
+ property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : []
+ property int versionMajor: versionParts[0]
+ property int versionMinor: versionParts[1]
+ property int versionPatch: versionParts[2]
+ property bool frameworkBuild
+ property bool staticBuild
+ property stringList buildVariant
+
+ // These are deliberately not path types
+ // We don't want to resolve them against the source directory
+ property string generatedFilesDir: "GeneratedFiles/" + product.name
+ property string qmFilesDir: product.destinationDirectory
+
+ // private properties
+ property string libraryInfix: cpp.debugInformation ? 'd' : ''
+
+ cpp.defines: {
+ var defines = ["QT_CORE_LIB"];
+ // ### QT_NO_DEBUG must be added if the current build variant is derived
+ // from the build variant "release"
+ if (!qbs.debugInformation)
+ defines.push("QT_NO_DEBUG");
+ if (namespace)
+ defines.push("QT_NAMESPACE=" + namespace);
+ if (qbs.targetOS.contains("ios"))
+ defines = defines.concat(["DARWIN_NO_CARBON", "QT_NO_CORESERVICES", "QT_NO_PRINTER",
+ "QT_NO_PRINTDIALOG", "main=qt_main"]);
+ return defines;
+ }
+ cpp.includePaths: {
+ var paths = [mkspecPath];
+ if (frameworkBuild)
+ paths.push(libPath + '/QtCore' + libInfix + '.framework/Versions/' + versionMajor + '/Headers');
+ paths.push(incPath + '/QtCore');
+ paths.push(incPath);
+ paths.push(product.buildDirectory + '/' + generatedFilesDir);
+ return paths;
+ }
+ cpp.libraryPaths: {
+ var libPaths = [libPath];
+ if (staticBuild && pluginPath)
+ libPaths.push(pluginPath + "/platforms");
+ return libPaths;
+ }
+ cpp.staticLibraries: {
+ if (qbs.targetOS.contains('windows') && !product.consoleApplication)
+ return ["qtmain" + libInfix + (cpp.debugInformation ? "d" : "") + (!qbs.toolchain.contains("mingw") ? ".lib" : "")];
+ }
+ cpp.dynamicLibraries: {
+ var libs = [];
+ if (!frameworkBuild)
+ libs=[QtFunctions.getQtLibraryName('Core' + libInfix, qtcore, qbs)];
+ if (qbs.targetOS.contains('ios') && staticBuild)
+ libs = libs.concat(["z", "m",
+ QtFunctions.getQtLibraryName("PlatformSupport", qtcore, qbs)]);
+ if (libs.length === 0)
+ return undefined;
+ return libs;
+ }
+ cpp.linkerFlags: ((qbs.targetOS.contains('ios') && staticBuild) ?
+ ["-force_load", pluginPath + "/platforms/" +
+ QtFunctions.getPlatformLibraryName("libqios", qtcore, qbs) + ".a"] : undefined)
+ cpp.frameworkPaths: frameworkBuild ? [libPath] : undefined
+ cpp.frameworks: {
+ var frameworks = [];
+ if (frameworkBuild)
+ frameworks = [QtFunctions.getQtLibraryName('Core' + libInfix, qtcore, qbs)]
+ if (qbs.targetOS.contains('ios') && staticBuild)
+ frameworks = frameworks.concat(["Foundation", "CoreFoundation"]);
+ if (frameworks.length === 0)
+ return undefined;
+ return frameworks;
+ }
+ cpp.rpaths: qbs.targetOS.contains('linux') ? [libPath] : undefined
+ cpp.positionIndependentCode: versionMajor >= 5 ? true : undefined
+ cpp.cxxFlags: {
+ var flags;
+ if (qbs.toolchain.contains('msvc')) {
+ flags = ['/Zm200'];
+ if (versionMajor < 5)
+ flags.push('/Zc:wchar_t-');
+ }
+ if (qbs.toolchain.contains('clang') && config.contains('c++11'))
+ flags.push('-stdlib=libc++');
+ return flags;
+ }
+
+ additionalProductFileTags: ["qm"]
+
+ validate: {
+ var requiredProperties = {
+ "binPath": binPath,
+ "incPath": incPath,
+ "libPath": libPath,
+ "mkspecPath": mkspecPath,
+ "version": version,
+ "config": config,
+ "qtConfig": qtConfig,
+ // Validate these in case 'version' is in some non-standard format
+ "versionMajor": versionMajor,
+ "versionMinor": versionMinor,
+ "versionPatch": versionPatch
+ };
+
+ if (!staticBuild) {
+ requiredProperties["pluginPath"] = pluginPath;
+ }
+
+ var missingProperties = [];
+ for (var i in requiredProperties) {
+ if (requiredProperties[i] === undefined) {
+ missingProperties.push("Qt.core." + i);
+ }
+ }
+
+ var invalidProperties = {};
+ if (versionMajor <= 0)
+ invalidProperties["versionMajor"] = "must be > 0";
+ if (versionMinor < 0)
+ invalidProperties["versionMinor"] = "must be >= 0";
+ if (versionPatch < 0)
+ invalidProperties["versionPatch"] = "must be >= 0";
+
+ var errorMessage = "";
+ if (missingProperties.length > 0) {
+ errorMessage += "The following Qt module properties are not set. " +
+ "Set them in your profile:\n" +
+ missingProperties.sort().join("\n");
+ }
+
+ if (Object.keys(invalidProperties).length > 0) {
+ errorMessage += "The following Qt module properties have invalid values:\n" +
+ Object.map(invalidProperties,
+ function(msg, prop) { return prop + ": " + msg; }).join("\n");
+ }
+
+ if (errorMessage.length > 0)
+ throw errorMessage;
+ }
+
+ setupRunEnvironment: {
+ if (qbs.targetOS.contains('windows')) {
+ var v = getEnv('PATH') || '';
+ if (v.length > 0 && v.charAt(0) != ';')
+ v = ';' + v;
+ v = FileInfo.toWindowsSeparators(binPath) + v;
+ putEnv('PATH', v);
+ }
+ }
+
+ FileTagger {
+ patterns: ["*.qrc"]
+ fileTags: ["qrc"]
+ }
+
+ FileTagger {
+ patterns: ["*.ts"]
+ fileTags: ["ts"]
+ }
+
+ FileTagger {
+ patterns: ["*.qdoc"]
+ fileTags: ["qdoc"]
+ }
+
+ FileTagger {
+ patterns: ["*.qdocconf"]
+ fileTags: ["qdocconf"]
+ }
+
+ FileTagger {
+ patterns: ["*.qhp"]
+ fileTags: ["qhp"]
+ }
+
+ Rule {
+ inputs: ["moc_cpp"]
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
+ + '/' + input.completeBaseName + ".moc"
+ fileTags: ["hpp"]
+ }
+
+ prepare: {
+ var cmd = new Command(Moc.fullPath(product),
+ Moc.args(product, input, output.fileName));
+ cmd.description = 'moc ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["moc_hpp"]
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
+ + "/moc_" + input.completeBaseName + ".cpp"
+ fileTags: [ "cpp" ]
+ }
+
+ prepare: {
+ var cmd = new Command(Moc.fullPath(product),
+ Moc.args(product, input, output.fileName));
+ cmd.description = 'moc ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["moc_hpp_inc"]
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
+ + "/moc_" + input.completeBaseName + ".cpp"
+ fileTags: [ "hpp" ]
+ }
+
+ prepare: {
+ var cmd = new Command(Moc.fullPath(product),
+ Moc.args(product, input, output.fileName));
+ cmd.description = 'moc ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["qrc"]
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
+ + "/qrc_" + input.completeBaseName + ".cpp";
+ fileTags: ["cpp"]
+ }
+ prepare: {
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/rcc',
+ [input.fileName, '-name',
+ FileInfo.completeBaseName(input.fileName),
+ '-o', output.fileName]);
+ cmd.description = 'rcc ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["ts"]
+
+ Artifact {
+ fileName: FileInfo.joinPaths(ModUtils.moduleProperty(product, "qmFilesDir"),
+ input.completeBaseName + ".qm")
+ fileTags: ["qm"]
+ }
+
+ prepare: {
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ + ModUtils.moduleProperty(product, "lreleaseName"),
+ ['-silent', input.fileName, '-qm', output.fileName]);
+ cmd.description = 'lrelease ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'filegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: "qdocconf-main"
+ explicitlyDependsOn: ["qdoc", "qdocconf"]
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir") + "/html"
+ fileTags: ["qdoc-html"]
+ }
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir") + "/html/"
+ + ModUtils.moduleProperty(product, "qdocQhpFileName")
+ fileTags: ["qhp"]
+ }
+
+ prepare: {
+ var outputDir = outputs["qdoc-html"][0].fileName;
+ var args = [input.fileName];
+ var qtVersion = ModUtils.moduleProperty(product, "versionMajor");
+ if (qtVersion >= 5) {
+ args.push("-outputdir");
+ args.push(outputDir);
+ }
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ + ModUtils.moduleProperty(product, "qdocName"), args);
+ cmd.description = 'qdoc ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'filegen';
+ cmd.environment = ModUtils.moduleProperty(product, "qdocEnvironment");
+ cmd.environment.push("OUTDIR=" + outputDir); // Qt 4 replacement for -outputdir
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: "qhp"
+
+ Artifact {
+ fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
+ + '/' + input.completeBaseName + ".qch"
+ fileTags: ["qch"]
+ }
+
+ prepare: {
+ var args = [input.fileName];
+ args = args.concat(ModUtils.moduleProperty(product, "helpGeneratorArgs"));
+ args.push("-o");
+ args.push(output.fileName);
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + "/qhelpgenerator",
+ args);
+ cmd.description = 'qhelpgenerator ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'filegen';
+ return cmd;
+ }
+ }
+}
diff --git a/src/lib/qtprofilesetup/templates/gui.qbs b/src/lib/qtprofilesetup/templates/gui.qbs
new file mode 100644
index 000000000..24b8f4b59
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/gui.qbs
@@ -0,0 +1,45 @@
+import qbs 1.0
+import qbs.FileInfo
+import '../QtModule.qbs' as QtModule
+import '../utils.js' as ModUtils
+
+QtModule {
+ qtModuleName: "Gui"
+
+ property string uicName: "uic"
+
+ FileTagger {
+ patterns: ["*.ui"]
+ fileTags: ["ui"]
+ }
+
+ Rule {
+ inputs: ["ui"]
+
+ Artifact {
+// ### TODO we want to access the module's property "Qt.core.generatedFilesDir" here. But without evaluating all available properties a priori.
+ fileName: 'GeneratedFiles/' + product.name + '/ui_' + input.completeBaseName + '.h'
+ fileTags: ["hpp"]
+ }
+
+ prepare: {
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ + ModUtils.moduleProperty(product, "uicName"),
+ [input.fileName, '-o', output.fileName])
+ cmd.description = 'uic ' + FileInfo.fileName(input.fileName);
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ cpp.includePaths: Qt.core.qtConfig.contains("angle")
+ ? base.concat([FileInfo.joinPaths(incPath, "QtANGLE")]) : base
+
+ Properties {
+ condition: Qt.core.staticBuild && qbs.targetOS.contains("ios")
+ cpp.frameworks: base.concat(["UIKit", "QuartzCore", "CoreText", "CoreGraphics",
+ "Foundation", "CoreFoundation"])
+ }
+ cpp.frameworks: base
+}
+
diff --git a/src/lib/qtprofilesetup/templates/moc.js b/src/lib/qtprofilesetup/templates/moc.js
new file mode 100644
index 000000000..e2f278491
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/moc.js
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+function args(product, input, outputFileName)
+{
+ var defines = ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'compilerDefines');
+ defines = defines.concat(ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'platformDefines'));
+ defines = defines.concat(ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'defines'));
+ var includePaths = ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'includePaths');
+ var args = [];
+ args = args.concat(
+ defines.map(function(item) { return '-D' + item; }),
+ includePaths.map(function(item) { return '-I' + item; }),
+ '-o', outputFileName,
+ input.fileName);
+ return args;
+}
+
+function fullPath(product)
+{
+ return ModUtils.moduleProperty(product, "binPath") + '/' + ModUtils.moduleProperty(product, "mocName");
+}
diff --git a/src/lib/qtprofilesetup/templates/module.qbs b/src/lib/qtprofilesetup/templates/module.qbs
new file mode 100644
index 000000000..0d6b87c2b
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/module.qbs
@@ -0,0 +1,10 @@
+import qbs 1.0
+import '../QtModule.qbs' as QtModule
+
+QtModule {
+ qtModuleName: ### name
+ Depends { name: "Qt"; submodules: [### dependencies] }
+ hasLibrary: ### has library
+ cpp.includePaths: base.concat([### includes])
+ ### special properties
+}
diff --git a/src/lib/qtprofilesetup/templates/qtfunctions.js b/src/lib/qtprofilesetup/templates/qtfunctions.js
new file mode 100644
index 000000000..f50f847b4
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/qtfunctions.js
@@ -0,0 +1,28 @@
+// helper functions for the Qt modules
+
+function getPlatformLibraryName(name, qtcore, qbs)
+{
+ var libName = name;
+ if (qbs.targetOS.contains('windows')) {
+ libName += (qbs.enableDebugCode ? 'd' : '');
+ if (qtcore.versionMajor < 5)
+ libName += qtcore.versionMajor;
+ if (!qbs.toolchain.contains("mingw"))
+ libName += '.lib';
+ }
+ if (qbs.targetOS.contains("darwin")) {
+ if (!qtcore.frameworkBuild && qtcore.buildVariant.contains("debug") &&
+ (!qtcore.buildVariant.contains("release") || qbs.enableDebugCode))
+ libName += '_debug';
+ }
+ return libName;
+}
+
+function getQtLibraryName(qtModule, qtcore, qbs)
+{
+ var libName = "Qt";
+ if (qtcore.versionMajor >= 5 && !qtcore.frameworkBuild)
+ libName += qtcore.versionMajor;
+ libName += qtModule;
+ return getPlatformLibraryName(libName, qtcore, qbs);
+}
diff --git a/src/lib/qtprofilesetup/templates/utils.js b/src/lib/qtprofilesetup/templates/utils.js
new file mode 100644
index 000000000..66f7c30bf
--- /dev/null
+++ b/src/lib/qtprofilesetup/templates/utils.js
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+//
+// utility functions for modules
+//
+
+/*!
+ * Given a list of file tags, returns the file tag (one of [c, cpp, objc, objcpp])
+ * corresponding to the C-family language the file should be compiled as.
+ *
+ * If no such tag is found, undefined is returned. If more than one match is
+ * found, an exception is thrown.
+ */
+function fileTagForTargetLanguage(fileTags)
+{
+ var srcTags = ["c", "cpp", "objc", "objcpp", "asm", "asm_cpp"];
+ var pchTags = ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"];
+
+ var canonicalTag = undefined;
+ var foundTagCount = 0;
+ for (var i = 0; i < fileTags.length; ++i) {
+ var idx = srcTags.indexOf(fileTags[i]);
+ if (idx === -1)
+ idx = pchTags.indexOf(fileTags[i]);
+
+ if (idx !== -1) {
+ canonicalTag = srcTags[idx];
+ if (++foundTagCount > 1)
+ break;
+ }
+ }
+
+ if (foundTagCount > 1)
+ throw ("source files cannot be identified as more than one language");
+
+ return foundTagCount == 1 ? canonicalTag : undefined;
+}
+
+/*
+ * Returns the name of a language-specific property given the file tag
+ * for that property, and the base property name.
+ *
+ * If \a fileTag is undefined, the language-agnostic property name is returned.
+ *
+ * \param propertyName flags, platformFlags, precompiledHeader
+ * \param fileTag c, cpp, objc, objcpp
+ */
+function languagePropertyName(propertyName, fileTag)
+{
+ if (!fileTag)
+ fileTag = 'common';
+
+ var map = {
+ 'c': {
+ 'flags': 'cFlags',
+ 'platformFlags': 'platformCFlags',
+ 'precompiledHeader': 'cPrecompiledHeader'
+ },
+ 'cpp': {
+ 'flags': 'cxxFlags',
+ 'platformFlags': 'platformCxxFlags',
+ 'precompiledHeader': 'cxxPrecompiledHeader'
+ },
+ 'objc': {
+ 'flags': 'objcFlags',
+ 'platformFlags': 'platformObjcFlags',
+ 'precompiledHeader': 'objcPrecompiledHeader'
+ },
+ 'objcpp': {
+ 'flags': 'objcxxFlags',
+ 'platformFlags': 'platformObjcxxFlags',
+ 'precompiledHeader': 'objcxxPrecompiledHeader'
+ },
+ 'common': {
+ 'flags': 'commonCompilerFlags',
+ 'platformFlags': 'platformCommonCompilerFlags',
+ 'precompiledHeader': 'precompiledHeader'
+ }
+ };
+
+ var lang = map[fileTag];
+ if (!lang)
+ return propertyName;
+
+ return lang[propertyName] || propertyName;
+}
+
+function moduleProperties(config, key, langFilter)
+{
+ return config.moduleProperties(config.moduleName, languagePropertyName(key, langFilter))
+}
+
+function modulePropertiesFromArtifacts(product, artifacts, moduleName, propertyName, langFilter)
+{
+ var result = product.moduleProperties(moduleName, languagePropertyName(propertyName, langFilter))
+ for (var i in artifacts)
+ result = result.concat(artifacts[i].moduleProperties(moduleName, languagePropertyName(propertyName, langFilter)))
+ return result
+}
+
+function moduleProperty(product, propertyName, langFilter)
+{
+ return product.moduleProperty(product.moduleName, languagePropertyName(propertyName, langFilter))
+}
+
+function dumpProperty(key, value, level)
+{
+ var indent = ''
+ for (var k=0; k < level; ++k)
+ indent += ' '
+ print(indent + key + ': ' + value)
+}
+
+function traverseObject(obj, func, level)
+{
+ if (!level)
+ level = 0
+ var i, children = {}
+ for (i in obj) {
+ if (typeof(obj[i]) === "object" && !(obj[i] instanceof Array))
+ children[i] = obj[i]
+ else
+ func.apply(this, [i, obj[i], level])
+ }
+ level++
+ for (i in children) {
+ func.apply(this, [i, children[i], level - 1])
+ traverseObject(children[i], func, level)
+ }
+ level--
+}
+
+function dumpObject(obj, description)
+{
+ if (!description)
+ description = 'object dump'
+ print('+++++++++ ' + description + ' +++++++++')
+ traverseObject(obj, dumpProperty)
+}
+
+function uniqueConcat(array1, array2)
+{
+ var result = array1;
+ for (i in array2) {
+ var elem = array2[i];
+ if (result.indexOf(elem) === -1)
+ result.push(elem);
+ }
+ return result;
+}
+
+
+//////////////////////////////////////////////////////////
+// The EnvironmentVariable class
+//
+function EnvironmentVariable(name, separator, convertPathSeparators)
+{
+ if (!name)
+ throw "EnvironmentVariable c'tor needs a name as first argument."
+ this.name = name
+ this.value = getEnv(name).toString()
+ this.separator = separator || ''
+ this.convertPathSeparators = convertPathSeparators || false
+}
+
+EnvironmentVariable.prototype.prepend = function(v)
+{
+ if (this.value.length > 0 && this.value.charAt(0) !== this.separator)
+ this.value = this.separator + this.value
+ if (this.convertPathSeparators)
+ v = FileInfo.toWindowsSeparators(v)
+ this.value = v + this.value
+}
+
+EnvironmentVariable.prototype.append = function(v)
+{
+ if (this.value.length > 0)
+ this.value += this.separator
+ if (this.convertPathSeparators)
+ v = FileInfo.toWindowsSeparators(v)
+ this.value += v
+}
+
+EnvironmentVariable.prototype.set = function()
+{
+ putEnv(this.name, this.value)
+}
+