diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2023-10-27 10:29:06 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2023-10-27 10:29:06 +0200 |
commit | 96ac6f85b71e384ca63eccc005581451d8a7f7d0 (patch) | |
tree | a53015c8102765f2d4e8172e89788b3d09d41ab3 | |
parent | 7eee1c926779624106196125e21fe5c683fe1831 (diff) | |
parent | d99256dd79460628aafb5fa34a8dde7761ff7b1c (diff) |
Merge 2.2 into master
Change-Id: I20763ec5ea0f96f01837b59b76e7bfcbcbf681e2
22 files changed, 1806 insertions, 1469 deletions
diff --git a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs new file mode 100644 index 000000000..47f162955 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** 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-pkg-config-probe.js" as PkgConfigProbeConfigure + +Probe { + // Inputs + + property string _executableFilePath + property stringList _extraPaths + property stringList _libDirs + property bool _staticMode: false + + property path _sysroot + + // TODO: deprecate in 2.2, remove in 2.3 + property bool _mergeDependencies: false + + // Output + property var packages + property var packagesByModuleName + property var brokenPackages + property varList qtInfos + + configure: { + var result = PkgConfigProbeConfigure.configure( + _executableFilePath, _extraPaths, _libDirs, _staticMode, _sysroot, _mergeDependencies); + packages = result.packages; + packagesByModuleName = result.packagesByModuleName; + brokenPackages = result.brokenPackages; + qtInfos = result.qtInfos; + found = true; + } +} diff --git a/share/qbs/imports/qbs/Probes/QmakeProbe.qbs b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs new file mode 100644 index 000000000..c50c6c851 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs @@ -0,0 +1,41 @@ + +/**************************************************************************** +** +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** 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 "qmake-probe.js" as QmakeProbeConfigure + +Probe { + property stringList qmakePaths + property varList qtInfos + + configure: { + qtInfos = QmakeProbeConfigure.configure(qmakePaths); + } +} diff --git a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js new file mode 100644 index 000000000..a14827353 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** 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 Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var PkgConfig = require("qbs.PkgConfig"); +var ProviderUtils = require("qbs.ProviderUtils"); +var Process = require("qbs.Process"); +var QmakeProbeConfigure = require("qmake-probe.js"); + +// We should probably use BinaryProbe instead in the provider +function getPkgConfigExecutable() { + function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } + + var pathValue = Environment.getEnv("PATH"); + if (!pathValue) + return undefined; + var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); + for (var i = 0; i < dirs.length; ++i) { + var candidate = + FileInfo.joinPaths(dirs[i], "pkg-config" + FileInfo.executableSuffix()); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + return canonicalCandidate; + } + return undefined; +} + +function configureQt(pkg) { + var packageName = pkg.baseFileName; + if (packageName === "QtCore" + || packageName === "Qt5Core" + || packageName === "Qt6Core") { + var binDir = pkg.variables["bindir"] || pkg.variables["host_bins"]; + if (!binDir) { + if (packageName === "QtCore") { // Qt4 does not have host_bins + var mocLocation = pkg.variables["moc_location"]; + if (!mocLocation) { + console.warn("No moc_location variable in " + packageName); + return; + } + binDir = FileInfo.path(mocLocation); + } else { + console.warn("No 'bindir' or 'host_bins' variable in " + packageName); + return; + } + } + var suffix = FileInfo.executableSuffix(); + var qmakePaths = [FileInfo.joinPaths(binDir, "qmake" + suffix)]; + return QmakeProbeConfigure.configure(qmakePaths); + } +} + +function configure( + executableFilePath, extraPaths, libDirs, staticMode, sysroot, mergeDependencies) { + + var result = {}; + result.packages = []; + result.packagesByModuleName = {}; + result.brokenPackages = []; + result.qtInfos = {}; + + var options = {}; + options.libDirs = libDirs; + options.sysroot = sysroot; + if (options.sysroot) + options.allowSystemLibraryPaths = true; + options.staticMode = staticMode; + options.mergeDependencies = mergeDependencies; + options.extraPaths = extraPaths; + if (options.sysroot && !options.libDirs) { + options.libDirs = [ + options.sysroot + "/usr/lib/pkgconfig", + options.sysroot + "/usr/share/pkgconfig" + ]; + } + if (!options.libDirs) { + // if we have pkg-config installed, let's ask it for its search paths (since + // built-in search paths can differ between platforms) + var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(); + if (executable) { + var p = new Process() + if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { + var stdout = p.readStdOut().trim(); + // TODO: pathListSeparator? depends on what pkg-config prints on Windows + options.libDirs = stdout ? stdout.split(':'): []; + } + } + } + var pkgConfig = new PkgConfig(options); + result.packages = pkgConfig.packages(); + for (var packageName in result.packages) { + var pkg = result.packages[packageName]; + var moduleName = ProviderUtils.pkgConfigToModuleName(packageName); + result.packagesByModuleName[moduleName] = pkg; + + if (packageName.startsWith("Qt")) { + if (!sysroot) { + var infos = configureQt(pkg); + if (infos !== undefined) + result.qtInfos = infos; + } + } + } + return result; +} diff --git a/share/qbs/imports/qbs/Probes/qmake-probe.js b/share/qbs/imports/qbs/Probes/qmake-probe.js new file mode 100644 index 000000000..ad0cb3fa7 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qmake-probe.js @@ -0,0 +1,1249 @@ +/**************************************************************************** +** +** 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 Host = require("qbs.Host"); +var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } + +function getQmakeFilePaths(qmakeFilePaths) { + if (qmakeFilePaths && qmakeFilePaths.length > 0) + return qmakeFilePaths; + console.info("Detecting Qt installations..."); + var filePaths = []; + var pathValue = Environment.getEnv("PATH"); + if (pathValue) { + var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); + for (var i = 0; i < dirs.length; ++i) { + var candidate = FileInfo.joinPaths(dirs[i], "qmake" + FileInfo.executableSuffix()); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") + candidate = canonicalCandidate; + if (!filePaths.contains(candidate)) { + console.info("Found Qt at '" + FileInfo.toNativeSeparators(candidate) + "'."); + filePaths.push(candidate); + } + } + } + if (filePaths.length === 0) { + console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " + + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " + + "to point a qmake executable."); + } + return filePaths; +} + +function queryQmake(qmakeFilePath) { + var qmakeProcess = new Process; + qmakeProcess.exec(qmakeFilePath, ["-query"]); + if (qmakeProcess.exitCode() !== 0) { + throw "The qmake executable '" + FileInfo.toNativeSeparators(qmakeFilePath) + + "' failed with exit code " + qmakeProcess.exitCode() + "."; + } + 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 msvcCompilerVersionForYear(year) { + var mapping = { + "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", + "2017": "19.1", "2019": "19.2" + }; + 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 guessMinimumWindowsVersion(qtProps) { + if (qtProps.mkspecName.startsWith("winrt-")) + return "10.0"; + if (!ProviderUtils.isDesktopWindowsQt(qtProps)) + return ""; + if (qtProps.qtMajorVersion >= 6) + return "10.0"; + 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 = ProviderUtils.isMinGwQt(qtProps); + + // Some Linux distributions rename the qtmain library. + var qtMainCandidates = ["qtmain"]; + if (isMinGW && qtProps.qtMajorVersion === 5) + qtMainCandidates.push("qt5main"); + if (qtProps.qtMajorVersion === 6) + qtMainCandidates.push("Qt6EntryPoint"); + + 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 && ProviderUtils.qtNeedsDSuffix(qtProps)) + 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 '" + + FileInfo.toNativeSeparators(qtProps.libraryPath) + + "'. You will not be able to link Qt applications."); + } + return result; +} + +function getQtProperties(qmakeFilePath) { + 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.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS"); + qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") + || pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.installPath = 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 (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) { + qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS") + || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); + } + + // QML tools were only moved in Qt 6.2. + qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + // qhelpgenerator was only moved in Qt 6.3. + qtProps.helpGeneratorLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.3") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + 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 (Host.os().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 '" + FileInfo.toNativeSeparators(qtProps.mkspecPath) + "' does not exist"; + + // Starting with qt 5.14, android sdk provides multi-abi + if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 + && qtProps.mkspecPath.contains("android")) { + var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qdevice.pri")); + qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); + } + + // determine MSVC version + if (ProviderUtils.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) { + var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, + "qmake.conf")); + for (var i = 0; i < lines.length; ++i) { + var line = lines[i].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; + } + } + 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 '" + + FileInfo.toNativeSeparators(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 && 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.libDir = ""; + 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 (ProviderUtils.qtIsFramework(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 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[ + ProviderUtils.qtLibNameForLinker(m, qtProps, true)] = m.libFilePathDebug; + linkerNamesToFilePathsRelease[ + ProviderUtils.qtLibNameForLinker(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, androidAbi) { + 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(ProviderUtils.qtLibNameForLinker(platformSupportModule, qtProps, debugBuild)); + } + if (modInfo.name === "qios") { + flags.push("-force_load", FileInfo.joinPaths( + qtProps.pluginPath, "platforms", + ProviderUtils.qtLibBaseName( + modInfo, "libqios", debugBuild, qtProps) + ".a")); + } + } + var prlFilePath = modInfo.isPlugin + ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) + : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); + var libDir = prlFilePath; + if (ProviderUtils.qtIsFramework(modInfo, qtProps)) { + prlFilePath = FileInfo.joinPaths( + prlFilePath, + ProviderUtils.qtLibraryBaseName(modInfo, qtProps, false) + ".framework"); + libDir = prlFilePath; + if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) + prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); + } + var baseName = ProviderUtils.qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (!qtProps.mkspecName.startsWith("win") && !ProviderUtils.qtIsFramework(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... + // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file + if (androidAbi.length > 0 + && modInfo.name !== "QtBootstrap" + && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" + && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) + && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 + || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { + prlFilePath += "_"; + prlFilePath += androidAbi; + } + + 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; + + var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); + for (i = 0; i < parts.length; ++i) { + var part = parts[i]; + part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + part = part.replace("$$[QT_INSTALL_PLUGINS]", qtProps.pluginPath); + part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath); + 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") { + // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread + // functionality is included in libc. + if (androidAbi.length === 0) + 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) { + // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file. + // So the pri file variable QMAKE_LIBS_LIBX points to the library + if (modInfo.isExternal) { + libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] : + modInfo.staticLibrariesRelease[0]; + } + if (!libFilePath || !File.exists(libFilePath)) + libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); + if (nonExistingPrlFiles.contains(prlFilePath)) + return; + nonExistingPrlFiles.push(prlFilePath); + if (modInfo.mustExist) { + console.warn("Could not open prl file '" + + FileInfo.toNativeSeparators(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, androidAbi) { + doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); + doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); +} + +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 getFileContentsRecursively(filePath) { + var file = new TextFile(filePath, TextFile.ReadOnly); + var lines = splitNonEmpty(file.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 '" + + FileInfo.toNativeSeparators(filePath) + "'"); + continue; + } + var includedFilePath = line.slice(offset, closingParenPos); + if (!FileInfo.isAbsolutePath(includedFilePath)) + includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); + var includedContents = getFileContentsRecursively(includedFilePath); + var j = i; + for (var k = 0; k < includedContents.length; ++k) + lines.splice(++j, 0, includedContents[k]); + lines.splice(i--, 1); + } + file.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 '" + + FileInfo.toNativeSeparators(filePath) + "'"); + break; + } + } else { + endIndex = rhs.indexOf(' ', startIndex + 1); + if (endIndex === -1) + endIndex = rhs.length; + } + paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) + .replace("$$PWD", FileInfo.path(filePath)))); + 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.qbsName]) + revDeps[depmod.qbsName] = []; + revDeps[depmod.qbsName].push(module); + } + } + } + + function roots(modules) { + var result = []; + for (i = 0; i < modules.length; ++i) { + var module = modules[i] + if (module.dependencies.length === 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(); + + var deps = revDeps[module.qbsName]; + for (i = 0; i < (deps || []).length; ++i) + traverse(deps[i], libs); + + 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, androidAbi) { + 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 externalFileNamePrefix = "qt_ext_"; + var moduleFileNamePrefix = "qt_lib_"; + var pluginFileNamePrefix = "qt_plugin_"; + var moduleFileNameSuffix = ".pri"; + var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix); + var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix); + var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); + if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix + || !priFileName.endsWith(moduleFileNameSuffix)) { + continue; + } + var moduleInfo = makeQtModuleInfo(); + moduleInfo.isPlugin = fileHasPluginPrefix; + moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix; + var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal + ? externalFileNamePrefix : 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 = getFileContentsRecursively(priFilePath); + if (moduleInfo.isExternal) { + moduleInfo.name = "qt" + moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + for (var k = 0; k < lines.length; ++k) { + var extLine = lines[k].trim(); + var extFirstEqualsOffset = extLine.indexOf('='); + if (extFirstEqualsOffset === -1) + continue; + var extKey = extLine.slice(0, extFirstEqualsOffset).trim(); + var extValue = extLine.slice(extFirstEqualsOffset + 1).trim(); + if (!extKey.startsWith("QMAKE_") || !extValue) + continue; + + var elements = extKey.split('_'); + if (elements.length >= 3) { + if (elements[1] === "LIBS") { + extValue = extValue.replace("/home/qt/work/qt/qtbase/lib", + qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath); + if (elements.length === 4 ) { + if (elements[3] === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements.length === 5 ) { + // That's for "x86_64" + var abi = elements[3] + '_' + elements[4]; + if (abi === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements[1] === "INCDIR") { + moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]", + qtProps.includePath)); + } + } + } + moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB"); + moduleInfo.mustExist = false; + } else { + 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_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } + } else if (key.endsWith(".libs")) { + var libDirs = extractPaths(value, priFilePath); + if (libDirs.length === 1) { + moduleInfo.libDir = libDirs[0] + .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } else { + moduleInfo.libDir = 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["extends"].splice(k, 1); + moduleInfo.pluginData.autoLoad = false; + break; + } + } + } else if (key.endsWith(".CLASS_NAME")) { + moduleInfo.pluginData.className = value; + } + } + } + if (hasV2 && !hasModuleEntry && !moduleInfo.isStaticLibrary) + moduleInfo.hasLibrary = false; + + // Fix include paths for Apple frameworks. + // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. + if (!hasV2 && ProviderUtils.qtIsFramework(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, androidAbi); + + modules.push(moduleInfo); + if (moduleInfo.qbsName === "testlib") + addTestModule(modules); + if (moduleInfo.qbsName === "designercomponents-private") + addDesignerComponentsModule(modules); + } + + replaceQtLibNamesWithFilePath(modules, qtProps); + removeDuplicatedDependencyLibs(modules); + + return modules; +} + +function getQtInfo(qmakeFilePath) { + if (!File.exists(qmakeFilePath)) { + throw "The specified qmake file path '" + + FileInfo.toNativeSeparators(qmakeFilePath) + "' does not exist."; + } + var qtProps = getQtProperties(qmakeFilePath); + var androidAbis = []; + if (qtProps.androidAbis !== undefined) { + // Multiple androidAbis detected: Qt >= 5.14 + androidAbis = qtProps.androidAbis; + } else { + // Single abi detected: Qt < 5.14 + androidAbis.push(''); + } + if (androidAbis.length > 1) + console.info("Qt with multiple abi detected: '" + androidAbis + "'"); + + var result = {}; + result.qtProps = qtProps; + result.abiInfos = []; + result.qmakeFilePath = qmakeFilePath; + for (a = 0; a < androidAbis.length; ++a) { + var abiInfo = {}; + if (androidAbis.length > 1) + console.info("Found abi '" + androidAbis[a] + "'..."); + abiInfo.androidAbi = androidAbis[a]; + var allModules = qtProps.qtMajorVersion < 5 + ? allQt4Modules(qtProps) : allQt5Modules(qtProps, androidAbis[a]);; + abiInfo.modules = {}; + for (var i = 0; i < allModules.length; ++i) { + var module = allModules[i]; + abiInfo.modules[module.qbsName] = module; + } + abiInfo.pluginsByType = {}; + abiInfo.nonEssentialPlugins = []; + for (var moduleName in abiInfo.modules) { + var m = abiInfo.modules[moduleName]; + if (m.isPlugin) { + if (!abiInfo.pluginsByType[m.pluginData.type]) + abiInfo.pluginsByType[m.pluginData.type] = []; + abiInfo.pluginsByType[m.pluginData.type].push(m.name); + if (!m.pluginData.autoLoad) + abiInfo.nonEssentialPlugins.push(m.name); + } + } + + result.abiInfos.push(abiInfo); + } + return result; +} + +function configure(qmakeFilePaths) { + var result = []; + qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths); + if (!qmakeFilePaths || qmakeFilePaths.length === 0) + return result; + for (var i = 0; i < qmakeFilePaths.length; ++i) { + var qtInfo = {}; + try { + console.info("Getting info about Qt at '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "'..."); + qtInfo = getQtInfo(qmakeFilePaths[i]); + result.push(qtInfo); + } catch (e) { + console.warn("Error getting info about Qt for '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "': " + e); + throw e; + } + } + return result; +} diff --git a/share/qbs/imports/qbs/ProviderUtils/provider-utils.js b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js new file mode 100644 index 000000000..06e703a7a --- /dev/null +++ b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** 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 Utilities = require("qbs.Utilities"); + +function pkgConfigToModuleName(packageName) { + return packageName.replace(/\./g, '-'); +} + +function msvcPrefix() { return "win32-msvc"; } + +function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } + +function isMinGwQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); +} + +function isDesktopWindowsQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-") || isMinGwQt(qtProps); +} + +function qtNeedsDSuffix(qtProps) { + return !isMinGwQt(qtProps) + || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 + || qtProps.configItems.contains("debug_and_release"); +} + +function qtIsFramework(modInfo, qtProps) { + if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) + return false; + var modulesNeverBuiltAsFrameworks = [ + "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" + ]; + + if (qtProps.qtMajorVersion <= 5) { + modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 + } + + return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); +} + +function qtLibBaseName(modInfo, libName, debugBuild, qtProps) { + var name = libName; + if (qtProps.mkspecName.startsWith("win")) { + if (debugBuild && qtNeedsDSuffix(qtProps)) + 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 (!qtIsFramework(modInfo, qtProps) + && qtProps.buildVariant.contains("debug") + && (!qtProps.buildVariant.contains("release") || debugBuild)) { + name += "_debug"; + } + } + return name; +} + +function qtModuleNameWithoutPrefix(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 qtLibraryBaseName(modInfo, qtProps, debugBuild) { + if (modInfo.isPlugin) + return qtLibBaseName(modInfo, 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 = ""; + if (!modInfo.isExternal) { + libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; + if (qtProps.qtMajorVersion >= 5 && !qtIsFramework(modInfo, qtProps) && !libNameBroken) + libName += qtProps.qtMajorVersion; + } + libName += qtModuleNameWithoutPrefix(modInfo); + if (!modInfo.isExternal) + libName += qtProps.qtLibInfix; + return qtLibBaseName(modInfo, libName, debugBuild, qtProps); +} + +function qtLibNameForLinker(modInfo, qtProps, debugBuild) { + if (!modInfo.hasLibrary) + return undefined; + var libName = qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (qtProps.mkspecName.contains("msvc")) + libName += ".lib"; + return libName; +} diff --git a/share/qbs/module-providers/Qt/templates/QtModule.qbs b/share/qbs/imports/qbs/base/QtModule.qbs index 35421436f..35421436f 100644 --- a/share/qbs/module-providers/Qt/templates/QtModule.qbs +++ b/share/qbs/imports/qbs/base/QtModule.qbs diff --git a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs b/share/qbs/imports/qbs/base/QtPlugin.qbs index 883e34465..883e34465 100644 --- a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs +++ b/share/qbs/imports/qbs/base/QtPlugin.qbs diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs index be026b1e0..0d036c04d 100644 --- a/share/qbs/module-providers/Qt/provider.qbs +++ b/share/qbs/module-providers/Qt/provider.qbs @@ -1,6 +1,14 @@ import "setup-qt.js" as SetupQt +import qbs.Probes ModuleProvider { + Probes.QmakeProbe { + id: probe + qmakePaths: qmakeFilePaths + } property stringList qmakeFilePaths - relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path) + readonly property varList _qtInfos: probe.qtInfos + condition: moduleName.startsWith("Qt.") + isEager: false + relativeSearchPaths: SetupQt.doSetup(moduleName, _qtInfos, outputBaseDir, path) } diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index f65c80d77..99c4f1d58 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -37,1243 +37,13 @@ ** ****************************************************************************/ -var Environment = require("qbs.Environment"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); -var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); -var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); 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 getQmakeFilePaths(qmakeFilePaths) { - if (qmakeFilePaths && qmakeFilePaths.length > 0) - return qmakeFilePaths; - console.info("Detecting Qt installations..."); - var filePaths = []; - var pathValue = Environment.getEnv("PATH"); - if (pathValue) { - var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "qmake" + FileInfo.executableSuffix()); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") - candidate = canonicalCandidate; - if (!filePaths.contains(candidate)) { - console.info("Found Qt at '" + toNative(candidate) + "'."); - filePaths.push(candidate); - } - } - } - if (filePaths.length === 0) { - console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " - + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " - + "to point a qmake executable."); - } - return filePaths; -} - -function queryQmake(qmakeFilePath) { - var qmakeProcess = new Process; - qmakeProcess.exec(qmakeFilePath, ["-query"]); - if (qmakeProcess.exitCode() !== 0) { - throw "The qmake executable '" + toNative(qmakeFilePath) + "' failed with exit code " - + qmakeProcess.exitCode() + "."; - } - 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", "2019": "19.2" - }; - 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.qtMajorVersion >= 6) - return "10.0"; - 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 needsDSuffix(qtProps) { - return !isForMinGw(qtProps) || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 - || qtProps.configItems.contains("debug_and_release"); -} - -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"); - if (qtProps.qtMajorVersion === 6) - qtMainCandidates.push("Qt6EntryPoint"); - - 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 && needsDSuffix(qtProps)) - 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 abiToArchitecture(abi) { - switch (abi) { - case "armeabi-v7a": - return "armv7a"; - case "arm64-v8a": - return "arm64"; - case "x86": - case "x86_64": - default: - return abi; - } -} - -function getQtProperties(qmakeFilePath) { - 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.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS"); - qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") - || pathQueryValue(queryResult, "QT_INSTALL_BINS"); - qtProps.installPath = 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 (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) { - qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS") - || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); - } - - // QML tools were only moved in Qt 6.2. - qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0 - ? qtProps.libExecPath : qtProps.binaryPath; - - // qhelpgenerator was only moved in Qt 6.3. - qtProps.helpGeneratorLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.3") >= 0 - ? qtProps.libExecPath : qtProps.binaryPath; - - 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 (Host.os().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"; - - // Starting with qt 5.14, android sdk provides multi-abi - if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 - && qtProps.mkspecPath.contains("android")) { - var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, - "qdevice.pri")); - qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); - } - - // 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) { - var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, - "qmake.conf")); - for (var i = 0; i < lines.length; ++i) { - var line = lines[i].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; - } - } - 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 && 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.libDir = ""; - 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", "harfbuzzng" - ]; - - if (qtProps.qtMajorVersion <= 5) { - modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 - } - - return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); -} - -function libBaseName(modInfo, libName, debugBuild, qtProps) { - var name = libName; - if (qtProps.mkspecName.startsWith("win")) { - if (debugBuild && needsDSuffix(qtProps)) - 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, 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 = ""; - if (!modInfo.isExternal) { - libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; - if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken) - libName += qtProps.qtMajorVersion; - } - libName += moduleNameWithoutPrefix(modInfo); - if (!modInfo.isExternal) - 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, androidAbi) { - 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) - : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); - var libDir = prlFilePath; - if (isFramework(modInfo, qtProps)) { - prlFilePath = FileInfo.joinPaths(prlFilePath, - libraryBaseName(modInfo, qtProps, false) + ".framework"); - libDir = prlFilePath; - if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) - prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); - } - 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... - // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file - if (androidAbi.length > 0 - && modInfo.name !== "QtBootstrap" - && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" - && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) - && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 - || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { - prlFilePath += "_"; - prlFilePath += androidAbi; - } - - 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; - - var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); - for (i = 0; i < parts.length; ++i) { - var part = parts[i]; - var defaultInstallPrefix = "/Users/qt/work/qt/qtbase/build/target"; - part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); - part = part.replace("$$[QT_INSTALL_PLUGINS]", qtProps.pluginPath); - part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath); - part = part.replace(defaultInstallPrefix, qtProps.installPrefixPath); - 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") { - // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread - // functionality is included in libc. - if (androidAbi.length === 0) - 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) { - // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file. - // So the pri file variable QMAKE_LIBS_LIBX points to the library - if (modInfo.isExternal) { - libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] : - modInfo.staticLibrariesRelease[0]; - } - if (!libFilePath || !File.exists(libFilePath)) - libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); - if (nonExistingPrlFiles.contains(prlFilePath)) - return; - nonExistingPrlFiles.push(prlFilePath); - if (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, androidAbi) { - doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); - doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); -} - -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 getFileContentsRecursively(filePath) { - var file = new TextFile(filePath, TextFile.ReadOnly); - var lines = splitNonEmpty(file.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(filePath) + "'"); - continue; - } - var includedFilePath = line.slice(offset, closingParenPos); - if (!FileInfo.isAbsolutePath(includedFilePath)) - includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); - var includedContents = getFileContentsRecursively(includedFilePath); - var j = i; - for (var k = 0; k < includedContents.length; ++k) - lines.splice(++j, 0, includedContents[k]); - lines.splice(i--, 1); - } - file.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(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) - .replace("$$PWD", FileInfo.path(filePath)))); - 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.qbsName]) - revDeps[depmod.qbsName] = []; - revDeps[depmod.qbsName].push(module); - } - } - } - - function roots(modules) { - var result = []; - for (i = 0; i < modules.length; ++i) { - var module = modules[i] - if (module.dependencies.length === 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(); - - var deps = revDeps[module.qbsName]; - for (i = 0; i < (deps || []).length; ++i) - traverse(deps[i], libs); - - 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, androidAbi) { - 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 externalFileNamePrefix = "qt_ext_"; - var moduleFileNamePrefix = "qt_lib_"; - var pluginFileNamePrefix = "qt_plugin_"; - var moduleFileNameSuffix = ".pri"; - var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix); - var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix); - var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); - if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix - || !priFileName.endsWith(moduleFileNameSuffix)) { - continue; - } - var moduleInfo = makeQtModuleInfo(); - moduleInfo.isPlugin = fileHasPluginPrefix; - moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix; - var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal - ? externalFileNamePrefix : 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 = getFileContentsRecursively(priFilePath); - if (moduleInfo.isExternal) { - moduleInfo.name = "qt" + moduleInfo.qbsName; - moduleInfo.isStaticLibrary = true; - for (var k = 0; k < lines.length; ++k) { - var extLine = lines[k].trim(); - var extFirstEqualsOffset = extLine.indexOf('='); - if (extFirstEqualsOffset === -1) - continue; - var extKey = extLine.slice(0, extFirstEqualsOffset).trim(); - var extValue = extLine.slice(extFirstEqualsOffset + 1).trim(); - if (!extKey.startsWith("QMAKE_") || !extValue) - continue; - - var elements = extKey.split('_'); - if (elements.length >= 3) { - if (elements[1] === "LIBS") { - extValue = extValue.replace("/home/qt/work/qt/qtbase/lib", - qtProps.libraryPath); - extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); - extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath); - if (elements.length === 4 ) { - if (elements[3] === androidAbi) { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else if (elements.length === 5 ) { - // That's for "x86_64" - var abi = elements[3] + '_' + elements[4]; - if (abi === androidAbi) { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else if (elements[1] === "INCDIR") { - moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]", - qtProps.includePath)); - } - } - } - moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB"); - moduleInfo.mustExist = false; - } else { - 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_HOST_LIB_BASE", qtProps.hostLibraryPath) - .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); - } - } else if (key.endsWith(".libs")) { - var libDirs = extractPaths(value, priFilePath); - if (libDirs.length === 1) { - moduleInfo.libDir = libDirs[0] - .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) - .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); - } else { - moduleInfo.libDir = 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["extends"].splice(k, 1); - moduleInfo.pluginData.autoLoad = false; - break; - } - } - } else if (key.endsWith(".CLASS_NAME")) { - moduleInfo.pluginData.className = value; - } - } - } - if (hasV2 && !hasModuleEntry && !moduleInfo.isStaticLibrary) - 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, androidAbi); - - 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 = []; @@ -1335,7 +105,7 @@ function qbsTargetPlatformFromQtMkspec(qtProps) { return "solaris"; if (mkspec.startsWith("vxworks-")) return "vxworks"; - if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-")) + if (ProviderUtils.isDesktopWindowsQt(qtProps) || mkspec.startsWith("winrt-")) return "windows"; } @@ -1391,7 +161,7 @@ function defaultQpaPlugin(module, qtProps) { function libraryFileTag(module, qtProps) { if (module.isStaticLibrary) return "staticlibrary"; - return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") + return ProviderUtils.isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") ? "dynamiclibrary_import" : "dynamiclibrary"; } @@ -1419,6 +189,19 @@ function minVersionJsString(minVersion) { return !minVersion ? "" : ModUtils.toJSLiteral(minVersion); } +function abiToArchitecture(abi) { + switch (abi) { + case "armeabi-v7a": + return "armv7a"; + case "arm64-v8a": + return "arm64"; + case "x86": + case "x86_64": + default: + return abi; + } +} + function replaceSpecialValues(content, module, qtProps, abi) { var architectures = []; if (abi.length > 0) @@ -1447,7 +230,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { availableBuildVariants: ModUtils.toJSLiteral(qtProps.buildVariant), staticBuild: ModUtils.toJSLiteral(qtProps.staticBuild), frameworkBuild: ModUtils.toJSLiteral(qtProps.frameworkBuild), - name: ModUtils.toJSLiteral(moduleNameWithoutPrefix(module)), + name: ModUtils.toJSLiteral(ProviderUtils.qtModuleNameWithoutPrefix(module)), has_library: ModUtils.toJSLiteral(module.hasLibrary), dependencies: ModUtils.toJSLiteral(module.dependencies), includes: ModUtils.toJSLiteral(module.includePaths), @@ -1464,10 +247,12 @@ function replaceSpecialValues(content, module, qtProps, abi) { frameworksRelease: ModUtils.toJSLiteral(module.frameworksRelease), libFilePathDebug: ModUtils.toJSLiteral(module.libFilePathDebug), libFilePathRelease: ModUtils.toJSLiteral(module.libFilePathRelease), - libNameForLinkerDebug: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, true)), + libNameForLinkerDebug: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, true)), pluginTypes: ModUtils.toJSLiteral(module.supportedPluginTypes), moduleConfig: ModUtils.toJSLiteral(module.config), - libNameForLinkerRelease: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, false)), + libNameForLinkerRelease: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, false)), entryPointLibsDebug: ModUtils.toJSLiteral(qtProps.entryPointLibsDebug), entryPointLibsRelease: ModUtils.toJSLiteral(qtProps.entryPointLibsRelease), minWinVersion_optional: minVersionJsString(qtProps.windowsVersion), @@ -1527,7 +312,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { + indent + indent + "fileTags: [\"qt.core.metatypes\"]\n" + indent + "}"; } - if (module.hasLibrary && !isFramework(module, qtProps)) { + if (module.hasLibrary && !ProviderUtils.qtIsFramework(module, qtProps)) { if (additionalContent) additionalContent += "\n" + indent; additionalContent += "Group {\n"; @@ -1569,7 +354,7 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all pluginMap, nonEssentialPlugins) { if (!File.makePath(targetDirectory)) { - throw "Cannot create directory '" + toNative(targetDirectory) + "'."; + throw "Cannot create directory '" + FileInfo.toNativeSeparators(targetDirectory) + "'."; } var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName), TextFile.ReadOnly); @@ -1591,67 +376,64 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all targetFile.close(); } -function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location) { - if (!File.exists(qmakeFilePath)) - throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist."; - var qtProps = getQtProperties(qmakeFilePath); - var androidAbis = []; - if (qtProps.androidAbis !== undefined) - // Multiple androidAbis detected: Qt >= 5.14 - androidAbis = qtProps.androidAbis; - else - // Single abi detected: Qt < 5.14 - androidAbis.push(''); - if (androidAbis.length > 1) - console.info("Qt with multiple abi detected: '" + androidAbis + "'"); +function setupOneQt(moduleName, qtInfo, outputBaseDir, uniquify, location) { + var qtProps = qtInfo.qtProps; var relativeSearchPaths = []; - for (a = 0; a < androidAbis.length; ++a) { - if (androidAbis.length > 1) - console.info("Configuring abi '" + androidAbis[a] + "'..."); - var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : - allQt5Modules(qtProps,androidAbis[a]); - 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) : ""; - relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbis[a]); + for (a = 0; a < qtInfo.abiInfos.length; ++a) { + var abiInfo = qtInfo.abiInfos[a]; + var androidAbi = abiInfo.androidAbi; + if (qtInfo.abiInfos.length > 1) + console.info("Configuring abi '" + androidAbi + "'..."); + + var relativeSearchPath = uniquify ? Utilities.getHash(qtInfo.qmakeFilePath) : ""; + relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbi); var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt"); - if (File.exists(qbsQtModuleBaseDir)) - File.remove(qbsQtModuleBaseDir); + // TODO: + // if (File.exists(qbsQtModuleBaseDir)) + // File.remove(qbsQtModuleBaseDir); var allFiles = []; - copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("plugin_support.qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, - androidAbis[a], location, allFiles, undefined, pluginsByType, - nonEssentialPlugins); + if (moduleName === "plugin_support") { + copyTemplateFile("plugin_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, + androidAbi, location, allFiles, undefined, abiInfo.pluginsByType, + abiInfo.nonEssentialPlugins); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "android_support") { + // 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, androidAbi, location, allFiles); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "qmlcache") { + var qmlcacheStr = "qmlcache"; + if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, + "qmlcachegen" + FileInfo.executableSuffix()))) { + copyTemplateFile(qmlcacheStr + ".qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, + androidAbi, location, allFiles); + } + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; + if (abiInfo.modules[moduleName] !== undefined) { + var module = abiInfo.modules[moduleName]; var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName); var moduleTemplateFileName; + if (module.qbsName === "core") { moduleTemplateFileName = "core.qbs"; - copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "gui") { moduleTemplateFileName = "gui.qbs"; @@ -1659,62 +441,51 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location) { moduleTemplateFileName = "scxml.qbs"; } else if (module.qbsName === "dbus") { moduleTemplateFileName = "dbus.qbs"; - copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "qml") { moduleTemplateFileName = "qml.qbs"; - copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - var qmlcacheStr = "qmlcache"; - if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, - "qmlcachegen" + FileInfo.executableSuffix()))) { - copyTemplateFile(qmlcacheStr + ".qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, - androidAbis[a], location, allFiles); - } } else if (module.qbsName === "quick") { moduleTemplateFileName = "quick.qbs"; - copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.isPlugin) { moduleTemplateFileName = "plugin.qbs"; } else { moduleTemplateFileName = "module.qbs"; } - copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbis[a], + copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbi, location, allFiles, module); + relativeSearchPaths.push(relativeSearchPath); } - - // 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, androidAbis[a], location, allFiles); - relativeSearchPaths.push(relativeSearchPath) } return relativeSearchPaths; } -function doSetup(qmakeFilePaths, outputBaseDir, location) { - qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths); - if (!qmakeFilePaths || qmakeFilePaths.length === 0) +function doSetup(moduleName, qtInfos, outputBaseDir, location) { + if (!qtInfos || qtInfos.length === 0) return []; - var uniquifySearchPath = qmakeFilePaths.length > 1; + var uniquifySearchPath = qtInfos.length > 1; var allSearchPaths = []; - for (var i = 0; i < qmakeFilePaths.length; ++i) { + moduleName = moduleName.substring(3); + for (var i = 0; i < qtInfos.length; ++i) { try { - console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'..."); - var searchPaths = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath, + console.info("Setting up Qt module '" + moduleName + "' for Qt located at '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "'."); + var searchPaths = setupOneQt(moduleName, qtInfos[i], outputBaseDir, uniquifySearchPath, location); if (searchPaths.length > 0) { for (var j = 0; j < searchPaths.length; ++j ) allSearchPaths.push(searchPaths[j]); - console.info("Qt was set up successfully."); } } catch (e) { - console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e); + console.warn("Error setting up Qt module '" + moduleName + "' for '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "': " + e); + throw e; } } return allSearchPaths; diff --git a/share/qbs/module-providers/Qt/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs index 08e1e49e7..bbda5a4f2 100644 --- a/share/qbs/module-providers/Qt/templates/dbus.qbs +++ b/share/qbs/module-providers/Qt/templates/dbus.qbs @@ -1,6 +1,5 @@ import qbs.FileInfo import qbs.ModUtils -import "../QtModule.qbs" as QtModule import "dbus.js" as DBus QtModule { diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs index 6ba5e48c4..db491eafe 100644 --- a/share/qbs/module-providers/Qt/templates/gui.qbs +++ b/share/qbs/module-providers/Qt/templates/gui.qbs @@ -1,7 +1,6 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Utilities -import '../QtModule.qbs' as QtModule QtModule { qtModuleName: "Gui" diff --git a/share/qbs/module-providers/Qt/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs index ccafe4122..9f0313ab5 100644 --- a/share/qbs/module-providers/Qt/templates/module.qbs +++ b/share/qbs/module-providers/Qt/templates/module.qbs @@ -1,5 +1,3 @@ -import '../QtModule.qbs' as QtModule - QtModule { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} diff --git a/share/qbs/module-providers/Qt/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs index e73e2a4d9..34d4f4153 100644 --- a/share/qbs/module-providers/Qt/templates/plugin.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin.qbs @@ -1,5 +1,3 @@ -import '../QtPlugin.qbs' as QtPlugin - QtPlugin { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs index de1318695..0a938e58d 100644 --- a/share/qbs/module-providers/Qt/templates/qml.qbs +++ b/share/qbs/module-providers/Qt/templates/qml.qbs @@ -1,7 +1,6 @@ import qbs.FileInfo import qbs.Host import qbs.TextFile -import '../QtModule.qbs' as QtModule import "qml.js" as Qml QtModule { diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs index 27f49cbd6..5fc4aa349 100644 --- a/share/qbs/module-providers/Qt/templates/quick.qbs +++ b/share/qbs/module-providers/Qt/templates/quick.qbs @@ -33,7 +33,6 @@ import qbs.FileInfo import qbs.Process import qbs.TextFile import qbs.Utilities -import '../QtModule.qbs' as QtModule import 'quick.js' as QC QtModule { diff --git a/share/qbs/module-providers/Qt/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs index b800dfe70..757041b52 100644 --- a/share/qbs/module-providers/Qt/templates/scxml.qbs +++ b/share/qbs/module-providers/Qt/templates/scxml.qbs @@ -1,6 +1,5 @@ import qbs.FileInfo import qbs.Utilities -import "../QtModule.qbs" as QtModule QtModule { qtModuleName: "Scxml" diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs index 4eda7cb13..ca04f0613 100644 --- a/share/qbs/module-providers/qbspkgconfig.qbs +++ b/share/qbs/module-providers/qbspkgconfig.qbs @@ -42,6 +42,8 @@ import qbs.File import qbs.FileInfo import qbs.ModUtils import qbs.PkgConfig +import qbs.ProviderUtils +import qbs.Probes import qbs.Process import qbs.TextFile @@ -64,30 +66,19 @@ ModuleProvider { removalVersion: "2.3.0" } - relativeSearchPaths: { - - function exeSuffix(qbs) { return FileInfo.executableSuffix(); } - - // we need Probes in Providers... - function getPkgConfigExecutable(qbs) { - function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } + Probes.QbsPkgConfigProbe { + id: theProbe + // TODO: without explicit 'parent' we do not have access to the fake "qbs" scope + _executableFilePath: parent.executableFilePath + _extraPaths: parent.extraPaths + _sysroot: parent.sysroot + _libDirs: parent.libDirs + _staticMode: parent.staticMode + } - var pathValue = Environment.getEnv("PATH"); - if (!pathValue) - return undefined; - var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); - var suffix = exeSuffix(qbs); - var filePaths = []; - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "pkg-config" + suffix); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - return canonicalCandidate; - } - return undefined; - } + isEager: false + relativeSearchPaths: { function getModuleInfo(pkg, staticMode) { var result = {}; @@ -129,14 +120,12 @@ ModuleProvider { return result; } - function getModuleName(packageName) { return packageName.replace(/\./g, '-'); } - function getModuleDependencies(pkg, staticMode) { var mapper = function(p) { var result = {}; for (var key in p) result[key] = p[key]; - result.name = getModuleName(result.name); + result.name = ProviderUtils.pkgConfigToModuleName(result.name); return result; } var result = pkg.requires.map(mapper); @@ -145,138 +134,85 @@ ModuleProvider { return result; } - console.debug("Running pkgconfig provider.") + console.debug("Running pkgconfig provider for " + moduleName + "."); var outputDir = FileInfo.joinPaths(outputBaseDir, "modules"); File.makePath(outputDir); - var options = {}; - options.libDirs = libDirs; - options.sysroot = sysroot; - if (options.sysroot) - options.allowSystemLibraryPaths = true; - options.staticMode = staticMode; - options.mergeDependencies = mergeDependencies; - options.extraPaths = extraPaths; - if (options.sysroot && !options.libDirs) { - options.libDirs = [ - sysroot + "/usr/lib/pkgconfig", - sysroot + "/usr/share/pkgconfig" - ]; - } - if (!options.libDirs) { - // if we have pkg-config installed, let's ask it for its search paths (since - // built-in search paths can differ between platforms) - var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(qbs); - if (executable) { - var p = new Process() - if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { - var stdout = p.readStdOut().trim(); - // TODO: pathListSeparator? depends on what pkg-config prints on Windows - options.libDirs = stdout ? stdout.split(':'): []; - } - } - } - - function setupQt(pkg) { - var packageName = pkg.baseFileName; - if (packageName === "QtCore" - || packageName === "Qt5Core" - || packageName === "Qt6Core") { - var binDir = pkg.variables["bindir"] || pkg.variables["host_bins"]; - if (!binDir) { - if (packageName === "QtCore") { // Qt4 does not have host_bins - var mocLocation = pkg.variables["moc_location"]; - if (!mocLocation) { - console.warn("No moc_location variable in " + packageName); - return; - } - binDir = FileInfo.path(mocLocation); - } else { - console.warn("No 'bindir' or 'host_bins' variable in " + packageName); - return; - } - } - var suffix = exeSuffix(qbs); - var qmakePaths = [FileInfo.joinPaths(binDir, "qmake" + suffix)]; - var qtProviderDir = FileInfo.joinPaths(path, "Qt"); - SetupQt.doSetup(qmakePaths, outputBaseDir, qtProviderDir, qbs); - } - } - + // TODO: ponder how we can solve forward mapping with Packages so we can fill deps var moduleMapping = { "protobuf": "protobuflib", "grpc++": "grpcpp" } - - var pkgConfig = new PkgConfig(options); - - var brokenPackages = []; - var packages = pkgConfig.packages(); - for (var packageName in packages) { - var pkg = packages[packageName]; - if (pkg.isBroken) { - brokenPackages.push(pkg); - continue; - } - if (packageName.startsWith("Qt")) { - if (!sysroot) { - setupQt(pkg); - } - continue; + var reverseMapping = {} + for (var key in moduleMapping) + reverseMapping[moduleMapping[key]] = key + + if (moduleName.startsWith("Qt")) { + function setupQt(packageName, qtInfos) { + if (qtInfos === undefined) + return []; + var qtProviderDir = FileInfo.joinPaths(path, "Qt"); + return SetupQt.doSetup(packageName, qtInfos, outputBaseDir, qtProviderDir); } - var moduleName = getModuleName(moduleMapping[packageName] - ? moduleMapping[packageName] - : packageName); - var moduleInfo = getModuleInfo(pkg, staticMode); - var deps = getModuleDependencies(pkg, staticMode); - var moduleDir = FileInfo.joinPaths(outputDir, moduleName); - File.makePath(moduleDir); - var module = - new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); - module.writeLine("Module {"); - module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); - module.writeLine(" Depends { name: 'cpp' }"); - deps.forEach(function(dep) { - var depName = getModuleName( - moduleMapping[dep.name] ? moduleMapping[dep.name] : dep.name); - module.write(" Depends { name: '" + depName + "'"); - for (var k in dep) { - if (k === "name") - continue; - module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); - } - module.writeLine(" }"); - }) - function writeProperty(propertyName) { - var value = moduleInfo[propertyName]; - if (value.length !== 0) { // skip empty props for simplicity of the module file - module.writeLine( - " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); - } + if (!sysroot) { + return setupQt(moduleName, theProbe.qtInfos); } - writeProperty("includePaths"); - writeProperty("systemIncludePaths"); - writeProperty("defines"); - writeProperty("commonCompilerFlags"); - writeProperty("dynamicLibraries"); - writeProperty("staticLibraries"); - writeProperty("libraryPaths"); - writeProperty("frameworks"); - writeProperty("frameworkPaths"); - writeProperty("driverLinkerFlags"); - module.writeLine("}"); - module.close(); + return []; } - if (brokenPackages.length !== 0) { - console.warn("Failed to load some pkg-config packages:"); - for (var i = 0; i < brokenPackages.length; ++i) { - console.warn(" " + brokenPackages[i].filePath - + ": " + brokenPackages[i].errorText); + var pkg; + pkg = theProbe.packages[reverseMapping[moduleName]]; + if (pkg === undefined) + pkg = theProbe.packagesByModuleName[moduleName]; + if (pkg === undefined) + return []; + + if (pkg.isBroken) { + console.warn("Failed to load " + moduleName + " as it's pkg-config package is broken"); + return []; + } + var moduleInfo = getModuleInfo(pkg, staticMode); + var deps = getModuleDependencies(pkg, staticMode); + + var moduleDir = FileInfo.joinPaths(outputDir, moduleName); + File.makePath(moduleDir); + var module = + new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); + module.writeLine("Module {"); + module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + module.writeLine(" Depends { name: 'cpp' }"); + deps.forEach(function(dep) { + var depName = ProviderUtils.pkgConfigToModuleName( + moduleMapping[dep.name] ? moduleMapping[dep.name] : dep.name); + module.write(" Depends { name: '" + depName + "'"); + for (var k in dep) { + if (k === "name") + continue; + module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); + } + module.writeLine(" }"); + }) + function writeProperty(propertyName) { + var value = moduleInfo[propertyName]; + if (value.length !== 0) { // skip empty props for simplicity of the module file + module.writeLine( + " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); } } + writeProperty("includePaths"); + writeProperty("systemIncludePaths"); + writeProperty("defines"); + writeProperty("commonCompilerFlags"); + writeProperty("dynamicLibraries"); + writeProperty("staticLibraries"); + writeProperty("libraryPaths"); + writeProperty("frameworks"); + writeProperty("frameworkPaths"); + writeProperty("driverLinkerFlags"); + module.writeLine("}"); + module.close(); return ""; } diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index d9213e951..b60608682 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -406,8 +406,8 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat // Flags for library search paths var allLibraryPaths = Cpp.collectLibraryPaths(product); - if (systemRunPaths.length > 0) - allLibraryPaths = allLibraryPaths.filter(isNotSystemRunPath); + var builtIns = product.cpp.compilerLibraryPaths + allLibraryPaths = allLibraryPaths.filter(function(p) { return !builtIns.includes(p); }); args = args.concat(allLibraryPaths.map(function(path) { return product.cpp.libraryPathFlag + path })); escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs)); diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp index 18287e39f..f263a3dbd 100644 --- a/src/lib/corelib/buildgraph/buildgraphloader.cpp +++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp @@ -858,8 +858,10 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt bool BuildGraphLoader::checkConfigCompatibility() { const TopLevelProjectConstPtr restoredProject = m_result.loadedProject; - if (m_parameters.topLevelProfile().isEmpty()) + if (m_parameters.topLevelProfile().isEmpty()) { m_parameters.setTopLevelProfile(restoredProject->profile()); + m_parameters.expandBuildConfiguration(); + } if (!m_parameters.overrideBuildGraphData()) { if (!m_parameters.overriddenValues().empty() && m_parameters.overriddenValues() != restoredProject->overriddenValues) { diff --git a/src/lib/corelib/loader/modulepropertymerger.cpp b/src/lib/corelib/loader/modulepropertymerger.cpp index abadd5b34..c9dd56ccf 100644 --- a/src/lib/corelib/loader/modulepropertymerger.cpp +++ b/src/lib/corelib/loader/modulepropertymerger.cpp @@ -176,6 +176,11 @@ void ModulePropertyMerger::mergePropertyFromLocalInstance( Item *loadingItem, const QString &loadingName, Item *globalInstance, const QString &name, const ValuePtr &value) { + if (loadingItem->type() == ItemType::Project) { + throw ErrorInfo(Tr::tr("Module properties cannot be set in Project items."), + value->location()); + } + const PropertyDeclaration decl = globalInstance->propertyDeclaration(name); if (!decl.isValid()) { if (value->type() == Value::ItemValueType || value->createdByPropertiesBlock()) @@ -287,6 +292,11 @@ bool ModulePropertyMerger::doFinalMerge(const PropertyDeclaration &propertyDecl, if (propertyValue == chosenValue) return false; + std::vector<ValuePtr> candidates = propertyValue->candidates(); + candidates.erase(std::find(candidates.begin(), candidates.end(), chosenValue)); + chosenValue->setCandidates(candidates); + chosenValue->addCandidate(propertyValue); + propertyValue->setCandidates({}); propertyValue = chosenValue; return true; } diff --git a/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs b/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs new file mode 100644 index 000000000..667088c17 --- /dev/null +++ b/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs @@ -0,0 +1,3 @@ +Project { + qbs.sysroot: "/" +} diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 0310205a7..6a29876dc 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -1019,6 +1019,8 @@ void TestLanguage::erroneousFiles_data() << "missing-js-file-module.qbs.*Cannot open '.*javascriptfile.js'"; QTest::newRow("frozen-object") << "'key' is read-only"; QTest::newRow("frozen-object-list") << "object is not extensible"; + QTest::newRow("module-property-binding-in-project") + << "Module properties cannot be set in Project items"; } void TestLanguage::erroneousFiles() |