aboutsummaryrefslogtreecommitdiffstats
path: root/share
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2019-01-18 16:02:43 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2019-02-12 09:12:39 +0000
commitc4e60ed8283aa7a86e13c09113e7fec6bf41cc42 (patch)
tree6cefbf3ece17e255c3962e69573abc88bef060ef /share
parent17058d1fc537e40e7dda9d6e48ccfb24ea1220f7 (diff)
Detect Qt via a module provider
Creation of qbs modules for Qt is now done on demand during project resolving. The qmake executable(s) are looked up via PATH or taken from the Qt.qmakeFilePaths provider property. As a result, Qt projects can now be built without a profile. The qtprofilesetup library is gone; its code is now in the module provider. I kept the C++ -> JavaScript conversion as straightforward as possible and mostly resisted the temptation to "optimize". The setup-qt tool still exists and mainly sets Qt.qmakeFilePaths. [ChangeLog] It is no longer required to call setup-qt before building Qt projects. Change-Id: I5b7e4711ec47b996911c499f29d8129d90e4731e Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'share')
-rw-r--r--share/qbs/module-providers/Qt/provider.qbs6
-rw-r--r--share/qbs/module-providers/Qt/setup-qt.js1504
-rw-r--r--share/qbs/module-providers/Qt/templates/QtModule.qbs86
-rw-r--r--share/qbs/module-providers/Qt/templates/QtPlugin.qbs49
-rw-r--r--share/qbs/module-providers/Qt/templates/android_support.qbs291
-rw-r--r--share/qbs/module-providers/Qt/templates/core.qbs510
-rw-r--r--share/qbs/module-providers/Qt/templates/dbus.js61
-rw-r--r--share/qbs/module-providers/Qt/templates/dbus.qbs74
-rw-r--r--share/qbs/module-providers/Qt/templates/gui.qbs65
-rw-r--r--share/qbs/module-providers/Qt/templates/moc.js102
-rw-r--r--share/qbs/module-providers/Qt/templates/module.qbs30
-rw-r--r--share/qbs/module-providers/Qt/templates/plugin.qbs27
-rw-r--r--share/qbs/module-providers/Qt/templates/plugin_support.qbs75
-rw-r--r--share/qbs/module-providers/Qt/templates/qdoc.js84
-rw-r--r--share/qbs/module-providers/Qt/templates/qml.js66
-rw-r--r--share/qbs/module-providers/Qt/templates/qml.qbs132
-rw-r--r--share/qbs/module-providers/Qt/templates/qmlcache.qbs85
-rw-r--r--share/qbs/module-providers/Qt/templates/quick.js84
-rw-r--r--share/qbs/module-providers/Qt/templates/quick.qbs211
-rw-r--r--share/qbs/module-providers/Qt/templates/scxml.qbs80
20 files changed, 3622 insertions, 0 deletions
diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs
new file mode 100644
index 000000000..33083c51d
--- /dev/null
+++ b/share/qbs/module-providers/Qt/provider.qbs
@@ -0,0 +1,6 @@
+import "setup-qt.js" as SetupQt
+
+ModuleProvider {
+ property stringList qmakeFilePaths
+ relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path, qbs)
+}
diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js
new file mode 100644
index 000000000..0f0755409
--- /dev/null
+++ b/share/qbs/module-providers/Qt/setup-qt.js
@@ -0,0 +1,1504 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var Environment = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var Process = require("qbs.Process");
+var TextFile = require("qbs.TextFile");
+var Utilities = require("qbs.Utilities");
+
+function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); }
+function toNative(p) { return FileInfo.toNativeSeparators(p); }
+function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; }
+
+function getQmakeFilePaths(qmakeFilePaths, qbs) {
+ if (qmakeFilePaths && qmakeFilePaths.length > 0)
+ return qmakeFilePaths;
+ console.info("Detecting Qt installations...");
+ var pathValue = Environment.getEnv("PATH");
+ if (!pathValue)
+ return [];
+ var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator);
+ var suffix = exeSuffix(qbs);
+ var filePaths = [];
+ for (var i = 0; i < dirs.length; ++i) {
+ var candidate = FileInfo.canonicalPath(FileInfo.joinPaths(dirs[i], "qmake" + suffix));
+ if (candidate && File.exists(candidate) && !filePaths.contains(candidate)) {
+ console.info("Found Qt at '" + toNative(candidate) + "'.");
+ filePaths.push(candidate);
+ }
+ }
+ return filePaths;
+}
+
+function queryQmake(qmakeFilePath) {
+ var qmakeProcess = new Process;
+ qmakeProcess.exec(qmakeFilePath, ["-query"]);
+ var queryResult = {};
+ while (!qmakeProcess.atEnd()) {
+ var line = qmakeProcess.readLine();
+ var index = (line || "").indexOf(":");
+ if (index !== -1)
+ queryResult[line.slice(0, index)] = line.slice(index + 1).trim();
+ }
+ return queryResult;
+}
+
+function pathQueryValue(queryResult, key) {
+ var p = queryResult[key];
+ if (p)
+ return FileInfo.fromNativeSeparators(p);
+}
+
+function readFileContent(filePath) {
+ var f = new TextFile(filePath, TextFile.ReadOnly);
+ var content = f.readAll();
+ f.close();
+ return content;
+}
+
+// TODO: Don't do the split every time...
+function configVariable(configContent, key) {
+ var configContentLines = configContent.split('\n');
+ var regexp = new RegExp("\\s*" + key + "\\s*\\+{0,1}=(.*)");
+ for (var i = 0; i < configContentLines.length; ++i) {
+ var line = configContentLines[i];
+ var match = regexp.exec(line);
+ if (match)
+ return match[1].trim();
+ }
+}
+
+function configVariableItems(configContent, key) {
+ return splitNonEmpty(configVariable(configContent, key), ' ');
+}
+
+function msvcPrefix() { return "win32-msvc"; }
+
+function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); }
+
+function msvcCompilerVersionForYear(year) {
+ var mapping = {
+ "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19",
+ "2017": "19.1"
+ };
+ return mapping[year];
+}
+
+function msvcCompilerVersionFromMkspecName(mkspecName) {
+ return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length));
+}
+
+function addQtBuildVariant(qtProps, buildVariantName) {
+ if (qtProps.qtConfigItems.contains(buildVariantName))
+ qtProps.buildVariant.push(buildVariantName);
+}
+
+function checkForStaticBuild(qtProps) {
+ if (qtProps.qtMajorVersion >= 5)
+ return qtProps.qtConfigItems.contains("static");
+ if (qtProps.frameworkBuild)
+ return false; // there are no Qt4 static frameworks
+ var isWin = qtProps.mkspecName.startsWith("win");
+ var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath;
+ var coreLibFiles = File.directoryEntries(libDir, File.Files)
+ .filter(function(fp) { return fp.contains("Core"); });
+ if (coreLibFiles.length === 0)
+ throw "Could not determine whether Qt is a static build.";
+ for (var i = 0; i < coreLibFiles.length; ++i) {
+ if (Utilities.isSharedLibrary(coreLibFiles[i]))
+ return false;
+ }
+ return true;
+}
+
+function isForMinGw(qtProps) {
+ return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw");
+}
+
+function targetsDesktopWindows(qtProps) {
+ return qtProps.mkspecName.startsWith("win32-") || isForMinGw(qtProps);
+}
+
+function guessMinimumWindowsVersion(qtProps) {
+ if (qtProps.mkspecName.startsWith("winrt-"))
+ return "10.0";
+ if (!targetsDesktopWindows(qtProps))
+ return "";
+ if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64")
+ return "5.2"
+ var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/);
+ if (match) {
+ var msvcVersion = match[1];
+ if (msvcVersion < 2012)
+ return "5.0";
+ return "5.1";
+ }
+ return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1";
+}
+
+function fillEntryPointLibs(qtProps, debug) {
+ result = [];
+ var isMinGW = isForMinGw(qtProps);
+
+ // Some Linux distributions rename the qtmain library.
+ var qtMainCandidates = ["qtmain"];
+ if (isMinGW && qtProps.qtMajorVersion === 5)
+ qtMainCandidates.push("qt5main");
+
+ for (var i = 0; i < qtMainCandidates.length; ++i) {
+ var baseNameCandidate = qtMainCandidates[i];
+ var qtmain = qtProps.libraryPath + '/';
+ if (isMinGW)
+ qtmain += "lib";
+ qtmain += baseNameCandidate + qtProps.qtLibInfix;
+ if (debug)
+ qtmain += 'd';
+ if (isMinGW) {
+ qtmain += ".a";
+ } else {
+ qtmain += ".lib";
+ if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0)
+ result.push("Shell32.lib");
+ }
+ if (File.exists(qtmain)) {
+ result.push(qtmain);
+ break;
+ }
+ }
+ if (result.length === 0) {
+ console.warn("Could not find the qtmain library at '" + toNative(qtProps.libraryPath)
+ + "'. You will not be able to link Qt applications.");
+ }
+ return result;
+}
+
+function getQtProperties(qmakeFilePath, qbs) {
+ var queryResult = queryQmake(qmakeFilePath);
+ var qtProps = {};
+ qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX");
+ qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
+ qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS");
+ qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS");
+ qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS")
+ || pathQueryValue(queryResult, "QT_INSTALL_BINS");
+ qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
+ qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS");
+ qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML");
+ qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS");
+ qtProps.qtVersion = queryResult.QT_VERSION;
+
+ var mkspecsBaseSrcPath;
+ if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) {
+ qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"),
+ "mkspecs");
+ mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"),
+ "mkspecs");
+ } else {
+ qtProps.mkspecBasePath = FileInfo.joinPaths
+ (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs");
+ }
+ if (!File.exists(qtProps.mkspecBasePath))
+ throw "Cannot extract the mkspecs directory.";
+
+ var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath,
+ "qconfig.pri"));
+ qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION"));
+ qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION"));
+ qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION"));
+ qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE");
+ qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || "";
+ qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH")
+ || configVariable(qconfigContent, "QT_ARCH") || "x86";
+ qtProps.configItems = configVariableItems(qconfigContent, "CONFIG");
+ qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG");
+
+ // retrieve the mkspec
+ if (qtProps.qtMajorVersion >= 5) {
+ qtProps.mkspecName = queryResult.QMAKE_XSPEC;
+ qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName);
+ if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath))
+ qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName);
+ } else {
+ if (qbs.hostOS.contains("windows")) {
+ var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default");
+ var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf"));
+ qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL");
+ if (!File.exists(qtProps.mkspecPath)) {
+ // Work around QTBUG-28792.
+ // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me?
+ var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m);
+ if (match) {
+ qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths(
+ baseDirPath, match[1]));
+ }
+ }
+ } else {
+ qtProps.mkspecPath = FileInfo.canonicalPath(
+ FileInfo.joinPaths(qtProps.mkspecBasePath, "default"));
+ }
+
+ // E.g. in qmake.conf for Qt 4.8/mingw we find this gem:
+ // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++
+ qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath);
+
+ qtProps.mkspecName = qtProps.mkspecPath;
+ var idx = qtProps.mkspecName.lastIndexOf('/');
+ if (idx !== -1)
+ qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1);
+ }
+ if (!File.exists(qtProps.mkspecPath))
+ throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist";
+
+ // determine MSVC version
+ if (isMsvcQt(qtProps)) {
+ var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION");
+ var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION");
+ var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION");
+ if (msvcMajor && msvcMinor && msvcPatch)
+ qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch;
+ else
+ qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName);
+ }
+
+ // determine whether we have a framework build
+ qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx")
+ && qtProps.configItems.contains("qt_framework");
+
+ // determine whether Qt is built with debug, release or both
+ qtProps.buildVariant = [];
+ addQtBuildVariant(qtProps, "debug");
+ addQtBuildVariant(qtProps, "release");
+
+ qtProps.staticBuild = checkForStaticBuild(qtProps);
+
+ // determine whether user apps require C++11
+ if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild)
+ qtProps.configItems.push("c++11");
+
+ // Set the minimum operating system versions appropriate for this Qt version
+ qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps);
+ if (qtProps.windowsVersion) { // Is target OS Windows?
+ if (qtProps.buildVariant.contains("debug"))
+ qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true);
+ if (qtProps.buildVariant.contains("release"))
+ qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false);
+ } else if (qtProps.mkspecPath.contains("macx")) {
+ if (qtProps.qtMajorVersion >= 5) {
+ try {
+ var qmakeConf = new TextFile(FileInfo.joinPaths(qtProps.mkspecPath, "qmake.conf"),
+ TextFile.ReadOnly);
+ while (!qmakeConf.atEof()) {
+ var line = qmakeConf.readLine().trim();
+ match = line.match
+ (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/);
+ if (match) {
+ var platform = match[1];
+ var version = match[2];
+ if (platform === "MACOSX")
+ qtProps.macosVersion = version;
+ else if (platform === "IOS")
+ qtProps.iosVersion = version;
+ else if (platform === "TVOS")
+ qtProps.tvosVersion = version;
+ else if (platform === "WATCHOS")
+ qtProps.watchosVersion = version;
+ }
+ }
+ }
+ catch (e) {}
+ finally {
+ if (qmakeConf)
+ qmakeConf.close();
+ }
+ var isMac = qtProps.mkspecName !== "macx-ios-clang"
+ && qtProps.mkspecName !== "macx-tvos-clang"
+ && qtProps.mkspecName !== "macx-watchos-clang";
+ if (isMac) {
+ // Qt 5.0.x placed the minimum version in a different file
+ if (!qtProps.macosVersion)
+ qtProps.macosVersion = "10.6";
+
+ // If we're using C++11 with libc++, make sure the deployment target is >= 10.7
+ if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0
+ && qtProps.qtConfigItems.contains("c++11")) {
+ qtProps.macosVersion = "10.7";
+ }
+ }
+ } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) {
+ var qconfigDir = qtProps.frameworkBuild
+ ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers")
+ : FileInfo.joinPaths(qtProps.includePath, "Qt");
+ try {
+ var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"),
+ TextFile.ReadOnly);
+ var qtCocoaBuild = false;
+ var ok = true;
+ do {
+ line = qconfig.readLine();
+ if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) {
+ qtCocoaBuild = true;
+ break;
+ }
+ } while (!qconfig.atEof());
+ qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4";
+ }
+ catch (e) {}
+ finally {
+ if (qconfig)
+ qconfig.close();
+ }
+ if (!qtProps.macosVersion) {
+ throw "Could not determine whether Qt is using Cocoa or Carbon from '"
+ + toNative(qconfig.filePath()) + "'.";
+ }
+ }
+ } else if (qtProps.mkspecPath.contains("android")) {
+ if (qtProps.qtMajorVersion >= 5)
+ qtProps.androidVersion = "2.3";
+ else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8)
+ qtProps.androidVersion = "1.6"; // Necessitas
+ }
+ return qtProps;
+}
+
+function makePluginData() {
+ var pluginData = {};
+ pluginData.type = undefined;
+ pluginData.className = undefined;
+ pluginData.autoLoad = true;
+ pluginData["extends"] = [];
+ return pluginData;
+}
+
+function makeQtModuleInfo(name, qbsName, deps) {
+ var moduleInfo = {};
+ moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files.
+ if (moduleInfo.name === undefined)
+ moduleInfo.name = "";
+ moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix.
+ moduleInfo.dependencies = deps || []; // qbs names.
+ if (moduleInfo.qbsName !== "core" && !moduleInfo.dependencies.contains("core"))
+ moduleInfo.dependencies.unshift("core");
+ moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private");
+ moduleInfo.hasLibrary = !moduleInfo.isPrivate;
+ moduleInfo.isStaticLibrary = false;
+ moduleInfo.isPlugin = false;
+ moduleInfo.mustExist = true;
+ moduleInfo.modulePrefix = ""; // empty value means "Qt".
+ moduleInfo.version = undefined;
+ moduleInfo.includePaths = [];
+ moduleInfo.compilerDefines = [];
+ moduleInfo.staticLibrariesDebug = [];
+ moduleInfo.staticLibrariesRelease = [];
+ moduleInfo.dynamicLibrariesDebug = [];
+ moduleInfo.dynamicLibrariesRelease = [];
+ moduleInfo.linkerFlagsDebug = [];
+ moduleInfo.linkerFlagsRelease = [];
+ moduleInfo.libFilePathDebug = undefined;
+ moduleInfo.libFilePathRelease = undefined;
+ moduleInfo.frameworksDebug = [];
+ moduleInfo.frameworksRelease = [];
+ moduleInfo.frameworkPathsDebug = [];
+ moduleInfo.frameworkPathsRelease = [];
+ moduleInfo.libraryPaths = [];
+ moduleInfo.config = [];
+ moduleInfo.supportedPluginTypes = [];
+ moduleInfo.pluginData = makePluginData();
+ return moduleInfo;
+}
+
+function frameworkHeadersPath(qtModuleInfo, qtProps) {
+ return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers");
+}
+
+function qt4ModuleIncludePaths(qtModuleInfo, qtProps) {
+ var paths = [];
+ if (isFramework(qtModuleInfo, qtProps))
+ paths.push(frameworkHeadersPath(qtModuleInfo, qtProps));
+ else
+ paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name));
+ return paths;
+}
+
+// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users
+// for that.
+function addTestModule(modules) {
+ var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]);
+ testModule.hasLibrary = false;
+ modules.push(testModule);
+}
+
+// See above.
+function addDesignerComponentsModule(modules) {
+ var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents",
+ ["designercomponents-private"]);
+ module.hasLibrary = false;
+ modules.push(module);
+}
+
+function isFramework(modInfo, qtProps) {
+ if (!qtProps.frameworkBuild || modInfo.isStaticLibrary)
+ return false;
+ var modulesNeverBuiltAsFrameworks = [
+ "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "uitools", "harfbuzzng"
+ ];
+ return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName);
+}
+
+function libBaseName(modInfo, libName, debugBuild, qtProps) {
+ var name = libName;
+ if (qtProps.mkspecName.startsWith("win")) {
+ if (debugBuild)
+ name += 'd';
+ if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5)
+ name += qtProps.qtMajorVersion;
+ }
+ if (qtProps.mkspecName.contains("macx")
+ || qtProps.mkspecName.contains("ios")
+ || qtProps.mkspecName.contains("darwin")) {
+ if (!isFramework(modInfo, qtProps)
+ && qtProps.buildVariant.contains("debug")
+ && (!qtProps.buildVariant.contains("release") || debugBuild)) {
+ name += "_debug";
+ }
+ }
+ return name;
+}
+
+function moduleNameWithoutPrefix(modInfo) {
+ if (modInfo.name === "Phonon")
+ return "phonon";
+ if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt"))
+ return modInfo.name.slice(2); // Strip off "Qt".
+ if (modInfo.name.startsWith(modInfo.modulePrefix))
+ return modInfo.name.slice(modInfo.modulePrefix.length);
+ return modInfo.name;
+}
+
+function libraryBaseName(modInfo, qtProps, debugBuild) {
+ if (modInfo.isPlugin)
+ return libBaseName(modInfo, name, debugBuild, qtProps);
+
+ // Some modules use a different naming scheme, so it doesn't get boring.
+ var libNameBroken = modInfo.name === "Enginio"
+ || modInfo.name === "DataVisualization"
+ || modInfo.name === "Phonon";
+
+ var libName = !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix;
+ if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken)
+ libName += qtProps.qtMajorVersion;
+ libName += moduleNameWithoutPrefix(modInfo);
+ libName += qtProps.qtLibInfix;
+ return libBaseName(modInfo, libName, debugBuild, qtProps);
+}
+
+function libNameForLinker(modInfo, qtProps, debugBuild) {
+ if (!modInfo.hasLibrary)
+ return undefined;
+ var libName = libraryBaseName(modInfo, qtProps, debugBuild);
+ if (qtProps.mkspecName.contains("msvc"))
+ libName += ".lib";
+ return libName;
+}
+
+function guessLibraryFilePath(prlFilePath, libDir, qtProps) {
+ var baseName = FileInfo.baseName(prlFilePath);
+ var prefixCandidates = ["", "lib"];
+ var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"];
+ for (var i = 0; i < prefixCandidates.length; ++i) {
+ var prefix = prefixCandidates[i];
+ for (var j = 0; j < suffixCandidates.length; ++j) {
+ var suffix = suffixCandidates[j];
+ var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix);
+ if (File.exists(candidate))
+ return candidate;
+ }
+ }
+}
+
+function doReplaceQtLibNamesWithFilePath(namePathMap, libList) {
+ for (var i = 0; i < libList.length; ++i) {
+ var lib = libList[i];
+ var path = namePathMap[lib];
+ if (path)
+ libList[i] = path;
+ }
+}
+
+function replaceQtLibNamesWithFilePath(modules, qtProps) {
+ // We don't want to add the libraries for Qt modules via "-l", because of the
+ // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead,
+ // we pull them in using the full file path.
+ var linkerNamesToFilePathsDebug = {};
+ var linkerNamesToFilePathsRelease = {};
+ for (var i = 0; i < modules.length; ++i) {
+ var m = modules[i];
+ linkerNamesToFilePathsDebug[libNameForLinker(m, qtProps, true)] = m.libFilePathDebug;
+ linkerNamesToFilePathsRelease[libNameForLinker(m, qtProps, false)] = m.libFilePathRelease;
+ }
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
+ module.dynamicLibrariesRelease);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
+ module.staticLibrariesRelease);
+ }
+}
+
+function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) {
+ if (!modInfo.hasLibrary)
+ return; // Can happen for Qt4 convenience modules, like "widgets".
+
+ if (debugBuild) {
+ if (!qtProps.buildVariant.contains("debug"))
+ return;
+ var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"];
+ for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) {
+ var m = modulesNeverBuiltAsDebug[i];
+ if (modInfo.qbsName === m || modInfo.qbsName === m + "-private")
+ return;
+ }
+ } else if (!qtProps.buildVariant.contains("release")) {
+ return;
+ }
+
+ var libs = modInfo.isStaticLibrary
+ ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease)
+ : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease);
+ var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease;
+ var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease;
+ var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease;
+ var libFilePath;
+
+ if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) {
+ libs.push("z", "m");
+ if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) {
+ var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport");
+ libs.push(libNameForLinker(platformSupportModule, qtProps, debugBuild));
+ }
+ if (modInfo.name === "qios") {
+ flags.push("-force_load", FileInfo.joinPaths(
+ qtProps.pluginPath, "platforms",
+ libBaseName(modInfo, "libqios", debugBuild, qtProps) + ".a"));
+ }
+ }
+ var prlFilePath = modInfo.isPlugin
+ ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type)
+ : qtProps.libraryPath;
+ if (isFramework(modInfo, qtProps)) {
+ prlFilePath = FileInfo.joinPaths(prlFilePath,
+ libraryBaseName(modInfo, qtProps, false) + ".framework");
+ }
+ var libDir = prlFilePath;
+ var baseName = libraryBaseName(modInfo, qtProps, debugBuild);
+ if (!qtProps.mkspecName.startsWith("win") && !isFramework(modInfo, qtProps))
+ baseName = "lib" + baseName;
+ prlFilePath = FileInfo.joinPaths(prlFilePath, baseName);
+ var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win")
+ && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5;
+ if (isNonStaticQt4OnWindows)
+ prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number...
+ prlFilePath += ".prl";
+ try {
+ var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
+ while (!prlFile.atEof()) {
+ var line = prlFile.readLine().trim();
+ var equalsOffset = line.indexOf('=');
+ if (equalsOffset === -1)
+ continue;
+ if (line.startsWith("QMAKE_PRL_TARGET")) {
+ var isMingw = qtProps.mkspecName.startsWith("win")
+ && qtProps.mkspecName.contains("g++");
+ var isQtVersionBefore56 = qtProps.qtMajorVersion < 5
+ || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6);
+
+ // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw.
+ // Of course, the exception has an exception too: For static libs, mingw *does*
+ // have the "lib" prefix.
+ var libFileName = "";
+ if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw
+ && !modInfo.isStaticLibrary) {
+ libFileName += "lib";
+ }
+
+ libFileName += line.slice(equalsOffset + 1).trim();
+ if (isNonStaticQt4OnWindows)
+ libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET...
+ if (isQtVersionBefore56) {
+ if (qtProps.mkspecName.contains("msvc")) {
+ libFileName += ".lib";
+ } else if (isMingw) {
+ libFileName += ".a";
+ if (!File.exists(FileInfo.joinPaths(libDir, libFileName)))
+ libFileName = libFileName.slice(0, -2) + ".dll";
+ }
+ }
+ libFilePath = FileInfo.joinPaths(libDir, libFileName);
+ continue;
+ }
+ if (line.startsWith("QMAKE_PRL_CONFIG")) {
+ modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' ');
+ continue;
+ }
+ if (!line.startsWith("QMAKE_PRL_LIBS"))
+ continue;
+
+ // Assuming lib names and directories without spaces here.
+ var parts = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' ');
+ for (i = 0; i < parts.length; ++i) {
+ var part = parts[i];
+ part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath);
+ if (part.startsWith("-l")) {
+ libs.push(part.slice(2));
+ } else if (part.startsWith("-L")) {
+ modInfo.libraryPaths.push(part.slice(2));
+ } else if (part.startsWith("-F")) {
+ frameworkPaths.push(part.slice(2));
+ } else if (part === "-framework") {
+ if (++i < parts.length)
+ frameworks.push(parts[i]);
+ } else if (part === "-pthread") {
+ libs.push("pthread");
+ } else if (part.startsWith('-')) { // Some other option
+ console.debug("QMAKE_PRL_LIBS contains non-library option '" + part
+ + "' in file '" + prlFilePath + "'");
+ flags.push(part);
+ } else if (part.startsWith("/LIBPATH:")) {
+ libraryPaths.push(part.slice(9).replace(/\\/g, '/'));
+ } else { // Assume it's a file path/name.
+ libs.push(part.replace(/\\/g, '/'));
+ }
+ }
+ }
+ } catch (e) {
+ libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps);
+ if (nonExistingPrlFiles.contains(prlFilePath))
+ return;
+ nonExistingPrlFiles.push(prlFilePath);
+ if (!libFilePath && modInfo.mustExist) {
+ console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '"
+ + modInfo.name
+ + "' (" + e + "), and failed to deduce the library file path. "
+ + " This module will likely not be usable by qbs.");
+ }
+ }
+ finally {
+ if (prlFile)
+ prlFile.close();
+ }
+
+ if (debugBuild)
+ modInfo.libFilePathDebug = libFilePath;
+ else
+ modInfo.libFilePathRelease = libFilePath;
+}
+
+function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles) {
+ doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles);
+ doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles);
+}
+
+function allQt4Modules(qtProps) {
+ // as per http://doc.qt.io/qt-4.8/modules.html + private stuff.
+ var modules = [];
+
+ var core = makeQtModuleInfo("QtCore", "core");
+ core.compilerDefines.push("QT_CORE_LIB");
+ if (qtProps.qtNameSpace)
+ core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace);
+ modules.push(core,
+ makeQtModuleInfo("QtCore", "core-private", ["core"]),
+ makeQtModuleInfo("QtGui", "gui"),
+ makeQtModuleInfo("QtGui", "gui-private", ["gui"]),
+ makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]),
+ makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]),
+ makeQtModuleInfo("QtNetwork", "network"),
+ makeQtModuleInfo("QtNetwork", "network-private", ["network"]),
+ makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]),
+ makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]),
+ makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]),
+ makeQtModuleInfo("QtScript", "script"),
+ makeQtModuleInfo("QtScript", "script-private", ["script"]),
+ makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]),
+ makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]),
+ makeQtModuleInfo("QtSql", "sql"),
+ makeQtModuleInfo("QtSql", "sql-private", ["sql"]),
+ makeQtModuleInfo("QtSvg", "svg", ["gui"]),
+ makeQtModuleInfo("QtSvg", "svg-private", ["svg"]),
+ makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]),
+ makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]),
+ makeQtModuleInfo("QtXml", "xml"),
+ makeQtModuleInfo("QtXml", "xml-private", ["xml"]),
+ makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]),
+ makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]),
+ makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]),
+ makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]),
+ makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]),
+ makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]),
+ makeQtModuleInfo("QtUiTools", "uitools"),
+ makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]),
+ makeQtModuleInfo("QtHelp", "help", ["network", "sql"]),
+ makeQtModuleInfo("QtHelp", "help-private", ["help"]),
+ makeQtModuleInfo("QtTest", "testlib"),
+ makeQtModuleInfo("QtTest", "testlib-private", ["testlib"]));
+ if (qtProps.mkspecName.startsWith("win")) {
+ var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer");
+ axcontainer.modulePrefix = "Q";
+ axcontainer.isStaticLibrary = true;
+ axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
+ modules.push(axcontainer);
+
+ var axserver = makeQtModuleInfo("QAxServer", "axserver");
+ axserver.modulePrefix = "Q";
+ axserver.isStaticLibrary = true;
+ axserver.compilerDefines.push("QAXSERVER");
+ axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
+ modules.push(axserver);
+ } else {
+ modules.push(makeQtModuleInfo("QtDBus", "dbus"));
+ modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"]));
+ }
+
+ var designerComponentsPrivate = makeQtModuleInfo(
+ "QtDesignerComponents", "designercomponents-private",
+ ["gui-private", "designer-private"]);
+ designerComponentsPrivate.hasLibrary = true;
+ modules.push(designerComponentsPrivate);
+
+ var phonon = makeQtModuleInfo("Phonon", "phonon");
+ phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps);
+ modules.push(phonon);
+
+ // Set up include paths that haven't been set up before this point.
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ if (module.includePaths.length > 0)
+ continue;
+ module.includePaths = qt4ModuleIncludePaths(module, qtProps);
+ }
+
+ // Set up compiler defines haven't been set up before this point.
+ for (i = 0; i < modules.length; ++i) {
+ module = modules[i];
+ if (module.compilerDefines.length > 0)
+ continue;
+ module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB");
+ }
+
+ // 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.
+ var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "concurrent");
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+
+ addTestModule(modules);
+ addDesignerComponentsModule(modules);
+
+ var modulesThatCanBeDisabled = [
+ "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools",
+ "declarative", "gui", "dbus", "opengl", "openvg"];
+ var nonExistingPrlFiles = [];
+ for (i = 0; i < modules.length; ++i) {
+ module = modules[i];
+ var name = module.qbsName;
+ var privateIndex = name.indexOf("-private");
+ if (privateIndex !== -1)
+ name = name.slice(0, privateIndex);
+ if (modulesThatCanBeDisabled.contains(name))
+ module.mustExist = false;
+ if (qtProps.staticBuild)
+ module.isStaticLibrary = true;
+ setupLibraries(module, qtProps, nonExistingPrlFiles);
+ }
+ replaceQtLibNamesWithFilePath(modules, qtProps);
+
+ return modules;
+}
+
+function getPriFileContentsRecursively(priFilePath) {
+ var priFile = new TextFile(priFilePath, TextFile.ReadOnly);
+ var lines = splitNonEmpty(priFile.readAll(), '\n');
+ for (var i = 0; i < lines.length; ++i) {
+ var includeString = "include(";
+ var line = lines[i].trim();
+ if (!line.startsWith(includeString))
+ continue;
+ var offset = includeString.length;
+ var closingParenPos = line.indexOf(')', offset);
+ if (closingParenPos === -1) {
+ console.warn("Invalid include statement in '" + toNative(priFilePath) + "'");
+ continue;
+ }
+ var includedFilePath = line.slice(offset, closingParenPos - offset);
+ var includedContents = getPriFileContentsRecursively(includedFilePath);
+ var j = i;
+ for (var k = 0; k < includedContents.length; ++k)
+ lines.splice(++j, 0, includedContents[k]);
+ lines.splice(i--, 1);
+ }
+ priFile.close();
+ return lines;
+}
+
+function extractPaths(rhs, filePath) {
+ var paths = [];
+ var startIndex = 0;
+ for (;;) {
+ while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ')
+ ++startIndex;
+ if (startIndex >= rhs.length)
+ break;
+ var endIndex;
+ if (rhs.charAt(startIndex) === '"') {
+ ++startIndex;
+ endIndex = rhs.indexOf('"', startIndex);
+ if (endIndex === -1) {
+ console.warn("Unmatched quote in file '" + toNative(filePath) + "'");
+ break;
+ }
+ } else {
+ endIndex = rhs.indexOf(' ', startIndex + 1);
+ if (endIndex === -1)
+ endIndex = rhs.length;
+ }
+ paths.push(rhs.slice(startIndex, endIndex));
+ startIndex = endIndex + 1;
+ }
+ return paths;
+}
+
+function removeDuplicatedDependencyLibs(modules) {
+ var revDeps = {};
+ var currentPath;
+ var getLibraries;
+ var getLibFilePath;
+
+ function setupReverseDependencies(modules) {
+ var moduleByName = {};
+ for (var i = 0; i < modules.length; ++i)
+ moduleByName[modules[i].qbsName] = modules[i];
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ for (var j = 0; j < module.dependencies.length; ++j) {
+ var depmod = moduleByName[module.dependencies[j]];
+ if (!depmod)
+ continue;
+ if (!revDeps[depmod])
+ revDeps[depmod] = [];
+ revDeps[depmod].push(module);
+ }
+ }
+ }
+
+ function roots(modules) {
+ var result = [];
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i]
+ if (module.dependencies.lenegth === 0)
+ result.push(module);
+ }
+ return result;
+ }
+
+ function traverse(module, libs) {
+ if (currentPath.contains(module))
+ return;
+ currentPath.push(module);
+
+ var moduleLibraryLists = getLibraries(module);
+ for (var i = 0; i < moduleLibraryLists.length; ++i) {
+ var modLibList = moduleLibraryLists[i];
+ for (j = modLibList.length - 1; j >= 0; --j) {
+ if (libs.contains(modLibList[j]))
+ modLibList.splice(j, 1);
+ }
+ }
+
+ var libFilePath = getLibFilePath(module);
+ if (libFilePath)
+ libs.push(libFilePath);
+ for (i = 0; i < moduleLibraryLists.length; ++i)
+ libs = libs.concat(moduleLibraryLists[i]);
+ libs.sort();
+
+ for (i = 0; i < (revDeps[module] || []).length; ++i)
+ traverse(revDeps[module][i], libs);
+
+ m_currentPath.pop();
+ }
+
+ setupReverseDependencies(modules);
+
+ // Traverse the debug variants of modules.
+ getLibraries = function(module) {
+ return [module.dynamicLibrariesDebug, module.staticLibrariesDebug];
+ };
+ getLibFilePath = function(module) { return module.libFilePathDebug; };
+ var rootModules = roots(modules);
+ for (var i = 0; i < rootModules.length; ++i)
+ traverse(rootModules[i], []);
+
+ // Traverse the release variants of modules.
+ getLibraries = function(module) {
+ return [module.dynamicLibrariesRelease, module.staticLibrariesRelease];
+ };
+ getLibFilePath = function(module) { return module.libFilePathRelease; };
+ for (i = 0; i < rootModules.length; ++i)
+ traverse(rootModules[i], []);
+}
+
+function allQt5Modules(qtProps) {
+ var nonExistingPrlFiles = [];
+ var modules = [];
+ var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules");
+ var modulePriFiles = File.directoryEntries(modulesDir, File.Files);
+ for (var i = 0; i < modulePriFiles.length; ++i) {
+ var priFileName = modulePriFiles[i];
+ var priFilePath = FileInfo.joinPaths(modulesDir, priFileName);
+ var moduleFileNamePrefix = "qt_lib_";
+ var pluginFileNamePrefix = "qt_plugin_";
+ var moduleFileNameSuffix = ".pri";
+ var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix);
+ if (!fileHasPluginPrefix && (!priFileName.startsWith(moduleFileNamePrefix))
+ || !priFileName.endsWith(moduleFileNameSuffix)) {
+ continue;
+ }
+ var moduleInfo = makeQtModuleInfo();
+ moduleInfo.isPlugin = fileHasPluginPrefix;
+ var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleFileNamePrefix;
+ moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length);
+ if (moduleInfo.isPlugin) {
+ moduleInfo.name = moduleInfo.qbsName;
+ moduleInfo.isStaticLibrary = true;
+ }
+ var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT")
+ + '.' + moduleInfo.qbsName + '.';
+ moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private");
+ var hasV2 = false;
+ var hasModuleEntry = false;
+ var lines = getPriFileContentsRecursively(priFilePath);
+ for (var j = 0; j < lines.length; ++j) {
+ var line = lines[j].trim();
+ var firstEqualsOffset = line.indexOf('=');
+ if (firstEqualsOffset === -1)
+ continue;
+ var key = line.slice(0, firstEqualsOffset).trim();
+ var value = line.slice(firstEqualsOffset + 1).trim();
+ if (!key.startsWith(moduleKeyPrefix) || !value)
+ continue;
+ if (key.endsWith(".name")) {
+ moduleInfo.name = value;
+ } else if (key.endsWith(".module")) {
+ hasModuleEntry = true;
+ } else if (key.endsWith(".depends")) {
+ moduleInfo.dependencies = splitNonEmpty(value, ' ');
+ for (var k = 0; k < moduleInfo.dependencies.length; ++k) {
+ moduleInfo.dependencies[k]
+ = moduleInfo.dependencies[k].replace("_private", "-private");
+ }
+ } else if (key.endsWith(".module_config")) {
+ var elems = splitNonEmpty(value, ' ');
+ for (k = 0; k < elems.length; ++k) {
+ var elem = elems[k];
+ if (elem === "no_link")
+ moduleInfo.hasLibrary = false;
+ else if (elem === "staticlib")
+ moduleInfo.isStaticLibrary = true;
+ else if (elem === "internal_module")
+ moduleInfo.isPrivate = true;
+ else if (elem === "v2")
+ hasV2 = true;
+ }
+ } else if (key.endsWith(".includes")) {
+ moduleInfo.includePaths = extractPaths(value, priFilePath);
+ for (k = 0; k < moduleInfo.includePaths.length; ++k) {
+ moduleInfo.includePaths[k] = moduleInfo.includePaths[k]
+ .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath)
+ .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath);
+ }
+ } else if (key.endsWith(".DEFINES")) {
+ moduleInfo.compilerDefines = splitNonEmpty(value, ' ');
+ } else if (key.endsWith(".VERSION")) {
+ moduleInfo.version = value;
+ } else if (key.endsWith(".plugin_types")) {
+ moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' ');
+ } else if (key.endsWith(".TYPE")) {
+ moduleInfo.pluginData.type = value;
+ } else if (key.endsWith(".EXTENDS")) {
+ moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' ');
+ for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) {
+ if (moduleInfo.pluginData["extends"][k] === "-") {
+ moduleInfo.pluginData.splice(k, 1);
+ moduleInfo.pluginData.autoLoad = false;
+ break;
+ }
+ }
+ } else if (key.endsWith(".CLASS_NAME")) {
+ moduleInfo.pluginData.className = value;
+ }
+ }
+ if (hasV2 && !hasModuleEntry)
+ moduleInfo.hasLibrary = false;
+
+ // Fix include paths for Apple frameworks.
+ // The qt_lib_XXX.pri files contain wrong values for versions < 5.6.
+ if (!hasV2 && isFramework(moduleInfo, qtProps)) {
+ moduleInfo.includePaths = [];
+ var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps);
+ if (moduleInfo.isPrivate) {
+ baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version);
+ moduleInfo.includePaths.push(baseIncDir,
+ FileInfo.joinPaths(baseIncDir, moduleInfo.name));
+ } else {
+ moduleInfo.includePaths.push(baseIncDir);
+ }
+ }
+
+ setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles);
+
+ modules.push(moduleInfo);
+ if (moduleInfo.qbsName === "testlib")
+ addTestModule(modules);
+ if (moduleInfo.qbsName === "designercomponents-private")
+ addDesignerComponentsModule(modules);
+ }
+
+ replaceQtLibNamesWithFilePath(modules, qtProps);
+ removeDuplicatedDependencyLibs(modules);
+ return modules;
+}
+
+function extractQbsArchs(module, qtProps) {
+ if (qtProps.mkspecName.startsWith("macx-")) {
+ var archs = [];
+ if (module.libFilePathRelease)
+ archs = Utilities.getArchitecturesFromBinary(module.libFilePathRelease);
+ return archs;
+ }
+ var qbsArch = Utilities.canonicalArchitecture(qtProps.architecture);
+ if (qbsArch === "arm" && qtProps.mkspecPath.contains("android"))
+ qbsArch = "armv7a";
+
+ // Qt4 has "QT_ARCH = windows" in qconfig.pri for both MSVC and mingw.
+ if (qbsArch === "windows")
+ return []
+
+ return [qbsArch];
+}
+
+function qbsTargetPlatformFromQtMkspec(qtProps) {
+ var mkspec = qtProps.mkspecName;
+ var idx = mkspec.lastIndexOf('/');
+ if (idx !== -1)
+ mkspec = mkspec.slice(idx + 1);
+ if (mkspec.startsWith("aix-"))
+ return "aix";
+ if (mkspec.startsWith("android-"))
+ return "android";
+ if (mkspec.startsWith("cygwin-"))
+ return "windows";
+ if (mkspec.startsWith("darwin-"))
+ return "macos";
+ if (mkspec.startsWith("freebsd-"))
+ return "freebsd";
+ if (mkspec.startsWith("haiku-"))
+ return "haiku";
+ if (mkspec.startsWith(("hpux-")) || mkspec.startsWith(("hpuxi-")))
+ return "hpux";
+ if (mkspec.startsWith("hurd-"))
+ return "hurd";
+ if (mkspec.startsWith("integrity-"))
+ return "integrity";
+ if (mkspec.startsWith("linux-"))
+ return "linux";
+ if (mkspec.startsWith("macx-")) {
+ if (mkspec.startsWith("macx-ios-"))
+ return "ios";
+ if (mkspec.startsWith("macx-tvos-"))
+ return "tvos";
+ if (mkspec.startsWith("macx-watchos-"))
+ return "watchos";
+ return "macos";
+ }
+ if (mkspec.startsWith("netbsd-"))
+ return "netbsd";
+ if (mkspec.startsWith("openbsd-"))
+ return "openbsd";
+ if (mkspec.startsWith("qnx-"))
+ return "qnx";
+ if (mkspec.startsWith("solaris-"))
+ return "solaris";
+ if (mkspec.startsWith("vxworks-"))
+ return "vxworks";
+ if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-"))
+ return "windows";
+}
+
+function pathToJSLiteral(path) { return JSON.stringify(FileInfo.fromNativeSeparators(path)); }
+
+function defaultQpaPlugin(module, qtProps) {
+ if (qtProps.qtMajorVersion < 5)
+ return undefined;
+ if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) {
+ var qConfigPri = new TextFile(FileInfo.joinPaths(qtProps.mkspecBasePath, "qconfig.pri"));
+ var magicString = "QT_DEFAULT_QPA_PLUGIN =";
+ while (!qConfigPri.atEof()) {
+ var line = qConfigPri.readLine().trim();
+ if (line.startsWith(magicString))
+ return line.slice(magicString.length).trim();
+ }
+ qConfigPri.close();
+ } else {
+ var gtGuiHeadersPath = qtProps.frameworkBuild
+ ? FileInfo.joinPaths(qtProps.libraryPath, "QtGui.framework", "Headers")
+ : FileInfo.joinPaths(qtProps.includePath, "QtGui");
+ var qtGuiConfigHeader = FileInfo.joinPaths(gtGuiHeadersPath, "qtgui-config.h");
+ var headerFiles = [];
+ headerFiles.push(qtGuiConfigHeader);
+ while (headerFiles.length > 0) {
+ var filePath = headerFiles.shift();
+ var headerFile = new TextFile(filePath, TextFile.ReadOnly);
+ var regexp = /^#define QT_QPA_DEFAULT_PLATFORM_NAME "(.+)".*$/;
+ var includeRegexp = /^#include "(.+)".*$/;
+ while (!headerFile.atEof()) {
+ line = headerFile.readLine().trim();
+ var match = line.match(regexp);
+ if (match)
+ return 'q' + match[1];
+ match = line.match(includeRegexp);
+ if (match) {
+ var includedFile = match[1];
+ if (!FileInfo.isAbsolute(includedFile)) {
+ includedFile = FileInfo.cleanPath(
+ FileInfo.joinPaths(FileInfo.path(filePath), includedFile));
+ }
+ headerFiles.push(includedFile);
+ }
+ }
+ headerFile.close();
+ }
+ }
+
+ if (module.isStaticLibrary)
+ console.warn("Could not determine default QPA plugin for static Qt.");
+}
+
+function libraryFileTag(module, qtProps) {
+ if (module.isStaticLibrary)
+ return "staticlibrary";
+ return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++")
+ ? "dynamiclibrary_import" : "dynamiclibrary";
+}
+
+function findVariable(content, start) {
+ var result = [-1, -1];
+ result[0] = content.indexOf('@', start);
+ if (result[0] === -1)
+ return result;
+ result[1] = content.indexOf('@', result[0] + 1);
+ if (result[1] === -1) {
+ result[0] = -1;
+ return result;
+ }
+ var forbiddenChars = [' ', '\n'];
+ for (var i = 0; i < forbiddenChars.length; ++i) {
+ var forbidden = forbiddenChars[i];
+ var k = content.indexOf(forbidden, result[0] + 1);
+ if (k !== -1 && k < result[1])
+ return findVariable(content, result[0] + 1);
+ }
+ return result;
+}
+
+function toJSLiteral(v) {
+ if (v === undefined)
+ return "undefined";
+ return JSON.stringify(v);
+}
+
+function minVersionJsString(minVersion) {
+ return !minVersion ? "original" : toJSLiteral(minVersion);
+}
+
+function replaceSpecialValues(content, module, qtProps) {
+ var dict = {
+ archs: toJSLiteral(extractQbsArchs(module, qtProps)),
+ targetPlatform: toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)),
+ config: toJSLiteral(qtProps.configItems),
+ qtConfig: toJSLiteral(qtProps.qtConfigItems),
+ binPath: toJSLiteral(qtProps.binaryPath),
+ libPath: toJSLiteral(qtProps.libraryPath),
+ pluginPath: toJSLiteral(qtProps.pluginPath),
+ incPath: toJSLiteral(qtProps.includePath),
+ docPath: toJSLiteral(qtProps.documentationPath),
+ mkspecName: toJSLiteral(qtProps.mkspecName),
+ mkspecPath: toJSLiteral(qtProps.mkspecPath),
+ version: toJSLiteral(qtProps.qtVersion),
+ libInfix: toJSLiteral(qtProps.qtLibInfix),
+ availableBuildVariants: toJSLiteral(qtProps.buildVariant),
+ staticBuild: toJSLiteral(qtProps.staticBuild),
+ frameworkBuild: toJSLiteral(qtProps.frameworkBuild),
+ name: toJSLiteral(moduleNameWithoutPrefix(module)),
+ has_library: toJSLiteral(module.hasLibrary),
+ dependencies: toJSLiteral(module.dependencies),
+ includes: toJSLiteral(module.includePaths),
+ staticLibsDebug: toJSLiteral(module.staticLibrariesDebug),
+ staticLibsRelease: toJSLiteral(module.staticLibrariesRelease),
+ dynamicLibsDebug: toJSLiteral(module.dynamicLibrariesDebug),
+ dynamicLibsRelease: toJSLiteral(module.dynamicLibrariesRelease),
+ linkerFlagsDebug: toJSLiteral(module.linkerFlagsDebug),
+ linkerFlagsRelease: toJSLiteral(module.linkerFlagsRelease),
+ libraryPaths: toJSLiteral(module.libraryPaths),
+ frameworkPathsDebug: toJSLiteral(module.frameworkPathsDebug),
+ frameworkPathsRelease: toJSLiteral(module.frameworkPathsRelease),
+ frameworksDebug: toJSLiteral(module.frameworksDebug),
+ frameworksRelease: toJSLiteral(module.frameworksRelease),
+ libFilePathDebug: toJSLiteral(module.libFilePathDebug),
+ libFilePathRelease: toJSLiteral(module.libFilePathRelease),
+ libNameForLinkerDebug: toJSLiteral(libNameForLinker(module, qtProps, true)),
+ pluginTypes: toJSLiteral(module.supportedPluginTypes),
+ moduleConfig: toJSLiteral(module.config),
+ libNameForLinkerRelease: toJSLiteral(libNameForLinker(module, qtProps, false)),
+ entryPointLibsDebug: toJSLiteral(qtProps.entryPointLibsDebug),
+ entryPointLibsRelease: toJSLiteral(qtProps.entryPointLibsRelease),
+ minWinVersion: minVersionJsString(qtProps.windowsVersion),
+ minMacVersion: minVersionJsString(qtProps.macosVersion),
+ minIosVersion: minVersionJsString(qtProps.iosVersion),
+ minTvosVersion: minVersionJsString(qtProps.tvosVersion),
+ minWatchosVersion: minVersionJsString(qtProps.watchosVersion),
+ minAndroidVersion: minVersionJsString(qtProps.androidVersion),
+ };
+
+ var additionalContent = "";
+ var compilerDefines = toJSLiteral(module.compilerDefines);
+ if (module.qbsName === "declarative" || module.qbsName === "quick") {
+ var debugMacro = module.qbsName === "declarative" || qtProps.qtMajorVersion < 5
+ ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG";
+ var indent = " ";
+ additionalContent = "property bool qmlDebugging: false\n"
+ + indent + "property string qmlPath";
+ if (qtProps.qmlPath)
+ additionalContent += ": " + pathToJSLiteral(qtProps.qmlPath) + '\n';
+ else
+ additionalContent += '\n';
+
+ additionalContent += indent + "property string qmlImportsPath: "
+ + pathToJSLiteral(qtProps.qmlImportPath);
+
+ compilerDefines = "{\n"
+ + indent + indent + "var result = " + compilerDefines + ";\n"
+ + indent + indent + "if (qmlDebugging)\n"
+ + indent + indent + indent + "result.push(\"" + debugMacro + "\");\n"
+ + indent + indent + "return result;\n"
+ + indent + "}";
+ }
+ dict.defines = compilerDefines;
+ if (module.qbsName === "gui")
+ dict.defaultQpaPlugin = toJSLiteral(defaultQpaPlugin(module, qtProps));
+ if (module.qbsName === "qml")
+ dict.qmlPath = pathToJSLiteral(qtProps.qmlPath);
+ if (module.isStaticLibrary && module.qbsName !== "core") {
+ if (additionalContent)
+ additionalContent += "\n ";
+ additionalContent += "isStaticLibrary: true";
+ }
+ if (module.isPlugin) {
+ dict.className = toJSLiteral(module.pluginData.className);
+ dict["extends"] = toJSLiteral(module.pluginData["extends"]);
+ }
+ if (module.hasLibrary && !isFramework(module, qtProps)) {
+ if (additionalContent)
+ additionalContent += "\n";
+ indent = " ";
+ additionalContent += "Group {\n";
+ if (module.isPlugin) {
+ additionalContent += indent + indent
+ + "condition: Qt[\"" + module.qbsName + "\"].enableLinking\n";
+ }
+ additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName + "\"]"
+ + ".libFilePath]\n"
+ + indent + indent + "filesAreTargets: true\n"
+ + indent + indent + "fileTags: [\"" + libraryFileTag(module, qtProps)
+ + "\"]\n"
+ + indent + "}";
+ }
+ dict.additionalContent = additionalContent;
+
+ for (var pos = findVariable(content, 0); pos[0] !== -1;
+ pos = findVariable(content, pos[0])) {
+ var replacement = dict[content.slice(pos[0] + 1, pos[1])] || "";
+ content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1);
+ pos[0] += replacement.length;
+ }
+ return content;
+}
+
+function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles, module, pluginMap,
+ nonEssentialPlugins)
+{
+ if (!File.makePath(targetDirectory)) {
+ throw "Cannot create directory '" + toNative(targetDirectory) + "'.";
+ }
+ var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName),
+ TextFile.ReadOnly);
+ var newContent = sourceFile.readAll();
+ if (module) {
+ newContent = replaceSpecialValues(newContent, module, qtProps);
+ } else {
+ newContent = newContent.replace("@allPluginsByType@",
+ '(' + toJSLiteral(pluginMap) + ')');
+ newContent = newContent.replace("@nonEssentialPlugins@",
+ toJSLiteral(nonEssentialPlugins));
+ }
+ sourceFile.close();
+ var targetPath = FileInfo.joinPaths(targetDirectory, fileName);
+ allFiles.push(targetPath);
+ var targetFile = new TextFile(targetPath, TextFile.WriteOnly);
+ targetFile.write(newContent);
+ targetFile.close();
+}
+
+function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) {
+ if (!File.exists(qmakeFilePath))
+ throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist.";
+ var qtProps = getQtProperties(qmakeFilePath, qbs);
+ var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : allQt5Modules(qtProps);
+ var pluginsByType = [];
+ var nonEssentialPlugins = [];
+ for (var i = 0; i < modules.length; ++i) {
+ var m = modules[i];
+ if (m.isPlugin) {
+ if (!pluginsByType[m.pluginData.type])
+ pluginsByType[m.pluginData.type] = [];
+ pluginsByType[m.pluginData.type].push(m.name);
+ if (!m.pluginData.autoLoad)
+ nonEssentialPlugins.push(m.name);
+ }
+ }
+
+ var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : "";
+ var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt");
+ if (File.exists(qbsQtModuleBaseDir))
+ File.remove(qbsQtModuleBaseDir);
+
+ var allFiles = [];
+ copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
+ copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
+ copyTemplateFile("plugin_support.qbs", FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"),
+ qtProps, location, allFiles, undefined, pluginsByType, nonEssentialPlugins);
+
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName);
+ var moduleTemplateFileName;
+ if (module.qbsName === "core") {
+ moduleTemplateFileName = "core.qbs";
+ copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, location, allFiles);
+ copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, location, allFiles);
+ } else if (module.qbsName === "gui") {
+ moduleTemplateFileName = "gui.qbs";
+ } else if (module.qbsName === "scxml") {
+ moduleTemplateFileName = "scxml.qbs";
+ } else if (module.qbsName === "dbus") {
+ moduleTemplateFileName = "dbus.qbs";
+ copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, location, allFiles);
+ } else if (module.qbsName === "qml") {
+ moduleTemplateFileName = "qml.qbs";
+ copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, location, allFiles);
+ var qmlcacheStr = "qmlcache";
+ if (File.exists(FileInfo.joinPaths(qtProps.binaryPath,
+ "qmlcachegen" + exeSuffix(qbs)))) {
+ copyTemplateFile(qmlcacheStr + ".qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps,
+ location, allFiles);
+ }
+ } else if (module.qbsName === "quick") {
+ moduleTemplateFileName = "quick.qbs";
+ copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, location, allFiles);
+ } else if (module.isPlugin) {
+ moduleTemplateFileName = "plugin.qbs";
+ } else {
+ moduleTemplateFileName = "module.qbs";
+ }
+ copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, location, allFiles,
+ module);
+ }
+
+ // Note that it's not strictly necessary to copy this one, as it has no variable content.
+ // But we do it anyway for consistency.
+ copyTemplateFile("android_support.qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"),
+ qtProps, location, allFiles);
+ return relativeSearchPath;
+}
+
+function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) {
+ qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths, qbs);
+ if (!qmakeFilePaths || qmakeFilePaths.length === 0)
+ return [];
+ var uniquifySearchPath = qmakeFilePaths.length > 1;
+ var searchPaths = [];
+ for (var i = 0; i < qmakeFilePaths.length; ++i) {
+ try {
+ console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'...");
+ var searchPath = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath,
+ location, qbs);
+ if (searchPath !== undefined) {
+ searchPaths.push(searchPath);
+ console.info("Qt was set up successfully.");
+ }
+ } catch (e) {
+ console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e);
+ }
+ }
+ return searchPaths;
+}
diff --git a/share/qbs/module-providers/Qt/templates/QtModule.qbs b/share/qbs/module-providers/Qt/templates/QtModule.qbs
new file mode 100644
index 000000000..aa7c1d15a
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/QtModule.qbs
@@ -0,0 +1,86 @@
+import qbs.FileInfo
+
+Module {
+ condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild)
+ && (!qbs.architecture
+ || architectures.length === 0
+ || architectures.contains(qbs.architecture))
+
+ readonly property bool isCombinedUIKitBuild: ["ios", "tvos", "watchos"].contains(targetPlatform)
+ && ["x86", "x86_64"].contains(qbs.architecture)
+ && qbs.targetPlatform === targetPlatform + "-simulator"
+
+ Depends { name: "cpp" }
+ Depends { name: "Qt.core" }
+
+ Depends { name: "Qt.plugin_support" }
+ property stringList pluginTypes
+ Qt.plugin_support.pluginTypes: pluginTypes
+ Depends {
+ condition: Qt.core.staticBuild && !isPlugin
+ name: "Qt";
+ submodules: {
+ // We have to pull in all plugins here, because dependency resolving happens
+ // before module merging, and we don't know yet if someone set
+ // Qt.pluginSupport.pluginsByType in the product.
+ // The real filtering is done later by the plugin module files themselves.
+ var list = [];
+ var allPlugins = Qt.plugin_support.allPluginsByType;
+ for (var i = 0; i < (pluginTypes || []).length; ++i)
+ Array.prototype.push.apply(list, allPlugins[pluginTypes[i]])
+ return list;
+ }
+ }
+
+ 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 libNameForLinkerDebug
+ property string libNameForLinkerRelease
+ property string libNameForLinker: Qt.core.qtBuildVariant === "debug"
+ ? libNameForLinkerDebug : libNameForLinkerRelease
+ property string libFilePathDebug
+ property string libFilePathRelease
+ property string libFilePath: Qt.core.qtBuildVariant === "debug"
+ ? libFilePathDebug : libFilePathRelease
+ version: Qt.core.version
+ property bool hasLibrary: true
+ property bool isStaticLibrary: false
+ property bool isPlugin: false
+
+ property stringList architectures
+ property string targetPlatform
+ property stringList staticLibsDebug
+ property stringList staticLibsRelease
+ property stringList dynamicLibsDebug
+ property stringList dynamicLibsRelease
+ property stringList linkerFlagsDebug
+ property stringList linkerFlagsRelease
+ property stringList staticLibs: Qt.core.qtBuildVariant === "debug"
+ ? staticLibsDebug : staticLibsRelease
+ property stringList dynamicLibs: Qt.core.qtBuildVariant === "debug"
+ ? dynamicLibsDebug : dynamicLibsRelease
+ property stringList frameworksDebug
+ property stringList frameworksRelease
+ property stringList frameworkPathsDebug
+ property stringList frameworkPathsRelease
+ property stringList mFrameworks: Qt.core.qtBuildVariant === "debug"
+ ? frameworksDebug : frameworksRelease
+ property stringList mFrameworkPaths: Qt.core.qtBuildVariant === "debug"
+ ? frameworkPathsDebug: frameworkPathsRelease
+ cpp.linkerFlags: Qt.core.qtBuildVariant === "debug"
+ ? linkerFlagsDebug : linkerFlagsRelease
+ property bool enableLinking: qtModuleName != undefined && hasLibrary
+ property stringList moduleConfig
+
+ Properties {
+ condition: enableLinking
+ cpp.staticLibraries: staticLibs
+ cpp.dynamicLibraries: dynamicLibs
+ cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild
+ ? [libNameForLinker] : [])
+ cpp.frameworkPaths: mFrameworkPaths
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs b/share/qbs/module-providers/Qt/templates/QtPlugin.qbs
new file mode 100644
index 000000000..23a6795f3
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/QtPlugin.qbs
@@ -0,0 +1,49 @@
+import qbs.FileInfo
+import qbs.TextFile
+
+QtModule {
+ isPlugin: true
+
+ property string className
+ property stringList extendsModules
+
+ enableLinking: {
+ if (!base)
+ return false;
+ if (!isStaticLibrary)
+ return false;
+ if (!(Qt.plugin_support.enabledPlugins || []).contains(qtModuleName))
+ return false;
+ if (!extendsModules || extendsModules.length === 0)
+ return true;
+ for (var i = 0; i < extendsModules.length; ++i) {
+ var moduleName = extendsModules[i];
+ if (product.Qt[moduleName] && product.Qt[moduleName].present)
+ return true;
+ }
+ return false;
+ }
+
+ Rule {
+ condition: enableLinking
+ multiplex: true
+ Artifact {
+ filePath: product.targetName + "_qt_plugin_import_"
+ + product.moduleProperty(product.moduleName, "qtModuleName") + ".cpp"
+ fileTags: "cpp"
+ }
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ var pluginName = product.moduleProperty(product.moduleName, "qtModuleName");
+ cmd.description = "Creating static import for plugin '" + pluginName + "'.";
+ cmd.sourceCode = function() {
+ var f = new TextFile(output.filePath, TextFile.WriteOnly);
+ var className = product.moduleProperty(product.moduleName, "className");
+ f.writeLine("#include <QtPlugin>\n\nQ_IMPORT_PLUGIN(" + className + ")");
+ f.close();
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs
new file mode 100644
index 000000000..79276a494
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/android_support.qbs
@@ -0,0 +1,291 @@
+import qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.TextFile
+import qbs.Utilities
+
+Module {
+ property bool useMinistro: false
+ property string qmlRootDir: product.sourceDirectory
+ property stringList extraPrefixDirs
+ property stringList deploymentDependencies // qmake: ANDROID_DEPLOYMENT_DEPENDENCIES
+ property stringList extraPlugins // qmake: ANDROID_EXTRA_PLUGINS
+ property bool verboseAndroidDeployQt: false
+
+ property string _androidDeployQtFilePath: FileInfo.joinPaths(_qtInstallDir, "bin",
+ "androiddeployqt")
+ property string _qtInstallDir
+ property bool _enableSdkSupport: product.type && product.type.contains("android.apk")
+ && !consoleApplication
+ property bool _enableNdkSupport: !product.aggregate || product.multiplexConfigurationId
+ property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android")
+ property string _deployQtOutDir: FileInfo.joinPaths(product.buildDirectory, "deployqt_out")
+
+ Depends { name: "Android.sdk"; condition: _enableSdkSupport }
+ Depends { name: "Android.ndk"; condition: _enableNdkSupport }
+ Depends { name: "java"; condition: _enableSdkSupport }
+
+ Properties {
+ condition: _enableNdkSupport && qbs.toolchain.contains("clang")
+ Android.ndk.appStl: "c++_shared"
+ }
+ Properties {
+ condition: _enableNdkSupport && !qbs.toolchain.contains("clang")
+ Android.ndk.appStl: "gnustl_shared"
+ }
+ Properties {
+ condition: _enableSdkSupport
+ Android.sdk.customManifestProcessing: true
+ java._tagJniHeaders: false // prevent rule cycle
+ }
+
+ Rule {
+ condition: _enableSdkSupport
+ multiplex: true
+ property stringList inputTags: "android.nativelibrary"
+ inputsFromDependencies: inputTags
+ inputs: product.aggregate ? [] : inputTags
+ Artifact {
+ filePath: "androiddeployqt.json"
+ fileTags: "qt_androiddeployqt_input"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "creating " + output.fileName;
+ cmd.sourceCode = function() {
+ var theBinary;
+ var nativeLibs = inputs["android.nativelibrary"];
+ if (nativeLibs.length === 1) {
+ theBinary = nativeLibs[0];
+ } else {
+ for (i = 0; i < nativeLibs.length; ++i) {
+ var candidate = nativeLibs[i];
+ if (!candidate.fileName.contains(candidate.product.targetName))
+ continue;
+ if (!theBinary) {
+ theBinary = candidate;
+ continue;
+ }
+ if (theBinary.product.name === product.name
+ && candidate.product.name !== product.name) {
+ continue; // We already have a better match.
+ }
+ if (candidate.product.name === product.name
+ && theBinary.product.name !== product.name) {
+ theBinary = candidate; // The new candidate is a better match.
+ continue;
+ }
+ throw "Qt applications for Android support only one native binary "
+ + "per package.\n"
+ + "In particular, you cannot build a Qt app for more than "
+ + "one architecture at the same time.";
+ }
+ }
+ var f = new TextFile(output.filePath, TextFile.WriteOnly);
+ f.writeLine("{");
+ f.writeLine('"description": "This file was generated by qbs to be read by '
+ + 'androiddeployqt and should not be modified by hand.",');
+ f.writeLine('"qt": "' + product.Qt.android_support._qtInstallDir + '",');
+ f.writeLine('"sdk": "' + product.Android.sdk.sdkDir + '",');
+ f.writeLine('"sdkBuildToolsRevision": "' + product.Android.sdk.buildToolsVersion
+ + '",');
+ f.writeLine('"ndk": "' + product.Android.sdk.ndkDir + '",');
+ var toolPrefix = theBinary.cpp.toolchainTriple;
+ var toolchainPrefix = toolPrefix.startsWith("i686-") ? "x86" : toolPrefix;
+ f.writeLine('"toolchain-prefix": "' + toolchainPrefix + '",');
+ f.writeLine('"tool-prefix": "' + toolPrefix + '",');
+ f.writeLine('"toolchain-version": "' + theBinary.Android.ndk.toolchainVersion
+ + '",');
+ f.writeLine('"ndk-host": "' + theBinary.Android.ndk.hostArch + '",');
+ f.writeLine('"target-architecture": "' + theBinary.Android.ndk.abi + '",');
+ f.writeLine('"qml-root-path": "' + product.Qt.android_support.qmlRootDir + '",');
+ var deploymentDeps = product.Qt.android_support.deploymentDependencies;
+ if (deploymentDeps && deploymentDeps.length > 0)
+ f.writeLine('"deployment-dependencies": "' + deploymentDeps.join() + '",');
+ var extraPlugins = product.Qt.android_support.extraPlugins;
+ if (extraPlugins && extraPlugins.length > 0)
+ f.writeLine('"android-extra-plugins": "' + extraPlugins.join() + '",');
+ var prefixDirs = product.Qt.android_support.extraPrefixDirs;
+ if (prefixDirs && prefixDirs.length > 0)
+ f.writeLine('"extraPrefixDirs": ' + JSON.stringify(prefixDirs) + ',');
+ if (Array.isArray(product.qmlImportPaths) && product.qmlImportPaths.length > 0)
+ f.writeLine('"qml-import-paths": "' + product.qmlImportPaths.join(',') + '",');
+ f.writeLine('"application-binary": "' + theBinary.filePath + '"');
+ f.writeLine("}");
+ f.close();
+ };
+ return cmd;
+ }
+ }
+
+ // We use the manifest template from the Qt installation if and only if the project
+ // does not provide a manifest file.
+ Rule {
+ condition: _enableSdkSupport
+ multiplex: true
+ requiresInputs: false
+ inputs: "android.manifest"
+ excludedInputs: "qt.android_manifest"
+ outputFileTags: ["android.manifest", "qt.android_manifest"]
+ outputArtifacts: {
+ if (inputs["android.manifest"])
+ return [];
+ return [{
+ filePath: "qt_manifest/AndroidManifest.xml",
+ fileTags: ["android.manifest", "qt.android_manifest"]
+ }];
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "copying Qt Android manifest template";
+ cmd.sourceCode = function() {
+ File.copy(FileInfo.joinPaths(product.Qt.android_support._templatesBaseDir,
+ "templates", "AndroidManifest.xml"), output.filePath);
+ };
+ return cmd;
+ }
+ }
+
+ Rule {
+ condition: _enableSdkSupport
+ multiplex: true
+ inputs: ["qt_androiddeployqt_input", "android.manifest_processed"]
+ outputFileTags: [
+ "android.manifest_final", "android.resources", "android.assets", "bundled_jar",
+ "android.deployqt_list",
+ ]
+ outputArtifacts: {
+ var artifacts = [
+ {
+ filePath: "AndroidManifest.xml",
+ fileTags: "android.manifest_final"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml",
+ fileTags: "android.resources"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir
+ + "/res/values/strings.xml",
+ fileTags: "android.resources"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir + "/assets/.dummy",
+ fileTags: "android.assets"
+ },
+ {
+ filePath: "deployqt.list",
+ fileTags: "android.deployqt_list"
+ },
+
+ ];
+ if (!product.Qt.android_support.useMinistro) {
+ artifacts.push({
+ filePath: FileInfo.joinPaths(product.java.classFilesDir, "QtAndroid.jar"),
+ fileTags: ["bundled_jar"]
+ });
+ }
+ return artifacts;
+ }
+ prepare: {
+ var copyCmd = new JavaScriptCommand();
+ copyCmd.description = "copying Qt resource templates";
+ copyCmd.sourceCode = function() {
+ File.copy(inputs["android.manifest_processed"][0].filePath,
+ product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml");
+ File.copy(product.Qt.android_support._templatesBaseDir + "/java/res",
+ product.Qt.android_support._deployQtOutDir + "/res");
+ File.copy(product.Qt.android_support._templatesBaseDir
+ + "/templates/res/values/libs.xml",
+ product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml");
+ try {
+ File.remove(FileInfo.path(outputs["android.assets"][0].filePath));
+ } catch (e) {
+ }
+ };
+ var androidDeployQtArgs = [
+ "--output", product.Qt.android_support._deployQtOutDir,
+ "--input", inputs["qt_androiddeployqt_input"][0].filePath, "--aux-mode",
+ "--deployment", product.Qt.android_support.useMinistro ? "ministro" : "bundled",
+ "--android-platform", product.Android.sdk.platform,
+ ];
+ if (product.Qt.android_support.verboseAndroidDeployQt)
+ args.push("--verbose");
+ var androidDeployQtCmd = new Command(
+ product.Qt.android_support._androidDeployQtFilePath, androidDeployQtArgs);
+ androidDeployQtCmd.description = "running androiddeployqt";
+
+ // We do not want androiddeployqt to write directly into our APK base dir, so
+ // we ran it on an isolated directory and now we move stuff over.
+ // We remember the files for which we do that, so if the next invocation
+ // of androiddeployqt creates fewer files, the other ones are removed from
+ // the APK base dir.
+ var moveCmd = new JavaScriptCommand();
+ moveCmd.description = "processing androiddeployqt outout";
+ moveCmd.sourceCode = function() {
+ File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml",
+ outputs["android.manifest_final"][0].filePath);
+ var libsDir = product.Qt.android_support._deployQtOutDir + "/libs";
+ var libDir = product.Android.sdk.apkContentsDir + "/lib";
+ var listFilePath = outputs["android.deployqt_list"][0].filePath;
+ var oldLibs = [];
+ try {
+ var listFile = new TextFile(listFilePath, TextFile.ReadOnly);
+ var listFileLine = listFile.readLine();
+ while (listFileLine) {
+ oldLibs.push(listFileLine);
+ listFileLine = listFile.readLine();
+ }
+ listFile.close();
+ } catch (e) {
+ }
+ listFile = new TextFile(listFilePath, TextFile.WriteOnly);
+ var newLibs = [];
+ var moveLibFiles = function(prefix) {
+ var fullSrcPrefix = FileInfo.joinPaths(libsDir, prefix);
+ var files = File.directoryEntries(fullSrcPrefix, File.Files);
+ for (var i = 0; i < files.length; ++i) {
+ var file = files[i];
+ var srcFilePath = FileInfo.joinPaths(fullSrcPrefix, file);
+ var targetFilePath;
+ if (file.endsWith(".jar"))
+ targetFilePath = FileInfo.joinPaths(product.java.classFilesDir, file);
+ else
+ targetFilePath = FileInfo.joinPaths(libDir, prefix, file);
+ File.move(srcFilePath, targetFilePath);
+ listFile.writeLine(targetFilePath);
+ newLibs.push(targetFilePath);
+ }
+ var dirs = File.directoryEntries(fullSrcPrefix,
+ File.Dirs | File.NoDotAndDotDot);
+ for (i = 0; i < dirs.length; ++i)
+ moveLibFiles(FileInfo.joinPaths(prefix, dirs[i]));
+ };
+ moveLibFiles("");
+ listFile.close();
+ for (i = 0; i < oldLibs.length; ++i) {
+ if (!newLibs.contains(oldLibs[i]))
+ File.remove(oldLibs[i]);
+ }
+ };
+ return [copyCmd, androidDeployQtCmd, moveCmd];
+ }
+ }
+
+ Group {
+ condition: Qt.android_support._enableSdkSupport
+ name: "helper sources from qt"
+ prefix: Qt.android_support._templatesBaseDir + "/java/"
+ Android.sdk.aidlSearchPaths: prefix + "src"
+ files: [
+ "**/*.java",
+ "**/*.aidl",
+ ]
+ }
+
+ validate: {
+ if (Utilities.versionCompare(version, "5.12") < 0)
+ throw ModUtils.ModuleError("Cannot use Qt " + version + " with Android. "
+ + "Version 5.12 or later is required.");
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs
new file mode 100644
index 000000000..b2f05d8e9
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/core.qbs
@@ -0,0 +1,510 @@
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.TextFile
+import qbs.Utilities
+import qbs.Xml
+import "moc.js" as Moc
+import "qdoc.js" as Qdoc
+
+Module {
+ condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild)
+ && (!qbs.architecture
+ || architectures.length === 0
+ || architectures.contains(qbs.architecture))
+
+ readonly property bool isCombinedUIKitBuild: ["ios", "tvos", "watchos"].contains(targetPlatform)
+ && ["x86", "x86_64"].contains(qbs.architecture)
+ && qbs.targetPlatform === targetPlatform + "-simulator"
+
+ Depends { name: "cpp" }
+
+ Depends { name: "Qt.android_support"; condition: qbs.targetOS.contains("android") }
+ Properties {
+ condition: qbs.targetOS.contains("android")
+ Qt.android_support._qtInstallDir: FileInfo.path(binPath)
+ Qt.android_support.version: version
+ }
+
+ version: @version@
+ property stringList architectures: @archs@
+ property string targetPlatform: @targetPlatform@
+ property string libInfix: @libInfix@
+ property stringList config: @config@
+ property stringList qtConfig: @qtConfig@
+ property path binPath: @binPath@
+ property path incPath: @incPath@
+ property path libPath: @libPath@
+ property path pluginPath: @pluginPath@
+ property string mkspecName: @mkspecName@
+ property path mkspecPath: @mkspecPath@
+ property string mocName: "moc"
+ property stringList mocFlags: []
+ property string lreleaseName: "lrelease"
+ property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3"
+ property stringList qdocEnvironment
+ property path docPath: @docPath@
+ property stringList helpGeneratorArgs: versionMajor >= 5 ? ["-platform", "minimal"] : []
+ 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: @frameworkBuild@
+ property bool staticBuild: @staticBuild@
+ property stringList pluginMetaData: []
+ property bool enableKeywords: true
+
+ property stringList availableBuildVariants: @availableBuildVariants@
+ property string qtBuildVariant: {
+ if (availableBuildVariants.contains(qbs.buildVariant))
+ return qbs.buildVariant;
+ return availableBuildVariants.length > 0 ? availableBuildVariants[0] : "";
+ }
+
+ property stringList staticLibsDebug: @staticLibsDebug@
+ property stringList staticLibsRelease: @staticLibsRelease@
+ property stringList dynamicLibsDebug: @dynamicLibsDebug@
+ property stringList dynamicLibsRelease: @dynamicLibsRelease@
+ property stringList staticLibs: qtBuildVariant === "debug"
+ ? staticLibsDebug : staticLibsRelease
+ property stringList dynamicLibs: qtBuildVariant === "debug"
+ ? dynamicLibsDebug : dynamicLibsRelease
+ property stringList linkerFlagsDebug: @linkerFlagsDebug@
+ property stringList linkerFlagsRelease: @linkerFlagsRelease@
+ property stringList coreLinkerFlags: qtBuildVariant === "debug"
+ ? linkerFlagsDebug : linkerFlagsRelease
+ property stringList frameworksDebug: @frameworksDebug@
+ property stringList frameworksRelease: @frameworksRelease@
+ property stringList coreFrameworks: qtBuildVariant === "debug"
+ ? frameworksDebug : frameworksRelease
+ property stringList frameworkPathsDebug: @frameworkPathsDebug@
+ property stringList frameworkPathsRelease: @frameworkPathsRelease@
+ property stringList coreFrameworkPaths: qtBuildVariant === "debug"
+ ? frameworkPathsDebug : frameworkPathsRelease
+ property string libNameForLinkerDebug: @libNameForLinkerDebug@
+ property string libNameForLinkerRelease: @libNameForLinkerRelease@
+ property string libNameForLinker: qtBuildVariant === "debug"
+ ? libNameForLinkerDebug : libNameForLinkerRelease
+ property string libFilePathDebug: @libFilePathDebug@
+ property string libFilePathRelease: @libFilePathRelease@
+ property string libFilePath: qtBuildVariant === "debug"
+ ? libFilePathDebug : libFilePathRelease
+
+ property stringList coreLibPaths: @libraryPaths@
+
+ // These are deliberately not path types
+ // We don't want to resolve them against the source directory
+ property string generatedHeadersDir: product.buildDirectory + "/qt.headers"
+ property string qdocOutputDir: product.buildDirectory + "/qdoc_html"
+ property string qmDir: product.destinationDirectory
+ property string qmBaseName: product.targetName
+ property bool lreleaseMultiplexMode: false
+
+ property stringList moduleConfig: @moduleConfig@
+ Properties {
+ condition: moduleConfig.contains("use_gold_linker")
+ cpp.linkerVariant: "gold"
+ }
+
+ cpp.entryPoint: qbs.targetOS.containsAny(["ios", "tvos"])
+ && Utilities.versionCompare(version, "5.6.0") >= 0
+ ? "_qt_main_wrapper"
+ : undefined
+ cpp.cxxLanguageVersion: Utilities.versionCompare(version, "5.7.0") >= 0 ? "c++11" : original
+ cpp.enableCompilerDefinesByLanguage: ["cpp"].concat(
+ qbs.targetOS.contains("darwin") ? ["objcpp"] : [])
+ cpp.defines: {
+ var defines = @defines@;
+ // ### 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 (!enableKeywords)
+ defines.push("QT_NO_KEYWORDS");
+ if (qbs.targetOS.containsAny(["ios", "tvos"])) {
+ defines = defines.concat(["DARWIN_NO_CARBON", "QT_NO_CORESERVICES", "QT_NO_PRINTER",
+ "QT_NO_PRINTDIALOG"]);
+ if (Utilities.versionCompare(version, "5.6.0") < 0)
+ defines.push("main=qtmn");
+ }
+ return defines;
+ }
+ cpp.driverFlags: {
+ var flags = [];
+ if (qbs.toolchain.contains("gcc")) {
+ if (config.contains("sanitize_address"))
+ flags.push("-fsanitize=address");
+ if (config.contains("sanitize_undefined"))
+ flags.push("-fsanitize=undefined");
+ if (config.contains("sanitize_thread"))
+ flags.push("-fsanitize=thread");
+ if (config.contains("sanitize_memory"))
+ flags.push("-fsanitize=memory");
+ }
+ return flags;
+ }
+ cpp.includePaths: {
+ var paths = @includes@;
+ paths.push(mkspecPath, generatedHeadersDir);
+ return paths;
+ }
+ cpp.libraryPaths: {
+ var libPaths = [libPath];
+ if (staticBuild && pluginPath)
+ libPaths.push(pluginPath + "/platforms");
+ libPaths = libPaths.concat(coreLibPaths);
+ return libPaths;
+ }
+ cpp.staticLibraries: {
+ var libs = [];
+ if (qbs.targetOS.contains('windows') && !product.consoleApplication) {
+ libs = libs.concat(qtBuildVariant === "debug"
+ ? @entryPointLibsDebug@ : @entryPointLibsRelease@);
+ }
+ libs = libs.concat(staticLibs);
+ return libs;
+ }
+ cpp.dynamicLibraries: dynamicLibs
+ cpp.linkerFlags: coreLinkerFlags
+ cpp.frameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : [])
+ cpp.frameworks: {
+ var frameworks = coreFrameworks
+ if (frameworkBuild)
+ frameworks.push(libNameForLinker);
+ 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.runtimeLibrary: qbs.toolchain.contains("msvc")
+ ? config.contains("static_runtime") ? "static" : "dynamic"
+ : original
+ cpp.positionIndependentCode: versionMajor >= 5 ? true : undefined
+ cpp.cxxFlags: {
+ var flags = [];
+ if (qbs.toolchain.contains('msvc')) {
+ if (versionMajor < 5)
+ flags.push('/Zc:wchar_t-');
+ }
+
+ return flags;
+ }
+ cpp.cxxStandardLibrary: {
+ if (qbs.targetOS.contains('darwin') && qbs.toolchain.contains('clang')
+ && config.contains('c++11'))
+ return "libc++";
+ return original;
+ }
+ cpp.minimumWindowsVersion: @minWinVersion@
+ cpp.minimumMacosVersion: @minMacVersion@
+ cpp.minimumIosVersion: @minIosVersion@
+ cpp.minimumTvosVersion: @minTvosVersion@
+ cpp.minimumWatchosVersion: @minWatchosVersion@
+ cpp.minimumAndroidVersion: @minAndroidVersion@
+
+ // Universal Windows Platform support
+ cpp.windowsApiFamily: mkspecName.startsWith("winrt-") ? "pc" : undefined
+ cpp.windowsApiAdditionalPartitions: mkspecPath.startsWith("winrt-") ? ["phone"] : undefined
+ cpp.requireAppContainer: mkspecName.startsWith("winrt-")
+
+ additionalProductTypes: ["qm"]
+
+ validate: {
+ var validator = new ModUtils.PropertyValidator("Qt.core");
+ validator.setRequiredProperty("binPath", binPath);
+ validator.setRequiredProperty("incPath", incPath);
+ validator.setRequiredProperty("libPath", libPath);
+ validator.setRequiredProperty("mkspecPath", mkspecPath);
+ validator.setRequiredProperty("version", version);
+ validator.setRequiredProperty("config", config);
+ validator.setRequiredProperty("qtConfig", qtConfig);
+ validator.setRequiredProperty("versionMajor", versionMajor);
+ validator.setRequiredProperty("versionMinor", versionMinor);
+ validator.setRequiredProperty("versionPatch", versionPatch);
+
+ if (!staticBuild)
+ validator.setRequiredProperty("pluginPath", pluginPath);
+
+ // Allow custom version suffix since some distributions might want to do this,
+ // but otherwise the version must start with a valid 3-component string
+ validator.addVersionValidator("version", version, 3, 3, true);
+ validator.addRangeValidator("versionMajor", versionMajor, 1);
+ validator.addRangeValidator("versionMinor", versionMinor, 0);
+ validator.addRangeValidator("versionPatch", versionPatch, 0);
+
+ validator.addCustomValidator("availableBuildVariants", availableBuildVariants, function (v) {
+ return v.length > 0;
+ }, "the Qt installation supports no build variants");
+
+ validator.addCustomValidator("qtBuildVariant", qtBuildVariant, function (variant) {
+ return availableBuildVariants.contains(variant);
+ }, "'" + qtBuildVariant + "' is not supported by this Qt installation");
+
+ validator.addCustomValidator("qtBuildVariant", qtBuildVariant, function (variant) {
+ return variant === qbs.buildVariant || !qbs.toolchain.contains("msvc");
+ }, " is '" + qtBuildVariant + "', but qbs.buildVariant is '" + qbs.buildVariant
+ + "', which is not allowed when using MSVC");
+
+ validator.addFileNameValidator("resourceFileBaseName", resourceFileBaseName);
+
+ validator.validate();
+ }
+
+ FileTagger {
+ patterns: ["*.qrc"]
+ fileTags: ["qrc"]
+ }
+
+ FileTagger {
+ patterns: ["*.ts"]
+ fileTags: ["ts"]
+ }
+
+ FileTagger {
+ patterns: ["*.qdoc", "*.qdocinc"]
+ fileTags: ["qdoc"]
+ }
+
+ FileTagger {
+ patterns: ["*.qdocconf"]
+ fileTags: ["qdocconf"]
+ }
+
+ FileTagger {
+ patterns: ["*.qhp"]
+ fileTags: ["qhp"]
+ }
+
+ property bool combineMocOutput: cpp.combineCxxSources
+ property bool enableBigResources: false
+
+ Rule {
+ name: "QtCoreMocRuleCpp"
+ property string cppInput: cpp.combineCxxSources ? "cpp.combine" : "cpp"
+ property string objcppInput: cpp.combineObjcxxSources ? "objcpp.combine" : "objcpp"
+ inputs: [objcppInput, cppInput]
+ auxiliaryInputs: "qt_plugin_metadata"
+ excludedInputs: "unmocable"
+ outputFileTags: ["hpp", "unmocable"]
+ outputArtifacts: Moc.outputArtifacts.apply(Moc, arguments)
+ prepare: Moc.commands.apply(Moc, arguments)
+ }
+ Rule {
+ name: "QtCoreMocRuleHpp"
+ inputs: "hpp"
+ auxiliaryInputs: ["qt_plugin_metadata", "cpp", "objcpp"];
+ excludedInputs: "unmocable"
+ outputFileTags: ["hpp", "cpp", "moc_cpp", "unmocable"]
+ outputArtifacts: Moc.outputArtifacts.apply(Moc, arguments)
+ prepare: Moc.commands.apply(Moc, arguments)
+ }
+
+ Rule {
+ multiplex: true
+ inputs: ["moc_cpp"]
+ Artifact {
+ filePath: "amalgamated_moc_" + product.targetName + ".cpp"
+ fileTags: ["cpp", "unmocable"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "creating " + output.fileName;
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ ModUtils.mergeCFiles(inputs["moc_cpp"], output.filePath);
+ };
+ return [cmd];
+ }
+ }
+
+ property path resourceSourceBase
+ property string resourcePrefix: "/"
+ property string resourceFileBaseName: product.targetName
+ Rule {
+ multiplex: true
+ inputs: ["qt.core.resource_data"]
+ Artifact {
+ filePath: product.Qt.core.resourceFileBaseName + ".qrc"
+ fileTags: ["qrc"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + output.fileName;
+ cmd.sourceCode = function() {
+ var doc = new Xml.DomDocument("RCC");
+
+ var rccNode = doc.createElement("RCC");
+ rccNode.setAttribute("version", "1.0");
+ doc.appendChild(rccNode);
+
+ var inputsByPrefix = {}
+ for (var i = 0; i < inputs["qt.core.resource_data"].length; ++i) {
+ var inp = inputs["qt.core.resource_data"][i];
+ var prefix = inp.Qt.core.resourcePrefix;
+ var inputsList = inputsByPrefix[prefix] || [];
+ inputsList.push(inp);
+ inputsByPrefix[prefix] = inputsList;
+ }
+
+ for (var prefix in inputsByPrefix) {
+ var qresourceNode = doc.createElement("qresource");
+ qresourceNode.setAttribute("prefix", prefix);
+ rccNode.appendChild(qresourceNode);
+
+ for (var i = 0; i < inputsByPrefix[prefix].length; ++i) {
+ var inp = inputsByPrefix[prefix][i];
+ var fullResPath = inp.filePath;
+ var baseDir = inp.Qt.core.resourceSourceBase;
+ var resAlias = baseDir
+ ? FileInfo.relativePath(baseDir, fullResPath) : inp.fileName;
+
+ var fileNode = doc.createElement("file");
+ fileNode.setAttribute("alias", resAlias);
+ qresourceNode.appendChild(fileNode);
+
+ var fileTextNode = doc.createTextNode(fullResPath);
+ fileNode.appendChild(fileTextNode);
+ }
+ }
+
+ doc.save(output.filePath, 4);
+ };
+ return [cmd];
+ }
+ }
+
+ Rule {
+ inputs: ["qrc"]
+ outputFileTags: ["cpp", "cpp_intermediate_object"]
+ outputArtifacts: {
+ var artifact = {
+ filePath: "qrc_" + input.completeBaseName + ".cpp",
+ fileTags: ["cpp"]
+ };
+ if (input.Qt.core.enableBigResources)
+ artifact.fileTags.push("cpp_intermediate_object");
+ return [artifact];
+ }
+ prepare: {
+ var args = [input.filePath,
+ "-name", FileInfo.completeBaseName(input.filePath),
+ "-o", output.filePath];
+ if (input.Qt.core.enableBigResources)
+ args.push("-pass", "1");
+ var cmd = new Command(product.Qt.core.binPath + '/rcc', args);
+ cmd.description = "rcc "
+ + (input.Qt.core.enableBigResources ? "(pass 1) " : "")
+ + input.fileName;
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["intermediate_obj"]
+ Artifact {
+ filePath: input.completeBaseName + ".2.o"
+ fileTags: ["obj"]
+ }
+ prepare: {
+ function findChild(artifact, predicate) {
+ var children = artifact.children;
+ var len = children.length;
+ for (var i = 0; i < len; ++i) {
+ var child = children[i];
+ if (predicate(child))
+ return child;
+ child = findChild(child, predicate);
+ if (child)
+ return child;
+ }
+ return undefined;
+ }
+ var qrcArtifact = findChild(input, function(c) { return c.fileTags.contains("qrc"); });
+ var cppArtifact = findChild(input, function(c) { return c.fileTags.contains("cpp"); });
+ var cmd = new Command(product.Qt.core.binPath + '/rcc',
+ [qrcArtifact.filePath,
+ "-temp", input.filePath,
+ "-name", FileInfo.completeBaseName(input.filePath),
+ "-o", output.filePath,
+ "-pass", "2"]);
+ cmd.description = "rcc (pass 2) " + qrcArtifact.fileName;
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: ["ts"]
+ multiplex: lreleaseMultiplexMode
+
+ Artifact {
+ filePath: FileInfo.joinPaths(product.Qt.core.qmDir,
+ (product.Qt.core.lreleaseMultiplexMode
+ ? product.Qt.core.qmBaseName
+ : input.baseName) + ".qm")
+ fileTags: ["qm"]
+ }
+
+ prepare: {
+ var inputFilePaths;
+ if (product.Qt.core.lreleaseMultiplexMode)
+ inputFilePaths = inputs["ts"].map(function(artifact) { return artifact.filePath; });
+ else
+ inputFilePaths = [input.filePath];
+ var args = ['-silent', '-qm', output.filePath].concat(inputFilePaths);
+ var cmd = new Command(product.Qt.core.binPath + '/'
+ + product.Qt.core.lreleaseName, args);
+ cmd.description = 'Creating ' + output.fileName;
+ cmd.highlight = 'filegen';
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: "qdocconf-main"
+ explicitlyDependsOn: ["qdoc", "qdocconf"]
+
+ outputFileTags: ModUtils.allFileTags(Qdoc.qdocFileTaggers())
+ outputArtifacts: Qdoc.outputArtifacts(product, input)
+
+ prepare: {
+ var outputDir = product.Qt.core.qdocOutputDir;
+ var args = Qdoc.qdocArgs(product, input, outputDir);
+ var cmd = new Command(product.Qt.core.binPath + '/' + product.Qt.core.qdocName, args);
+ cmd.description = 'qdoc ' + input.fileName;
+ cmd.highlight = 'filegen';
+ cmd.environment = product.Qt.core.qdocEnvironment;
+ cmd.environment.push("OUTDIR=" + outputDir); // Qt 4 replacement for -outputdir
+ return cmd;
+ }
+ }
+
+ Rule {
+ inputs: "qhp"
+ auxiliaryInputs: ModUtils.allFileTags(Qdoc.qdocFileTaggers())
+ .filter(function(tag) { return tag !== "qhp"; })
+
+ Artifact {
+ filePath: input.completeBaseName + ".qch"
+ fileTags: ["qch"]
+ }
+
+ prepare: {
+ var args = [input.filePath];
+ args = args.concat(product.Qt.core.helpGeneratorArgs);
+ args.push("-o");
+ args.push(output.filePath);
+ var cmd = new Command(product.Qt.core.binPath + "/qhelpgenerator", args);
+ cmd.description = 'qhelpgenerator ' + input.fileName;
+ cmd.highlight = 'filegen';
+ cmd.stdoutFilterFunction = function(output) {
+ return "";
+ };
+ return cmd;
+ }
+ }
+
+ @additionalContent@
+}
diff --git a/share/qbs/module-providers/Qt/templates/dbus.js b/share/qbs/module-providers/Qt/templates/dbus.js
new file mode 100644
index 000000000..0674bf684
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/dbus.js
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+var FileInfo = require("qbs.FileInfo");
+
+function outputFileName(input, suffix)
+{
+ var parts = input.fileName.split('.').filter(function(s) { return s.length > 0; });
+ if (parts.length === 0)
+ throw "Cannot run qdbusxml2cpp on '" + input.filePath + "': Unsuitable file name.";
+ var outputBaseName = parts.length === 1 ? parts[0] : parts[parts.length - 2];
+ return outputBaseName.toLowerCase() + suffix;
+}
+
+function createCommands(product, input, outputs, option)
+{
+ var exe = ModUtils.moduleProperty(product, "binPath") + '/'
+ + ModUtils.moduleProperty(product, "xml2cppName");
+ var hppOutput = outputs["hpp"][0];
+ var hppArgs = ModUtils.moduleProperty(product, "xml2CppHeaderFlags");
+ hppArgs.push(option, hppOutput.fileName + ':', input.filePath); // Can't use filePath on Windows
+ var hppCmd = new Command(exe, hppArgs)
+ hppCmd.description = "qdbusxml2cpp " + input.fileName + " -> " + hppOutput.fileName;
+ hppCmd.highlight = "codegen";
+ hppCmd.workingDirectory = FileInfo.path(hppOutput.filePath);
+ var cppOutput = outputs["cpp"][0];
+ var cppArgs = ModUtils.moduleProperty(product, "xml2CppSourceFlags");
+ cppArgs.push("-i", hppOutput.filePath, option, ':' + cppOutput.fileName, input.filePath);
+ var cppCmd = new Command(exe, cppArgs)
+ cppCmd.description = "qdbusxml2cpp " + input.fileName + " -> " + cppOutput.fileName;
+ cppCmd.highlight = "codegen";
+ cppCmd.workingDirectory = FileInfo.path(cppOutput.filePath);
+ return [hppCmd, cppCmd];
+}
diff --git a/share/qbs/module-providers/Qt/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs
new file mode 100644
index 000000000..6556e2c9b
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/dbus.qbs
@@ -0,0 +1,74 @@
+import qbs.FileInfo
+import qbs.ModUtils
+import "../QtModule.qbs" as QtModule
+import "dbus.js" as DBus
+
+QtModule {
+ qtModuleName: "DBus"
+
+ property string xml2cppName: "qdbusxml2cpp"
+ property stringList xml2CppHeaderFlags: []
+ property stringList xml2CppSourceFlags: []
+
+ Rule {
+ inputs: ["qt.dbus.adaptor"]
+
+ Artifact {
+ filePath: FileInfo.joinPaths(input.moduleProperty("Qt.core", "generatedHeadersDir"),
+ DBus.outputFileName(input, "_adaptor.h"))
+ fileTags: ["hpp"]
+ }
+ Artifact {
+ filePath: DBus.outputFileName(input, "_adaptor.cpp")
+ fileTags: ["cpp"]
+ }
+
+ prepare: {
+ return DBus.createCommands(product, input, outputs, "-a");
+ }
+ }
+
+ Rule {
+ inputs: ["qt.dbus.interface"]
+
+ Artifact {
+ filePath: FileInfo.joinPaths(input.moduleProperty("Qt.core", "generatedHeadersDir"),
+ DBus.outputFileName(input, "_interface.h"))
+ fileTags: ["hpp"]
+ }
+ Artifact {
+ filePath: DBus.outputFileName(input, "_interface.cpp")
+ fileTags: ["cpp"]
+ }
+
+ prepare: {
+ return DBus.createCommands(product, input, outputs, "-p");
+ }
+ }
+
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+ moduleConfig: @moduleConfig@
+
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+
+ @additionalContent@
+}
+
diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs
new file mode 100644
index 000000000..eb69e0cad
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/gui.qbs
@@ -0,0 +1,65 @@
+import qbs.FileInfo
+import qbs.ModUtils
+import '../QtModule.qbs' as QtModule
+
+QtModule {
+ qtModuleName: "Gui"
+
+ property string uicName: "uic"
+
+ FileTagger {
+ patterns: ["*.ui"]
+ fileTags: ["ui"]
+ }
+
+ Rule {
+ inputs: ["ui"]
+
+ Artifact {
+ filePath: FileInfo.joinPaths(input.moduleProperty("Qt.core", "generatedHeadersDir"),
+ 'ui_' + input.completeBaseName + '.h')
+ fileTags: ["hpp"]
+ }
+
+ prepare: {
+ var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
+ + ModUtils.moduleProperty(product, "uicName"),
+ [input.filePath, '-o', output.filePath])
+ cmd.description = 'uic ' + input.fileName;
+ cmd.highlight = 'codegen';
+ return cmd;
+ }
+ }
+
+ property string defaultQpaPlugin: @defaultQpaPlugin@
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+
+ Properties {
+ condition: Qt.core.staticBuild && qbs.targetOS.contains("ios")
+ cpp.frameworks: base.concat(["UIKit", "QuartzCore", "CoreText", "CoreGraphics",
+ "Foundation", "CoreFoundation", "AudioToolbox"])
+ }
+ cpp.frameworks: base
+ @additionalContent@
+}
+
diff --git a/share/qbs/module-providers/Qt/templates/moc.js b/share/qbs/module-providers/Qt/templates/moc.js
new file mode 100644
index 000000000..aa67766b9
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/moc.js
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+var ModUtils = require("qbs.ModUtils");
+
+function args(product, input, outputFileName)
+{
+ var defines = product.cpp.compilerDefinesByLanguage;
+ if (input.fileTags.contains("objcpp"))
+ defines = ModUtils.flattenDictionary(defines["objcpp"]) || [];
+ else if (input.fileTags.contains("cpp"))
+ defines = ModUtils.flattenDictionary(defines["cpp"]) || [];
+ else
+ defines = [];
+ defines = defines.uniqueConcat(product.cpp.platformDefines);
+ defines = defines.uniqueConcat(input.cpp.defines);
+ var includePaths = input.cpp.includePaths;
+ includePaths = includePaths.uniqueConcat(input.cpp.systemIncludePaths);
+ var useCompilerPaths = product.Qt.core.versionMajor >= 5;
+ if (useCompilerPaths) {
+ includePaths = includePaths.uniqueConcat(input.cpp.compilerIncludePaths);
+ }
+ var frameworkPaths = product.cpp.frameworkPaths;
+ frameworkPaths = frameworkPaths.uniqueConcat(
+ product.cpp.systemFrameworkPaths);
+ if (useCompilerPaths) {
+ frameworkPaths = frameworkPaths.uniqueConcat(
+ product.cpp.compilerFrameworkPaths);
+ }
+ var pluginMetaData = product.Qt.core.pluginMetaData;
+ var args = [];
+ args = args.concat(
+ defines.map(function(item) { return '-D' + item; }),
+ includePaths.map(function(item) { return '-I' + item; }),
+ frameworkPaths.map(function(item) { return '-F' + item; }),
+ pluginMetaData.map(function(item) { return '-M' + item; }),
+ product.Qt.core.mocFlags,
+ '-o', outputFileName,
+ input.filePath);
+ return args;
+}
+
+function fullPath(product)
+{
+ return product.Qt.core.binPath + '/' + product.Qt.core.mocName;
+}
+
+function outputArtifacts(project, product, inputs, input)
+{
+ var mocInfo = QtMocScanner.apply(input);
+ if (!mocInfo.hasQObjectMacro)
+ return [];
+ var artifact = { fileTags: ["unmocable"] };
+ if (mocInfo.hasPluginMetaDataMacro)
+ artifact.explicitlyDependsOn = ["qt_plugin_metadata"];
+ if (input.fileTags.contains("hpp")) {
+ artifact.filePath = input.Qt.core.generatedHeadersDir
+ + "/moc_" + input.completeBaseName + ".cpp";
+ var amalgamate = input.Qt.core.combineMocOutput;
+ artifact.fileTags.push(mocInfo.mustCompile ? (amalgamate ? "moc_cpp" : "cpp") : "hpp");
+ } else {
+ artifact.filePath = input.Qt.core.generatedHeadersDir + '/'
+ + input.completeBaseName + ".moc";
+ artifact.fileTags.push("hpp");
+ }
+ return [artifact];
+}
+
+function commands(project, product, inputs, outputs, input, output)
+{
+ var cmd = new Command(fullPath(product), args(product, input, output.filePath));
+ cmd.description = 'moc ' + input.fileName;
+ cmd.highlight = 'codegen';
+ return cmd;
+}
diff --git a/share/qbs/module-providers/Qt/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs
new file mode 100644
index 000000000..b09f79a87
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/module.qbs
@@ -0,0 +1,30 @@
+import '../QtModule.qbs' as QtModule
+
+QtModule {
+ qtModuleName: @name@
+ Depends { name: "Qt"; submodules: @dependencies@}
+
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ hasLibrary: @has_library@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+ moduleConfig: @moduleConfig@
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+ @additionalContent@
+}
diff --git a/share/qbs/module-providers/Qt/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs
new file mode 100644
index 000000000..e73e2a4d9
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/plugin.qbs
@@ -0,0 +1,27 @@
+import '../QtPlugin.qbs' as QtPlugin
+
+QtPlugin {
+ qtModuleName: @name@
+ Depends { name: "Qt"; submodules: @dependencies@}
+
+ className: @className@
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ cpp.libraryPaths: @libraryPaths@
+ extendsModules: @extends@
+ @additionalContent@
+}
diff --git a/share/qbs/module-providers/Qt/templates/plugin_support.qbs b/share/qbs/module-providers/Qt/templates/plugin_support.qbs
new file mode 100644
index 000000000..13d95c383
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/plugin_support.qbs
@@ -0,0 +1,75 @@
+Module {
+ // Set by user.
+ property varList pluginsByType
+
+ // Set by Qt modules.
+ property stringList pluginTypes
+
+ // Set by setup-qt.
+ readonly property var allPluginsByType: @allPluginsByType@
+ readonly property stringList nonEssentialPlugins: @nonEssentialPlugins@
+
+ // Derived.
+ readonly property var defaultPluginsByType: {
+ var map = {};
+ for (var i = 0; i < (pluginTypes || []).length; ++i) {
+ var pType = pluginTypes[i];
+ map[pType] = (allPluginsByType[pType] || []).filter(function(p) {
+ return !nonEssentialPlugins.contains(p); });
+ }
+ return map;
+ }
+ readonly property var effectivePluginsByType: {
+ var ppt = pluginsByType || [];
+ var eppt = {};
+ for (var i = 0; i < ppt.length; ++i) {
+ var listEntry = ppt[i];
+ for (var pluginType in listEntry) {
+ var newValue = listEntry[pluginType];
+ if (!newValue)
+ newValue = [];
+ else if (typeof newValue == "string")
+ newValue = [newValue];
+ if (!Array.isArray(newValue))
+ throw "Invalid value '" + newValue + "' in Qt.plugin_support.pluginsByType";
+ eppt[pluginType] = (eppt[pluginType] || []).uniqueConcat(newValue);
+ }
+ }
+ var dppt = defaultPluginsByType;
+ for (var pluginType in dppt) {
+ if (!eppt[pluginType])
+ eppt[pluginType] = dppt[pluginType];
+ }
+ return eppt;
+ }
+ readonly property stringList enabledPlugins: {
+ var list = [];
+ var eppt = effectivePluginsByType;
+ for (var t in eppt)
+ Array.prototype.push.apply(list, eppt[t]);
+ return list;
+ }
+
+ validate: {
+ var ppt = pluginsByType;
+ if (!ppt)
+ return;
+ var appt = allPluginsByType;
+ for (var i = 0; i < ppt.length; ++i) {
+ for (var pluginType in ppt[i]) {
+ var requestedPlugins = ppt[i][pluginType];
+ if (!requestedPlugins)
+ continue;
+ var availablePlugins = appt[pluginType] || [];
+ if (typeof requestedPlugins === "string")
+ requestedPlugins = [requestedPlugins];
+ for (var j = 0; j < requestedPlugins.length; ++j) {
+ if (!availablePlugins.contains(requestedPlugins[j])) {
+ throw "Plugin '" + requestedPlugins[j] + "' of type '" + pluginType
+ + "' was requested, but is not available.";
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/qdoc.js b/share/qbs/module-providers/Qt/templates/qdoc.js
new file mode 100644
index 000000000..72c161c56
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/qdoc.js
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Jake Petroules.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+
+function qdocArgs(product, input, outputDir) {
+ var args = [input.filePath];
+ var qtVersion = ModUtils.moduleProperty(product, "versionMajor");
+ if (qtVersion >= 5) {
+ args.push("-outputdir");
+ args.push(outputDir);
+ }
+
+ return args;
+}
+
+var _qdocDefaultFileTag = "qdoc-output";
+function qdocFileTaggers() {
+ var t = _qdocDefaultFileTag;
+ return {
+ ".qhp": [t, "qhp"],
+ ".qhp.sha1": [t, "qhp-sha1"],
+ ".css": [t, "qdoc-css"],
+ ".html": [t, "qdoc-html"],
+ ".index": [t, "qdoc-index"],
+ ".png": [t, "qdoc-png"]
+ };
+}
+
+function outputArtifacts(product, input) {
+ var tracker = new ModUtils.BlackboxOutputArtifactTracker();
+ tracker.hostOS = product.moduleProperty("qbs", "hostOS");
+ tracker.shellPath = product.moduleProperty("qbs", "shellPath");
+ tracker.defaultFileTags = [_qdocDefaultFileTag];
+ tracker.fileTaggers = qdocFileTaggers();
+ tracker.command = FileInfo.joinPaths(ModUtils.moduleProperty(product, "binPath"),
+ ModUtils.moduleProperty(product, "qdocName"));
+ tracker.commandArgsFunction = function (outputDirectory) {
+ return qdocArgs(product, input, outputDirectory);
+ };
+ tracker.commandEnvironmentFunction = function (outputDirectory) {
+ var env = {};
+ var qdocEnv = ModUtils.moduleProperty(product, "qdocEnvironment");
+ for (var j = 0; j < qdocEnv.length; ++j) {
+ var e = qdocEnv[j];
+ var idx = e.indexOf("=");
+ var name = e.slice(0, idx);
+ var value = e.slice(idx + 1, e.length);
+ env[name] = value;
+ }
+ env["OUTDIR"] = outputDirectory; // Qt 4 replacement for -outputdir
+ return env;
+ };
+ return tracker.artifacts(ModUtils.moduleProperty(product, "qdocOutputDir"));
+}
diff --git a/share/qbs/module-providers/Qt/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js
new file mode 100644
index 000000000..c7829d81b
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/qml.js
@@ -0,0 +1,66 @@
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var Process = require("qbs.Process");
+var TextFile = require("qbs.TextFile");
+
+function scannerData(scannerFilePath, qmlFiles, qmlPath)
+{
+ var p;
+ try {
+ p = new Process();
+ p.exec(scannerFilePath, ["-qmlFiles"].concat(qmlFiles).concat(["-importPath", qmlPath]),
+ true);
+ return JSON.parse(p.readStdOut());
+ } finally {
+ if (p)
+ p.close();
+ }
+}
+
+function getPrlRhs(line)
+{
+ return line.split('=')[1].trim();
+}
+
+function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDir)
+{
+ if (!pluginData.path)
+ return "";
+ var prlFileName = "";
+ if (!targetOS.contains("windows"))
+ prlFileName += "lib";
+ prlFileName += pluginData.plugin;
+ if (buildVariant === "debug" && targetOS.contains("windows"))
+ prlFileName += "d";
+ prlFileName += ".prl";
+ var prlFilePath = FileInfo.joinPaths(pluginData.path, prlFileName);
+ if (!File.exists(prlFilePath)) {
+ console.warn("prl file for QML plugin '" + pluginData.plugin + "' not present at '"
+ + prlFilePath + "'. Linking may fail.");
+ return "";
+ }
+ var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
+ try {
+ var pluginLib;
+ var otherLibs = "";
+ var line;
+ while (line = prlFile.readLine()) {
+ if (line.startsWith("QMAKE_PRL_TARGET"))
+ pluginLib = FileInfo.joinPaths(pluginData.path, getPrlRhs(line));
+ if (line.startsWith("QMAKE_PRL_LIBS")) {
+ var otherLibsLine = ' ' + getPrlRhs(line);
+ if (toolchain.contains("msvc")) {
+ otherLibsLine = otherLibsLine.replace(/ -L/g, " /LIBPATH:");
+ otherLibsLine = otherLibsLine.replace(/-l([^ ]+)/g, "$1" + ".lib");
+ }
+ otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_LIBS\]/g, qtLibDir);
+ otherLibs += otherLibsLine;
+ }
+ }
+ if (!pluginLib)
+ throw "Malformed prl file '" + prlFilePath + "'.";
+ return pluginLib + ' ' + otherLibs;
+ } finally {
+ prlFile.close();
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs
new file mode 100644
index 000000000..2b11abbd5
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/qml.qbs
@@ -0,0 +1,132 @@
+import qbs.TextFile
+import '../QtModule.qbs' as QtModule
+import "qml.js" as Qml
+
+QtModule {
+ qtModuleName: "Qml"
+ Depends { name: "Qt"; submodules: @dependencies@}
+
+ property string qmlImportScannerName: "qmlimportscanner"
+ property string qmlImportScannerFilePath: Qt.core.binPath + '/' + qmlImportScannerName
+ property string qmlPath: @qmlPath@
+
+ property bool generateCacheFiles: false
+ Depends { name: "Qt.qmlcache"; condition: generateCacheFiles; required: false }
+ readonly property bool cachingEnabled: generateCacheFiles && Qt.qmlcache.present
+ property string qmlCacheGenPath
+ Properties {
+ condition: cachingEnabled
+ Qt.qmlcache.qmlCacheGenPath: qmlCacheGenPath || original
+ Qt.qmlcache.installDir: cacheFilesInstallDir || original
+ }
+
+ property string cacheFilesInstallDir
+
+ readonly property string pluginListFilePathDebug: product.buildDirectory + "/plugins.list.d"
+ readonly property string pluginListFilePathRelease: product.buildDirectory + "/plugins.list"
+
+ hasLibrary: @has_library@
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: (isStaticLibrary ? ['@' + pluginListFilePathDebug] : []).concat(@staticLibsDebug@)
+ staticLibsRelease: (isStaticLibrary ? ['@' + pluginListFilePathRelease] : []).concat(@staticLibsRelease@)
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+ moduleConfig: @moduleConfig@
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+ @additionalContent@
+
+ FileTagger {
+ patterns: ["*.qml"]
+ fileTags: ["qt.qml.qml"]
+ }
+
+ FileTagger {
+ patterns: ["*.js"]
+ fileTags: ["qt.qml.js"]
+ }
+
+ Rule {
+ condition: isStaticLibrary
+ multiplex: true
+ requiresInputs: false
+ inputs: ["qt.qml.qml"]
+ outputFileTags: ["cpp", "qt.qml.pluginlist"]
+ outputArtifacts: {
+ var list = [];
+ if (inputs["qt.qml.qml"])
+ list.push({ filePath: "qml_plugin_import.cpp", fileTags: ["cpp"] });
+ list.push({
+ filePath: product.Qt.core.qtBuildVariant === "debug"
+ ? product.Qt.qml.pluginListFilePathDebug
+ : product.Qt.qml.pluginListFilePathRelease,
+ fileTags: ["qt.qml.pluginlist"]
+ });
+ return list;
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ if (inputs["qt.qml.qml"])
+ cmd.description = "Creating " + outputs["cpp"][0].fileName;
+ else
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ var qmlInputs = inputs["qt.qml.qml"];
+ if (!qmlInputs)
+ qmlInputs = [];
+ var scannerData = Qml.scannerData(product.Qt.qml.qmlImportScannerFilePath,
+ qmlInputs.map(function(inp) { return inp.filePath; }),
+ product.Qt.qml.qmlPath);
+ var cppFile;
+ var listFile;
+ try {
+ if (qmlInputs.length > 0)
+ cppFile = new TextFile(outputs["cpp"][0].filePath, TextFile.WriteOnly);
+ listFile = new TextFile(outputs["qt.qml.pluginlist"][0].filePath,
+ TextFile.WriteOnly);
+ if (cppFile)
+ cppFile.writeLine("#include <QtPlugin>");
+ var plugins = { };
+ for (var p in scannerData) {
+ var plugin = scannerData[p].plugin;
+ if (!plugin || plugins[plugin])
+ continue;
+ plugins[plugin] = true;
+ var className = scannerData[p].classname;
+ if (!className) {
+ throw "QML plugin '" + plugin + "' is missing a classname entry. " +
+ "Please add one to the qmldir file.";
+ }
+ if (cppFile)
+ cppFile.writeLine("Q_IMPORT_PLUGIN(" + className + ")");
+ var libs = Qml.getLibsForPlugin(scannerData[p],
+ product.Qt.core.qtBuildVariant,
+ product.qbs.targetOS,
+ product.qbs.toolchain,
+ product.Qt.core.libPath);
+ listFile.write(libs + ' ');
+ }
+ } finally {
+ if (cppFile)
+ cppFile.close();
+ if (listFile)
+ listFile.close();
+ };
+ };
+ return [cmd];
+ }
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/qmlcache.qbs b/share/qbs/module-providers/Qt/templates/qmlcache.qbs
new file mode 100644
index 000000000..9111eb500
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/qmlcache.qbs
@@ -0,0 +1,85 @@
+import qbs.File
+import qbs.FileInfo
+import qbs.Process
+import qbs.Utilities
+
+Module {
+ additionalProductTypes: ["qt.qml.qmlc", "qt.qml.jsc"]
+ validate: {
+ if (!qmlcachegenProbe.found)
+ throw "qmlcachegen unsupported for this target";
+ }
+ property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.binPath, "qmlcachegen")
+ + (qbs.hostOS.contains("windows") ? ".exe" : "")
+ property bool supportsAllArchitectures: Utilities.versionCompare(Qt.core.version, "5.11") >= 0
+ property string installDir
+
+ readonly property stringList _targetArgs: {
+ if (supportsAllArchitectures)
+ return [];
+ function translateArch(arch) {
+ if (arch === "x86")
+ return "i386";
+ if (arch.startsWith("armv"))
+ return "arm";
+ return arch;
+ }
+
+ var args = ["--target-architecture", translateArch(qbs.architecture)];
+ return args;
+ }
+
+ Depends { name: "Qt.core" }
+ Probe {
+ id: qmlcachegenProbe
+
+ property string arch: qbs.architecture
+ property string _qmlCacheGenPath: qmlCacheGenPath
+ property stringList targetArgs: _targetArgs
+ property bool _supportsAllArchitectures: supportsAllArchitectures
+
+ configure: {
+ if (_supportsAllArchitectures) {
+ found = File.exists(_qmlCacheGenPath);
+ return;
+ }
+ var process = new Process();
+ found = false;
+ try {
+ found = process.exec(_qmlCacheGenPath,
+ targetArgs.concat("--check-if-supported")) == 0;
+ if (!found) {
+ var msg = "QML cache generation was requested but is unsupported on "
+ + "architecture '" + arch + "'.";
+ console.warn(msg);
+ }
+ } finally {
+ process.close();
+ }
+ }
+ }
+ Rule {
+ condition: qmlcachegenProbe.found
+ inputs: ["qt.qml.qml", "qt.qml.js"]
+ outputArtifacts: [{
+ filePath: input.fileName + 'c',
+ fileTags: input.fileTags.filter(function(tag) {
+ return tag === "qt.qml.qml" || tag === "qt.qml.js";
+ })[0] + 'c'
+ }]
+ outputFileTags: ["qt.qml.qmlc", "qt.qml.jsc"]
+ prepare: {
+ var args = input.Qt.qmlcache._targetArgs.concat(input.filePath, "-o", output.filePath);
+ var cmd = new Command(input.Qt.qmlcache.qmlCacheGenPath, args);
+ cmd.description = "precompiling " + input.fileName;
+ cmd.highlight = "compiler";
+ return cmd;
+ }
+ }
+ Group {
+ condition: product.Qt.qmlcache.installDir !== undefined
+ fileTagsFilter: product.Qt.qmlcache.additionalProductTypes
+ qbs.install: true
+ qbs.installDir: product.Qt.qmlcache.installDir
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js
new file mode 100644
index 000000000..4f3da2fb0
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/quick.js
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+var FileInfo = require("qbs.FileInfo");
+var Process = require("qbs.Process");
+
+function scanQrc(qrcFilePath) {
+ var absInputDir = FileInfo.path(qrcFilePath);
+ var result = [];
+ var process = new Process();
+ try {
+ var rcc = FileInfo.joinPaths(product.Qt.core.binPath, 'rcc' + product.cpp.executableSuffix);
+ var exitCode = process.exec(rcc, ["--list", qrcFilePath], true);
+ for (;;) {
+ var line = process.readLine();
+ if (!line)
+ break;
+ line = line.trim();
+ line = FileInfo.relativePath(absInputDir, line);
+ result.push(line);
+ }
+ } finally {
+ process.close();
+ }
+ return result;
+}
+
+function qtQuickCompilerOutputName(filePath) {
+ var str = filePath.replace(/\//g, '_');
+ var i = str.lastIndexOf('.');
+ if (i != -1)
+ str = str.substr(0, i) + '_' + str.substr(i + 1);
+ str += ".cpp";
+ return str;
+}
+
+function qtQuickResourceFileOutputName(fileName) {
+ return fileName.replace(/\.qrc$/, "_qtquickcompiler.qrc");
+}
+
+function contentFromQrc(qrcFilePath) {
+ var filesInQrc = scanQrc(qrcFilePath);
+ var qmlJsFiles = filesInQrc.filter(function (filePath) {
+ return (/\.(js|qml)$/).test(filePath);
+ } );
+ var content = {};
+ if (filesInQrc.length - qmlJsFiles.length > 0) {
+ content.newQrcFileName = qtQuickResourceFileOutputName(input.fileName);
+ }
+ content.qmlJsFiles = qmlJsFiles.map(function (filePath) {
+ return {
+ input: filePath,
+ output: qtQuickCompilerOutputName(filePath)
+ };
+ });
+ return content;
+}
diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs
new file mode 100644
index 000000000..5968949c8
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/quick.qbs
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import qbs.File
+import qbs.FileInfo
+import qbs.Process
+import qbs.TextFile
+import qbs.Utilities
+import '../QtModule.qbs' as QtModule
+import 'quick.js' as QC
+
+QtModule {
+ qtModuleName: @name@
+ Depends { name: "Qt"; submodules: @dependencies@.concat("qml-private") }
+
+ hasLibrary: @has_library@
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+ moduleConfig: @moduleConfig@
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+ @additionalContent@
+
+ readonly property bool _compilerIsQmlCacheGen: Utilities.versionCompare(Qt.core.version,
+ "5.11") >= 0
+ readonly property string _generatedLoaderFileName: _compilerIsQmlCacheGen
+ ? "qmlcache_loader.cpp"
+ : "qtquickcompiler_loader.cpp"
+ property string compilerBaseName: (_compilerIsQmlCacheGen ? "qmlcachegen" : "qtquickcompiler")
+ property string compilerFilePath: FileInfo.joinPaths(Qt.core.binPath,
+ compilerBaseName + product.cpp.executableSuffix)
+ property bool compilerAvailable: File.exists(compilerFilePath);
+ property bool useCompiler: compilerAvailable && !_compilerIsQmlCacheGen
+
+ Scanner {
+ condition: useCompiler
+ inputs: 'qt.quick.qrc'
+ searchPaths: [FileInfo.path(input.filePath)]
+ scan: QC.scanQrc(input.filePath)
+ }
+
+ FileTagger {
+ condition: useCompiler
+ patterns: "*.qrc"
+ fileTags: ["qt.quick.qrc"]
+ priority: 100
+ }
+
+ Rule {
+ condition: useCompiler
+ inputs: ["qt.quick.qrc"]
+ Artifact {
+ filePath: input.fileName + ".json"
+ fileTags: ["qt.quick.qrcinfo"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ var content = QC.contentFromQrc(input.filePath);
+ content.qrcFilePath = input.filePath;
+
+ var tf = new TextFile(output.filePath, TextFile.WriteOnly);
+ tf.write(JSON.stringify(content));
+ tf.close();
+ }
+ return cmd;
+ }
+ }
+
+ Rule {
+ condition: useCompiler
+ inputs: ["qt.quick.qrcinfo"]
+ outputFileTags: ["cpp", "qrc"]
+ multiplex: true
+ outputArtifacts: {
+ var infos = [];
+ inputs["qt.quick.qrcinfo"].forEach(function (input) {
+ var tf = new TextFile(input.filePath, TextFile.ReadOnly);
+ infos.push(JSON.parse(tf.readAll()));
+ tf.close();
+ });
+
+ var result = [];
+ infos.forEach(function (info) {
+ if (info.newQrcFileName) {
+ result.push({
+ filePath: info.newQrcFileName,
+ fileTags: ["qrc"]
+ });
+ }
+ info.qmlJsFiles.forEach(function (qmlJsFile) {
+ result.push({
+ filePath: qmlJsFile.output,
+ fileTags: ["cpp"]
+ });
+ });
+ });
+ result.push({
+ filePath: product.Qt.quick._generatedLoaderFileName,
+ fileTags: ["cpp"]
+ });
+ return result;
+ }
+ prepare: {
+ var infos = [];
+ inputs["qt.quick.qrcinfo"].forEach(function (input) {
+ var tf = new TextFile(input.filePath, TextFile.ReadOnly);
+ infos.push(JSON.parse(tf.readAll()));
+ tf.close();
+ });
+
+ var cmds = [];
+ var qmlCompiler = product.Qt.quick.compilerFilePath;
+ var useCacheGen = product.Qt.quick._compilerIsQmlCacheGen;
+ var cmd;
+ var loaderFlags = [];
+
+ function findOutput(fileName) {
+ for (var k in outputs) {
+ for (var i in outputs[k]) {
+ if (outputs[k][i].fileName === fileName)
+ return outputs[k][i];
+ }
+ }
+ throw new Error("Qt Quick compiler rule: Cannot find output artifact "
+ + fileName + ".");
+ }
+
+ infos.forEach(function (info) {
+ if (info.newQrcFileName) {
+ loaderFlags.push("--resource-file-mapping="
+ + FileInfo.fileName(info.qrcFilePath)
+ + ":" + info.newQrcFileName);
+ var args = ["--filter-resource-file",
+ info.qrcFilePath];
+ if (useCacheGen)
+ args.push("-o");
+ args.push(findOutput(info.newQrcFileName).filePath);
+ cmd = new Command(qmlCompiler, args);
+ cmd.description = "generating " + info.newQrcFileName;
+ cmds.push(cmd);
+ } else {
+ loaderFlags.push("--resource-file-mapping=" + info.qrcFilePath);
+ }
+ info.qmlJsFiles.forEach(function (qmlJsFile) {
+ var args = ["--resource=" + info.qrcFilePath, qmlJsFile.input];
+ if (useCacheGen)
+ args.push("-o");
+ args.push(findOutput(qmlJsFile.output).filePath);
+ cmd = new Command(qmlCompiler, args);
+ cmd.description = "generating " + qmlJsFile.output;
+ cmd.workingDirectory = FileInfo.path(info.qrcFilePath);
+ cmds.push(cmd);
+ });
+ });
+
+ var args = loaderFlags.concat(infos.map(function (info) { return info.qrcFilePath; }));
+ if (useCacheGen)
+ args.push("-o");
+ args.push(findOutput(product.Qt.quick._generatedLoaderFileName).filePath);
+ cmd = new Command(qmlCompiler, args);
+ cmd.description = "generating loader source";
+ cmds.push(cmd);
+ return cmds;
+ }
+ }
+}
diff --git a/share/qbs/module-providers/Qt/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs
new file mode 100644
index 000000000..7125ec53c
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/scxml.qbs
@@ -0,0 +1,80 @@
+import qbs.FileInfo
+import qbs.Utilities
+import "../QtModule.qbs" as QtModule
+
+QtModule {
+ qtModuleName: "Scxml"
+
+ property string qscxmlcName: "qscxmlc"
+ property string className
+ property string namespace
+ property bool generateStateMethods
+ property stringList additionalCompilerFlags
+
+ Rule {
+ inputs: ["qt.scxml.compilable"]
+
+ Artifact {
+ filePath: FileInfo.joinPaths(input.moduleProperty("Qt.core", "generatedHeadersDir"),
+ input.baseName + ".h")
+ fileTags: ["hpp", "unmocable"]
+ }
+ Artifact {
+ filePath: input.baseName + ".cpp"
+ fileTags: ["cpp", "unmocable"]
+ }
+
+ prepare: {
+ var compilerName = product.moduleProperty("Qt.scxml", "qscxmlcName");
+ var compilerPath = FileInfo.joinPaths(input.moduleProperty("Qt.core", "binPath"),
+ compilerName);
+ var args = ["--header", outputs["hpp"][0].filePath,
+ "--impl", outputs["cpp"][0].filePath];
+ var cxx98 = input.moduleProperty("cpp", "cxxLanguageVersion") === "c++98";
+ if (cxx98)
+ args.push("-no-c++11");
+ var className = input.moduleProperty("Qt.scxml", "className");
+ if (className)
+ args.push("--classname", className);
+ var namespace = input.moduleProperty("Qt.scxml", "namespace");
+ if (namespace)
+ args.push("--namespace", namespace);
+ if (input.Qt.scxml.generateStateMethods
+ && Utilities.versionCompare(product.Qt.scxml.version, "5.9") >= 0) {
+ args.push("--statemethods");
+ }
+ if (input.Qt.scxml.additionalCompilerFlags)
+ args = args.concat(input.Qt.scxml.additionalCompilerFlags);
+ args.push(input.filePath);
+ var cmd = new Command(compilerPath, args);
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "codegen";
+ return [cmd];
+ }
+ }
+
+ architectures: @archs@
+ targetPlatform: @targetPlatform@
+ staticLibsDebug: @staticLibsDebug@
+ staticLibsRelease: @staticLibsRelease@
+ dynamicLibsDebug: @dynamicLibsDebug@
+ dynamicLibsRelease: @dynamicLibsRelease@
+ linkerFlagsDebug: @linkerFlagsDebug@
+ linkerFlagsRelease: @linkerFlagsRelease@
+ frameworksDebug: @frameworksDebug@
+ frameworksRelease: @frameworksRelease@
+ frameworkPathsDebug: @frameworkPathsDebug@
+ frameworkPathsRelease: @frameworkPathsRelease@
+ libNameForLinkerDebug: @libNameForLinkerDebug@
+ libNameForLinkerRelease: @libNameForLinkerRelease@
+ libFilePathDebug: @libFilePathDebug@
+ libFilePathRelease: @libFilePathRelease@
+ pluginTypes: @pluginTypes@
+ moduleConfig: @moduleConfig@
+
+ cpp.defines: @defines@
+ cpp.includePaths: @includes@
+ cpp.libraryPaths: @libraryPaths@
+
+ @additionalContent@
+}