diff options
45 files changed, 1906 insertions, 2593 deletions
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index f559b481d..a87e88bdc 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -675,7 +675,7 @@ For example, a profile for building C++ applications contains at least the installation path and the type of the compiler toolchain. A profile for building Qt applications contains the toolchain-specific properties as well - as the installation paths of the Qt modules. + as \l{Qt-specific Module Provider Properties}{the path to the Qt installation}. This topic describes profiles stored in the \QBS settings. In some cases it might be beneficial to keep profiles explicitly in the project sources. This @@ -707,7 +707,7 @@ \endcode You have successfully built your first \QBS project. If you want to build - projects that use Qt, additional steps are necessary. Please refer to + projects that use Qt, additional steps might be necessary. Please refer to \l{Managing Qt Versions} for more information. \section1 Global Preferences @@ -754,10 +754,10 @@ \section1 Introduction - To let \QBS know where the Qt build or Qt version is that you want to use, - you must register it. + If your environment has the right \c qmake binary in its \c PATH and is also set up + properly for a matching toolchain, then you do not necessarily need a profile + to build projects with a Qt dependency. Otherwise, you should create one: - Register a Qt version like this: \code qbs setup-qt /usr/bin/qmake myqt \endcode diff --git a/doc/reference/modules/qt-modules.qdoc b/doc/reference/modules/qt-modules.qdoc index 638160104..df5219ccd 100644 --- a/doc/reference/modules/qt-modules.qdoc +++ b/doc/reference/modules/qt-modules.qdoc @@ -55,6 +55,22 @@ The Qt modules that have properties and relevant file tags are described in separate topics. + \section1 Qt-specific Module Provider Properties + + Looking up a Qt installation happens via a \l{Module Providers}{module provider}. + By default, if a dependency to a Qt module is encountered, \QBS collects all Qt installations + it can find. This lookup happens by searching for \c qmake executables in the \c PATH + environment variable. Alternatively, you can explicitly tell \QBS which Qt + installations it should consider by setting the \c Qt.qmakeFilePaths + \l{Parameterizing Module Providers}{module provider property}. In that case, + the environment will be ignored. For instance, with the following Linux command line, + \QBS will build the project against a custom Qt instead of the standard one in \c{/usr/bin}: + \code + $ qbs moduleProviders.Qt.qmakeFilePaths:/opt/myqt/bin/qmake + \endcode + You can also set the module provider property in a profile. The simplest way to do + this is via the \l setup-qt tool. + \section1 List of Submodules \table @@ -31,10 +31,8 @@ defineTest(minQtVersion) { TEMPLATE = subdirs corelib.file = src/lib/corelib/corelib.pro -setupqtprofilelib.subdir = src/lib/qtprofilesetup -setupqtprofilelib.depends = corelib src_app.subdir = src/app -src_app.depends = setupqtprofilelib +src_app.depends = corelib src_libexec.subdir = src/libexec src_plugins.subdir = src/plugins CONFIG(shared, static|shared): src_plugins.depends = corelib @@ -48,7 +46,6 @@ qbs_use_bundled_qtscript { } SUBDIRS += \ corelib\ - setupqtprofilelib\ src_app\ src_libexec\ src_plugins\ diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs new file mode 100644 index 000000000..33083c51d --- /dev/null +++ b/share/qbs/module-providers/Qt/provider.qbs @@ -0,0 +1,6 @@ +import "setup-qt.js" as SetupQt + +ModuleProvider { + property stringList qmakeFilePaths + relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path, qbs) +} diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js new file mode 100644 index 000000000..0f0755409 --- /dev/null +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -0,0 +1,1504 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var Process = require("qbs.Process"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } +function toNative(p) { return FileInfo.toNativeSeparators(p); } +function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; } + +function getQmakeFilePaths(qmakeFilePaths, qbs) { + if (qmakeFilePaths && qmakeFilePaths.length > 0) + return qmakeFilePaths; + console.info("Detecting Qt installations..."); + var pathValue = Environment.getEnv("PATH"); + if (!pathValue) + return []; + var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator); + var suffix = exeSuffix(qbs); + var filePaths = []; + for (var i = 0; i < dirs.length; ++i) { + var candidate = FileInfo.canonicalPath(FileInfo.joinPaths(dirs[i], "qmake" + suffix)); + if (candidate && File.exists(candidate) && !filePaths.contains(candidate)) { + console.info("Found Qt at '" + toNative(candidate) + "'."); + filePaths.push(candidate); + } + } + return filePaths; +} + +function queryQmake(qmakeFilePath) { + var qmakeProcess = new Process; + qmakeProcess.exec(qmakeFilePath, ["-query"]); + var queryResult = {}; + while (!qmakeProcess.atEnd()) { + var line = qmakeProcess.readLine(); + var index = (line || "").indexOf(":"); + if (index !== -1) + queryResult[line.slice(0, index)] = line.slice(index + 1).trim(); + } + return queryResult; +} + +function pathQueryValue(queryResult, key) { + var p = queryResult[key]; + if (p) + return FileInfo.fromNativeSeparators(p); +} + +function readFileContent(filePath) { + var f = new TextFile(filePath, TextFile.ReadOnly); + var content = f.readAll(); + f.close(); + return content; +} + +// TODO: Don't do the split every time... +function configVariable(configContent, key) { + var configContentLines = configContent.split('\n'); + var regexp = new RegExp("\\s*" + key + "\\s*\\+{0,1}=(.*)"); + for (var i = 0; i < configContentLines.length; ++i) { + var line = configContentLines[i]; + var match = regexp.exec(line); + if (match) + return match[1].trim(); + } +} + +function configVariableItems(configContent, key) { + return splitNonEmpty(configVariable(configContent, key), ' '); +} + +function msvcPrefix() { return "win32-msvc"; } + +function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } + +function msvcCompilerVersionForYear(year) { + var mapping = { + "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", + "2017": "19.1" + }; + return mapping[year]; +} + +function msvcCompilerVersionFromMkspecName(mkspecName) { + return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length)); +} + +function addQtBuildVariant(qtProps, buildVariantName) { + if (qtProps.qtConfigItems.contains(buildVariantName)) + qtProps.buildVariant.push(buildVariantName); +} + +function checkForStaticBuild(qtProps) { + if (qtProps.qtMajorVersion >= 5) + return qtProps.qtConfigItems.contains("static"); + if (qtProps.frameworkBuild) + return false; // there are no Qt4 static frameworks + var isWin = qtProps.mkspecName.startsWith("win"); + var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath; + var coreLibFiles = File.directoryEntries(libDir, File.Files) + .filter(function(fp) { return fp.contains("Core"); }); + if (coreLibFiles.length === 0) + throw "Could not determine whether Qt is a static build."; + for (var i = 0; i < coreLibFiles.length; ++i) { + if (Utilities.isSharedLibrary(coreLibFiles[i])) + return false; + } + return true; +} + +function isForMinGw(qtProps) { + return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); +} + +function targetsDesktopWindows(qtProps) { + return qtProps.mkspecName.startsWith("win32-") || isForMinGw(qtProps); +} + +function guessMinimumWindowsVersion(qtProps) { + if (qtProps.mkspecName.startsWith("winrt-")) + return "10.0"; + if (!targetsDesktopWindows(qtProps)) + return ""; + if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64") + return "5.2" + var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/); + if (match) { + var msvcVersion = match[1]; + if (msvcVersion < 2012) + return "5.0"; + return "5.1"; + } + return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1"; +} + +function fillEntryPointLibs(qtProps, debug) { + result = []; + var isMinGW = isForMinGw(qtProps); + + // Some Linux distributions rename the qtmain library. + var qtMainCandidates = ["qtmain"]; + if (isMinGW && qtProps.qtMajorVersion === 5) + qtMainCandidates.push("qt5main"); + + for (var i = 0; i < qtMainCandidates.length; ++i) { + var baseNameCandidate = qtMainCandidates[i]; + var qtmain = qtProps.libraryPath + '/'; + if (isMinGW) + qtmain += "lib"; + qtmain += baseNameCandidate + qtProps.qtLibInfix; + if (debug) + qtmain += 'd'; + if (isMinGW) { + qtmain += ".a"; + } else { + qtmain += ".lib"; + if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0) + result.push("Shell32.lib"); + } + if (File.exists(qtmain)) { + result.push(qtmain); + break; + } + } + if (result.length === 0) { + console.warn("Could not find the qtmain library at '" + toNative(qtProps.libraryPath) + + "'. You will not be able to link Qt applications."); + } + return result; +} + +function getQtProperties(qmakeFilePath, qbs) { + var queryResult = queryQmake(qmakeFilePath); + var qtProps = {}; + qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS"); + qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS"); + qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") + || pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS"); + qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML"); + qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS"); + qtProps.qtVersion = queryResult.QT_VERSION; + + var mkspecsBaseSrcPath; + if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) { + qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"), + "mkspecs"); + mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"), + "mkspecs"); + } else { + qtProps.mkspecBasePath = FileInfo.joinPaths + (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs"); + } + if (!File.exists(qtProps.mkspecBasePath)) + throw "Cannot extract the mkspecs directory."; + + var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qconfig.pri")); + qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION")); + qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION")); + qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION")); + qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE"); + qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || ""; + qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH") + || configVariable(qconfigContent, "QT_ARCH") || "x86"; + qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); + qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); + + // retrieve the mkspec + if (qtProps.qtMajorVersion >= 5) { + qtProps.mkspecName = queryResult.QMAKE_XSPEC; + qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName); + if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath)) + qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName); + } else { + if (qbs.hostOS.contains("windows")) { + var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default"); + var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf")); + qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL"); + if (!File.exists(qtProps.mkspecPath)) { + // Work around QTBUG-28792. + // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? + var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m); + if (match) { + qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths( + baseDirPath, match[1])); + } + } + } else { + qtProps.mkspecPath = FileInfo.canonicalPath( + FileInfo.joinPaths(qtProps.mkspecBasePath, "default")); + } + + // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: + // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ + qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath); + + qtProps.mkspecName = qtProps.mkspecPath; + var idx = qtProps.mkspecName.lastIndexOf('/'); + if (idx !== -1) + qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1); + } + if (!File.exists(qtProps.mkspecPath)) + throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist"; + + // determine MSVC version + if (isMsvcQt(qtProps)) { + var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION"); + var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION"); + var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION"); + if (msvcMajor && msvcMinor && msvcPatch) + qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch; + else + qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName); + } + + // determine whether we have a framework build + qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx") + && qtProps.configItems.contains("qt_framework"); + + // determine whether Qt is built with debug, release or both + qtProps.buildVariant = []; + addQtBuildVariant(qtProps, "debug"); + addQtBuildVariant(qtProps, "release"); + + qtProps.staticBuild = checkForStaticBuild(qtProps); + + // determine whether user apps require C++11 + if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild) + qtProps.configItems.push("c++11"); + + // Set the minimum operating system versions appropriate for this Qt version + qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps); + if (qtProps.windowsVersion) { // Is target OS Windows? + if (qtProps.buildVariant.contains("debug")) + qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true); + if (qtProps.buildVariant.contains("release")) + qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false); + } else if (qtProps.mkspecPath.contains("macx")) { + if (qtProps.qtMajorVersion >= 5) { + try { + var qmakeConf = new TextFile(FileInfo.joinPaths(qtProps.mkspecPath, "qmake.conf"), + TextFile.ReadOnly); + while (!qmakeConf.atEof()) { + var line = qmakeConf.readLine().trim(); + match = line.match + (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/); + if (match) { + var platform = match[1]; + var version = match[2]; + if (platform === "MACOSX") + qtProps.macosVersion = version; + else if (platform === "IOS") + qtProps.iosVersion = version; + else if (platform === "TVOS") + qtProps.tvosVersion = version; + else if (platform === "WATCHOS") + qtProps.watchosVersion = version; + } + } + } + catch (e) {} + finally { + if (qmakeConf) + qmakeConf.close(); + } + var isMac = qtProps.mkspecName !== "macx-ios-clang" + && qtProps.mkspecName !== "macx-tvos-clang" + && qtProps.mkspecName !== "macx-watchos-clang"; + if (isMac) { + // Qt 5.0.x placed the minimum version in a different file + if (!qtProps.macosVersion) + qtProps.macosVersion = "10.6"; + + // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 + if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0 + && qtProps.qtConfigItems.contains("c++11")) { + qtProps.macosVersion = "10.7"; + } + } + } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) { + var qconfigDir = qtProps.frameworkBuild + ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers") + : FileInfo.joinPaths(qtProps.includePath, "Qt"); + try { + var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"), + TextFile.ReadOnly); + var qtCocoaBuild = false; + var ok = true; + do { + line = qconfig.readLine(); + if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) { + qtCocoaBuild = true; + break; + } + } while (!qconfig.atEof()); + qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4"; + } + catch (e) {} + finally { + if (qconfig) + qconfig.close(); + } + if (!qtProps.macosVersion) { + throw "Could not determine whether Qt is using Cocoa or Carbon from '" + + toNative(qconfig.filePath()) + "'."; + } + } + } else if (qtProps.mkspecPath.contains("android")) { + if (qtProps.qtMajorVersion >= 5) + qtProps.androidVersion = "2.3"; + else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8) + qtProps.androidVersion = "1.6"; // Necessitas + } + return qtProps; +} + +function makePluginData() { + var pluginData = {}; + pluginData.type = undefined; + pluginData.className = undefined; + pluginData.autoLoad = true; + pluginData["extends"] = []; + return pluginData; +} + +function makeQtModuleInfo(name, qbsName, deps) { + var moduleInfo = {}; + moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files. + if (moduleInfo.name === undefined) + moduleInfo.name = ""; + moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix. + moduleInfo.dependencies = deps || []; // qbs names. + if (moduleInfo.qbsName !== "core" && !moduleInfo.dependencies.contains("core")) + moduleInfo.dependencies.unshift("core"); + moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private"); + moduleInfo.hasLibrary = !moduleInfo.isPrivate; + moduleInfo.isStaticLibrary = false; + moduleInfo.isPlugin = false; + moduleInfo.mustExist = true; + moduleInfo.modulePrefix = ""; // empty value means "Qt". + moduleInfo.version = undefined; + moduleInfo.includePaths = []; + moduleInfo.compilerDefines = []; + moduleInfo.staticLibrariesDebug = []; + moduleInfo.staticLibrariesRelease = []; + moduleInfo.dynamicLibrariesDebug = []; + moduleInfo.dynamicLibrariesRelease = []; + moduleInfo.linkerFlagsDebug = []; + moduleInfo.linkerFlagsRelease = []; + moduleInfo.libFilePathDebug = undefined; + moduleInfo.libFilePathRelease = undefined; + moduleInfo.frameworksDebug = []; + moduleInfo.frameworksRelease = []; + moduleInfo.frameworkPathsDebug = []; + moduleInfo.frameworkPathsRelease = []; + moduleInfo.libraryPaths = []; + moduleInfo.config = []; + moduleInfo.supportedPluginTypes = []; + moduleInfo.pluginData = makePluginData(); + return moduleInfo; +} + +function frameworkHeadersPath(qtModuleInfo, qtProps) { + return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers"); +} + +function qt4ModuleIncludePaths(qtModuleInfo, qtProps) { + var paths = []; + if (isFramework(qtModuleInfo, qtProps)) + paths.push(frameworkHeadersPath(qtModuleInfo, qtProps)); + else + paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name)); + return paths; +} + +// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users +// for that. +function addTestModule(modules) { + var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]); + testModule.hasLibrary = false; + modules.push(testModule); +} + +// See above. +function addDesignerComponentsModule(modules) { + var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents", + ["designercomponents-private"]); + module.hasLibrary = false; + modules.push(module); +} + +function isFramework(modInfo, qtProps) { + if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) + return false; + var modulesNeverBuiltAsFrameworks = [ + "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "uitools", "harfbuzzng" + ]; + return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); +} + +function libBaseName(modInfo, libName, debugBuild, qtProps) { + var name = libName; + if (qtProps.mkspecName.startsWith("win")) { + if (debugBuild) + name += 'd'; + if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5) + name += qtProps.qtMajorVersion; + } + if (qtProps.mkspecName.contains("macx") + || qtProps.mkspecName.contains("ios") + || qtProps.mkspecName.contains("darwin")) { + if (!isFramework(modInfo, qtProps) + && qtProps.buildVariant.contains("debug") + && (!qtProps.buildVariant.contains("release") || debugBuild)) { + name += "_debug"; + } + } + return name; +} + +function moduleNameWithoutPrefix(modInfo) { + if (modInfo.name === "Phonon") + return "phonon"; + if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt")) + return modInfo.name.slice(2); // Strip off "Qt". + if (modInfo.name.startsWith(modInfo.modulePrefix)) + return modInfo.name.slice(modInfo.modulePrefix.length); + return modInfo.name; +} + +function libraryBaseName(modInfo, qtProps, debugBuild) { + if (modInfo.isPlugin) + return libBaseName(modInfo, name, debugBuild, qtProps); + + // Some modules use a different naming scheme, so it doesn't get boring. + var libNameBroken = modInfo.name === "Enginio" + || modInfo.name === "DataVisualization" + || modInfo.name === "Phonon"; + + var libName = !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; + if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken) + libName += qtProps.qtMajorVersion; + libName += moduleNameWithoutPrefix(modInfo); + libName += qtProps.qtLibInfix; + return libBaseName(modInfo, libName, debugBuild, qtProps); +} + +function libNameForLinker(modInfo, qtProps, debugBuild) { + if (!modInfo.hasLibrary) + return undefined; + var libName = libraryBaseName(modInfo, qtProps, debugBuild); + if (qtProps.mkspecName.contains("msvc")) + libName += ".lib"; + return libName; +} + +function guessLibraryFilePath(prlFilePath, libDir, qtProps) { + var baseName = FileInfo.baseName(prlFilePath); + var prefixCandidates = ["", "lib"]; + var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"]; + for (var i = 0; i < prefixCandidates.length; ++i) { + var prefix = prefixCandidates[i]; + for (var j = 0; j < suffixCandidates.length; ++j) { + var suffix = suffixCandidates[j]; + var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix); + if (File.exists(candidate)) + return candidate; + } + } +} + +function doReplaceQtLibNamesWithFilePath(namePathMap, libList) { + for (var i = 0; i < libList.length; ++i) { + var lib = libList[i]; + var path = namePathMap[lib]; + if (path) + libList[i] = path; + } +} + +function replaceQtLibNamesWithFilePath(modules, qtProps) { + // We don't want to add the libraries for Qt modules via "-l", because of the + // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, + // we pull them in using the full file path. + var linkerNamesToFilePathsDebug = {}; + var linkerNamesToFilePathsRelease = {}; + for (var i = 0; i < modules.length; ++i) { + var m = modules[i]; + linkerNamesToFilePathsDebug[libNameForLinker(m, qtProps, true)] = m.libFilePathDebug; + linkerNamesToFilePathsRelease[libNameForLinker(m, qtProps, false)] = m.libFilePathRelease; + } + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.dynamicLibrariesRelease); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.staticLibrariesRelease); + } +} + +function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) { + if (!modInfo.hasLibrary) + return; // Can happen for Qt4 convenience modules, like "widgets". + + if (debugBuild) { + if (!qtProps.buildVariant.contains("debug")) + return; + var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"]; + for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) { + var m = modulesNeverBuiltAsDebug[i]; + if (modInfo.qbsName === m || modInfo.qbsName === m + "-private") + return; + } + } else if (!qtProps.buildVariant.contains("release")) { + return; + } + + var libs = modInfo.isStaticLibrary + ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease) + : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease); + var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease; + var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease; + var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease; + var libFilePath; + + if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) { + libs.push("z", "m"); + if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { + var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport"); + libs.push(libNameForLinker(platformSupportModule, qtProps, debugBuild)); + } + if (modInfo.name === "qios") { + flags.push("-force_load", FileInfo.joinPaths( + qtProps.pluginPath, "platforms", + libBaseName(modInfo, "libqios", debugBuild, qtProps) + ".a")); + } + } + var prlFilePath = modInfo.isPlugin + ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) + : qtProps.libraryPath; + if (isFramework(modInfo, qtProps)) { + prlFilePath = FileInfo.joinPaths(prlFilePath, + libraryBaseName(modInfo, qtProps, false) + ".framework"); + } + var libDir = prlFilePath; + var baseName = libraryBaseName(modInfo, qtProps, debugBuild); + if (!qtProps.mkspecName.startsWith("win") && !isFramework(modInfo, qtProps)) + baseName = "lib" + baseName; + prlFilePath = FileInfo.joinPaths(prlFilePath, baseName); + var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win") + && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; + if (isNonStaticQt4OnWindows) + prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... + prlFilePath += ".prl"; + try { + var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); + while (!prlFile.atEof()) { + var line = prlFile.readLine().trim(); + var equalsOffset = line.indexOf('='); + if (equalsOffset === -1) + continue; + if (line.startsWith("QMAKE_PRL_TARGET")) { + var isMingw = qtProps.mkspecName.startsWith("win") + && qtProps.mkspecName.contains("g++"); + var isQtVersionBefore56 = qtProps.qtMajorVersion < 5 + || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6); + + // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. + // Of course, the exception has an exception too: For static libs, mingw *does* + // have the "lib" prefix. + var libFileName = ""; + if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw + && !modInfo.isStaticLibrary) { + libFileName += "lib"; + } + + libFileName += line.slice(equalsOffset + 1).trim(); + if (isNonStaticQt4OnWindows) + libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET... + if (isQtVersionBefore56) { + if (qtProps.mkspecName.contains("msvc")) { + libFileName += ".lib"; + } else if (isMingw) { + libFileName += ".a"; + if (!File.exists(FileInfo.joinPaths(libDir, libFileName))) + libFileName = libFileName.slice(0, -2) + ".dll"; + } + } + libFilePath = FileInfo.joinPaths(libDir, libFileName); + continue; + } + if (line.startsWith("QMAKE_PRL_CONFIG")) { + modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); + continue; + } + if (!line.startsWith("QMAKE_PRL_LIBS")) + continue; + + // Assuming lib names and directories without spaces here. + var parts = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); + for (i = 0; i < parts.length; ++i) { + var part = parts[i]; + part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + if (part.startsWith("-l")) { + libs.push(part.slice(2)); + } else if (part.startsWith("-L")) { + modInfo.libraryPaths.push(part.slice(2)); + } else if (part.startsWith("-F")) { + frameworkPaths.push(part.slice(2)); + } else if (part === "-framework") { + if (++i < parts.length) + frameworks.push(parts[i]); + } else if (part === "-pthread") { + libs.push("pthread"); + } else if (part.startsWith('-')) { // Some other option + console.debug("QMAKE_PRL_LIBS contains non-library option '" + part + + "' in file '" + prlFilePath + "'"); + flags.push(part); + } else if (part.startsWith("/LIBPATH:")) { + libraryPaths.push(part.slice(9).replace(/\\/g, '/')); + } else { // Assume it's a file path/name. + libs.push(part.replace(/\\/g, '/')); + } + } + } + } catch (e) { + libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); + if (nonExistingPrlFiles.contains(prlFilePath)) + return; + nonExistingPrlFiles.push(prlFilePath); + if (!libFilePath && modInfo.mustExist) { + console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '" + + modInfo.name + + "' (" + e + "), and failed to deduce the library file path. " + + " This module will likely not be usable by qbs."); + } + } + finally { + if (prlFile) + prlFile.close(); + } + + if (debugBuild) + modInfo.libFilePathDebug = libFilePath; + else + modInfo.libFilePathRelease = libFilePath; +} + +function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles) { + doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles); + doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles); +} + +function allQt4Modules(qtProps) { + // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. + var modules = []; + + var core = makeQtModuleInfo("QtCore", "core"); + core.compilerDefines.push("QT_CORE_LIB"); + if (qtProps.qtNameSpace) + core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace); + modules.push(core, + makeQtModuleInfo("QtCore", "core-private", ["core"]), + makeQtModuleInfo("QtGui", "gui"), + makeQtModuleInfo("QtGui", "gui-private", ["gui"]), + makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]), + makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]), + makeQtModuleInfo("QtNetwork", "network"), + makeQtModuleInfo("QtNetwork", "network-private", ["network"]), + makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]), + makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]), + makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]), + makeQtModuleInfo("QtScript", "script"), + makeQtModuleInfo("QtScript", "script-private", ["script"]), + makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]), + makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]), + makeQtModuleInfo("QtSql", "sql"), + makeQtModuleInfo("QtSql", "sql-private", ["sql"]), + makeQtModuleInfo("QtSvg", "svg", ["gui"]), + makeQtModuleInfo("QtSvg", "svg-private", ["svg"]), + makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]), + makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]), + makeQtModuleInfo("QtXml", "xml"), + makeQtModuleInfo("QtXml", "xml-private", ["xml"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]), + makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]), + makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]), + makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]), + makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]), + makeQtModuleInfo("QtUiTools", "uitools"), + makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]), + makeQtModuleInfo("QtHelp", "help", ["network", "sql"]), + makeQtModuleInfo("QtHelp", "help-private", ["help"]), + makeQtModuleInfo("QtTest", "testlib"), + makeQtModuleInfo("QtTest", "testlib-private", ["testlib"])); + if (qtProps.mkspecName.startsWith("win")) { + var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer"); + axcontainer.modulePrefix = "Q"; + axcontainer.isStaticLibrary = true; + axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axcontainer); + + var axserver = makeQtModuleInfo("QAxServer", "axserver"); + axserver.modulePrefix = "Q"; + axserver.isStaticLibrary = true; + axserver.compilerDefines.push("QAXSERVER"); + axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axserver); + } else { + modules.push(makeQtModuleInfo("QtDBus", "dbus")); + modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"])); + } + + var designerComponentsPrivate = makeQtModuleInfo( + "QtDesignerComponents", "designercomponents-private", + ["gui-private", "designer-private"]); + designerComponentsPrivate.hasLibrary = true; + modules.push(designerComponentsPrivate); + + var phonon = makeQtModuleInfo("Phonon", "phonon"); + phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps); + modules.push(phonon); + + // Set up include paths that haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + if (module.includePaths.length > 0) + continue; + module.includePaths = qt4ModuleIncludePaths(module, qtProps); + } + + // Set up compiler defines haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + if (module.compilerDefines.length > 0) + continue; + module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB"); + } + + // These are for the convenience of project file authors. It allows them + // to add a dependency to e.g. "Qt.widgets" without a version check. + var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "concurrent"); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + + addTestModule(modules); + addDesignerComponentsModule(modules); + + var modulesThatCanBeDisabled = [ + "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools", + "declarative", "gui", "dbus", "opengl", "openvg"]; + var nonExistingPrlFiles = []; + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + var name = module.qbsName; + var privateIndex = name.indexOf("-private"); + if (privateIndex !== -1) + name = name.slice(0, privateIndex); + if (modulesThatCanBeDisabled.contains(name)) + module.mustExist = false; + if (qtProps.staticBuild) + module.isStaticLibrary = true; + setupLibraries(module, qtProps, nonExistingPrlFiles); + } + replaceQtLibNamesWithFilePath(modules, qtProps); + + return modules; +} + +function getPriFileContentsRecursively(priFilePath) { + var priFile = new TextFile(priFilePath, TextFile.ReadOnly); + var lines = splitNonEmpty(priFile.readAll(), '\n'); + for (var i = 0; i < lines.length; ++i) { + var includeString = "include("; + var line = lines[i].trim(); + if (!line.startsWith(includeString)) + continue; + var offset = includeString.length; + var closingParenPos = line.indexOf(')', offset); + if (closingParenPos === -1) { + console.warn("Invalid include statement in '" + toNative(priFilePath) + "'"); + continue; + } + var includedFilePath = line.slice(offset, closingParenPos - offset); + var includedContents = getPriFileContentsRecursively(includedFilePath); + var j = i; + for (var k = 0; k < includedContents.length; ++k) + lines.splice(++j, 0, includedContents[k]); + lines.splice(i--, 1); + } + priFile.close(); + return lines; +} + +function extractPaths(rhs, filePath) { + var paths = []; + var startIndex = 0; + for (;;) { + while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ') + ++startIndex; + if (startIndex >= rhs.length) + break; + var endIndex; + if (rhs.charAt(startIndex) === '"') { + ++startIndex; + endIndex = rhs.indexOf('"', startIndex); + if (endIndex === -1) { + console.warn("Unmatched quote in file '" + toNative(filePath) + "'"); + break; + } + } else { + endIndex = rhs.indexOf(' ', startIndex + 1); + if (endIndex === -1) + endIndex = rhs.length; + } + paths.push(rhs.slice(startIndex, endIndex)); + startIndex = endIndex + 1; + } + return paths; +} + +function removeDuplicatedDependencyLibs(modules) { + var revDeps = {}; + var currentPath; + var getLibraries; + var getLibFilePath; + + function setupReverseDependencies(modules) { + var moduleByName = {}; + for (var i = 0; i < modules.length; ++i) + moduleByName[modules[i].qbsName] = modules[i]; + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + for (var j = 0; j < module.dependencies.length; ++j) { + var depmod = moduleByName[module.dependencies[j]]; + if (!depmod) + continue; + if (!revDeps[depmod]) + revDeps[depmod] = []; + revDeps[depmod].push(module); + } + } + } + + function roots(modules) { + var result = []; + for (i = 0; i < modules.length; ++i) { + var module = modules[i] + if (module.dependencies.lenegth === 0) + result.push(module); + } + return result; + } + + function traverse(module, libs) { + if (currentPath.contains(module)) + return; + currentPath.push(module); + + var moduleLibraryLists = getLibraries(module); + for (var i = 0; i < moduleLibraryLists.length; ++i) { + var modLibList = moduleLibraryLists[i]; + for (j = modLibList.length - 1; j >= 0; --j) { + if (libs.contains(modLibList[j])) + modLibList.splice(j, 1); + } + } + + var libFilePath = getLibFilePath(module); + if (libFilePath) + libs.push(libFilePath); + for (i = 0; i < moduleLibraryLists.length; ++i) + libs = libs.concat(moduleLibraryLists[i]); + libs.sort(); + + for (i = 0; i < (revDeps[module] || []).length; ++i) + traverse(revDeps[module][i], libs); + + m_currentPath.pop(); + } + + setupReverseDependencies(modules); + + // Traverse the debug variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesDebug, module.staticLibrariesDebug]; + }; + getLibFilePath = function(module) { return module.libFilePathDebug; }; + var rootModules = roots(modules); + for (var i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); + + // Traverse the release variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesRelease, module.staticLibrariesRelease]; + }; + getLibFilePath = function(module) { return module.libFilePathRelease; }; + for (i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); +} + +function allQt5Modules(qtProps) { + var nonExistingPrlFiles = []; + var modules = []; + var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules"); + var modulePriFiles = File.directoryEntries(modulesDir, File.Files); + for (var i = 0; i < modulePriFiles.length; ++i) { + var priFileName = modulePriFiles[i]; + var priFilePath = FileInfo.joinPaths(modulesDir, priFileName); + var moduleFileNamePrefix = "qt_lib_"; + var pluginFileNamePrefix = "qt_plugin_"; + var moduleFileNameSuffix = ".pri"; + var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); + if (!fileHasPluginPrefix && (!priFileName.startsWith(moduleFileNamePrefix)) + || !priFileName.endsWith(moduleFileNameSuffix)) { + continue; + } + var moduleInfo = makeQtModuleInfo(); + moduleInfo.isPlugin = fileHasPluginPrefix; + var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleFileNamePrefix; + moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length); + if (moduleInfo.isPlugin) { + moduleInfo.name = moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + } + var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") + + '.' + moduleInfo.qbsName + '.'; + moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private"); + var hasV2 = false; + var hasModuleEntry = false; + var lines = getPriFileContentsRecursively(priFilePath); + for (var j = 0; j < lines.length; ++j) { + var line = lines[j].trim(); + var firstEqualsOffset = line.indexOf('='); + if (firstEqualsOffset === -1) + continue; + var key = line.slice(0, firstEqualsOffset).trim(); + var value = line.slice(firstEqualsOffset + 1).trim(); + if (!key.startsWith(moduleKeyPrefix) || !value) + continue; + if (key.endsWith(".name")) { + moduleInfo.name = value; + } else if (key.endsWith(".module")) { + hasModuleEntry = true; + } else if (key.endsWith(".depends")) { + moduleInfo.dependencies = splitNonEmpty(value, ' '); + for (var k = 0; k < moduleInfo.dependencies.length; ++k) { + moduleInfo.dependencies[k] + = moduleInfo.dependencies[k].replace("_private", "-private"); + } + } else if (key.endsWith(".module_config")) { + var elems = splitNonEmpty(value, ' '); + for (k = 0; k < elems.length; ++k) { + var elem = elems[k]; + if (elem === "no_link") + moduleInfo.hasLibrary = false; + else if (elem === "staticlib") + moduleInfo.isStaticLibrary = true; + else if (elem === "internal_module") + moduleInfo.isPrivate = true; + else if (elem === "v2") + hasV2 = true; + } + } else if (key.endsWith(".includes")) { + moduleInfo.includePaths = extractPaths(value, priFilePath); + for (k = 0; k < moduleInfo.includePaths.length; ++k) { + moduleInfo.includePaths[k] = moduleInfo.includePaths[k] + .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } + } else if (key.endsWith(".DEFINES")) { + moduleInfo.compilerDefines = splitNonEmpty(value, ' '); + } else if (key.endsWith(".VERSION")) { + moduleInfo.version = value; + } else if (key.endsWith(".plugin_types")) { + moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' '); + } else if (key.endsWith(".TYPE")) { + moduleInfo.pluginData.type = value; + } else if (key.endsWith(".EXTENDS")) { + moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' '); + for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) { + if (moduleInfo.pluginData["extends"][k] === "-") { + moduleInfo.pluginData.splice(k, 1); + moduleInfo.pluginData.autoLoad = false; + break; + } + } + } else if (key.endsWith(".CLASS_NAME")) { + moduleInfo.pluginData.className = value; + } + } + if (hasV2 && !hasModuleEntry) + moduleInfo.hasLibrary = false; + + // Fix include paths for Apple frameworks. + // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. + if (!hasV2 && isFramework(moduleInfo, qtProps)) { + moduleInfo.includePaths = []; + var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps); + if (moduleInfo.isPrivate) { + baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version); + moduleInfo.includePaths.push(baseIncDir, + FileInfo.joinPaths(baseIncDir, moduleInfo.name)); + } else { + moduleInfo.includePaths.push(baseIncDir); + } + } + + setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles); + + modules.push(moduleInfo); + if (moduleInfo.qbsName === "testlib") + addTestModule(modules); + if (moduleInfo.qbsName === "designercomponents-private") + addDesignerComponentsModule(modules); + } + + replaceQtLibNamesWithFilePath(modules, qtProps); + removeDuplicatedDependencyLibs(modules); + return modules; +} + +function extractQbsArchs(module, qtProps) { + if (qtProps.mkspecName.startsWith("macx-")) { + var archs = []; + if (module.libFilePathRelease) + archs = Utilities.getArchitecturesFromBinary(module.libFilePathRelease); + return archs; + } + var qbsArch = Utilities.canonicalArchitecture(qtProps.architecture); + if (qbsArch === "arm" && qtProps.mkspecPath.contains("android")) + qbsArch = "armv7a"; + + // Qt4 has "QT_ARCH = windows" in qconfig.pri for both MSVC and mingw. + if (qbsArch === "windows") + return [] + + return [qbsArch]; +} + +function qbsTargetPlatformFromQtMkspec(qtProps) { + var mkspec = qtProps.mkspecName; + var idx = mkspec.lastIndexOf('/'); + if (idx !== -1) + mkspec = mkspec.slice(idx + 1); + if (mkspec.startsWith("aix-")) + return "aix"; + if (mkspec.startsWith("android-")) + return "android"; + if (mkspec.startsWith("cygwin-")) + return "windows"; + if (mkspec.startsWith("darwin-")) + return "macos"; + if (mkspec.startsWith("freebsd-")) + return "freebsd"; + if (mkspec.startsWith("haiku-")) + return "haiku"; + if (mkspec.startsWith(("hpux-")) || mkspec.startsWith(("hpuxi-"))) + return "hpux"; + if (mkspec.startsWith("hurd-")) + return "hurd"; + if (mkspec.startsWith("integrity-")) + return "integrity"; + if (mkspec.startsWith("linux-")) + return "linux"; + if (mkspec.startsWith("macx-")) { + if (mkspec.startsWith("macx-ios-")) + return "ios"; + if (mkspec.startsWith("macx-tvos-")) + return "tvos"; + if (mkspec.startsWith("macx-watchos-")) + return "watchos"; + return "macos"; + } + if (mkspec.startsWith("netbsd-")) + return "netbsd"; + if (mkspec.startsWith("openbsd-")) + return "openbsd"; + if (mkspec.startsWith("qnx-")) + return "qnx"; + if (mkspec.startsWith("solaris-")) + return "solaris"; + if (mkspec.startsWith("vxworks-")) + return "vxworks"; + if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-")) + return "windows"; +} + +function pathToJSLiteral(path) { return JSON.stringify(FileInfo.fromNativeSeparators(path)); } + +function defaultQpaPlugin(module, qtProps) { + if (qtProps.qtMajorVersion < 5) + return undefined; + if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { + var qConfigPri = new TextFile(FileInfo.joinPaths(qtProps.mkspecBasePath, "qconfig.pri")); + var magicString = "QT_DEFAULT_QPA_PLUGIN ="; + while (!qConfigPri.atEof()) { + var line = qConfigPri.readLine().trim(); + if (line.startsWith(magicString)) + return line.slice(magicString.length).trim(); + } + qConfigPri.close(); + } else { + var gtGuiHeadersPath = qtProps.frameworkBuild + ? FileInfo.joinPaths(qtProps.libraryPath, "QtGui.framework", "Headers") + : FileInfo.joinPaths(qtProps.includePath, "QtGui"); + var qtGuiConfigHeader = FileInfo.joinPaths(gtGuiHeadersPath, "qtgui-config.h"); + var headerFiles = []; + headerFiles.push(qtGuiConfigHeader); + while (headerFiles.length > 0) { + var filePath = headerFiles.shift(); + var headerFile = new TextFile(filePath, TextFile.ReadOnly); + var regexp = /^#define QT_QPA_DEFAULT_PLATFORM_NAME "(.+)".*$/; + var includeRegexp = /^#include "(.+)".*$/; + while (!headerFile.atEof()) { + line = headerFile.readLine().trim(); + var match = line.match(regexp); + if (match) + return 'q' + match[1]; + match = line.match(includeRegexp); + if (match) { + var includedFile = match[1]; + if (!FileInfo.isAbsolute(includedFile)) { + includedFile = FileInfo.cleanPath( + FileInfo.joinPaths(FileInfo.path(filePath), includedFile)); + } + headerFiles.push(includedFile); + } + } + headerFile.close(); + } + } + + if (module.isStaticLibrary) + console.warn("Could not determine default QPA plugin for static Qt."); +} + +function libraryFileTag(module, qtProps) { + if (module.isStaticLibrary) + return "staticlibrary"; + return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") + ? "dynamiclibrary_import" : "dynamiclibrary"; +} + +function findVariable(content, start) { + var result = [-1, -1]; + result[0] = content.indexOf('@', start); + if (result[0] === -1) + return result; + result[1] = content.indexOf('@', result[0] + 1); + if (result[1] === -1) { + result[0] = -1; + return result; + } + var forbiddenChars = [' ', '\n']; + for (var i = 0; i < forbiddenChars.length; ++i) { + var forbidden = forbiddenChars[i]; + var k = content.indexOf(forbidden, result[0] + 1); + if (k !== -1 && k < result[1]) + return findVariable(content, result[0] + 1); + } + return result; +} + +function toJSLiteral(v) { + if (v === undefined) + return "undefined"; + return JSON.stringify(v); +} + +function minVersionJsString(minVersion) { + return !minVersion ? "original" : toJSLiteral(minVersion); +} + +function replaceSpecialValues(content, module, qtProps) { + var dict = { + archs: toJSLiteral(extractQbsArchs(module, qtProps)), + targetPlatform: toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)), + config: toJSLiteral(qtProps.configItems), + qtConfig: toJSLiteral(qtProps.qtConfigItems), + binPath: toJSLiteral(qtProps.binaryPath), + libPath: toJSLiteral(qtProps.libraryPath), + pluginPath: toJSLiteral(qtProps.pluginPath), + incPath: toJSLiteral(qtProps.includePath), + docPath: toJSLiteral(qtProps.documentationPath), + mkspecName: toJSLiteral(qtProps.mkspecName), + mkspecPath: toJSLiteral(qtProps.mkspecPath), + version: toJSLiteral(qtProps.qtVersion), + libInfix: toJSLiteral(qtProps.qtLibInfix), + availableBuildVariants: toJSLiteral(qtProps.buildVariant), + staticBuild: toJSLiteral(qtProps.staticBuild), + frameworkBuild: toJSLiteral(qtProps.frameworkBuild), + name: toJSLiteral(moduleNameWithoutPrefix(module)), + has_library: toJSLiteral(module.hasLibrary), + dependencies: toJSLiteral(module.dependencies), + includes: toJSLiteral(module.includePaths), + staticLibsDebug: toJSLiteral(module.staticLibrariesDebug), + staticLibsRelease: toJSLiteral(module.staticLibrariesRelease), + dynamicLibsDebug: toJSLiteral(module.dynamicLibrariesDebug), + dynamicLibsRelease: toJSLiteral(module.dynamicLibrariesRelease), + linkerFlagsDebug: toJSLiteral(module.linkerFlagsDebug), + linkerFlagsRelease: toJSLiteral(module.linkerFlagsRelease), + libraryPaths: toJSLiteral(module.libraryPaths), + frameworkPathsDebug: toJSLiteral(module.frameworkPathsDebug), + frameworkPathsRelease: toJSLiteral(module.frameworkPathsRelease), + frameworksDebug: toJSLiteral(module.frameworksDebug), + frameworksRelease: toJSLiteral(module.frameworksRelease), + libFilePathDebug: toJSLiteral(module.libFilePathDebug), + libFilePathRelease: toJSLiteral(module.libFilePathRelease), + libNameForLinkerDebug: toJSLiteral(libNameForLinker(module, qtProps, true)), + pluginTypes: toJSLiteral(module.supportedPluginTypes), + moduleConfig: toJSLiteral(module.config), + libNameForLinkerRelease: toJSLiteral(libNameForLinker(module, qtProps, false)), + entryPointLibsDebug: toJSLiteral(qtProps.entryPointLibsDebug), + entryPointLibsRelease: toJSLiteral(qtProps.entryPointLibsRelease), + minWinVersion: minVersionJsString(qtProps.windowsVersion), + minMacVersion: minVersionJsString(qtProps.macosVersion), + minIosVersion: minVersionJsString(qtProps.iosVersion), + minTvosVersion: minVersionJsString(qtProps.tvosVersion), + minWatchosVersion: minVersionJsString(qtProps.watchosVersion), + minAndroidVersion: minVersionJsString(qtProps.androidVersion), + }; + + var additionalContent = ""; + var compilerDefines = toJSLiteral(module.compilerDefines); + if (module.qbsName === "declarative" || module.qbsName === "quick") { + var debugMacro = module.qbsName === "declarative" || qtProps.qtMajorVersion < 5 + ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG"; + var indent = " "; + additionalContent = "property bool qmlDebugging: false\n" + + indent + "property string qmlPath"; + if (qtProps.qmlPath) + additionalContent += ": " + pathToJSLiteral(qtProps.qmlPath) + '\n'; + else + additionalContent += '\n'; + + additionalContent += indent + "property string qmlImportsPath: " + + pathToJSLiteral(qtProps.qmlImportPath); + + compilerDefines = "{\n" + + indent + indent + "var result = " + compilerDefines + ";\n" + + indent + indent + "if (qmlDebugging)\n" + + indent + indent + indent + "result.push(\"" + debugMacro + "\");\n" + + indent + indent + "return result;\n" + + indent + "}"; + } + dict.defines = compilerDefines; + if (module.qbsName === "gui") + dict.defaultQpaPlugin = toJSLiteral(defaultQpaPlugin(module, qtProps)); + if (module.qbsName === "qml") + dict.qmlPath = pathToJSLiteral(qtProps.qmlPath); + if (module.isStaticLibrary && module.qbsName !== "core") { + if (additionalContent) + additionalContent += "\n "; + additionalContent += "isStaticLibrary: true"; + } + if (module.isPlugin) { + dict.className = toJSLiteral(module.pluginData.className); + dict["extends"] = toJSLiteral(module.pluginData["extends"]); + } + if (module.hasLibrary && !isFramework(module, qtProps)) { + if (additionalContent) + additionalContent += "\n"; + indent = " "; + additionalContent += "Group {\n"; + if (module.isPlugin) { + additionalContent += indent + indent + + "condition: Qt[\"" + module.qbsName + "\"].enableLinking\n"; + } + additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName + "\"]" + + ".libFilePath]\n" + + indent + indent + "filesAreTargets: true\n" + + indent + indent + "fileTags: [\"" + libraryFileTag(module, qtProps) + + "\"]\n" + + indent + "}"; + } + dict.additionalContent = additionalContent; + + for (var pos = findVariable(content, 0); pos[0] !== -1; + pos = findVariable(content, pos[0])) { + var replacement = dict[content.slice(pos[0] + 1, pos[1])] || ""; + content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1); + pos[0] += replacement.length; + } + return content; +} + +function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles, module, pluginMap, + nonEssentialPlugins) +{ + if (!File.makePath(targetDirectory)) { + throw "Cannot create directory '" + toNative(targetDirectory) + "'."; + } + var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName), + TextFile.ReadOnly); + var newContent = sourceFile.readAll(); + if (module) { + newContent = replaceSpecialValues(newContent, module, qtProps); + } else { + newContent = newContent.replace("@allPluginsByType@", + '(' + toJSLiteral(pluginMap) + ')'); + newContent = newContent.replace("@nonEssentialPlugins@", + toJSLiteral(nonEssentialPlugins)); + } + sourceFile.close(); + var targetPath = FileInfo.joinPaths(targetDirectory, fileName); + allFiles.push(targetPath); + var targetFile = new TextFile(targetPath, TextFile.WriteOnly); + targetFile.write(newContent); + targetFile.close(); +} + +function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { + if (!File.exists(qmakeFilePath)) + throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist."; + var qtProps = getQtProperties(qmakeFilePath, qbs); + var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : allQt5Modules(qtProps); + var pluginsByType = []; + var nonEssentialPlugins = []; + for (var i = 0; i < modules.length; ++i) { + var m = modules[i]; + if (m.isPlugin) { + if (!pluginsByType[m.pluginData.type]) + pluginsByType[m.pluginData.type] = []; + pluginsByType[m.pluginData.type].push(m.name); + if (!m.pluginData.autoLoad) + nonEssentialPlugins.push(m.name); + } + } + + var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : ""; + var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt"); + if (File.exists(qbsQtModuleBaseDir)) + File.remove(qbsQtModuleBaseDir); + + var allFiles = []; + copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles); + copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles); + copyTemplateFile("plugin_support.qbs", FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), + qtProps, location, allFiles, undefined, pluginsByType, nonEssentialPlugins); + + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName); + var moduleTemplateFileName; + if (module.qbsName === "core") { + moduleTemplateFileName = "core.qbs"; + copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, location, allFiles); + copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, location, allFiles); + } else if (module.qbsName === "gui") { + moduleTemplateFileName = "gui.qbs"; + } else if (module.qbsName === "scxml") { + moduleTemplateFileName = "scxml.qbs"; + } else if (module.qbsName === "dbus") { + moduleTemplateFileName = "dbus.qbs"; + copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, location, allFiles); + } else if (module.qbsName === "qml") { + moduleTemplateFileName = "qml.qbs"; + copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, location, allFiles); + var qmlcacheStr = "qmlcache"; + if (File.exists(FileInfo.joinPaths(qtProps.binaryPath, + "qmlcachegen" + exeSuffix(qbs)))) { + copyTemplateFile(qmlcacheStr + ".qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, + location, allFiles); + } + } else if (module.qbsName === "quick") { + moduleTemplateFileName = "quick.qbs"; + copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, location, allFiles); + } else if (module.isPlugin) { + moduleTemplateFileName = "plugin.qbs"; + } else { + moduleTemplateFileName = "module.qbs"; + } + copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, location, allFiles, + module); + } + + // Note that it's not strictly necessary to copy this one, as it has no variable content. + // But we do it anyway for consistency. + copyTemplateFile("android_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"), + qtProps, location, allFiles); + return relativeSearchPath; +} + +function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) { + qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths, qbs); + if (!qmakeFilePaths || qmakeFilePaths.length === 0) + return []; + var uniquifySearchPath = qmakeFilePaths.length > 1; + var searchPaths = []; + for (var i = 0; i < qmakeFilePaths.length; ++i) { + try { + console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'..."); + var searchPath = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath, + location, qbs); + if (searchPath !== undefined) { + searchPaths.push(searchPath); + console.info("Qt was set up successfully."); + } + } catch (e) { + console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e); + } + } + return searchPaths; +} diff --git a/src/lib/qtprofilesetup/templates/QtModule.qbs b/share/qbs/module-providers/Qt/templates/QtModule.qbs index aa7c1d15a..aa7c1d15a 100644 --- a/src/lib/qtprofilesetup/templates/QtModule.qbs +++ b/share/qbs/module-providers/Qt/templates/QtModule.qbs diff --git a/src/lib/qtprofilesetup/templates/QtPlugin.qbs b/share/qbs/module-providers/Qt/templates/QtPlugin.qbs index 23a6795f3..23a6795f3 100644 --- a/src/lib/qtprofilesetup/templates/QtPlugin.qbs +++ b/share/qbs/module-providers/Qt/templates/QtPlugin.qbs diff --git a/src/lib/qtprofilesetup/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 79276a494..79276a494 100644 --- a/src/lib/qtprofilesetup/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index b2f05d8e9..b2f05d8e9 100644 --- a/src/lib/qtprofilesetup/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs diff --git a/src/lib/qtprofilesetup/templates/dbus.js b/share/qbs/module-providers/Qt/templates/dbus.js index 0674bf684..0674bf684 100644 --- a/src/lib/qtprofilesetup/templates/dbus.js +++ b/share/qbs/module-providers/Qt/templates/dbus.js diff --git a/src/lib/qtprofilesetup/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs index 6556e2c9b..6556e2c9b 100644 --- a/src/lib/qtprofilesetup/templates/dbus.qbs +++ b/share/qbs/module-providers/Qt/templates/dbus.qbs diff --git a/src/lib/qtprofilesetup/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs index eb69e0cad..eb69e0cad 100644 --- a/src/lib/qtprofilesetup/templates/gui.qbs +++ b/share/qbs/module-providers/Qt/templates/gui.qbs diff --git a/src/lib/qtprofilesetup/templates/moc.js b/share/qbs/module-providers/Qt/templates/moc.js index aa67766b9..aa67766b9 100644 --- a/src/lib/qtprofilesetup/templates/moc.js +++ b/share/qbs/module-providers/Qt/templates/moc.js diff --git a/src/lib/qtprofilesetup/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs index b09f79a87..b09f79a87 100644 --- a/src/lib/qtprofilesetup/templates/module.qbs +++ b/share/qbs/module-providers/Qt/templates/module.qbs diff --git a/src/lib/qtprofilesetup/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs index e73e2a4d9..e73e2a4d9 100644 --- a/src/lib/qtprofilesetup/templates/plugin.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin.qbs diff --git a/src/lib/qtprofilesetup/templates/plugin_support.qbs b/share/qbs/module-providers/Qt/templates/plugin_support.qbs index 13d95c383..13d95c383 100644 --- a/src/lib/qtprofilesetup/templates/plugin_support.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin_support.qbs diff --git a/src/lib/qtprofilesetup/templates/qdoc.js b/share/qbs/module-providers/Qt/templates/qdoc.js index 72c161c56..72c161c56 100644 --- a/src/lib/qtprofilesetup/templates/qdoc.js +++ b/share/qbs/module-providers/Qt/templates/qdoc.js diff --git a/src/lib/qtprofilesetup/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js index c7829d81b..c7829d81b 100644 --- a/src/lib/qtprofilesetup/templates/qml.js +++ b/share/qbs/module-providers/Qt/templates/qml.js diff --git a/src/lib/qtprofilesetup/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs index 2b11abbd5..2b11abbd5 100644 --- a/src/lib/qtprofilesetup/templates/qml.qbs +++ b/share/qbs/module-providers/Qt/templates/qml.qbs diff --git a/src/lib/qtprofilesetup/templates/qmlcache.qbs b/share/qbs/module-providers/Qt/templates/qmlcache.qbs index 9111eb500..9111eb500 100644 --- a/src/lib/qtprofilesetup/templates/qmlcache.qbs +++ b/share/qbs/module-providers/Qt/templates/qmlcache.qbs diff --git a/src/lib/qtprofilesetup/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js index 4f3da2fb0..4f3da2fb0 100644 --- a/src/lib/qtprofilesetup/templates/quick.js +++ b/share/qbs/module-providers/Qt/templates/quick.js diff --git a/src/lib/qtprofilesetup/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs index 5968949c8..5968949c8 100644 --- a/src/lib/qtprofilesetup/templates/quick.qbs +++ b/share/qbs/module-providers/Qt/templates/quick.qbs diff --git a/src/lib/qtprofilesetup/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs index 7125ec53c..7125ec53c 100644 --- a/src/lib/qtprofilesetup/templates/scxml.qbs +++ b/share/qbs/module-providers/Qt/templates/scxml.qbs diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp index d32cdbc05..8c7373652 100644 --- a/src/app/qbs-setup-android/android-setup.cpp +++ b/src/app/qbs-setup-android/android-setup.cpp @@ -224,7 +224,7 @@ static void setupNdk(qbs::Settings *settings, const QString &profileName, const mainProfile.setValue(qls("qbs.architecture"), archsForProfile.front()); else mainProfile.setValue(qls("qbs.architectures"), archsForProfile); - QStringList searchPaths; + QStringList qmakeFilePaths; QString platform; for (const QString &arch : archs) { const QtAndroidInfo qtAndroidInfo = infoPerArch.value(arch); @@ -248,11 +248,11 @@ static void setupNdk(qbs::Settings *settings, const QString &profileName, const } settings->sync(); qbs::Internal::TemporaryProfile p(subProName, settings); - searchPaths << p.p.value(qls("preferences.qbsSearchPaths")).toStringList(); + qmakeFilePaths << p.p.value(qls("moduleProviders.Qt.qmakeFilePaths")).toStringList(); platform = maximumPlatform(platform, qtAndroidInfo.platform); } - if (!searchPaths.empty()) - mainProfile.setValue(qls("preferences.qbsSearchPaths"), searchPaths); + if (!qmakeFilePaths.empty()) + mainProfile.setValue(qls("moduleProviders.Qt.qmakeFilePaths"), qmakeFilePaths); if (!platform.isEmpty()) mainProfile.setValue(qls("Android.ndk.platform"), platform); } diff --git a/src/app/qbs-setup-qt/main.cpp b/src/app/qbs-setup-qt/main.cpp index d1bfd9614..94185c4d7 100644 --- a/src/app/qbs-setup-qt/main.cpp +++ b/src/app/qbs-setup-qt/main.cpp @@ -40,7 +40,6 @@ #include "commandlineparser.h" -#include <qtprofilesetup.h> #include <logging/translator.h> #include <tools/settings.h> @@ -72,16 +71,16 @@ int main(int argc, char *argv[]) if (clParser.autoDetectionMode()) { // search all Qt's in path and dump their settings - const std::vector<EnhancedQtEnvironment> qtEnvironments = SetupQt::fetchEnvironments(); + const std::vector<QtEnvironment> qtEnvironments = SetupQt::fetchEnvironments(); if (qtEnvironments.empty()) { std::cout << qPrintable(Tr::tr("No Qt installations detected. " "No profiles created.")) << std::endl; } - for (const EnhancedQtEnvironment &qtEnvironment : qtEnvironments) { - QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion; + for (const QtEnvironment &qtEnvironment : qtEnvironments) { + QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion.toString(); if (SetupQt::checkIfMoreThanOneQtWithTheSameVersion(qtEnvironment.qtVersion, qtEnvironments)) { - QStringList prefixPathParts = qtEnvironment.installPrefixPath + QStringList prefixPathParts = QFileInfo(qtEnvironment.qmakeFilePath).path() .split(QLatin1Char('/'), QString::SkipEmptyParts); if (!prefixPathParts.empty()) profileName += QLatin1String("-") + prefixPathParts.last(); @@ -97,7 +96,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - EnhancedQtEnvironment qtEnvironment = SetupQt::fetchEnvironment(clParser.qmakePath()); + const QtEnvironment qtEnvironment = SetupQt::fetchEnvironment(clParser.qmakePath()); QString profileName = clParser.profileName(); profileName.replace(QLatin1Char('.'), QLatin1Char('-')); SetupQt::saveToQbsSettings(profileName, qtEnvironment, &settings); diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.pro b/src/app/qbs-setup-qt/qbs-setup-qt.pro index 036a4e74c..a5694d6b7 100644 --- a/src/app/qbs-setup-qt/qbs-setup-qt.pro +++ b/src/app/qbs-setup-qt/qbs-setup-qt.pro @@ -1,5 +1,4 @@ include(../app.pri) -include($${PWD}/../../lib/qtprofilesetup/use_qtprofilesetup.pri) TARGET = qbs-setup-qt diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.qbs b/src/app/qbs-setup-qt/qbs-setup-qt.qbs index 8bd4ae263..f4cd9d6b8 100644 --- a/src/app/qbs-setup-qt/qbs-setup-qt.qbs +++ b/src/app/qbs-setup-qt/qbs-setup-qt.qbs @@ -2,7 +2,6 @@ import qbs 1.0 QbsApp { name: "qbs-setup-qt" - Depends { name: "qbsqtprofilesetup" } files: [ "commandlineparser.cpp", "commandlineparser.h", diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp index f54411b48..d4a59e30b 100644 --- a/src/app/qbs-setup-qt/setupqt.cpp +++ b/src/app/qbs-setup-qt/setupqt.cpp @@ -40,8 +40,6 @@ #include "setupqt.h" #include "../shared/logging/consolelogger.h" -#include <qtmsvctools.h> -#include <qtprofilesetup.h> #include <logging/translator.h> #include <tools/architectures.h> #include <tools/hostosinfo.h> @@ -56,10 +54,10 @@ #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> #include <QtCore/qprocess.h> -#include <QtCore/qregexp.h> #include <QtCore/qstringlist.h> #include <algorithm> +#include <regex> namespace qbs { using Internal::none_of; @@ -105,210 +103,91 @@ bool SetupQt::isQMakePathValid(const QString &qmakePath) return qmakeFileInfo.exists() && qmakeFileInfo.isFile() && qmakeFileInfo.isExecutable(); } -std::vector<EnhancedQtEnvironment> SetupQt::fetchEnvironments() +std::vector<QtEnvironment> SetupQt::fetchEnvironments() { - std::vector<EnhancedQtEnvironment> qtEnvironments; - + std::vector<QtEnvironment> qtEnvironments; const auto qmakePaths = collectQmakePaths(); for (const QString &qmakePath : qmakePaths) { - const EnhancedQtEnvironment env = fetchEnvironment(qmakePath); - if (none_of(qtEnvironments, [&env](const EnhancedQtEnvironment &otherEnv) { - return env.includePath == otherEnv.includePath; + const QtEnvironment env = fetchEnvironment(qmakePath); + if (none_of(qtEnvironments, [&env](const QtEnvironment &otherEnv) { + return env.qmakeFilePath == otherEnv.qmakeFilePath; })) { qtEnvironments.push_back(env); } } - return qtEnvironments; } -void SetupQt::addQtBuildVariant(QtEnvironment *env, const QString &buildVariantName) +// These functions work only for Qt from installer. +static QStringList qbsToolchainFromDirName(const QString &dir) { - if (env->qtConfigItems.contains(buildVariantName)) - env->buildVariant << buildVariantName; + if (dir.startsWith(QLatin1String("msvc"))) + return QStringList(QLatin1String("msvc")); + if (dir.startsWith(QLatin1String("mingw"))) + return QStringList{QLatin1String("mingw"), QLatin1String("gcc")}; + if (dir.startsWith(QLatin1String("clang"))) + return QStringList{QLatin1String("clang"), QLatin1String("llvm"), QLatin1String("gcc")}; + if (dir.startsWith(QLatin1String("gcc"))) + return QStringList(QLatin1String("gcc")); + return QStringList(); } -static QMap<QByteArray, QByteArray> qmakeQueryOutput(const QString &qmakePath) +static Version msvcVersionFromDirName(const QString &dir) { - QProcess qmakeProcess; - qmakeProcess.start(qmakePath, QStringList() << QLatin1String("-query")); - if (!qmakeProcess.waitForStarted()) - throw ErrorInfo(SetupQt::tr("%1 cannot be started.").arg(qmakePath)); - qmakeProcess.waitForFinished(); - const QByteArray output = qmakeProcess.readAllStandardOutput(); - - QMap<QByteArray, QByteArray> ret; - const auto lines = output.split('\n'); - for (const QByteArray &line : lines) { - int idx = line.indexOf(':'); - if (idx >= 0) - ret[line.left(idx)] = line.mid(idx + 1).trimmed(); - } - return ret; + static const std::regex regexp("^msvc(\\d\\d\\d\\d)_.*$"); + std::smatch match; + const std::string dirString = dir.toStdString(); + if (!std::regex_match(dirString, match, regexp)) + return Version(); + QMap<std::string, std::string> mapping{ + std::make_pair("2005", "14"), std::make_pair("2008", "15"), std::make_pair("2010", "16"), + std::make_pair("2012", "17"), std::make_pair("2013", "18"), std::make_pair("2015", "19"), + std::make_pair("2017", "19.1") + }; + return Version::fromString(QString::fromStdString(mapping.value(match[1].str()))); } -static QByteArray readFileContent(const QString &filePath) +static QString archFromDirName(const QString &dir) { - QFile file(filePath); - if (file.open(QFile::ReadOnly)) - return file.readAll(); - - return QByteArray(); + static const std::regex regexp("^[^_]+_(.*).*$"); + std::smatch match; + const std::string dirString = dir.toStdString(); + if (!std::regex_match(dirString, match, regexp)) + return QString(); + const QString arch = QString::fromStdString(match[1]); + if (arch == QLatin1String("32")) + return QLatin1String("x86"); + if (arch == QLatin1String("64")) + return QLatin1String("x86_64"); + if (arch.contains(QLatin1String("arm64"))) + return QLatin1String("arm64"); + return arch; } -static QString configVariable(const QByteArray &configContent, const QString &key) +static QString platformFromDirName(const QString &dir) { - QRegExp regexp(QLatin1String("\\s*") + key + QLatin1String("\\s*\\+{0,1}=(.*)"), - Qt::CaseSensitive); - - const QList<QByteArray> configContentLines = configContent.split('\n'); - - bool success = false; - - for (const QByteArray &configContentLine : configContentLines) { - success = regexp.exactMatch(QString::fromLocal8Bit(configContentLine)); - if (success) - break; - } - - if (success) - return regexp.capturedTexts()[1].simplified(); - - return QString(); -} - -static QStringList configVariableItems(const QByteArray &configContent, const QString &key) -{ - return configVariable(configContent, key).split(QLatin1Char(' '), QString::SkipEmptyParts); -} - -typedef QMap<QByteArray, QByteArray> QueryMap; - -static QString pathQueryValue(const QueryMap &queryMap, const QByteArray &key) -{ - return QDir::fromNativeSeparators(QString::fromLocal8Bit(queryMap.value(key))); + if (dir.startsWith(QLatin1String("android"))) + return QLatin1String("android"); + if (dir == QLatin1String("Boot2Qt")) + return QLatin1String("linux"); + return QString::fromStdString(HostOsInfo::hostOSIdentifier()); } -EnhancedQtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath) +QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath) { - EnhancedQtEnvironment qtEnvironment; - QueryMap queryOutput = qmakeQueryOutput(qmakePath); - - qtEnvironment.installPrefixPath = pathQueryValue(queryOutput, "QT_INSTALL_PREFIX"); - qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS"); - qtEnvironment.includePath = pathQueryValue(queryOutput, "QT_INSTALL_HEADERS"); - qtEnvironment.libraryPath = pathQueryValue(queryOutput, "QT_INSTALL_LIBS"); - qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_HOST_BINS"); - if (qtEnvironment.binaryPath.isEmpty()) - qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_INSTALL_BINS"); - qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS"); - qtEnvironment.pluginPath = pathQueryValue(queryOutput, "QT_INSTALL_PLUGINS"); - qtEnvironment.qmlPath = pathQueryValue(queryOutput, "QT_INSTALL_QML"); - qtEnvironment.qmlImportPath = pathQueryValue(queryOutput, "QT_INSTALL_IMPORTS"); - qtEnvironment.qtVersion = QString::fromLocal8Bit(queryOutput.value("QT_VERSION")); - - const Version qtVersion = Version::fromString(qtEnvironment.qtVersion); - - QString mkspecsBaseSrcPath; - if (qtVersion.majorVersion() >= 5) { - qtEnvironment.mkspecBasePath - = pathQueryValue(queryOutput, "QT_HOST_DATA") + QLatin1String("/mkspecs"); - mkspecsBaseSrcPath - = pathQueryValue(queryOutput, "QT_HOST_DATA/src") + QLatin1String("/mkspecs"); - } else { - qtEnvironment.mkspecBasePath - = pathQueryValue(queryOutput, "QT_INSTALL_DATA") + QLatin1String("/mkspecs"); - } - - if (!QFile::exists(qtEnvironment.mkspecBasePath)) - throw ErrorInfo(tr("Cannot extract the mkspecs directory.")); - - const QByteArray qconfigContent = readFileContent(qtEnvironment.mkspecBasePath - + QLatin1String("/qconfig.pri")); - qtEnvironment.qtMajorVersion = configVariable(qconfigContent, - QLatin1String("QT_MAJOR_VERSION")).toInt(); - qtEnvironment.qtMinorVersion = configVariable(qconfigContent, - QLatin1String("QT_MINOR_VERSION")).toInt(); - qtEnvironment.qtPatchVersion = configVariable(qconfigContent, - QLatin1String("QT_PATCH_VERSION")).toInt(); - qtEnvironment.qtNameSpace = configVariable(qconfigContent, QLatin1String("QT_NAMESPACE")); - qtEnvironment.qtLibInfix = configVariable(qconfigContent, QLatin1String("QT_LIBINFIX")); - qtEnvironment.architecture = configVariable(qconfigContent, QLatin1String("QT_TARGET_ARCH")); - if (qtEnvironment.architecture.isEmpty()) - qtEnvironment.architecture = configVariable(qconfigContent, QLatin1String("QT_ARCH")); - if (qtEnvironment.architecture.isEmpty()) - qtEnvironment.architecture = QLatin1String("x86"); - qtEnvironment.configItems = configVariableItems(qconfigContent, QLatin1String("CONFIG")); - qtEnvironment.qtConfigItems = configVariableItems(qconfigContent, QLatin1String("QT_CONFIG")); - - // retrieve the mkspec - if (qtVersion.majorVersion() >= 5) { - const QString mkspecName = QString::fromLocal8Bit(queryOutput.value("QMAKE_XSPEC")); - qtEnvironment.mkspecName = mkspecName; - qtEnvironment.mkspecPath = qtEnvironment.mkspecBasePath + QLatin1Char('/') + mkspecName; - if (!mkspecsBaseSrcPath.isEmpty() && !QFile::exists(qtEnvironment.mkspecPath)) - qtEnvironment.mkspecPath = mkspecsBaseSrcPath + QLatin1Char('/') + mkspecName; - } else { - if (HostOsInfo::isWindowsHost()) { - const QString baseDirPath = qtEnvironment.mkspecBasePath + QLatin1String("/default/"); - const QByteArray fileContent = readFileContent(baseDirPath - + QLatin1String("qmake.conf")); - qtEnvironment.mkspecPath = configVariable(fileContent, - QLatin1String("QMAKESPEC_ORIGINAL")); - if (!QFile::exists(qtEnvironment.mkspecPath)) { - // Work around QTBUG-28792. - // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? - const QRegExp rex(QLatin1String("\\binclude\\(([^)]+)/qmake\\.conf\\)")); - if (rex.indexIn(QString::fromLocal8Bit(fileContent)) != -1) - qtEnvironment.mkspecPath = QDir::cleanPath(baseDirPath + rex.cap(1)); - } - } else { - qtEnvironment.mkspecPath = QFileInfo(qtEnvironment.mkspecBasePath - + QLatin1String("/default")).symLinkTarget(); - } - - // 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++ - qtEnvironment.mkspecPath = QDir::cleanPath(qtEnvironment.mkspecPath); - - qtEnvironment.mkspecName = qtEnvironment.mkspecPath; - int idx = qtEnvironment.mkspecName.lastIndexOf(QLatin1Char('/')); - if (idx != -1) - qtEnvironment.mkspecName.remove(0, idx + 1); + QtEnvironment env; + env.qmakeFilePath = qmakePath; + QDir qtDir = QFileInfo(qmakePath).dir(); + if (qtDir.dirName() == QLatin1String("bin")) { + qtDir.cdUp(); + env.qbsToolchain = qbsToolchainFromDirName(qtDir.dirName()); + env.msvcVersion = msvcVersionFromDirName(qtDir.dirName()); + env.architecture = archFromDirName(qtDir.dirName()); + env.targetPlatform = platformFromDirName(qtDir.dirName()); + qtDir.cdUp(); + env.qtVersion = Version::fromString(qtDir.dirName()); } - - // determine MSVC version - if (isMsvcQt(qtEnvironment)) { - bool ok; - qtEnvironment.msvcVersion.setMajorVersion( - configVariable(qconfigContent, - QLatin1String("QT_MSVC_MAJOR_VERSION")).toInt(&ok)); - if (ok) { - qtEnvironment.msvcVersion.setMinorVersion( - configVariable(qconfigContent, - QLatin1String("QT_MSVC_MINOR_VERSION")).toInt(&ok)); - } - if (ok) { - qtEnvironment.msvcVersion.setPatchLevel( - configVariable(qconfigContent, - QLatin1String("QT_MSVC_PATCH_VERSION")).toInt(&ok)); - } - if (!ok) - qtEnvironment.msvcVersion = msvcCompilerVersionFromMkspecName(qtEnvironment.mkspecName); - } - - // determine whether we have a framework build - qtEnvironment.frameworkBuild = qtEnvironment.mkspecPath.contains(QLatin1String("macx")) - && qtEnvironment.configItems.contains(QLatin1String("qt_framework")); - - // determine whether Qt is built with debug, release or both - addQtBuildVariant(&qtEnvironment, QLatin1String("debug")); - addQtBuildVariant(&qtEnvironment, QLatin1String("release")); - - if (!QFileInfo(qtEnvironment.mkspecPath).exists()) - throw ErrorInfo(tr("mkspec '%1' does not exist").arg(qtEnvironment.mkspecPath)); - - return qtEnvironment; + return env; } static bool isToolchainProfile(const Profile &profile) @@ -323,6 +202,12 @@ static bool isToolchainProfile(const Profile &profile) static bool isQtProfile(const Profile &profile) { + if (!profile.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")).toStringList() + .empty()) { + return true; + } + + // For Profiles created with setup-qt < 5.13. const QStringList searchPaths = profile.value(QStringLiteral("preferences.qbsSearchPaths")).toStringList(); return std::any_of(searchPaths.cbegin(), searchPaths.cend(), [] (const QString &path) { @@ -337,39 +222,18 @@ template <typename T> bool areProfilePropertiesIncompatible(const T &set1, const return set1.size() > 0 && set2.size() > 0 && set1 != set2; } -static QStringList qbsToolchainFromQtMkspec(const QtEnvironment &qtEnv) -{ - const QString mkspec = qtEnv.mkspecName; - if (mkspec.contains(QLatin1String("-msvc"))) - return QStringList() << QLatin1String("msvc"); - if (qtEnv.isForMinGw()) - return QStringList() << QLatin1String("mingw") << QLatin1String("gcc"); - - if (mkspec.contains(QLatin1String("-clang"))) - return QStringList() << QLatin1String("clang") << QLatin1String("llvm") - << QLatin1String("gcc"); - if (mkspec.contains(QLatin1String("-llvm"))) - return QStringList() << QLatin1String("llvm") << QLatin1String("gcc"); - if (mkspec.contains(QLatin1String("-g++"))) - return QStringList() << QLatin1String("gcc"); - - // Worry about other, less common toolchains (ICC, QCC, etc.) later... - return QStringList(); -} - enum Match { MatchFull, MatchPartial, MatchNone }; -static Match compatibility(const EnhancedQtEnvironment &env, const Profile &toolchainProfile) +static Match compatibility(const QtEnvironment &env, const Profile &toolchainProfile) { Match match = MatchFull; const auto toolchainNames = Internal::Set<QString>::fromList( toolchainProfile.value(QLatin1String("qbs.toolchain")).toStringList()); - const auto mkspecToolchainNames = Internal::Set<QString>::fromList( - qbsToolchainFromQtMkspec(env)); - if (areProfilePropertiesIncompatible(toolchainNames, mkspecToolchainNames)) { + const auto qtToolchainNames = Internal::Set<QString>::fromList(env.qbsToolchain); + if (areProfilePropertiesIncompatible(toolchainNames, qtToolchainNames)) { auto intersection = toolchainNames; - intersection.intersect(mkspecToolchainNames); + intersection.intersect(qtToolchainNames); if (!intersection.empty()) match = MatchPartial; else @@ -378,7 +242,7 @@ static Match compatibility(const EnhancedQtEnvironment &env, const Profile &tool const auto targetPlatform = toolchainProfile.value( QLatin1String("qbs.targetPlatform")).toString(); - if (!targetPlatform.isEmpty() && targetPlatform != qbsTargetPlatformFromQtMkspec(env)) + if (!targetPlatform.isEmpty() && targetPlatform != env.targetPlatform) return MatchNone; const QString toolchainArchitecture = toolchainProfile.value(QLatin1String("qbs.architecture")) @@ -438,7 +302,7 @@ static void compressMsvcProfiles(QStringList &profiles) } void SetupQt::saveToQbsSettings(const QString &qtVersionName, - const EnhancedQtEnvironment &qtEnvironment, + const QtEnvironment &qtEnvironment, Settings *settings) { const QString cleanQtVersionName = Profile::cleanName(qtVersionName); @@ -446,14 +310,10 @@ void SetupQt::saveToQbsSettings(const QString &qtVersionName, .arg(cleanQtVersionName); printf("%s\n", qPrintable(msg)); - const ErrorInfo errorInfo = setupQtProfile(cleanQtVersionName, settings, qtEnvironment); - if (errorInfo.hasError()) - throw errorInfo; - - // If this profile does not specify a toolchain and we find exactly one profile that looks - // like it might have been added by qbs-setup-toolchains, let's use that one as our - // base profile. Profile profile(cleanQtVersionName, settings); + profile.removeProfile(); + profile.setValue(QLatin1String("moduleProviders.Qt.qmakeFilePaths"), + QStringList(qtEnvironment.qmakeFilePath)); if (!profile.baseProfile().isEmpty()) return; if (isToolchainProfile(profile)) @@ -490,19 +350,14 @@ void SetupQt::saveToQbsSettings(const QString &qtVersionName, else if (fullMatches.empty() && partialMatches.size() == 1) bestMatch = partialMatches.front(); if (bestMatch.isEmpty()) { - QString message = Tr::tr("You need to set up toolchain information before you can " - "use this Qt version for building. "); - if (fullMatches.empty() && partialMatches.empty()) { - message += Tr::tr("However, no toolchain profile was found. Either create one " - "using qbs-setup-toolchains and set it as this profile's " - "base profile or add the toolchain settings manually " - "to this profile."); - } else { + QString message = Tr::tr("You may want to set up toolchain information " + "for the generated Qt profile. "); + if (!fullMatches.empty() || !partialMatches.empty()) { message += Tr::tr("Consider setting one of these profiles as this profile's base " "profile: %1.").arg((fullMatches + partialMatches) .join(QLatin1String(", "))); } - qbsWarning() << message; + qbsInfo() << message; } else { profile.setBaseProfile(bestMatch); qbsInfo() << Tr::tr("Setting profile '%1' as the base profile for this profile.") @@ -510,8 +365,8 @@ void SetupQt::saveToQbsSettings(const QString &qtVersionName, } } -bool SetupQt::checkIfMoreThanOneQtWithTheSameVersion(const QString &qtVersion, - const std::vector<EnhancedQtEnvironment> &qtEnvironments) +bool SetupQt::checkIfMoreThanOneQtWithTheSameVersion(const Version &qtVersion, + const std::vector<QtEnvironment> &qtEnvironments) { bool foundOneVersion = false; for (const QtEnvironment &qtEnvironment : qtEnvironments) { diff --git a/src/app/qbs-setup-qt/setupqt.h b/src/app/qbs-setup-qt/setupqt.h index 73c0afefe..c10ae637c 100644 --- a/src/app/qbs-setup-qt/setupqt.h +++ b/src/app/qbs-setup-qt/setupqt.h @@ -40,8 +40,8 @@ #ifndef QBS_SETUPQT_H #define QBS_SETUPQT_H -#include <qtenvironment.h> #include <tools/error.h> +#include <tools/version.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qstringlist.h> @@ -50,27 +50,30 @@ namespace qbs { -class EnhancedQtEnvironment : public QtEnvironment +class Settings; + +class QtEnvironment { public: + QString qmakeFilePath; + QStringList qbsToolchain; + QString architecture; + QString targetPlatform; + Version qtVersion; Version msvcVersion; }; -class Settings; - class SetupQt { Q_DECLARE_TR_FUNCTIONS(SetupQt) public: static bool isQMakePathValid(const QString &qmakePath); - static std::vector<EnhancedQtEnvironment> fetchEnvironments(); - static void addQtBuildVariant(QtEnvironment *env, const QString &buildVariantName); - static EnhancedQtEnvironment fetchEnvironment(const QString &qmakePath); - static void saveToQbsSettings(const QString &qtVersionName, - const EnhancedQtEnvironment &qtEnvironment, + static std::vector<QtEnvironment> fetchEnvironments(); + static QtEnvironment fetchEnvironment(const QString &qmakePath); + static bool checkIfMoreThanOneQtWithTheSameVersion(const Version &qtVersion, + const std::vector<QtEnvironment> &qtEnvironments); + static void saveToQbsSettings(const QString &qtVersionName, const QtEnvironment &qtEnvironment, Settings *settings); - static bool checkIfMoreThanOneQtWithTheSameVersion(const QString &qtVersion, - const std::vector<EnhancedQtEnvironment> &qtEnvironments); }; } // namespace qbs diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index a10251c54..4cbe6ed2f 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -51,6 +51,26 @@ #include <tools/applecodesignutils.h> #endif +#ifdef __APPLE__ +#include <ar.h> +#include <mach/machine.h> +#include <mach-o/fat.h> +#include <mach-o/loader.h> +#ifndef FAT_MAGIC_64 +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca +struct fat_arch_64 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; +}; +#endif +#endif + + #ifdef Q_OS_WIN #include <tools/msvcinfo.h> #include <tools/vsenvironmentdetector.h> @@ -58,6 +78,9 @@ #include <QtCore/qcryptographichash.h> #include <QtCore/qdir.h> +#include <QtCore/qendian.h> +#include <QtCore/qfile.h> +#include <QtCore/qlibrary.h> #include <QtScript/qscriptable.h> #include <QtScript/qscriptengine.h> @@ -91,6 +114,10 @@ public: static QScriptValue js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_builtinExtensionNames(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue js_getArchitecturesFromBinary(QScriptContext *context, + QScriptEngine *engine); }; QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) @@ -504,6 +531,237 @@ QScriptValue UtilitiesExtension::js_builtinExtensionNames(QScriptContext *contex return engine->toScriptValue(JsExtensions::extensionNames()); } +QScriptValue UtilitiesExtension::js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() == 1) { + const QScriptValue value = context->argument(0); + if (value.isString()) + return engine->toScriptValue(QLibrary::isLibrary(value.toString())); + } + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("isSharedLibrary expects one argument of type string")); +} + +#ifdef __APPLE__ +template <typename T = uint32_t> T readInt(QIODevice *ioDevice, bool *ok, + bool swap, bool peek = false) { + const auto bytes = peek + ? ioDevice->peek(sizeof(T)) + : ioDevice->read(sizeof(T)); + if (bytes.size() != sizeof(T)) { + if (ok) + *ok = false; + return T(); + } + if (ok) + *ok = true; + T n = *reinterpret_cast<const T *>(bytes.constData()); + return swap ? qbswap(n) : n; +} + +static QString archName(cpu_type_t cputype, cpu_subtype_t cpusubtype) +{ + switch (cputype) { + case CPU_TYPE_X86: + switch (cpusubtype) { + case CPU_SUBTYPE_X86_ALL: + return QStringLiteral("i386"); + default: + return QString(); + } + case CPU_TYPE_X86_64: + switch (cpusubtype) { + case CPU_SUBTYPE_X86_64_ALL: + return QStringLiteral("x86_64"); + case CPU_SUBTYPE_X86_64_H: + return QStringLiteral("x86_64h"); + default: + return QString(); + } + case CPU_TYPE_ARM: + switch (cpusubtype) { + case CPU_SUBTYPE_ARM_V7: + return QStringLiteral("armv7a"); + case CPU_SUBTYPE_ARM_V7S: + return QStringLiteral("armv7s"); + case CPU_SUBTYPE_ARM_V7K: + return QStringLiteral("armv7k"); + default: + return QString(); + } + case CPU_TYPE_ARM64: + switch (cpusubtype) { + case CPU_SUBTYPE_ARM64_ALL: + return QStringLiteral("arm64"); + default: + return QString(); + } + default: + return QString(); + } +} + +static QStringList detectMachOArchs(QIODevice *device) +{ + bool ok; + bool foundMachO = false; + qint64 pos = device->pos(); + + char ar_header[SARMAG]; + if (device->read(ar_header, SARMAG) == SARMAG) { + if (strncmp(ar_header, ARMAG, SARMAG) == 0) { + while (!device->atEnd()) { + static_assert(sizeof(ar_hdr) == 60, "sizeof(ar_hdr) != 60"); + ar_hdr header; + if (device->read(reinterpret_cast<char *>(&header), + sizeof(ar_hdr)) != sizeof(ar_hdr)) + return { }; + + // If the file name is stored in the "extended format" manner, + // the real filename is prepended to the data section, so skip that many bytes + int filenameLength = 0; + if (strncmp(header.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0) { + char arName[sizeof(header.ar_name)] = { 0 }; + memcpy(arName, header.ar_name + sizeof(AR_EFMT1) - 1, + sizeof(header.ar_name) - (sizeof(AR_EFMT1) - 1) - 1); + filenameLength = strtoul(arName, nullptr, 10); + if (device->read(filenameLength).size() != filenameLength) + return { }; + } + + switch (readInt(device, nullptr, false, true)) { + case MH_CIGAM: + case MH_CIGAM_64: + case MH_MAGIC: + case MH_MAGIC_64: + foundMachO = true; + break; + default: { + // Skip the data and go to the next archive member... + char szBuf[sizeof(header.ar_size) + 1] = { 0 }; + memcpy(szBuf, header.ar_size, sizeof(header.ar_size)); + int sz = static_cast<int>(strtoul(szBuf, nullptr, 10)); + if (sz % 2 != 0) + ++sz; + sz -= filenameLength; + const auto data = device->read(sz); + if (data.size() != sz) + return { }; + } + } + + if (foundMachO) + break; + } + } + } + + // Wasn't an archive file, so try a fat file + if (!foundMachO && !device->seek(pos)) + return QStringList(); + + pos = device->pos(); + + fat_header fatheader; + fatheader.magic = readInt(device, nullptr, false); + if (fatheader.magic == FAT_MAGIC || fatheader.magic == FAT_CIGAM || + fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64) { + const bool swap = fatheader.magic == FAT_CIGAM || fatheader.magic == FAT_CIGAM_64; + const bool is64bit = fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64; + fatheader.nfat_arch = readInt(device, &ok, swap); + if (!ok) + return QStringList(); + + QStringList archs; + + for (uint32_t n = 0; n < fatheader.nfat_arch; ++n) { + fat_arch_64 fatarch; + static_assert(sizeof(fat_arch_64) == 32, "sizeof(fat_arch_64) != 32"); + static_assert(sizeof(fat_arch) == 20, "sizeof(fat_arch) != 20"); + const qint64 expectedBytes = is64bit ? sizeof(fat_arch_64) : sizeof(fat_arch); + if (device->read(reinterpret_cast<char *>(&fatarch), expectedBytes) != expectedBytes) + return QStringList(); + + if (swap) { + fatarch.cputype = qbswap(fatarch.cputype); + fatarch.cpusubtype = qbswap(fatarch.cpusubtype); + } + + const QString name = archName(fatarch.cputype, fatarch.cpusubtype); + if (name.isEmpty()) { + qWarning("Unknown cputype %d and cpusubtype %d", + fatarch.cputype, fatarch.cpusubtype); + return QStringList(); + } + archs.push_back(name); + } + + std::sort(archs.begin(), archs.end()); + return archs; + } + + // Wasn't a fat file, so we just read a thin Mach-O from the original offset + if (!device->seek(pos)) + return QStringList(); + + bool swap = false; + mach_header header; + header.magic = readInt(device, nullptr, swap); + switch (header.magic) { + case MH_CIGAM: + case MH_CIGAM_64: + swap = true; + break; + case MH_MAGIC: + case MH_MAGIC_64: + break; + default: + return QStringList(); + } + + header.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap)); + if (!ok) + return QStringList(); + + header.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap)); + if (!ok) + return QStringList(); + + const QString name = archName(header.cputype, header.cpusubtype); + if (name.isEmpty()) { + qWarning("Unknown cputype %d and cpusubtype %d", + header.cputype, header.cpusubtype); + return { }; + } + return { name }; +} +#endif + +QScriptValue UtilitiesExtension::js_getArchitecturesFromBinary(QScriptContext *context, + QScriptEngine *engine) +{ + if (context->argumentCount() != 1) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("getArchitecturesFromBinary expects exactly one argument")); + } + const QScriptValue arg = context->argument(0); + if (!arg.isString()) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("getArchitecturesFromBinary expects a string argument")); + } + QStringList archs; +#ifdef __APPLE__ + QFile file(arg.toString()); + if (!file.open(QIODevice::ReadOnly)) { + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("Failed to open file '%1': %2") + .arg(file.fileName(), file.errorString())); + } + archs = detectMachOArchs(&file); +#endif // __APPLE__ + return engine->toScriptValue(archs); +} + } // namespace Internal } // namespace qbs @@ -548,6 +806,10 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject) engine->newFunction(UtilitiesExtension::js_qmlTypeInfo, 0)); environmentObj.setProperty(QStringLiteral("builtinExtensionNames"), engine->newFunction(UtilitiesExtension::js_builtinExtensionNames, 0)); + environmentObj.setProperty(QStringLiteral("isSharedLibrary"), + engine->newFunction(UtilitiesExtension::js_isSharedLibrary, 1)); + environmentObj.setProperty(QStringLiteral("getArchitecturesFromBinary"), + engine->newFunction(UtilitiesExtension::js_getArchitecturesFromBinary, 1)); extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj); } diff --git a/src/lib/libs.qbs b/src/lib/libs.qbs index 478db3286..dd21cc081 100644 --- a/src/lib/libs.qbs +++ b/src/lib/libs.qbs @@ -3,7 +3,6 @@ import qbs Project { references: [ "corelib/corelib.qbs", - "qtprofilesetup/qtprofilesetup.qbs", "scriptengine/scriptengine.qbs", ] } diff --git a/src/lib/qtprofilesetup/qtenvironment.h b/src/lib/qtprofilesetup/qtenvironment.h deleted file mode 100644 index 72725cfae..000000000 --- a/src/lib/qtprofilesetup/qtenvironment.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#ifndef QBS_QTENVIRONMENT_H -#define QBS_QTENVIRONMENT_H - -#include <tools/qbs_export.h> -#include <tools/version.h> - -#include <QtCore/qstringlist.h> - -namespace qbs { - -class QBS_EXPORT QtEnvironment { -public: - inline bool targetsDesktopWindows() const; - inline bool isForMinGw() const; - - QString installPrefixPath; - QString libraryPath; - QString includePath; - QString binaryPath; - QString qmlPath; - QString qmlImportPath; - QString documentationPath; - QString dataPath; - QString pluginPath; - QString qtLibInfix; - QString qtNameSpace; - QString mkspecPath; - QString mkspecName; - QString mkspecBasePath; - QStringList entryPointLibsDebug; - QStringList entryPointLibsRelease; - QStringList buildVariant; - QStringList configItems; - QStringList qtConfigItems; - QString architecture; - QString qtVersion; - QString windowsVersion; - QString macosVersion; - QString iosVersion; - QString tvosVersion; - QString watchosVersion; - QString androidVersion; - int qtMajorVersion; - int qtMinorVersion; - int qtPatchVersion; - bool staticBuild = false; - bool frameworkBuild = false; -}; - -bool QtEnvironment::targetsDesktopWindows() const -{ - return mkspecName.startsWith(QLatin1String("win32-")) || isForMinGw(); -} - -bool QtEnvironment::isForMinGw() const -{ - return mkspecName.startsWith(QLatin1String("win32-g++")) - || mkspecName.startsWith(QLatin1String("mingw")); -} - -} // namespace qbs - -#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtmoduleinfo.cpp b/src/lib/qtprofilesetup/qtmoduleinfo.cpp deleted file mode 100644 index bc3d25911..000000000 --- a/src/lib/qtprofilesetup/qtmoduleinfo.cpp +++ /dev/null @@ -1,839 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#include "qtmoduleinfo.h" - -#include "qtenvironment.h" - -#include <logging/translator.h> -#include <tools/error.h> -#include <tools/profile.h> -#include <tools/qttools.h> -#include <tools/set.h> -#include <tools/stlutils.h> - -#include <QtCore/qdiriterator.h> -#include <QtCore/qfile.h> -#include <QtCore/qhash.h> - -#include <algorithm> -#include <functional> -#include <map> -#include <unordered_map> - -namespace qbs { -namespace Internal { - -typedef QHash<QString, QString> NamePathHash; -static void replaceQtLibNamesWithFilePath(const NamePathHash &namePathHash, QStringList *libList) -{ - for (QString &lib : *libList) { - const NamePathHash::ConstIterator it = namePathHash.find(lib); - if (it != namePathHash.constEnd()) - lib = it.value(); - } -} - -static void replaceQtLibNamesWithFilePath(QList<QtModuleInfo> *modules, const QtEnvironment &qtEnv) -{ - // 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. - typedef QHash<QString, QString> NamePathHash; - NamePathHash linkerNamesToFilePathsDebug; - NamePathHash linkerNamesToFilePathsRelease; - for (const QtModuleInfo &m : qAsConst(*modules)) { - linkerNamesToFilePathsDebug.insert(m.libNameForLinker(qtEnv, true), m.libFilePathDebug); - linkerNamesToFilePathsRelease.insert(m.libNameForLinker(qtEnv, false), - m.libFilePathRelease); - } - for (QtModuleInfo &module : *modules) { - replaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, &module.dynamicLibrariesDebug); - replaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, &module.staticLibrariesDebug); - replaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - &module.dynamicLibrariesRelease); - replaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - &module.staticLibrariesRelease); - } -} - -class DuplicatedDependencyLibsRemover -{ -public: - void apply(QList<QtModuleInfo> *modules) - { - setupReverseDependencies(modules); - - // Traverse the debug variants of modules. - m_getLibraries = [](QtModuleInfo *module) { - return std::vector<QStringList *>{ - &module->dynamicLibrariesDebug, &module->staticLibrariesDebug - }; - }; - m_getLibFilePath = [](QtModuleInfo *module) { - return module->libFilePathDebug; - }; - const auto &rootModules = roots(modules); - for (QtModuleInfo *module : rootModules) - traverse(module, QStringList()); - - // Traverse the release variants of modules. - m_getLibraries = [](QtModuleInfo *module) { - return std::vector<QStringList *>{ - &module->dynamicLibrariesRelease, &module->staticLibrariesRelease - }; - }; - m_getLibFilePath = [](QtModuleInfo *module) { - return module->libFilePathRelease; - }; - for (QtModuleInfo *module : rootModules) - traverse(module, QStringList()); - } - -private: - void setupReverseDependencies(QList<QtModuleInfo> *modules) - { - std::map<QString, QtModuleInfo *> moduleByName; - for (QtModuleInfo &module : *modules) - moduleByName[module.qbsName] = &module; - for (QtModuleInfo &module : *modules) { - for (const QString &dep : module.dependencies) { - QtModuleInfo *depmod = moduleByName[dep]; - if (!depmod) - continue; - m_revDeps[depmod].push_back(&module); - } - } - } - - std::vector<QtModuleInfo *> roots(QList<QtModuleInfo> *modules) - { - std::vector<QtModuleInfo *> result; - for (auto it = modules->begin(); it != modules->end(); ++it) { - QtModuleInfo &module = *it; - if (module.dependencies.empty()) - result.push_back(&module); - } - return result; - } - - void traverse(QtModuleInfo *module, QStringList libs) - { - if (contains(m_currentPath, module)) - return; - m_currentPath.push_back(module); - - auto isInLibs = [&libs](const QString &str) { - return std::binary_search(libs.begin(), libs.end(), str); - }; - auto moduleLibraryLists = m_getLibraries(module); - for (QStringList *lst : moduleLibraryLists) { - auto it = std::remove_if(lst->begin(), lst->end(), isInLibs); - if (it != lst->end()) - lst->erase(it, lst->end()); - } - const QString libFilePath = m_getLibFilePath(module); - if (!libFilePath.isEmpty()) - libs.push_back(libFilePath); - for (QStringList *lst : moduleLibraryLists) - std::copy(lst->begin(), lst->end(), std::back_inserter(libs)); - std::sort(libs.begin(), libs.end()); - - for (auto rdep : m_revDeps[module]) - traverse(rdep, libs); - - m_currentPath.pop_back(); - } - - std::unordered_map<QtModuleInfo *, std::vector<QtModuleInfo *>> m_revDeps; - std::vector<QtModuleInfo *> m_currentPath; - std::function<std::vector<QStringList *>(QtModuleInfo *)> m_getLibraries; - std::function<QString(QtModuleInfo *)> m_getLibFilePath; -}; - -static void removeDuplicatedDependencyLibs(QList<QtModuleInfo> *modules) -{ - DuplicatedDependencyLibsRemover dlr; - dlr.apply(modules); -} - -QtModuleInfo::QtModuleInfo() - : isPrivate(false), hasLibrary(true), isStaticLibrary(false), isPlugin(false), mustExist(true) -{ -} - -QtModuleInfo::QtModuleInfo(const QString &name, const QString &qbsName, const QStringList &deps) - : name(name), qbsName(qbsName), dependencies(deps), - isPrivate(qbsName.endsWith(QLatin1String("-private"))), - hasLibrary(!isPrivate), - isStaticLibrary(false), - isPlugin(false), - mustExist(true) -{ - const QString coreModule = QLatin1String("core"); - if (qbsName != coreModule && !dependencies.contains(coreModule)) - dependencies.prepend(coreModule); -} - -QString QtModuleInfo::moduleNameWithoutPrefix() const -{ - if (name == QLatin1String("Phonon")) - return QLatin1String("phonon"); - if (modulePrefix.isEmpty() && name.startsWith(QLatin1String("Qt"))) - return name.mid(2); // Strip off "Qt". - if (name.startsWith(modulePrefix)) - return name.mid(modulePrefix.length()); - return name; -} - -QString QtModuleInfo::frameworkHeadersPath(const QtEnvironment &qtEnvironment) const -{ - return qtEnvironment.libraryPath + QLatin1Char('/') + name - + QLatin1String(".framework/Headers"); -} - -QStringList QtModuleInfo::qt4ModuleIncludePaths(const QtEnvironment &qtEnvironment) const -{ - QStringList paths; - if (isFramework(qtEnvironment)) { - paths << frameworkHeadersPath(qtEnvironment); - } else { - paths << qtEnvironment.includePath - << qtEnvironment.includePath + QLatin1Char('/') + name; - } - return paths; -} - -QString QtModuleInfo::libraryBaseName(const QtEnvironment &qtEnvironment, - bool debugBuild) const -{ - if (isPlugin) - return libBaseName(name, debugBuild, qtEnvironment); - - // Some modules use a different naming scheme, so it doesn't get boring. - const bool libNameBroken = name == QLatin1String("Enginio") - || name == QLatin1String("DataVisualization") - || name == QLatin1String("Phonon"); - - QString libName = modulePrefix.isEmpty() && !libNameBroken ? QLatin1String("Qt") : modulePrefix; - if (qtEnvironment.qtMajorVersion >= 5 && !isFramework(qtEnvironment) && !libNameBroken) - libName += QString::number(qtEnvironment.qtMajorVersion); - libName += moduleNameWithoutPrefix(); - libName += qtEnvironment.qtLibInfix; - return libBaseName(libName, debugBuild, qtEnvironment); -} - -QString QtModuleInfo::libNameForLinker(const QtEnvironment &qtEnvironment, bool debugBuild) const -{ - if (!hasLibrary) - return QString(); - QString libName = libraryBaseName(qtEnvironment, debugBuild); - if (qtEnvironment.mkspecName.contains(QLatin1String("msvc"))) - libName += QLatin1String(".lib"); - return libName; -} - -void QtModuleInfo::setupLibraries(const QtEnvironment &qtEnv, - Internal::Set<QString> *nonExistingPrlFiles) -{ - setupLibraries(qtEnv, true, nonExistingPrlFiles); - setupLibraries(qtEnv, false, nonExistingPrlFiles); -} - -static QStringList makeList(const QByteArray &s) -{ - return QString::fromLatin1(s).split(QLatin1Char(' '), QString::SkipEmptyParts); -} - -static QString guessLibraryFilePath(const QString &prlFilePath, const QString &libDir, - const QtEnvironment &qtEnv) -{ - const QString baseName = QFileInfo(prlFilePath).baseName(); - const QStringList prefixCandidates{QString(), QLatin1String("lib")}; - const QStringList suffixCandidates{QLatin1String("so.") + qtEnv.qtVersion, - QLatin1String("so"), QLatin1String("a"), QLatin1String("lib"), - QLatin1String("dll.a")}; - for (const QString &prefix : prefixCandidates) { - for (const QString &suffix : suffixCandidates) { - const QString candidate = libDir + QLatin1Char('/') + prefix + baseName - + QLatin1Char('.') + suffix; - if (QFile::exists(candidate)) - return candidate; - } - } - return QString(); -} - -void QtModuleInfo::setupLibraries(const QtEnvironment &qtEnv, bool debugBuild, - Internal::Set<QString> *nonExistingPrlFiles) -{ - if (!hasLibrary) - return; // Can happen for Qt4 convenience modules, like "widgets". - - if (debugBuild) { - if (!qtEnv.buildVariant.contains(QLatin1String("debug"))) - return; - const QStringList modulesNeverBuiltAsDebug = QStringList() - << QLatin1String("bootstrap") << QLatin1String("qmldevtools"); - for (const QString &m : modulesNeverBuiltAsDebug) { - if (qbsName == m || qbsName == m + QLatin1String("-private")) - return; - } - } else if (!qtEnv.buildVariant.contains(QLatin1String("release"))) { - return; - } - - QStringList &libs = isStaticLibrary - ? (debugBuild ? staticLibrariesDebug : staticLibrariesRelease) - : (debugBuild ? dynamicLibrariesDebug : dynamicLibrariesRelease); - QStringList &frameworks = debugBuild ? frameworksDebug : frameworksRelease; - QStringList &frameworkPaths = debugBuild ? frameworkPathsDebug : frameworkPathsRelease; - QStringList &flags = debugBuild ? linkerFlagsDebug : linkerFlagsRelease; - QString &libFilePath = debugBuild ? libFilePathDebug : libFilePathRelease; - - if (qtEnv.mkspecName.contains(QLatin1String("ios")) && isStaticLibrary) { - libs << QLatin1String("z") << QLatin1String("m"); - if (qtEnv.qtMajorVersion == 5 && qtEnv.qtMinorVersion < 8) { - const QtModuleInfo platformSupportModule(QLatin1String("QtPlatformSupport"), - QLatin1String("platformsupport")); - libs << platformSupportModule.libNameForLinker(qtEnv, debugBuild); - } - if (name == QStringLiteral("qios")) { - flags << QLatin1String("-force_load") - << qtEnv.pluginPath + QLatin1String("/platforms/") - + libBaseName(QLatin1String("libqios"), debugBuild, qtEnv) - + QLatin1String(".a"); - } - } - - QString prlFilePath = isPlugin - ? qtEnv.pluginPath + QLatin1Char('/') + pluginData.type - : qtEnv.libraryPath; - prlFilePath += QLatin1Char('/'); - if (isFramework(qtEnv)) - prlFilePath.append(libraryBaseName(qtEnv, false)).append(QLatin1String(".framework/")); - const QString libDir = prlFilePath; - if (!qtEnv.mkspecName.startsWith(QLatin1String("win")) && !isFramework(qtEnv)) - prlFilePath += QLatin1String("lib"); - prlFilePath.append(libraryBaseName(qtEnv, debugBuild)); - const bool isNonStaticQt4OnWindows = qtEnv.mkspecName.startsWith(QLatin1String("win")) - && !isStaticLibrary && qtEnv.qtMajorVersion < 5; - if (isNonStaticQt4OnWindows) - prlFilePath.chop(1); // The prl file base name does *not* contain the version number... - prlFilePath.append(QLatin1String(".prl")); - QFile prlFile(prlFilePath); - if (!prlFile.open(QIODevice::ReadOnly)) { - libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtEnv); - if (nonExistingPrlFiles->insert(prlFilePath).second) { - if (mustExist && libFilePath.isEmpty()) { - qDebug("Could not open prl file '%s' for module '%s' (%s), and failed to deduce " - "the library file path. This module will likely not be usable by qbs.", - qPrintable(prlFilePath), qPrintable(name), - qPrintable(prlFile.errorString())); - } - } - return; - } - const QList<QByteArray> prlLines = prlFile.readAll().split('\n'); - for (const QByteArray &line : prlLines) { - const QByteArray simplifiedLine = line.simplified(); - const int equalsOffset = simplifiedLine.indexOf('='); - if (equalsOffset == -1) - continue; - if (simplifiedLine.startsWith("QMAKE_PRL_TARGET")) { - const bool isMingw = qtEnv.mkspecName.startsWith(QLatin1String("win")) - && qtEnv.mkspecName.contains(QLatin1String("g++")); - const bool isQtVersionBefore56 = qtEnv.qtMajorVersion < 5 - || (qtEnv.qtMajorVersion == 5 && qtEnv.qtMinorVersion < 6); - libFilePath = libDir; - - // 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. TODO: Shoot the people responsible for this. - if (isQtVersionBefore56 && isMingw && !isStaticLibrary) - libFilePath += QLatin1String("lib"); - - libFilePath += QString::fromLatin1(simplifiedLine.mid(equalsOffset + 1).trimmed()); - if (isNonStaticQt4OnWindows) - libFilePath += QString::number(4); // This is *not* part of QMAKE_PRL_TARGET... - if (isQtVersionBefore56) { - if (qtEnv.mkspecName.contains(QLatin1String("msvc"))) - libFilePath += QLatin1String(".lib"); - else if (isMingw) - libFilePath += QLatin1String(".a"); - } - continue; - } - if (simplifiedLine.startsWith("QMAKE_PRL_CONFIG")) { - config = QString::fromLatin1(simplifiedLine.mid(equalsOffset + 1).trimmed()) - .split(QLatin1Char(' '), QString::SkipEmptyParts); - continue; - } - if (!simplifiedLine.startsWith("QMAKE_PRL_LIBS")) - continue; - - // Assuming lib names and directories without spaces here. - QStringList parts = QString::fromLatin1(simplifiedLine.mid(equalsOffset + 1).trimmed()) - .split(QLatin1Char(' '), QString::SkipEmptyParts); - for (int i = 0; i < parts.size(); ++i) { - QString part = parts.at(i); - part.replace(QLatin1String("$$[QT_INSTALL_LIBS]"), qtEnv.libraryPath); - if (part.startsWith(QLatin1String("-l"))) { - libs << part.mid(2); - } else if (part.startsWith(QLatin1String("-L"))) { - libraryPaths << part.mid(2); - } else if (part.startsWith(QLatin1String("-F"))) { - frameworkPaths << part.mid(2); - } else if (part == QLatin1String("-framework")) { - if (++i < parts.size()) - frameworks << parts.at(i); - } else if (part == QLatin1String("-pthread")) { - libs << QLatin1String("pthread"); - } else if (part.startsWith(QLatin1Char('-'))) { // Some other option - qDebug("QMAKE_PRL_LIBS contains non-library option '%s' in file '%s'", - qPrintable(part), qPrintable(prlFilePath)); - flags << part; - } else if (part.startsWith(QLatin1String("/LIBPATH:"))) { - libraryPaths << part.mid(9).replace(QLatin1String("\\\\"), QLatin1String("/")); - } else { // Assume it's a file path/name. - libs << part.replace(QLatin1String("\\\\"), QLatin1String("/")); - } - } - - return; - } -} - -bool QtModuleInfo::isFramework(const QtEnvironment &qtEnv) const -{ - if (!qtEnv.frameworkBuild || isStaticLibrary) - return false; - const QStringList modulesNeverBuiltAsFrameworks = QStringList() - << QLatin1String("bootstrap") << QLatin1String("openglextensions") - << QLatin1String("platformsupport") << QLatin1String("qmldevtools") - << QLatin1String("uitools") << QLatin1String("harfbuzzng"); - return !modulesNeverBuiltAsFrameworks.contains(qbsName); -} - -// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users -// for that. -static void addTestModule(QList<QtModuleInfo> &modules) -{ - QtModuleInfo testModule(QLatin1String("QtTest"), QLatin1String("test"), - QStringList() << QLatin1String("testlib")); - testModule.hasLibrary = false; - modules.push_back(testModule); -} - -// See above. -static void addDesignerComponentsModule(QList<QtModuleInfo> &modules) -{ - QtModuleInfo module(QLatin1String("QtDesignerComponents"), - QLatin1String("designercomponents"), - QStringList() << QLatin1String("designercomponents-private")); - module.hasLibrary = false; - modules.push_back(module); -} - - -QList<QtModuleInfo> allQt4Modules(const QtEnvironment &qtEnvironment) -{ - // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. - QList<QtModuleInfo> modules; - - QtModuleInfo core(QLatin1String("QtCore"), QLatin1String("core")); - core.compilerDefines << QLatin1String("QT_CORE_LIB"); - if (!qtEnvironment.qtNameSpace.isEmpty()) - core.compilerDefines << QLatin1String("QT_NAMESPACE=") + qtEnvironment.qtNameSpace; - - modules = QList<QtModuleInfo>() - << core - << QtModuleInfo(QLatin1String("QtCore"), QLatin1String("core-private"), - QStringList() << QLatin1String("core")) - << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui-private"), - QStringList() << QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia"), - QStringList() << QLatin1String("gui") << QLatin1String("network")) - << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia-private"), - QStringList() << QLatin1String("multimedia")) - << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network")) - << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network-private"), - QStringList() << QLatin1String("network")) - << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl"), - QStringList() << QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl-private"), - QStringList() << QLatin1String("opengl")) - << QtModuleInfo(QLatin1String("QtOpenVG"), QLatin1String("openvg"), - QStringList() << QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script")) - << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script-private"), - QStringList() << QLatin1String("script")) - << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttools"), - QStringList() << QLatin1String("script") << QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttools-private"), - QStringList() << QLatin1String("scripttools")) - << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql")) - << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql-private"), - QStringList() << QLatin1String("sql")) - << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg"), - QStringList() << QLatin1String("gui")) - << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg-private"), - QStringList() << QLatin1String("svg")) - << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit"), - QStringList() << QLatin1String("gui") << QLatin1String("network")) - << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit-private"), - QStringList() << QLatin1String("webkit")) - << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml")) - << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml-private"), - QStringList() << QLatin1String("xml")) - << QtModuleInfo(QLatin1String("QtXmlPatterns"), QLatin1String("xmlpatterns"), - QStringList() << QLatin1String("network")) - << QtModuleInfo(QLatin1String("QtXmlPatterns"), - QLatin1String("xmlpatterns-private"), - QStringList() << QLatin1String("xmlpatterns")) - << QtModuleInfo(QLatin1String("QtDeclarative"), QLatin1String("declarative"), - QStringList() << QLatin1String("gui") << QLatin1String("script")) - << QtModuleInfo(QLatin1String("QtDeclarative"), - QLatin1String("declarative-private"), - QStringList() << QLatin1String("declarative")) - << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer"), - QStringList() << QLatin1String("gui") << QLatin1String("xml")) - << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer-private"), - QStringList() << QLatin1String("designer")) - << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools")) - << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools-private"), - QStringList() << QLatin1String("uitools")) - << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help"), - QStringList() << QLatin1String("network") << QLatin1String("sql")) - << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help-private"), - QStringList() << QLatin1String("help")) - << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib")) - << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib-private"), - QStringList() << QLatin1String("testlib")); - - if (qtEnvironment.mkspecName.startsWith(QLatin1String("win"))) { - QtModuleInfo axcontainer(QLatin1String("QAxContainer"), QLatin1String("axcontainer")); - axcontainer.modulePrefix = QLatin1String("Q"); - axcontainer.isStaticLibrary = true; - axcontainer.includePaths << qtEnvironment.includePath + QLatin1String("/ActiveQt"); - modules.push_back(axcontainer); - - QtModuleInfo axserver = axcontainer; - axserver.name = QLatin1String("QAxServer"); - axserver.qbsName = QLatin1String("axserver"); - axserver.compilerDefines = QStringList() << QLatin1String("QAXSERVER"); - modules.push_back(axserver); - } else { - modules.push_back(QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus"))); - modules.push_back(QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus-private"), - { QLatin1String("dbus") })); - } - - QtModuleInfo designerComponentsPrivate(QLatin1String("QtDesignerComponents"), - QLatin1String("designercomponents-private"), - QStringList() << QLatin1String("gui-private") << QLatin1String("designer-private")); - designerComponentsPrivate.hasLibrary = true; - modules.push_back(designerComponentsPrivate); - - QtModuleInfo phonon(QLatin1String("Phonon"), QLatin1String("phonon")); - phonon.includePaths = phonon.qt4ModuleIncludePaths(qtEnvironment); - modules.push_back(phonon); - - // Set up include paths that haven't been set up before this point. - for (auto &module : modules) { - if (!module.includePaths.empty()) - continue; - module.includePaths = module.qt4ModuleIncludePaths(qtEnvironment); - } - - // Set up compiler defines haven't been set up before this point. - for (auto &module : modules) { - if (!module.compilerDefines.empty()) - continue; - module.compilerDefines - << QLatin1String("QT_") + module.qbsName.toUpper() + QLatin1String("_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. - QtModuleInfo virtualModule; - virtualModule.hasLibrary = false; - virtualModule.qbsName = QLatin1String("widgets"); - virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); - modules.push_back(virtualModule); - virtualModule.qbsName = QLatin1String("quick"); - virtualModule.dependencies = QStringList() << QLatin1String("declarative"); - modules.push_back(virtualModule); - virtualModule.qbsName = QLatin1String("concurrent"); - virtualModule.dependencies = QStringList() << QLatin1String("core"); - modules.push_back(virtualModule); - virtualModule.qbsName = QLatin1String("printsupport"); - virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); - modules.push_back(virtualModule); - - addTestModule(modules); - addDesignerComponentsModule(modules); - - const QStringList modulesThatCanBeDisabled = QStringList() << QLatin1String("xmlpatterns") - << QLatin1String("multimedia") << QLatin1String("phonon") << QLatin1String("svg") - << QLatin1String("webkit") << QLatin1String("script") << QLatin1String("scripttools") - << QLatin1String("declarative") << QLatin1String("gui") << QLatin1String("dbus") - << QLatin1String("opengl") << QLatin1String("openvg"); - for (auto &module : modules) { - QString name = module.qbsName; - name.remove(QLatin1String("-private")); - if (modulesThatCanBeDisabled.contains(name)) - module.mustExist = false; - } - - Internal::Set<QString> nonExistingPrlFiles; - for (QtModuleInfo &module : modules) { - if (qtEnvironment.staticBuild) - module.isStaticLibrary = true; - module.setupLibraries(qtEnvironment, &nonExistingPrlFiles); - } - replaceQtLibNamesWithFilePath(&modules, qtEnvironment); - - return modules; -} - -static QList<QByteArray> getPriFileContentsRecursively(const Profile &profile, - const QString &priFilePath) -{ - QFile priFile(priFilePath); - if (!priFile.open(QIODevice::ReadOnly)) { - throw ErrorInfo(Tr::tr("Setting up Qt profile '%1' failed: Cannot open " - "file '%2' (%3).").arg(profile.name(), priFile.fileName(), priFile.errorString())); - } - QList<QByteArray> lines = priFile.readAll().split('\n'); - for (int i = 0; i < lines.size(); ++i) { - const QByteArray includeString = "include("; - const QByteArray &line = lines.at(i).trimmed(); - if (!line.startsWith(includeString)) - continue; - const int offset = includeString.size(); - const int closingParenPos = line.indexOf(')', offset); - if (closingParenPos == -1) { - qDebug("Warning: Invalid include statement in '%s'", qPrintable(priFilePath)); - continue; - } - const QString includedFilePath - = QString::fromLocal8Bit(line.mid(offset, closingParenPos - offset)); - const QList<QByteArray> &includedContents - = getPriFileContentsRecursively(profile, includedFilePath); - int j = i; - for (const QByteArray &includedLine : includedContents) - lines.insert(++j, includedLine); - lines.removeAt(i--); - } - return lines; -} - -static QStringList extractPaths(const QByteArray &rhs, const QString &filePath) -{ - QStringList paths; - int startIndex = 0; - for (;;) { - while (startIndex < rhs.size() && rhs.at(startIndex) == ' ') - ++startIndex; - if (startIndex >= rhs.size()) - break; - int endIndex; - if (rhs.at(startIndex) == '"') { - ++startIndex; - endIndex = rhs.indexOf('"', startIndex); - if (endIndex == -1) { - qDebug("Unmatched quote in file '%s'", qPrintable(filePath)); - break; - } - } else { - endIndex = rhs.indexOf(' ', startIndex + 1); - if (endIndex == -1) - endIndex = rhs.size(); - } - paths << QString::fromLocal8Bit(rhs.mid(startIndex, endIndex - startIndex)); - startIndex = endIndex + 1; - } - return paths; -} - -QList<QtModuleInfo> allQt5Modules(const Profile &profile, const QtEnvironment &qtEnvironment) -{ - Internal::Set<QString> nonExistingPrlFiles; - QList<QtModuleInfo> modules; - QDirIterator dit(qtEnvironment.mkspecBasePath + QLatin1String("/modules")); - while (dit.hasNext()) { - const QString moduleFileNamePrefix = QLatin1String("qt_lib_"); - const QString pluginFileNamePrefix = QLatin1String("qt_plugin_"); - const QString moduleFileNameSuffix = QLatin1String(".pri"); - dit.next(); - const bool fileHasPluginPrefix = dit.fileName().startsWith(pluginFileNamePrefix); - if ((!fileHasPluginPrefix && !dit.fileName().startsWith(moduleFileNamePrefix)) - || !dit.fileName().endsWith(moduleFileNameSuffix)) { - continue; - } - QtModuleInfo moduleInfo; - moduleInfo.isPlugin = fileHasPluginPrefix; - const QString fileNamePrefix - = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleFileNamePrefix; - moduleInfo.qbsName = dit.fileName().mid(fileNamePrefix.size(), - dit.fileName().size() - fileNamePrefix.size() - - moduleFileNameSuffix.size()); - if (moduleInfo.isPlugin) { - moduleInfo.name = moduleInfo.qbsName; - moduleInfo.isStaticLibrary = true; - } - const QByteArray moduleKeyPrefix = QByteArray(moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") - + '.' + moduleInfo.qbsName.toLatin1() + '.'; - moduleInfo.qbsName.replace(QLatin1String("_private"), QLatin1String("-private")); - bool hasV2 = false; - bool hasModuleEntry = false; - const auto lines = getPriFileContentsRecursively(profile, dit.filePath()); - for (const QByteArray &line : lines) { - const QByteArray simplifiedLine = line.simplified(); - const int firstEqualsOffset = simplifiedLine.indexOf('='); - if (firstEqualsOffset == -1) - continue; - const QByteArray key = simplifiedLine.left(firstEqualsOffset).trimmed(); - const QByteArray value = simplifiedLine.mid(firstEqualsOffset + 1).trimmed(); - if (!key.startsWith(moduleKeyPrefix) || value.isEmpty()) - continue; - if (key.endsWith(".name")) { - moduleInfo.name = QString::fromLocal8Bit(value); - } else if (key.endsWith(".module")) { - hasModuleEntry = true; - } else if (key.endsWith(".depends")) { - moduleInfo.dependencies = QString::fromLocal8Bit(value).split(QLatin1Char(' ')); - for (auto &dependency : moduleInfo.dependencies) - dependency.replace(QLatin1String("_private"), QLatin1String("-private")); - } else if (key.endsWith(".module_config")) { - const auto elems = value.split(' '); - for (const QByteArray &elem : elems) { - 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, dit.filePath()); - for (auto &includePath : moduleInfo.includePaths) { - includePath - .replace(QLatin1String("$$QT_MODULE_INCLUDE_BASE"), - qtEnvironment.includePath) - .replace(QLatin1String("$$QT_MODULE_LIB_BASE"), - qtEnvironment.libraryPath); - } - } else if (key.endsWith(".DEFINES")) { - moduleInfo.compilerDefines = QString::fromLocal8Bit(value) - .split(QLatin1Char(' '), QString::SkipEmptyParts); - } else if (key.endsWith(".VERSION")) { - moduleInfo.version = QString::fromLocal8Bit(value); - } else if (key.endsWith(".plugin_types")) { - moduleInfo.supportedPluginTypes = makeList(value); - } else if (key.endsWith(".TYPE")) { - moduleInfo.pluginData.type = QString::fromLatin1(value); - } else if (key.endsWith(".EXTENDS")) { - moduleInfo.pluginData.extends = makeList(value); - if (moduleInfo.pluginData.extends.removeOne(QLatin1String("-"))) - moduleInfo.pluginData.autoLoad = false; - } else if (key.endsWith(".CLASS_NAME")) { - moduleInfo.pluginData.className = QString::fromLatin1(value); - } - } - if (hasV2 && !hasModuleEntry) - moduleInfo.hasLibrary = false; - - // Fix include paths for Apple frameworks. - // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. - if (!hasV2 && moduleInfo.isFramework(qtEnvironment)) { - moduleInfo.includePaths.clear(); - QString baseIncDir = moduleInfo.frameworkHeadersPath(qtEnvironment); - if (moduleInfo.isPrivate) { - baseIncDir += QLatin1Char('/') + moduleInfo.version; - moduleInfo.includePaths - << baseIncDir - << baseIncDir + QLatin1Char('/') + moduleInfo.name; - } else { - moduleInfo.includePaths << baseIncDir; - } - } - - moduleInfo.setupLibraries(qtEnvironment, &nonExistingPrlFiles); - - modules.push_back(moduleInfo); - if (moduleInfo.qbsName == QLatin1String("testlib")) - addTestModule(modules); - if (moduleInfo.qbsName == QLatin1String("designercomponents-private")) - addDesignerComponentsModule(modules); - } - - replaceQtLibNamesWithFilePath(&modules, qtEnvironment); - removeDuplicatedDependencyLibs(&modules); - return modules; -} - -QString QtModuleInfo::libBaseName(const QString &libName, bool debugBuild, - const QtEnvironment &qtEnvironment) const -{ - QString name = libName; - if (qtEnvironment.mkspecName.startsWith(QLatin1String("win"))) { - if (debugBuild) - name += QLatin1Char('d'); - if (!isStaticLibrary && qtEnvironment.qtMajorVersion < 5) - name += QString::number(qtEnvironment.qtMajorVersion); - } - if (qtEnvironment.mkspecName.contains(QLatin1String("macx")) - || qtEnvironment.mkspecName.contains(QLatin1String("ios")) - || qtEnvironment.mkspecName.contains(QLatin1String("darwin"))) { - if (!isFramework(qtEnvironment) - && qtEnvironment.buildVariant.contains(QLatin1String("debug")) - && (!qtEnvironment.buildVariant.contains(QLatin1String("release")) || debugBuild)) { - name += QLatin1String("_debug"); - } - } - return name; -} - -} // namespace Internal -} // namespace qbs - diff --git a/src/lib/qtprofilesetup/qtmoduleinfo.h b/src/lib/qtprofilesetup/qtmoduleinfo.h deleted file mode 100644 index 38a654edf..000000000 --- a/src/lib/qtprofilesetup/qtmoduleinfo.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#ifndef QBS_QTMODULEINFO_H -#define QBS_QTMODULEINFO_H - -#include <QtCore/qstringlist.h> - -namespace qbs { -class QtEnvironment; -class Profile; - -namespace Internal { - -template<typename T> class Set; - -class QtModuleInfo -{ -public: - QtModuleInfo(); - QtModuleInfo(const QString &name, const QString &qbsName, - const QStringList &deps = QStringList()); - - QString moduleNameWithoutPrefix() const; - QString frameworkHeadersPath(const QtEnvironment &qtEnvironment) const; - QStringList qt4ModuleIncludePaths(const QtEnvironment &qtEnvironment) const; - QString libraryBaseName(const QtEnvironment &qtEnvironment, bool debugBuild) const; - QString libBaseName(const QString &libName, bool debugBuild, - const QtEnvironment &qtEnvironment) const; - QString libNameForLinker(const QtEnvironment &qtEnvironment, bool debugBuild) const; - void setupLibraries(const QtEnvironment &qtEnv, Internal::Set<QString> *nonExistingPrlFiles); - bool isFramework(const QtEnvironment &qtEnv) const; - - QString modulePrefix; // default is empty and means "Qt". - QString name; // As in the path to the headers and ".name" in the pri files. - QString qbsName; // Lower-case version without "qt" prefix. - QString version; - QStringList dependencies; // qbs names. - QStringList includePaths; - QStringList compilerDefines; - QStringList staticLibrariesDebug; - QStringList staticLibrariesRelease; - QStringList dynamicLibrariesDebug; - QStringList dynamicLibrariesRelease; - QStringList linkerFlagsDebug; - QStringList linkerFlagsRelease; - QString libFilePathDebug; - QString libFilePathRelease; - QStringList frameworksDebug; - QStringList frameworksRelease; - QStringList frameworkPathsDebug; - QStringList frameworkPathsRelease; - QStringList libraryPaths; - QStringList config; - bool isPrivate; - bool hasLibrary; - bool isStaticLibrary; - bool isPlugin; - bool mustExist; - QStringList supportedPluginTypes; - - struct PluginData { - QString type; - QStringList extends; - QString className; - bool autoLoad = true; - } pluginData; - -private: - void setupLibraries(const QtEnvironment &qtEnv, bool debugBuild, - Internal::Set<QString> *nonExistingPrlFiles); -}; - -QList<QtModuleInfo> allQt4Modules(const QtEnvironment &qtEnvironment); -QList<QtModuleInfo> allQt5Modules(const Profile &profile, const QtEnvironment &qtEnvironment); - -} // namespace Internal -} // namespace qbs - -#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtmsvctools.cpp b/src/lib/qtprofilesetup/qtmsvctools.cpp deleted file mode 100644 index 0c41b928c..000000000 --- a/src/lib/qtprofilesetup/qtmsvctools.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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$ -** -****************************************************************************/ - -#include "qtmsvctools.h" - -namespace qbs { - -static const QString msvcPrefix = QLatin1String("win32-msvc"); - -bool isMsvcQt(const QtEnvironment &env) -{ - return env.mkspecName.startsWith(msvcPrefix); -} - -static Version msvcCompilerVersionForYear(int year) -{ - switch (year) - { - case 2005: - return Version(14); - case 2008: - return Version(15); - case 2010: - return Version(16); - case 2012: - return Version(17); - case 2013: - return Version(18); - case 2015: - return Version(19); - case 2017: - return Version(19, 1); - default: - return Version(); - } -} - -Version msvcCompilerVersionFromMkspecName(const QString &mkspecName) -{ - return msvcCompilerVersionForYear(mkspecName.mid(msvcPrefix.size()).toInt()); -} - -} // namespace qbs diff --git a/src/lib/qtprofilesetup/qtmsvctools.h b/src/lib/qtprofilesetup/qtmsvctools.h deleted file mode 100644 index 195d0e0ec..000000000 --- a/src/lib/qtprofilesetup/qtmsvctools.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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$ -** -****************************************************************************/ - -#include "qtenvironment.h" -#include <tools/qbs_export.h> -#include <tools/version.h> - -namespace qbs { - -QBS_EXPORT bool isMsvcQt(const QtEnvironment &env); -QBS_EXPORT Version msvcCompilerVersionFromMkspecName(const QString &mkspecName); - -} // namespace qbs diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp deleted file mode 100644 index 7fabcb5c9..000000000 --- a/src/lib/qtprofilesetup/qtprofilesetup.cpp +++ /dev/null @@ -1,930 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ - -#include "qtprofilesetup.h" - -#include "qtmoduleinfo.h" -#include "qtmsvctools.h" - -#include <logging/translator.h> -#include <tools/architectures.h> -#include <tools/error.h> -#include <tools/hostosinfo.h> -#include <tools/jsliterals.h> -#include <tools/profile.h> -#include <tools/settings.h> -#include <tools/version.h> - -#include <QtCore/qdir.h> -#include <QtCore/qdiriterator.h> -#include <QtCore/qendian.h> -#include <QtCore/qfile.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qlibrary.h> -#include <QtCore/qregexp.h> -#include <QtCore/qtextstream.h> - -#include <queue> -#include <regex> - -#ifdef __APPLE__ -#include <ar.h> -#include <mach/machine.h> -#include <mach-o/fat.h> -#include <mach-o/loader.h> -#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 -#define FAT_MAGIC_64 0xcafebabf -#define FAT_CIGAM_64 0xbfbafeca -struct fat_arch_64 { - cpu_type_t cputype; - cpu_subtype_t cpusubtype; - uint64_t offset; - uint64_t size; - uint32_t align; - uint32_t reserved; -}; -#endif -#endif - -namespace qbs { -using namespace Internal; - -template <class T> -QByteArray utf8JSLiteral(T t) -{ - return toJSLiteral(t).toUtf8(); -} - -static QString pathToJSLiteral(const QString &path) -{ - return toJSLiteral(QDir::fromNativeSeparators(path)); -} - -static QString defaultQpaPlugin(const Profile &profile, const QtModuleInfo &module, - const QtEnvironment &qtEnv) -{ - if (qtEnv.qtMajorVersion < 5) - return QString(); - - if (qtEnv.qtMajorVersion == 5 && qtEnv.qtMinorVersion < 8) { - QFile qConfigPri(qtEnv.mkspecBasePath + QLatin1String("/qconfig.pri")); - if (!qConfigPri.open(QIODevice::ReadOnly)) { - throw ErrorInfo(Tr::tr("Setting up Qt profile '%1' failed: Cannot open " - "file '%2' (%3).") - .arg(profile.name(), qConfigPri.fileName(), qConfigPri.errorString())); - } - const QList<QByteArray> lines = qConfigPri.readAll().split('\n'); - const QByteArray magicString = "QT_DEFAULT_QPA_PLUGIN ="; - for (const QByteArray &line : lines) { - const QByteArray simplifiedLine = line.simplified(); - if (simplifiedLine.startsWith(magicString)) - return QString::fromLatin1(simplifiedLine.mid(magicString.size()).trimmed()); - } - } else { - const QString qtGuiConfigHeader = (qtEnv.frameworkBuild - ? qtEnv.libraryPath - : qtEnv.includePath) - + QStringLiteral("/QtGui") - + (qtEnv.frameworkBuild ? QStringLiteral(".framework/Headers") : QString()) - + QStringLiteral("/qtgui-config.h"); - std::queue<QString> headerFiles; - headerFiles.push(qtGuiConfigHeader); - while (!headerFiles.empty()) { - QFile headerFile(headerFiles.front()); - headerFiles.pop(); - if (!headerFile.open(QIODevice::ReadOnly)) { - throw ErrorInfo(Tr::tr("Setting up Qt profile '%1' failed: Cannot open " - "file '%2' (%3).") - .arg(profile.name(), headerFile.fileName(), - headerFile.errorString())); - } - static const std::regex regexp( - "^#define QT_QPA_DEFAULT_PLATFORM_NAME \"(.+)\".*$"); - static const std::regex includeRegexp( - "^#include \"(.+)\".*$"); - const QList<QByteArray> lines = headerFile.readAll().split('\n'); - for (const QByteArray &line: lines) { - const auto lineStr = QString::fromLatin1(line.simplified()).toStdString(); - std::smatch match; - if (std::regex_match(lineStr, match, regexp)) - return QLatin1Char('q') + QString::fromStdString(match[1]); - if (std::regex_match(lineStr, match, includeRegexp)) { - QString filePath = QString::fromStdString(match[1]); - if (QFileInfo(filePath).isRelative()) { - filePath = QDir::cleanPath(QFileInfo(headerFile.fileName()).absolutePath() - + QLatin1Char('/') + filePath); - } - headerFiles.push(filePath); - } - } - } - } - - if (module.isStaticLibrary) - qDebug("Warning: Could not determine default QPA plugin for static Qt."); - return QString(); -} - -static QByteArray minVersionJsString(const QString &minVersion) -{ - if (minVersion.isEmpty()) - return "original"; - return utf8JSLiteral(minVersion); -} - -#ifdef __APPLE__ -template <typename T = uint32_t> T readInt(QIODevice *ioDevice, bool *ok, - bool swap, bool peek = false) { - const auto bytes = peek - ? ioDevice->peek(sizeof(T)) - : ioDevice->read(sizeof(T)); - if (bytes.size() != sizeof(T)) { - if (ok) - *ok = false; - return T(); - } - if (ok) - *ok = true; - T n = *reinterpret_cast<const T *>(bytes.constData()); - return swap ? qbswap(n) : n; -} - -static QString archName(cpu_type_t cputype, cpu_subtype_t cpusubtype) -{ - switch (cputype) { - case CPU_TYPE_X86: - switch (cpusubtype) { - case CPU_SUBTYPE_X86_ALL: - return QStringLiteral("i386"); - default: - return QString(); - } - case CPU_TYPE_X86_64: - switch (cpusubtype) { - case CPU_SUBTYPE_X86_64_ALL: - return QStringLiteral("x86_64"); - case CPU_SUBTYPE_X86_64_H: - return QStringLiteral("x86_64h"); - default: - return QString(); - } - case CPU_TYPE_ARM: - switch (cpusubtype) { - case CPU_SUBTYPE_ARM_V7: - return QStringLiteral("armv7a"); - case CPU_SUBTYPE_ARM_V7S: - return QStringLiteral("armv7s"); - case CPU_SUBTYPE_ARM_V7K: - return QStringLiteral("armv7k"); - default: - return QString(); - } - case CPU_TYPE_ARM64: - switch (cpusubtype) { - case CPU_SUBTYPE_ARM64_ALL: - return QStringLiteral("arm64"); - default: - return QString(); - } - default: - return QString(); - } -} - -static QStringList detectMachOArchs(QIODevice *device) -{ - bool ok; - bool foundMachO = false; - qint64 pos = device->pos(); - - char ar_header[SARMAG]; - if (device->read(ar_header, SARMAG) == SARMAG) { - if (strncmp(ar_header, ARMAG, SARMAG) == 0) { - while (!device->atEnd()) { - static_assert(sizeof(ar_hdr) == 60, "sizeof(ar_hdr) != 60"); - ar_hdr header; - if (device->read(reinterpret_cast<char *>(&header), - sizeof(ar_hdr)) != sizeof(ar_hdr)) - return { }; - - // If the file name is stored in the "extended format" manner, - // the real filename is prepended to the data section, so skip that many bytes - int filenameLength = 0; - if (strncmp(header.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0) { - char arName[sizeof(header.ar_name)] = { 0 }; - memcpy(arName, header.ar_name + sizeof(AR_EFMT1) - 1, - sizeof(header.ar_name) - (sizeof(AR_EFMT1) - 1) - 1); - filenameLength = strtoul(arName, nullptr, 10); - if (device->read(filenameLength).size() != filenameLength) - return { }; - } - - switch (readInt(device, nullptr, false, true)) { - case MH_CIGAM: - case MH_CIGAM_64: - case MH_MAGIC: - case MH_MAGIC_64: - foundMachO = true; - break; - default: { - // Skip the data and go to the next archive member... - char szBuf[sizeof(header.ar_size) + 1] = { 0 }; - memcpy(szBuf, header.ar_size, sizeof(header.ar_size)); - int sz = static_cast<int>(strtoul(szBuf, nullptr, 10)); - if (sz % 2 != 0) - ++sz; - sz -= filenameLength; - const auto data = device->read(sz); - if (data.size() != sz) - return { }; - } - } - - if (foundMachO) - break; - } - } - } - - // Wasn't an archive file, so try a fat file - if (!foundMachO && !device->seek(pos)) - return QStringList(); - - pos = device->pos(); - - fat_header fatheader; - fatheader.magic = readInt(device, nullptr, false); - if (fatheader.magic == FAT_MAGIC || fatheader.magic == FAT_CIGAM || - fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64) { - const bool swap = fatheader.magic == FAT_CIGAM || fatheader.magic == FAT_CIGAM_64; - const bool is64bit = fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64; - fatheader.nfat_arch = readInt(device, &ok, swap); - if (!ok) - return QStringList(); - - QStringList archs; - - for (uint32_t n = 0; n < fatheader.nfat_arch; ++n) { - fat_arch_64 fatarch; - static_assert(sizeof(fat_arch_64) == 32, "sizeof(fat_arch_64) != 32"); - static_assert(sizeof(fat_arch) == 20, "sizeof(fat_arch) != 20"); - const qint64 expectedBytes = is64bit ? sizeof(fat_arch_64) : sizeof(fat_arch); - if (device->read(reinterpret_cast<char *>(&fatarch), expectedBytes) != expectedBytes) - return QStringList(); - - if (swap) { - fatarch.cputype = qbswap(fatarch.cputype); - fatarch.cpusubtype = qbswap(fatarch.cpusubtype); - } - - const QString name = archName(fatarch.cputype, fatarch.cpusubtype); - if (name.isEmpty()) { - qWarning("Unknown cputype %d and cpusubtype %d", - fatarch.cputype, fatarch.cpusubtype); - return QStringList(); - } - archs.push_back(name); - } - - std::sort(archs.begin(), archs.end()); - return archs; - } - - // Wasn't a fat file, so we just read a thin Mach-O from the original offset - if (!device->seek(pos)) - return QStringList(); - - bool swap = false; - mach_header header; - header.magic = readInt(device, nullptr, swap); - switch (header.magic) { - case MH_CIGAM: - case MH_CIGAM_64: - swap = true; - break; - case MH_MAGIC: - case MH_MAGIC_64: - break; - default: - return QStringList(); - } - - header.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap)); - if (!ok) - return QStringList(); - - header.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap)); - if (!ok) - return QStringList(); - - const QString name = archName(header.cputype, header.cpusubtype); - if (name.isEmpty()) { - qWarning("Unknown cputype %d and cpusubtype %d", - header.cputype, header.cpusubtype); - return { }; - } - return { name }; -} -#endif - -static QStringList extractQbsArchs(const QtModuleInfo &module, const QtEnvironment &qtEnv) -{ -#ifdef __APPLE__ - if (qtEnv.mkspecName.startsWith(QLatin1String("macx-"))) { - QStringList archs; - if (!module.libFilePathRelease.isEmpty()) { - QFile file(module.libFilePathRelease); - if (file.open(QIODevice::ReadOnly)) - archs = detectMachOArchs(&file); - else - qWarning("Failed to open %s for reading", - qUtf8Printable(module.libFilePathRelease)); - if (archs.isEmpty()) - qWarning("Could not detect architectures for module %s in Qt build (%s)", - qPrintable(module.name), qUtf8Printable(qtEnv.mkspecName)); - } - return archs; - } -#else - Q_UNUSED(module); -#endif - QString qbsArch = canonicalArchitecture(qtEnv.architecture); - if (qbsArch == QLatin1String("arm") && qtEnv.mkspecPath.contains(QLatin1String("android"))) - qbsArch = QLatin1String("armv7a"); - - // Qt4 has "QT_ARCH = windows" in qconfig.pri for both MSVC and mingw. - if (qbsArch == QLatin1String("windows")) - return QStringList(); - - return QStringList { qbsArch }; -} - -static std::pair<int, int> findVariable(const QByteArray &content, int start) -{ - std::pair<int, int> result = std::make_pair(-1, -1); - result.first = content.indexOf('@', start); - if (result.first == -1) - return result; - result.second = content.indexOf('@', result.first + 1); - if (result.second == -1) { - result.first = -1; - return result; - } - for (char forbidden : {' ', '\n'}) { - int k = content.indexOf(forbidden, result.first + 1); - if (k != -1 && k < result.second) { - return findVariable(content, result.first + 1); - } - } - return result; -} - -static QByteArray libraryFileTag(const QtEnvironment &env, const QtModuleInfo &module) -{ - QByteArray result; - if (module.isStaticLibrary) { - result = "staticlibrary"; - } else { - result = isMsvcQt(env) || env.mkspecName.startsWith(QLatin1String("win32-g++")) - ? "dynamiclibrary_import" : "dynamiclibrary"; - } - return result; -} - -static void replaceSpecialValues(QByteArray *content, const Profile &profile, - const QtModuleInfo &module, const QtEnvironment &qtEnvironment) -{ - QHash<QByteArray, QByteArray> dict; - dict.insert("archs", utf8JSLiteral(extractQbsArchs(module, qtEnvironment))); - dict.insert("targetPlatform", utf8JSLiteral(qbsTargetPlatformFromQtMkspec(qtEnvironment))); - dict.insert("config", utf8JSLiteral(qtEnvironment.configItems)); - dict.insert("qtConfig", utf8JSLiteral(qtEnvironment.qtConfigItems)); - dict.insert("binPath", utf8JSLiteral(qtEnvironment.binaryPath)); - dict.insert("libPath", utf8JSLiteral(qtEnvironment.libraryPath)); - dict.insert("pluginPath", utf8JSLiteral(qtEnvironment.pluginPath)); - dict.insert("incPath", utf8JSLiteral(qtEnvironment.includePath)); - dict.insert("docPath", utf8JSLiteral(qtEnvironment.documentationPath)); - dict.insert("mkspecName", utf8JSLiteral(qtEnvironment.mkspecName)); - dict.insert("mkspecPath", utf8JSLiteral(qtEnvironment.mkspecPath)); - dict.insert("version", utf8JSLiteral(qtEnvironment.qtVersion)); - dict.insert("libInfix", utf8JSLiteral(qtEnvironment.qtLibInfix)); - dict.insert("availableBuildVariants", utf8JSLiteral(qtEnvironment.buildVariant)); - dict.insert("staticBuild", utf8JSLiteral(qtEnvironment.staticBuild)); - dict.insert("frameworkBuild", utf8JSLiteral(qtEnvironment.frameworkBuild)); - dict.insert("name", utf8JSLiteral(module.moduleNameWithoutPrefix())); - dict.insert("has_library", utf8JSLiteral(module.hasLibrary)); - dict.insert("dependencies", utf8JSLiteral(module.dependencies)); - dict.insert("includes", utf8JSLiteral(module.includePaths)); - dict.insert("staticLibsDebug", utf8JSLiteral(module.staticLibrariesDebug)); - dict.insert("staticLibsRelease", utf8JSLiteral(module.staticLibrariesRelease)); - dict.insert("dynamicLibsDebug", utf8JSLiteral(module.dynamicLibrariesDebug)); - dict.insert("dynamicLibsRelease", utf8JSLiteral(module.dynamicLibrariesRelease)); - dict.insert("linkerFlagsDebug", utf8JSLiteral(module.linkerFlagsDebug)); - dict.insert("linkerFlagsRelease", utf8JSLiteral(module.linkerFlagsRelease)); - dict.insert("libraryPaths", utf8JSLiteral(module.libraryPaths)); - dict.insert("frameworkPathsDebug", utf8JSLiteral(module.frameworkPathsDebug)); - dict.insert("frameworkPathsRelease", utf8JSLiteral(module.frameworkPathsRelease)); - dict.insert("frameworksDebug", utf8JSLiteral(module.frameworksDebug)); - dict.insert("frameworksRelease", utf8JSLiteral(module.frameworksRelease)); - dict.insert("libFilePathDebug", utf8JSLiteral(module.libFilePathDebug)); - dict.insert("libFilePathRelease", utf8JSLiteral(module.libFilePathRelease)); - dict.insert("libNameForLinkerDebug", - utf8JSLiteral(module.libNameForLinker(qtEnvironment, true))); - dict.insert("pluginTypes", utf8JSLiteral(module.supportedPluginTypes)); - dict.insert("moduleConfig", utf8JSLiteral(module.config)); - dict.insert("libNameForLinkerRelease", - utf8JSLiteral(module.libNameForLinker(qtEnvironment, false))); - dict.insert("entryPointLibsDebug", utf8JSLiteral(qtEnvironment.entryPointLibsDebug)); - dict.insert("entryPointLibsRelease", utf8JSLiteral(qtEnvironment.entryPointLibsRelease)); - dict.insert("minWinVersion", minVersionJsString(qtEnvironment.windowsVersion)); - dict.insert("minMacVersion", minVersionJsString(qtEnvironment.macosVersion)); - dict.insert("minIosVersion", minVersionJsString(qtEnvironment.iosVersion)); - dict.insert("minTvosVersion", minVersionJsString(qtEnvironment.tvosVersion)); - dict.insert("minWatchosVersion", minVersionJsString(qtEnvironment.watchosVersion)); - dict.insert("minAndroidVersion", minVersionJsString(qtEnvironment.androidVersion)); - - QByteArray additionalContent; - QByteArray compilerDefines = utf8JSLiteral(module.compilerDefines); - if (module.qbsName == QLatin1String("declarative") - || module.qbsName == QLatin1String("quick")) { - const QByteArray debugMacro = module.qbsName == QLatin1String("declarative") - || qtEnvironment.qtMajorVersion < 5 - ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG"; - - const QString indent = QLatin1String(" "); - QTextStream s(&additionalContent); - s << "property bool qmlDebugging: false" << endl; - s << indent << "property string qmlPath"; - if (qtEnvironment.qmlPath.isEmpty()) - s << endl; - else - s << ": " << pathToJSLiteral(qtEnvironment.qmlPath) << endl; - - s << indent << "property string qmlImportsPath: " - << pathToJSLiteral(qtEnvironment.qmlImportPath); - - const QByteArray baIndent(4, ' '); - compilerDefines = "{\n" - + baIndent + baIndent + "var result = " + compilerDefines + ";\n" - + baIndent + baIndent + "if (qmlDebugging)\n" - + baIndent + baIndent + baIndent + "result.push(\"" + debugMacro + "\");\n" - + baIndent + baIndent + "return result;\n" - + baIndent + "}"; - } - dict.insert("defines", compilerDefines); - if (module.qbsName == QLatin1String("gui")) { - dict.insert("defaultQpaPlugin", - utf8JSLiteral(defaultQpaPlugin(profile, module, qtEnvironment))); - } - if (module.qbsName == QLatin1String("qml")) - dict.insert("qmlPath", pathToJSLiteral(qtEnvironment.qmlPath).toUtf8()); - if (module.isStaticLibrary && module.qbsName != QLatin1String("core")) { - if (!additionalContent.isEmpty()) - additionalContent += "\n "; - additionalContent += "isStaticLibrary: true"; - } - if (module.isPlugin) { - dict.insert("className", utf8JSLiteral(module.pluginData.className)); - dict.insert("extends", utf8JSLiteral(module.pluginData.extends)); - } - if (module.hasLibrary && !module.isFramework(qtEnvironment)) { - if (!additionalContent.isEmpty()) - additionalContent += "\n"; - const QByteArray indent(4, ' '); - additionalContent += "Group {\n"; - if (module.isPlugin) { - additionalContent += indent + indent - + "condition: Qt[\"" + module.qbsName.toUtf8() + "\"].enableLinking\n"; - } - additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName.toUtf8() + "\"]" - + ".libFilePath]\n" - + indent + indent + "filesAreTargets: true\n" - + indent + indent + "fileTags: [\"" + libraryFileTag(qtEnvironment, module) - + "\"]\n" - + indent + "}"; - } - dict.insert("additionalContent", additionalContent); - - for (std::pair<int, int> pos = findVariable(*content, 0); pos.first != -1; - pos = findVariable(*content, pos.first)) { - const QByteArray &replacement = dict.value( - content->mid(pos.first + 1, pos.second - pos.first - 1)); - content->replace(pos.first, pos.second - pos.first + 1, replacement); - pos.first += replacement.length(); - } -} - -static void copyTemplateFile(const QString &fileName, const QString &targetDirectory, - const Profile &profile, const QtEnvironment &qtEnv, QStringList *allFiles, - const QtModuleInfo *module = nullptr, const QVariantMap &pluginMap = QVariantMap(), - const QStringList &nonEssentialPlugins = QStringList()) -{ - if (!QDir::root().mkpath(targetDirectory)) { - throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " - "Cannot create directory '%2'.") - .arg(profile.name(), targetDirectory)); - } - QFile sourceFile(QLatin1String(":/templates/") + fileName); - if (!sourceFile.open(QIODevice::ReadOnly)) { - throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " - "Cannot open '%1' (%2).").arg(sourceFile.fileName(), sourceFile.errorString())); - } - QByteArray newContent = sourceFile.readAll(); - if (module) { - replaceSpecialValues(&newContent, profile, *module, qtEnv); - } else { - newContent.replace("@allPluginsByType@", '(' + utf8JSLiteral(pluginMap) + ')'); - newContent.replace("@nonEssentialPlugins@", utf8JSLiteral(nonEssentialPlugins)); - } - sourceFile.close(); - const QString targetPath = targetDirectory + QLatin1Char('/') + fileName; - allFiles->push_back(QFileInfo(targetPath).absoluteFilePath()); - QFile targetFile(targetPath); - if (targetFile.open(QIODevice::ReadOnly)) { - if (newContent == targetFile.readAll()) // No need to overwrite anything in this case. - return; - targetFile.close(); - } - if (!targetFile.open(QIODevice::WriteOnly)) { - throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " - "Cannot open '%1' (%2).").arg(targetFile.fileName(), targetFile.errorString())); - } - targetFile.resize(0); - targetFile.write(newContent); -} - -static void createModules(Profile &profile, Settings *settings, - const QtEnvironment &qtEnvironment) -{ - const QList<QtModuleInfo> modules = qtEnvironment.qtMajorVersion < 5 - ? allQt4Modules(qtEnvironment) - : allQt5Modules(profile, qtEnvironment); - QVariantMap pluginsByType; - QStringList nonEssentialPlugins; - for (const QtModuleInfo &m : modules) { - if (m.isPlugin) { - QVariant &v = pluginsByType[m.pluginData.type]; - v = v.toStringList() << m.name; - if (!m.pluginData.autoLoad) - nonEssentialPlugins << m.name; - } - } - const QString profileBaseDir = QString::fromLatin1("%1/profiles/%2") - .arg(QFileInfo(settings->fileName()).dir().absolutePath(), - profile.name()); - const QString qbsQtModuleBaseDir = profileBaseDir + QLatin1String("/modules/Qt"); - QStringList allFiles; - copyTemplateFile(QLatin1String("QtModule.qbs"), qbsQtModuleBaseDir, profile, qtEnvironment, - &allFiles); - copyTemplateFile(QLatin1String("QtPlugin.qbs"), qbsQtModuleBaseDir, profile, qtEnvironment, - &allFiles); - copyTemplateFile(QLatin1String("plugin_support.qbs"), - qbsQtModuleBaseDir + QLatin1String("/plugin_support"), profile, qtEnvironment, - &allFiles, nullptr, pluginsByType, nonEssentialPlugins); - for (const QtModuleInfo &module : modules) { - const QString qbsQtModuleDir = qbsQtModuleBaseDir + QLatin1Char('/') + module.qbsName; - QString moduleTemplateFileName; - if (module.qbsName == QLatin1String("core")) { - moduleTemplateFileName = QLatin1String("core.qbs"); - copyTemplateFile(QLatin1String("moc.js"), qbsQtModuleDir, profile, qtEnvironment, - &allFiles); - copyTemplateFile(QLatin1String("qdoc.js"), qbsQtModuleDir, profile, qtEnvironment, - &allFiles); - } else if (module.qbsName == QLatin1String("gui")) { - moduleTemplateFileName = QLatin1String("gui.qbs"); - } else if (module.qbsName == QLatin1String("scxml")) { - moduleTemplateFileName = QLatin1String("scxml.qbs"); - } else if (module.qbsName == QLatin1String("dbus")) { - moduleTemplateFileName = QLatin1String("dbus.qbs"); - copyTemplateFile(QLatin1String("dbus.js"), qbsQtModuleDir, profile, qtEnvironment, - &allFiles); - } else if (module.qbsName == QLatin1String("qml")) { - moduleTemplateFileName = QLatin1String("qml.qbs"); - copyTemplateFile(QLatin1String("qml.js"), qbsQtModuleDir, profile, qtEnvironment, - &allFiles); - const QString qmlcacheStr = QStringLiteral("qmlcache"); - if (QFileInfo::exists(HostOsInfo::appendExecutableSuffix( - qtEnvironment.binaryPath + QStringLiteral("/qmlcachegen")))) { - copyTemplateFile(qmlcacheStr + QStringLiteral(".qbs"), - qbsQtModuleBaseDir + QLatin1Char('/') + qmlcacheStr, profile, - qtEnvironment, &allFiles); - } - } else if (module.qbsName == QLatin1String("quick")) { - moduleTemplateFileName = QLatin1String("quick.qbs"); - copyTemplateFile(QLatin1String("quick.js"), qbsQtModuleDir, profile, - qtEnvironment, &allFiles); - } else if (module.isPlugin) { - moduleTemplateFileName = QLatin1String("plugin.qbs"); - } else { - moduleTemplateFileName = QLatin1String("module.qbs"); - } - copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, profile, qtEnvironment, &allFiles, - &module); - } - - // Note that it's not strictly necessary to copy this one, as it has no variable content. - // But we do it anyway for consistency (and it has no impact on the project files this way). - copyTemplateFile(QLatin1String("android_support.qbs"), - qbsQtModuleBaseDir + QLatin1String("/android_support"), profile, qtEnvironment, - &allFiles); - - QDirIterator dit(qbsQtModuleBaseDir, QDirIterator::Subdirectories); - while (dit.hasNext()) { - dit.next(); - const QFileInfo &fi = dit.fileInfo(); - if (!fi.isFile()) - continue; - const QString filePath = fi.absoluteFilePath(); - if (!allFiles.contains(filePath) && !QFile::remove(filePath)) - qDebug("Warning: Failed to remove outdated file '%s'.", qPrintable(filePath)); - } - profile.setValue(QLatin1String("preferences.qbsSearchPaths"), profileBaseDir); -} - -static QString guessMinimumWindowsVersion(const QtEnvironment &qt) -{ - if (qt.mkspecName.startsWith(QLatin1String("winrt-"))) - return QLatin1String("10.0"); - - if (!qt.targetsDesktopWindows()) - return QString(); - - if (qt.architecture == QLatin1String("x86_64") - || qt.architecture == QLatin1String("ia64")) { - return QLatin1String("5.2"); - } - - QRegExp rex(QLatin1String("^win32-msvc(\\d+)$")); - if (rex.exactMatch(qt.mkspecName)) { - int msvcVersion = rex.cap(1).toInt(); - if (msvcVersion < 2012) - return QLatin1String("5.0"); - else - return QLatin1String("5.1"); - } - - return qt.qtMajorVersion < 5 ? QLatin1String("5.0") : QLatin1String("5.1"); -} - -static bool checkForStaticBuild(const QtEnvironment &qt) -{ - if (qt.qtMajorVersion >= 5) - return qt.qtConfigItems.contains(QLatin1String("static")); - if (qt.frameworkBuild) - return false; // there are no Qt4 static frameworks - const bool isWin = qt.mkspecName.startsWith(QLatin1String("win")); - const QDir libdir(isWin ? qt.binaryPath : qt.libraryPath); - const QStringList coreLibFiles - = libdir.entryList(QStringList(QLatin1String("*Core*")), QDir::Files); - if (coreLibFiles.empty()) - throw ErrorInfo(Internal::Tr::tr("Could not determine whether Qt is a static build.")); - for (const QString &fileName : coreLibFiles) { - if (QLibrary::isLibrary(fileName)) - return false; - } - return true; -} - -static QStringList fillEntryPointLibs(const QtEnvironment &qtEnvironment, const Version &qtVersion, - bool debug) -{ - QStringList result; - const bool isMinGW = qtEnvironment.isForMinGw(); - - // Some Linux distributions rename the qtmain library. - QStringList qtMainCandidates(QLatin1String("qtmain")); - if (isMinGW && qtEnvironment.qtMajorVersion == 5) - qtMainCandidates << QLatin1String("qt5main"); - - for (const QString &baseNameCandidate : qtMainCandidates) { - QString qtmain = qtEnvironment.libraryPath + QLatin1Char('/'); - if (isMinGW) - qtmain += QLatin1String("lib"); - qtmain += baseNameCandidate + qtEnvironment.qtLibInfix; - if (debug) - qtmain += QLatin1Char('d'); - if (isMinGW) { - qtmain += QLatin1String(".a"); - } else { - qtmain += QLatin1String(".lib"); - if (qtVersion >= Version(5, 4, 0)) - result << QLatin1String("Shell32.lib"); - } - if (QFile::exists(qtmain)) { - result << qtmain; - break; - } - } - if (result.isEmpty()) { - qDebug("Warning: Could not find the qtmain library. " - "You will not be able to link Qt applications."); - } - return result; -} - -void doSetupQtProfile(const QString &profileName, Settings *settings, - const QtEnvironment &_qtEnvironment) -{ - QtEnvironment qtEnvironment = _qtEnvironment; - qtEnvironment.staticBuild = checkForStaticBuild(qtEnvironment); - - // determine whether user apps require C++11 - if (qtEnvironment.qtConfigItems.contains(QLatin1String("c++11")) && qtEnvironment.staticBuild) - qtEnvironment.configItems.push_back(QLatin1String("c++11")); - - // Set the minimum operating system versions appropriate for this Qt version - qtEnvironment.windowsVersion = guessMinimumWindowsVersion(qtEnvironment); - if (!qtEnvironment.windowsVersion.isEmpty()) { // Is target OS Windows? - const Version qtVersion = Version(qtEnvironment.qtMajorVersion, - qtEnvironment.qtMinorVersion, - qtEnvironment.qtPatchVersion); - if (qtEnvironment.buildVariant.contains(QLatin1String("debug"))) { - qtEnvironment.entryPointLibsDebug = fillEntryPointLibs(qtEnvironment, qtVersion, - true); - } - if (qtEnvironment.buildVariant.contains(QLatin1String("release"))) { - qtEnvironment.entryPointLibsRelease = fillEntryPointLibs(qtEnvironment, qtVersion, - false); - } - } else if (qtEnvironment.mkspecPath.contains(QLatin1String("macx"))) { - if (qtEnvironment.qtMajorVersion >= 5) { - QFile qmakeConf(qtEnvironment.mkspecPath + QStringLiteral("/qmake.conf")); - if (qmakeConf.open(QIODevice::ReadOnly)) { - static const std::regex re( - "^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\\s*=\\s*(.*)\\s*$"); - while (!qmakeConf.atEnd()) { - std::smatch match; - const auto line = qmakeConf.readLine().trimmed().toStdString(); - if (std::regex_match(line, match, re)) { - const auto platform = QString::fromStdString(match[1]); - const auto version = QString::fromStdString(match[2]); - if (platform == QStringLiteral("MACOSX")) - qtEnvironment.macosVersion = version; - else if (platform == QStringLiteral("IOS")) - qtEnvironment.iosVersion = version; - else if (platform == QStringLiteral("TVOS")) - qtEnvironment.tvosVersion = version; - else if (platform == QStringLiteral("WATCHOS")) - qtEnvironment.watchosVersion = version; - } - } - } - - const bool isMac = qtEnvironment.mkspecName != QStringLiteral("macx-ios-clang") - && qtEnvironment.mkspecName != QStringLiteral("macx-tvos-clang") - && qtEnvironment.mkspecName != QStringLiteral("macx-watchos-clang"); - if (isMac) { - // Qt 5.0.x placed the minimum version in a different file - if (qtEnvironment.macosVersion.isEmpty()) { - qtEnvironment.macosVersion = QLatin1String("10.6"); - } - - // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 - if (Version::fromString(qtEnvironment.macosVersion) < Version(10, 7) - && qtEnvironment.qtConfigItems.contains(QLatin1String("c++11"))) - qtEnvironment.macosVersion = QLatin1String("10.7"); - } - } else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 6) { - QDir qconfigDir; - if (qtEnvironment.frameworkBuild) { - qconfigDir.setPath(qtEnvironment.libraryPath); - qconfigDir.cd(QLatin1String("QtCore.framework/Headers")); - } else { - qconfigDir.setPath(qtEnvironment.includePath); - qconfigDir.cd(QLatin1String("Qt")); - } - QFile qconfig(qconfigDir.absoluteFilePath(QLatin1String("qconfig.h"))); - if (qconfig.open(QIODevice::ReadOnly)) { - bool qtCocoaBuild = false; - QTextStream ts(&qconfig); - QString line; - do { - line = ts.readLine(); - if (QRegExp(QLatin1String("\\s*#define\\s+QT_MAC_USE_COCOA\\s+1\\s*"), - Qt::CaseSensitive).exactMatch(line)) { - qtCocoaBuild = true; - break; - } - } while (!line.isNull()); - - if (ts.status() == QTextStream::Ok) { - qtEnvironment.macosVersion = qtCocoaBuild ? QLatin1String("10.5") - : QLatin1String("10.4"); - } - } - - if (qtEnvironment.macosVersion.isEmpty()) { - throw ErrorInfo(Internal::Tr::tr("Error reading qconfig.h; could not determine " - "whether Qt is using Cocoa or Carbon")); - } - } - } else if (qtEnvironment.mkspecPath.contains(QLatin1String("android"))) { - if (qtEnvironment.qtMajorVersion >= 5) - qtEnvironment.androidVersion = QLatin1String("2.3"); - else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 8) - qtEnvironment.androidVersion = QLatin1String("1.6"); // Necessitas - } - - Profile profile(profileName, settings); - profile.removeProfile(); - createModules(profile, settings, qtEnvironment); -} - -QString qbsTargetPlatformFromQtMkspec(const QtEnvironment &qtEnv) -{ - QString mkspec = qtEnv.mkspecName; - int idx = mkspec.lastIndexOf(QLatin1Char('/')); - if (idx != -1) - mkspec = mkspec.mid(idx + 1); - if (mkspec.startsWith(QLatin1String("aix-"))) - return QLatin1String("aix"); - if (mkspec.startsWith(QLatin1String("android-"))) - return QLatin1String("android"); - if (mkspec.startsWith(QLatin1String("cygwin-"))) - return QLatin1String("windows"); - if (mkspec.startsWith(QLatin1String("darwin-"))) - return QLatin1String("macos"); - if (mkspec.startsWith(QLatin1String("freebsd-"))) - return QLatin1String("freebsd"); - if (mkspec.startsWith(QLatin1String("haiku-"))) - return QLatin1String("haiku"); - if (mkspec.startsWith(QLatin1String("hpux-")) || mkspec.startsWith(QLatin1String("hpuxi-"))) - return QLatin1String("hpux"); - if (mkspec.startsWith(QLatin1String("hurd-"))) - return QLatin1String("hurd"); - if (mkspec.startsWith(QLatin1String("integrity-"))) - return QLatin1String("integrity"); - if (mkspec.startsWith(QLatin1String("linux-"))) - return QLatin1String("linux"); - if (mkspec.startsWith(QLatin1String("macx-"))) { - if (mkspec.startsWith(QLatin1String("macx-ios-"))) - return QLatin1String("ios"); - if (mkspec.startsWith(QLatin1String("macx-tvos-"))) - return QLatin1String("tvos"); - if (mkspec.startsWith(QLatin1String("macx-watchos-"))) - return QLatin1String("watchos"); - return QLatin1String("macos"); - } - if (mkspec.startsWith(QLatin1String("netbsd-"))) - return QLatin1String("netbsd"); - if (mkspec.startsWith(QLatin1String("openbsd-"))) - return QLatin1String("openbsd"); - if (mkspec.startsWith(QLatin1String("qnx-"))) - return QLatin1String("qnx"); - if (mkspec.startsWith(QLatin1String("solaris-"))) - return QLatin1String("solaris"); - if (mkspec.startsWith(QLatin1String("vxworks-"))) - return QLatin1String("vxworks"); - if (qtEnv.targetsDesktopWindows() || mkspec.startsWith(QLatin1String("winrt-"))) - return QLatin1String("windows"); - return QString(); -} - -ErrorInfo setupQtProfile(const QString &profileName, Settings *settings, - const QtEnvironment &qtEnvironment) -{ - try { - doSetupQtProfile(profileName, settings, qtEnvironment); - return ErrorInfo(); - } catch (const ErrorInfo &e) { - return e; - } -} - -} // namespace qbs diff --git a/src/lib/qtprofilesetup/qtprofilesetup.h b/src/lib/qtprofilesetup/qtprofilesetup.h deleted file mode 100644 index f28733a1b..000000000 --- a/src/lib/qtprofilesetup/qtprofilesetup.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#ifndef QBS_SETUPQTPROFILE_H -#define QBS_SETUPQTPROFILE_H - -#include "qtenvironment.h" - -#include <tools/qbs_export.h> - -#include <QtCore/qstring.h> - -namespace qbs { -class ErrorInfo; -class Settings; - -QBS_EXPORT QString qbsTargetPlatformFromQtMkspec(const QtEnvironment &qtEnv); - -QBS_EXPORT ErrorInfo setupQtProfile(const QString &profileName, Settings *settings, - const QtEnvironment &qtEnvironment); - -} // namespace qbs - -#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtprofilesetup.pro b/src/lib/qtprofilesetup/qtprofilesetup.pro deleted file mode 100644 index a32ff97aa..000000000 --- a/src/lib/qtprofilesetup/qtprofilesetup.pro +++ /dev/null @@ -1,31 +0,0 @@ -TARGET = qbsqtprofilesetup -include(../library.pri) -include(../corelib/use_corelib.pri) - -CONFIG -= qtquickcompiler - -HEADERS = \ - qtenvironment.h \ - qtmoduleinfo.h \ - qtmsvctools.h \ - qtprofilesetup.h - -SOURCES = \ - qtmoduleinfo.cpp \ - qtmsvctools.cpp \ - qtprofilesetup.cpp - -RESOURCES = templates.qrc - -!qbs_no_dev_install { - header.files = \ - qtenvironment.h \ - qtmsvctools.h \ - qtprofilesetup.h - header.path = $${QBS_INSTALL_PREFIX}/include/qbs - use_pri.files = use_installed_qtprofilesetup.pri - use_pri.path = $${header.path} - INSTALLS += header use_pri -} - -OTHER_FILES += templates/* diff --git a/src/lib/qtprofilesetup/qtprofilesetup.qbs b/src/lib/qtprofilesetup/qtprofilesetup.qbs deleted file mode 100644 index 8c8b77020..000000000 --- a/src/lib/qtprofilesetup/qtprofilesetup.qbs +++ /dev/null @@ -1,31 +0,0 @@ -import qbs - -QbsLibrary { - name: "qbsqtprofilesetup" - Depends { name: "qbscore" } - - Group { - name: "Public API headers" - files: [ - "qtenvironment.h", - "qtprofilesetup.h", - "qtmsvctools.h", - "use_installed_qtprofilesetup.pri", - ] - qbs.install: qbsbuildconfig.installApiHeaders - qbs.installDir: headerInstallPrefix - } - - files: [ - "qtprofilesetup.cpp", - "qtmoduleinfo.cpp", - "qtmoduleinfo.h", - "qtmsvctools.cpp", - "templates.qrc", - "templates/*" - ] - - Export { - Depends { name: "qbscore" } - } -} diff --git a/src/lib/qtprofilesetup/templates.qrc b/src/lib/qtprofilesetup/templates.qrc deleted file mode 100644 index 35bddf8d7..000000000 --- a/src/lib/qtprofilesetup/templates.qrc +++ /dev/null @@ -1,22 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>templates/core.qbs</file> - <file>templates/gui.qbs</file> - <file>templates/module.qbs</file> - <file>templates/QtModule.qbs</file> - <file>templates/moc.js</file> - <file>templates/plugin.qbs</file> - <file>templates/qdoc.js</file> - <file>templates/QtPlugin.qbs</file> - <file>templates/dbus.js</file> - <file>templates/dbus.qbs</file> - <file>templates/scxml.qbs</file> - <file>templates/qml.qbs</file> - <file>templates/qml.js</file> - <file>templates/qmlcache.qbs</file> - <file>templates/quick.js</file> - <file>templates/quick.qbs</file> - <file>templates/plugin_support.qbs</file> - <file>templates/android_support.qbs</file> - </qresource> -</RCC> diff --git a/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri b/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri deleted file mode 100644 index c3fa4a83d..000000000 --- a/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri +++ /dev/null @@ -1,20 +0,0 @@ -include(use_installed_corelib.pri) - -LIBNAME=qbsqtprofilesetup - -unix:LIBS += -l$${LIBNAME} - -win32 { - CONFIG(debug, debug|release) { - QBSQTPROFILELIB = $${LIBNAME}d$${QBSCORELIBSUFFIX} - } - CONFIG(release, debug|release) { - QBSQTPROFILELIB = $${LIBNAME}$${QBSCORELIBSUFFIX} - } - msvc { - QBSQTPROFILELIB = $${QBSQTPROFILELIB}.lib - } else { - QBSQTPROFILELIB = lib$${QBSQTPROFILELIB} - } - LIBS += $${QBSQTPROFILELIB} -} diff --git a/src/lib/qtprofilesetup/use_qtprofilesetup.pri b/src/lib/qtprofilesetup/use_qtprofilesetup.pri deleted file mode 100644 index 87390fc10..000000000 --- a/src/lib/qtprofilesetup/use_qtprofilesetup.pri +++ /dev/null @@ -1,46 +0,0 @@ -include(../../../qbs_version.pri) -include(../../library_dirname.pri) - -isEmpty(QBSLIBDIR) { - QBSLIBDIR = $${OUT_PWD}/../../../$${QBS_LIBRARY_DIRNAME} -} - -LIBNAME=qbsqtprofilesetup - -unix { - LIBS += -L$${QBSLIBDIR} -l$${LIBNAME} -} - -!qbs_disable_rpath { - linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/../$${QBS_LIBRARY_DIRNAME}\' - macx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../$${QBS_LIBRARY_DIRNAME} -} - -!CONFIG(static, static|shared) { - QBSQTPROFILELIBSUFFIX = $${QBS_VERSION_MAJ} -} - -win32 { - CONFIG(debug, debug|release) { - QBSQTPROFILELIB = $${LIBNAME}d$${QBSQTPROFILELIBSUFFIX} - } - CONFIG(release, debug|release) { - QBSQTPROFILELIB = $${LIBNAME}$${QBSQTPROFILELIBSUFFIX} - } - msvc { - LIBS += /LIBPATH:$$QBSLIBDIR - QBSQTPROFILELIB = $${QBSQTPROFILELIB}.lib - LIBS += Shell32.lib - } else { - LIBS += -L$${QBSLIBDIR} - QBSQTPROFILELIB = lib$${QBSQTPROFILELIB} - } - LIBS += $${QBSQTPROFILELIB} -} - -INCLUDEPATH += \ - $$PWD - -CONFIG(static, static|shared) { - DEFINES += QBS_STATIC_LIB -} diff --git a/src/packages/archive/archive.qbs b/src/packages/archive/archive.qbs index 39d762884..7deaddc3a 100644 --- a/src/packages/archive/archive.qbs +++ b/src/packages/archive/archive.qbs @@ -8,7 +8,6 @@ QbsProduct { Depends { name: "qbs_processlauncher" } Depends { name: "qbscore" } Depends { name: "bundledqt" } - Depends { name: "qbsqtprofilesetup" } Depends { name: "qbs documentation" } Depends { name: "qbs resources" } Depends { name: "qbs man page"; condition: qbs.targetOS.contains("unix") } diff --git a/tests/auto/blackbox/tst_blackboxqt.cpp b/tests/auto/blackbox/tst_blackboxqt.cpp index 128c61133..4428ddb1d 100644 --- a/tests/auto/blackbox/tst_blackboxqt.cpp +++ b/tests/auto/blackbox/tst_blackboxqt.cpp @@ -50,15 +50,12 @@ void TestBlackboxQt::validateTestProfile() if (profileName() != "none" && !s->profiles().contains(profileName())) QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() + "' could not be found. Please set it up on your machine.")); - - const QStringList searchPaths - = qbs::Preferences(s.get(), profileName()).searchPaths( - QDir::cleanPath(QCoreApplication::applicationDirPath())); - for (const auto &searchPath : searchPaths) { - if (QFileInfo(searchPath + "/modules/Qt").isDir()) - return; - } - + const QStringList qmakeFilePaths = Profile(profileName(), s.get()) + .value("moduleProviders.Qt.qmakeFilePaths").toStringList(); + if (!qmakeFilePaths.empty()) + return; + if (!findExecutable(QStringList{"qmake"}).isEmpty()) + return; QSKIP(QByteArray("The build profile '" + profileName().toLocal8Bit() + "' is not a valid Qt profile and Qt was not found " "in the global search paths.")); |