aboutsummaryrefslogtreecommitdiffstats
path: root/share/qbs/module-providers/Qt
diff options
context:
space:
mode:
Diffstat (limited to 'share/qbs/module-providers/Qt')
-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@
+}