diff options
Diffstat (limited to 'share/qbs/imports')
51 files changed, 2865 insertions, 297 deletions
diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 5f9076004..9f7d92e59 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -166,7 +166,14 @@ var PropertyListVariableExpander = (function () { var syntax; var idx = -1; for (var i in syntaxes) { - var j = str.lastIndexOf(syntaxes[i].open); + var j; + // normal case - we search for the last occurrence to do a correct replacement + // for nested variables, e.g. ${VAR1_${VAR2}}. This doesn't work in case + // when start == end, e.g. @VAR@ - in that case we search from the start + if (syntaxes[i].open !== syntaxes[i].close) + j = str.lastIndexOf(syntaxes[i].open); + else + j = str.indexOf(syntaxes[i].open); if (j > idx) { syntax = syntaxes[i]; idx = j; @@ -175,6 +182,48 @@ var PropertyListVariableExpander = (function () { return { "syntax": syntax, "index": idx }; } + function expandString(key, str, env, seenVars) { + if (!str) + return str; + var repl = indexOfReplacementStart(syntaxes, str); + var i = repl.index; + while (i !== -1) { + var j = str.indexOf(repl.syntax.close, i + repl.syntax.open.length); + if (j === -1) + return str; + var varParts = str.slice(i + repl.syntax.open.length, j).split(':'); + var varName = varParts[0]; + var varFormatter = varParts[1]; + var envValue = env[varName]; + // if we end up expanding the same variable again, break the recursion + if (seenVars.indexOf(varName) !== -1) + envValue = ""; + else + seenVars.push(varName); + var varValue = expandString(key, envValue, env, seenVars); + seenVars.pop(); + if (undefined === varValue) { + // skip replacement + if ($this.undefinedVariableFunction) + $this.undefinedVariableFunction(key, varName); + varValue = ""; + } + varValue = String(varValue); + if (varFormatter !== undefined) { + // TODO: XCode supports multiple formatters separated by a comma + var varFormatterLower = varFormatter.toLowerCase(); + if (varFormatterLower === "rfc1034identifier" || varFormatterLower === "identifier") + varValue = Utilities.rfc1034Identifier(varValue); + if (varValue === "" && varFormatterLower.startsWith("default=")) + varValue = varFormatter.split("=")[1]; + } + str = str.slice(0, i) + varValue + str.slice(j + repl.syntax.close.length); + repl = indexOfReplacementStart(syntaxes, str); + i = repl.index; + } + return str; + } + function expandRecursive(obj, env, checked) { checked.push(obj); for (var key in obj) { @@ -187,35 +236,9 @@ var PropertyListVariableExpander = (function () { } if (type !== "string") continue; - var repl = indexOfReplacementStart(syntaxes, value); - var i = repl.index; - var changes = false; - while (i !== -1) { - var j = value.indexOf(repl.syntax.close, i + repl.syntax.open.length); - if (j === -1) - break; - var varParts = value.slice(i + repl.syntax.open.length, j).split(':'); - var varName = varParts[0]; - var varFormatter = varParts[1]; - var varValue = env[varName]; - if (undefined === varValue) { - // skip replacement - if ($this.undefinedVariableFunction) - $this.undefinedVariableFunction(key, varName); - varValue = ""; - } - varValue = String(varValue); - if (varFormatter !== undefined) - varFormatter = varFormatter.toLowerCase(); - if (varFormatter === "rfc1034identifier") - varValue = Utilities.rfc1034Identifier(varValue); - value = value.slice(0, i) + varValue + value.slice(j + repl.syntax.close.length); - changes = true; - repl = indexOfReplacementStart(syntaxes, value); - i = repl.index; - } - if (changes) - obj[key] = value; + var expandedValue = expandString(key, value, env, []); + if (expandedValue !== value) + obj[key] = expandedValue; } } expandRecursive(obj, env, []); @@ -253,19 +276,3 @@ function cleanPropertyList(plist) { cleanPropertyList(plist[key]); } } - -function _codeSignTimestampFlags(product) { - // If signingTimestamp is undefined, do not specify the flag at all - - // this uses the system-specific default behavior - var signingTimestamp = product.moduleProperty("xcode", "signingTimestamp"); - if (signingTimestamp !== undefined) { - // If signingTimestamp is an empty string, specify the flag but do - // not specify a value - this uses a default Apple-provided server - var flag = "--timestamp"; - if (signingTimestamp) - flag += "=" + signingTimestamp; - return [flag]; - } - - return []; -} diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index b48b5eb94..a1ede5344 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -48,7 +48,7 @@ function mergeCFiles(inputs, outputFilePath) } function sanitizedList(list, product, fullPropertyName) { - if (!Array.isArray(list)) + if (!(list instanceof Array)) return list; var filterFunc = function(elem) { if (typeof elem === "string" && elem.length === 0) { @@ -87,7 +87,7 @@ function artifactInstalledFilePath(artifact) { throw "installSourceBase is not an absolute path"; if (!artifact.filePath.startsWith(installSourceBase)) throw "artifact file path doesn't start with the value of qbs.installSourceBase"; - return FileInfo.joinPaths(targetDir, artifact.filePath.substr(installSourceBase.length + 1)); + return FileInfo.joinPaths(targetDir, artifact.filePath.substr(installSourceBase.length)); } return FileInfo.joinPaths(targetDir, artifact.fileName); } @@ -408,7 +408,7 @@ var PropertyValidator = (function () { errorMessage += "The following properties have invalid values:\n"; lines = []; for (i in invalidProperties) { - for (j in invalidProperties[i]) { + for (j = 0; j < invalidProperties[i].length; ++j) { lines.push(this.moduleName + "." + i + ": " + invalidProperties[i][j]); } } @@ -538,8 +538,13 @@ function guessArchitecture(m) { break; } } - } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) { + } else if (hasAnyOf(m, ["__i386", "__i386__", "__386__"])) { architecture = "x86"; + } else if (hasAnyOf(m, ["_M_IX86"])) { + var code = parseInt(m["_M_IX86"]); + architecture = (code < 300) ? "x86_16" : "x86"; + } else if (hasAnyOf(m, ["_M_I86"])) { + architecture = "x86_16"; } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64", "_M_AMD64"])) { architecture = "x86_64"; if (hasAnyOf(m, ["__x86_64h", "__x86_64h__"])) @@ -570,6 +575,30 @@ function guessArchitecture(m) { architecture = "msp430"; } else if (hasAnyOf(m, ["__RL78__"])) { architecture = "rl78"; + } else if (hasAnyOf(m, ["__RX__"])) { + architecture = "rx"; + } else if (hasAnyOf(m, ["__v850__"])) { + architecture = "v850"; + } else if (hasAnyOf(m, ["__riscv"])) { + architecture = "riscv"; + } else if (hasAnyOf(m, ["__xtensa__", "__XTENSA__"])) { + architecture = "xtensa"; + } else if (hasAnyOf(m, ["__m68k__"])) { + architecture = "m68k"; + } else if (hasAnyOf(m, ["__m32c__"])) { + architecture = "m32c"; + } else if (hasAnyOf(m, ["__m32r__", "__M32R__"])) { + architecture = "m32r"; + } else if (hasAnyOf(m, ["__sh__", "__SH__"])) { + architecture = "sh"; + } else if (hasAnyOf(m, ["__CR16__"])) { + architecture = "cr16"; + } else if (hasAnyOf(m, ["__mc68hc1x__", "__mc68hc1x"])) { + architecture = "hcs12"; + } else if (hasAnyOf(m, ["__e2k__"])) { + architecture = "e2k"; + } else if (hasAnyOf(m, ["__hppa__"])) { + architecture = "hppa"; } } @@ -588,15 +617,19 @@ function guessTargetPlatform(m) { return "vxworks"; if (hasAnyOf(m, ["__APPLE__"])) return "darwin"; - if (hasAnyOf(m, ["WIN32", "_WIN32", "__WIN32__", "__NT__"])) + if (hasAnyOf(m, ["WIN32", "_WIN32", "__WIN32__", "__NT__", "__WINDOWS__", "_WINDOWS"])) return "windows"; + if (hasAnyOf(m, ["MSDOS", "__DOS__", "DOS386"])) + return "dos"; + if (hasAnyOf(m, ["__OS2__"])) + return "os2"; if (hasAnyOf(m, ["_AIX"])) return "aix"; if (hasAnyOf(m, ["hpux", "__hpux"])) return "hpux"; if (hasAnyOf(m, ["__sun", "sun"])) return "solaris"; - if (hasAnyOf(m, ["__linux__", "__linux"])) + if (hasAnyOf(m, ["__linux__", "__linux", "__LINUX__"])) return "linux"; if (hasAnyOf(m, ["__FreeBSD__", "__DragonFly__", "__FreeBSD_kernel__"])) return "freebsd"; @@ -610,3 +643,9 @@ function guessTargetPlatform(m) { return "haiku"; } } + +function toJSLiteral(v) { + if (v === undefined) + return "undefined"; + return JSON.stringify(v); +} diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js index b2cb63e39..c928c33e4 100644 --- a/share/qbs/imports/qbs/PathTools/path-tools.js +++ b/share/qbs/imports/qbs/PathTools/path-tools.js @@ -107,6 +107,8 @@ function dynamicLibraryFilePath(product, variantSuffix, version, maxParts) { version = undefined; } + fileName += product.moduleProperty("cpp", "archSuffix"); + // Append the suffix (i.e. libqbs.1.0.0.dylib, libqbs.so, qbs.dll) fileName += product.moduleProperty("cpp", "dynamicLibrarySuffix"); @@ -230,3 +232,25 @@ function prependOrSetPath(path, pathList, separator) { return path; return path + separator + pathList; } + +function librarySuffixes(targetOS, types, forImport) { + if (targetOS.includes("windows")) { + if (forImport) + return [".lib"]; + return [].concat(types.includes("shared") ? [".dll"] : []); + } + if (targetOS.includes("darwin")) { + return [] + .concat(types.includes("shared") ? [".dylib"] : []) + .concat(types.includes("static") ? [".a"] : []); + } + return [] + .concat(types.includes("shared") ? [".so"] : []) + .concat(types.includes("static") ? [".a"] : []); +} + +function libraryNameFilter(targetOS) { + if (targetOS.contains("unix")) + return function(name) { return "lib" + name; } + return function(name) { return name; } +} diff --git a/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs index 4a86ee935..e50d4251a 100644 --- a/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs @@ -32,12 +32,12 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.TextFile import "../../../modules/Android/android-utils.js" as AndroidUtils PathProbe { // Inputs - property stringList hostOS: qbs.hostOS property path sdkPath environmentPaths: Environment.getEnv("ANDROID_NDK_ROOT") @@ -45,13 +45,13 @@ PathProbe { var paths = []; if (sdkPath) paths.push(FileInfo.joinPaths(sdkPath, "ndk-bundle")); - if (qbs.hostOS.contains("windows")) + if (Host.os().contains("windows")) paths.push(FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"), "Android", "sdk", "ndk-bundle")); - if (qbs.hostOS.contains("macos")) + if (Host.os().contains("macos")) paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"), "Library", "Android", "sdk", "ndk-bundle")); - if (qbs.hostOS.contains("linux")) + if (Host.os().contains("linux")) paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"), "Android", "Sdk", "ndk-bundle")); return paths; @@ -83,11 +83,11 @@ PathProbe { candidatePaths = allPaths; for (i in allPaths) { var platforms = []; - if (hostOS.contains("windows")) + if (Host.os().contains("windows")) platforms.push("windows-x86_64", "windows"); - if (hostOS.contains("darwin")) + if (Host.os().contains("darwin")) platforms.push("darwin-x86_64", "darwin-x86"); - if (hostOS.contains("linux")) + if (Host.os().contains("linux")) platforms.push("linux-x86_64", "linux-x86"); for (j in platforms) { if (File.exists(FileInfo.joinPaths(allPaths[i], "prebuilt", platforms[j]))) { diff --git a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs index 38feecdfa..5b777d3e2 100644 --- a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs @@ -31,17 +31,18 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import "../../../modules/Android/sdk/utils.js" as SdkUtils import "../../../modules/Android/android-utils.js" as AndroidUtils -BinaryProbe { +PathProbe { environmentPaths: Environment.getEnv("ANDROID_HOME") platformSearchPaths: { - if (qbs.hostOS.contains("windows")) + if (Host.os().contains("windows")) return [FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"), "Android", "sdk")]; - if (qbs.hostOS.contains("macos")) + if (Host.os().contains("macos")) return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Library", "Android", "sdk")]; - if (qbs.hostOS.contains("linux")) + if (Host.os().contains("linux")) return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Android", "Sdk")]; } @@ -53,21 +54,17 @@ BinaryProbe { property string platform configure: { - var suffixes = nameSuffixes || [""]; var i, allPaths = (environmentPaths || []).concat(platformSearchPaths || []); candidatePaths = allPaths; for (i in allPaths) { - for (var j in suffixes) { - if (File.exists(FileInfo.joinPaths(allPaths[i], - "tools", "android" + suffixes[j]))) { - path = allPaths[i]; - buildToolsVersions = SdkUtils.availableBuildToolsVersions(path) - buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1]; - platforms = AndroidUtils.availablePlatforms(path) - platform = platforms[platforms.length - 1]; - found = true; - return; - } + if (File.exists(FileInfo.joinPaths(allPaths[i], "build-tools"))) { + path = allPaths[i]; + buildToolsVersions = SdkUtils.availableBuildToolsVersions(path) + buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1]; + platforms = AndroidUtils.availablePlatforms(path) + platform = platforms[platforms.length - 1]; + found = true; + return; } } } diff --git a/share/qbs/imports/qbs/Probes/BinaryProbe.qbs b/share/qbs/imports/qbs/Probes/BinaryProbe.qbs index 0bb264bd6..52b78df03 100644 --- a/share/qbs/imports/qbs/Probes/BinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/BinaryProbe.qbs @@ -28,8 +28,10 @@ ** ****************************************************************************/ +import qbs.Host + PathProbe { - nameSuffixes: qbs.hostOS.contains("windows") ? [".com", ".exe", ".bat", ".cmd"] : undefined - platformSearchPaths: hostOS.contains("unix") ? ["/usr/bin", "/usr/local/bin"] : [] + nameSuffixes: Host.os().contains("windows") ? [".com", ".exe", ".bat", ".cmd"] : undefined + platformSearchPaths: Host.os().contains("unix") ? ["/usr/bin", "/usr/local/bin"] : [] platformEnvironmentPaths: [ "PATH" ] } diff --git a/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs new file mode 100644 index 000000000..3b3959017 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.FileInfo +import qbs.ModUtils +import qbs.Utilities +import "path-probe.js" as PathProbeConfigure + +BinaryProbe { + // input + property string preferredArchitecture + + configure: { + var _selectors; + var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter, + candidateFilter, searchPaths, pathSuffixes, + platformSearchPaths, environmentPaths, + platformEnvironmentPaths); + if (!results.found) { + var msvcs = Utilities.installedMSVCs(preferredArchitecture); + if (msvcs.length >= 1) { + var result = {}; + result.fileName = "cl.exe"; + result.path = msvcs[0].binPath; + result.filePath = FileInfo.joinPaths(path, fileName); + result.candidatePaths = result.filePath; + results.found = true; + results.files = [result]; + } + } + + found = results.found; + allResults = results.files; + + if (allResults.length === 1) { + var result = allResults[0]; + candidatePaths = result.candidatePaths; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + } + + } +} diff --git a/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs new file mode 100644 index 000000000..8dc01d376 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.FileInfo +import qbs.ModUtils +import qbs.Utilities +import "path-probe.js" as PathProbeConfigure + +BinaryProbe { + // output + property string vcvarsallPath + + configure: { + var _selectors; + var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter, + candidateFilter, searchPaths, pathSuffixes, + platformSearchPaths, environmentPaths, + platformEnvironmentPaths); + var compilerPath; + if (results.found) + compilerPath = results.files[0].filePath; + var compilers = Utilities.installedClangCls(compilerPath); + if (compilers.length >= 1) { + var result = {}; + result.fileName = "clang-cl.exe"; + result.path = compilers[0].toolchainInstallPath; + result.filePath = FileInfo.joinPaths(result.path, result.fileName); + result.candidatePaths = result.filePath; + result.vcvarsallPath = compilers[0].vcvarsallPath; + results.found = true; + results.files = [result]; + } + + found = results.found; + allResults = results.files; + + if (allResults.length === 1) { + var result = allResults[0]; + candidatePaths = result.candidatePaths; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + vcvarsallPath = result.vcvarsallPath; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs index c7687f0e9..f0d930be5 100644 --- a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities import "../../../modules/cpp/gcc.js" as Gcc @@ -39,15 +40,15 @@ PathProbe { property string compilerFilePath property string vcvarsallFilePath property stringList enableDefinesByLanguage - property string architecture - property string _nullDevice: qbs.nullDevice - property string _pathListSeparator: qbs.pathListSeparator + property string preferredArchitecture + property string winSdkVersion // Outputs property int versionMajor property int versionMinor property int versionPatch property stringList includePaths + property string architecture property var buildEnv property var compilerDefinesByLanguage @@ -57,9 +58,21 @@ PathProbe { languages = ["c"]; var info = languages.contains("c") - ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "c") : {}; + ? Utilities.clangClCompilerInfo( + compilerFilePath, + preferredArchitecture, + vcvarsallFilePath, + "c", + winSdkVersion) + : {}; var infoCpp = languages.contains("cpp") - ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "cpp") : {}; + ? Utilities.clangClCompilerInfo( + compilerFilePath, + preferredArchitecture, + vcvarsallFilePath, + "cpp", + winSdkVersion) + : {}; found = (!languages.contains("c") || (!!info && !!info.macros && !!info.buildEnvironment)) && (!languages.contains("cpp") || @@ -81,8 +94,9 @@ PathProbe { var clangPath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "clang.exe"); var defaultPaths = Gcc.dumpDefaultPaths(buildEnv, clangPath, - [], _nullDevice, - _pathListSeparator, "", ""); + [], Host.nullDevice(), + FileInfo.pathListSeparator(), "", ""); includePaths = defaultPaths.includePaths; + architecture = ModUtils.guessArchitecture(macros); } } diff --git a/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs new file mode 100644 index 000000000..097b1b6f1 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Richard Weickelt +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.Process +import qbs.File +import qbs.FileInfo +import qbs.TextFile +import qbs.Utilities + +Probe { + // Inputs + property stringList additionalArguments: [] + property path conanfilePath + property path packageReference + property path executable: "conan" + FileInfo.executableSuffix() + property stringList generators: ["json"] + property var options + property var settings + property bool verbose: false + + // Output + property var dependencies + property path generatedFilesPath + property var json + + // Internal + // Ensure that the probe is re-run automatically whenever conanfile changes + // by making a file system property part of the probe's signature. + property int _conanfileLastModified: conanfilePath ? File.lastModified(conanfilePath) : 0 + property path _projectBuildDirectory: project.buildDirectory + + configure: { + if (conanfilePath && packageReference) + throw("conanfilePath and packageReference must not be defined at the same time."); + + if (!conanfilePath && !packageReference) + throw("Either conanfilePath or packageReference must be defined."); + + var reference = packageReference || FileInfo.cleanPath(conanfilePath); + console.info("Probing '" + reference + "'. This might take a while..."); + if (conanfilePath && !File.exists(reference)) + throw("The conanfile '" + reference + "' does not exist."); + + var args = [ + "install", reference, + ]; + + if (options) { + if (typeof options !== "object") + throw("The property 'options' must be an object."); + Object.keys(options).forEach(function(key,index) { + args.push("-o"); + args.push(key + "=" + options[key]); + }); + } + + if (settings) { + if (typeof settings !== "object") + throw("The property 'settings' must be an object."); + Object.keys(settings).forEach(function(key,index) { + args.push("-s"); + args.push(key + "=" + settings[key]); + }); + } + + if (!generators.contains("json")) + generators.push("json"); + + for (var i = 0; i < generators.length; i++) + args = args.concat(["-g", generators[i]]); + + for (var i = 0; i < additionalArguments.length; i++) + args.push(additionalArguments[i]); + + generatedFilesPath = FileInfo.cleanPath(_projectBuildDirectory + + "/genconan/" + + Utilities.getHash(args.join())); + + args = args.concat(["-if", generatedFilesPath]); + var p = new Process(); + p.start(executable, args); + while (!p.waitForFinished(500)) { + const output = p.readStdOut(); + if (verbose && output) { + console.info(output); + } + } + while (!p.atEnd()) { + const output = p.readStdOut(); + if (verbose && output) { + console.info(output); + } + } + if (p.exitCode()) { + const errorOutput = p.readStdErr(); + p.close(); + throw errorOutput; + } + p.close(); + + if (generators.contains("json")) { + if (!File.exists(generatedFilesPath + "/conanbuildinfo.json")) + throw("No conanbuildinfo.json has been generated."); + + var jsonFile = new TextFile(generatedFilesPath + "/conanbuildinfo.json", TextFile.ReadOnly); + json = JSON.parse(jsonFile.readAll()); + jsonFile.close(); + + dependencies = {}; + for (var i = 0; i < json.dependencies.length; ++i) { + var dep = json.dependencies[i]; + dependencies[dep.name] = dep; + } + } + + found = true; + } +} diff --git a/share/qbs/imports/qbs/Probes/CosmicProbe.qbs b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs new file mode 100644 index 000000000..7de781e6e --- /dev/null +++ b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import "../../../modules/cpp/cosmic.js" as COSMIC + +PathProbe { + // Inputs + property string compilerFilePath + property stringList enableDefinesByLanguage + + // Outputs + property string architecture + property string endianness + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage + + configure: { + compilerDefinesByLanguage = {}; + + if (!File.exists(compilerFilePath)) { + found = false; + return; + } + + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + // COSMIC compiler support only the C-language. + if (!languages.contains("c")) { + found = false; + return; + } + + var macros = COSMIC.dumpMacros(compilerFilePath); + if (!macros) { + found = false; + return; + } + + compilerDefinesByLanguage["c"] = macros; + + architecture = COSMIC.guessArchitecture(compilerFilePath); + endianness = COSMIC.guessEndianness(architecture); + var defaultPaths = COSMIC.dumpDefaultPaths(compilerFilePath, architecture); + includePaths = defaultPaths.includePaths; + + var version = COSMIC.dumpVersion(compilerFilePath); + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!architecture && !!endianness; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/DmcProbe.qbs b/share/qbs/imports/qbs/Probes/DmcProbe.qbs new file mode 100644 index 000000000..6a8723a3b --- /dev/null +++ b/share/qbs/imports/qbs/Probes/DmcProbe.qbs @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import qbs.ModUtils +import "../../../modules/cpp/dmc.js" as DMC + +PathProbe { + // Inputs + property string compilerFilePath + property stringList enableDefinesByLanguage + + property string _targetPlatform + property string _targetArchitecture + property string _targetExtender // Only for DOS 16/32 bit. + + // Outputs + property string architecture + property string targetPlatform + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage + + configure: { + compilerDefinesByLanguage = {}; + + if (!File.exists(compilerFilePath)) { + found = false; + return; + } + + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + var defaultPathsByLanguage = {}; + for (var i = 0; i < languages.length; ++i) { + var tag = languages[i]; + compilerDefinesByLanguage[tag] = DMC.dumpMacros( + compilerFilePath, + _targetPlatform, + _targetArchitecture, + _targetExtender, + tag); + var paths = DMC.dumpDefaultPaths(compilerFilePath, tag); + defaultPathsByLanguage[tag] = paths; + } + + var macros = compilerDefinesByLanguage["c"] + || compilerDefinesByLanguage["cpp"]; + + architecture = ModUtils.guessArchitecture(macros); + targetPlatform = ModUtils.guessTargetPlatform(macros); + + var defaultPaths = defaultPathsByLanguage["cpp"] + || defaultPathsByLanguage["c"]; + + includePaths = defaultPaths.includePaths; + + var version = DMC.guessVersion(macros); + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!architecture; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs index e0fe73b40..c3d98a49f 100644 --- a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs @@ -35,10 +35,5 @@ PathProbe { "/Library/Frameworks", "/System/Library/Frameworks" ]) - - nameFilter: { - return function(name) { - return name + ".framework"; - } - } + nameSuffixes: ".framework" } diff --git a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs index 693fb6a01..0872e6cc0 100644 --- a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs @@ -1,8 +1,10 @@ import qbs.Environment import qbs.FileInfo +import qbs.Host import "path-probe.js" as PathProbeConfigure BinaryProbe { + nameSuffixes: undefined // _compilerName already contains ".exe" suffix on Windows // Inputs property string _compilerName property string _toolchainPrefix @@ -12,7 +14,7 @@ BinaryProbe { platformSearchPaths: { var paths = base; - if (qbs.targetOS.contains("windows") && qbs.hostOS.contains("windows")) + if (qbs.targetOS.contains("windows") && Host.os().contains("windows")) paths.push(FileInfo.joinPaths( Environment.getEnv("SystemDrive"), "MinGW", "bin")); return paths; @@ -48,20 +50,29 @@ BinaryProbe { configure: { var selectors; - var _results = PathProbeConfigure.configure( + var results = PathProbeConfigure.configure( selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, - pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, - pathListSeparator); - found = _results.found; - var resultFile = _results.files[0]; - candidatePaths = resultFile.candidatePaths; - path = resultFile.path; - filePath = resultFile.filePath; - fileName = resultFile.fileName; - (nameSuffixes || [""]).forEach(function(suffix) { - var end = _compilerName + suffix; - if (fileName.endsWith(end)) - tcPrefix = fileName.slice(0, -end.length); - }); + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths); + + found = results.found; + if (!found) + return; + + var resultsMapper = function(result) { + (nameSuffixes || [""]).forEach(function(suffix) { + var end = _compilerName + suffix; + if (result.fileName.endsWith(end)) + result.tcPrefix = result.fileName.slice(0, -end.length); + }); + return result; + }; + results.files = results.files.map(resultsMapper); + allResults = results.files; + var result = results.files[0]; + candidatePaths = result.candidatePaths; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + tcPrefix = result.tcPrefix; } } diff --git a/share/qbs/imports/qbs/Probes/GccProbe.qbs b/share/qbs/imports/qbs/Probes/GccProbe.qbs index 9106ff27b..5c6dc1936 100644 --- a/share/qbs/imports/qbs/Probes/GccProbe.qbs +++ b/share/qbs/imports/qbs/Probes/GccProbe.qbs @@ -29,6 +29,8 @@ ****************************************************************************/ import qbs.File +import qbs.FileInfo +import qbs.Host import qbs.ModUtils import "../../../modules/cpp/gcc.js" as Gcc @@ -39,8 +41,6 @@ PathProbe { property stringList flags: [] property var environment - property string _nullDevice: qbs.nullDevice - property string _pathListSeparator: qbs.pathListSeparator property string _sysroot: qbs.sysroot property stringList _targetOS: qbs.targetOS @@ -63,7 +63,8 @@ PathProbe { if (fp && File.exists(fp)) { try { compilerDefinesByLanguage[languages[i]] = Gcc.dumpMacros(environment, fp, - flags, _nullDevice, + flags, + Host.nullDevice(), languages[i]); } catch (e) { // Only throw errors when determining the compiler defines for the C language; @@ -84,8 +85,8 @@ PathProbe { || compilerDefinesByLanguage["objcpp"]; var defaultPaths = Gcc.dumpDefaultPaths(environment, compilerFilePathByLanguage["cpp"] || compilerFilePathByLanguage["c"], - flags, _nullDevice, - _pathListSeparator, _sysroot, _targetOS); + flags, Host.nullDevice(), + FileInfo.pathListSeparator(), _sysroot, _targetOS); found = !!macros && !!defaultPaths; includePaths = defaultPaths.includePaths; diff --git a/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs b/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs index 528719e5b..497835479 100644 --- a/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs +++ b/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ import qbs.File +import qbs.Host import "../../../modules/cpp/gcc.js" as Gcc PathProbe { @@ -36,7 +37,6 @@ PathProbe { property string compilerFilePath property var environment - property string _nullDevice: qbs.nullDevice property stringList _toolchain: qbs.toolchain // Outputs @@ -50,7 +50,7 @@ PathProbe { return; } - var macros = Gcc.dumpMacros(environment, compilerFilePath, undefined, _nullDevice); + var macros = Gcc.dumpMacros(environment, compilerFilePath, undefined, Host.nullDevice()); if (_toolchain.contains("clang")) { versionMajor = parseInt(macros["__clang_major__"], 10); diff --git a/share/qbs/imports/qbs/Probes/IarProbe.qbs b/share/qbs/imports/qbs/Probes/IarProbe.qbs index d261e9065..a0008be47 100644 --- a/share/qbs/imports/qbs/Probes/IarProbe.qbs +++ b/share/qbs/imports/qbs/Probes/IarProbe.qbs @@ -33,19 +33,17 @@ import "../../../modules/cpp/iar.js" as IAR PathProbe { // Inputs - property string compilerFilePath; - property stringList enableDefinesByLanguage; - - property string _nullDevice: qbs.nullDevice + property string compilerFilePath + property stringList enableDefinesByLanguage // Outputs - property string architecture; - property string endianness; - property int versionMajor; - property int versionMinor; - property int versionPatch; - property stringList includePaths; - property var compilerDefinesByLanguage; + property string architecture + property string endianness + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage configure: { compilerDefinesByLanguage = {}; @@ -59,10 +57,13 @@ PathProbe { if (!languages || languages.length === 0) languages = ["c"]; + var defaultPathsByLanguage = {}; for (var i = 0; i < languages.length; ++i) { var tag = languages[i]; compilerDefinesByLanguage[tag] = IAR.dumpMacros( compilerFilePath, tag); + var paths = IAR.dumpDefaultPaths(compilerFilePath, tag); + defaultPathsByLanguage[tag] = paths; } var macros = compilerDefinesByLanguage["c"] @@ -71,10 +72,8 @@ PathProbe { architecture = IAR.guessArchitecture(macros); endianness = IAR.guessEndianness(macros); - // FIXME: Do we need dump the default paths for both C - // and C++ languages? - var defaultPaths = IAR.dumpDefaultPaths( - compilerFilePath, languages[0]); + var defaultPaths = defaultPathsByLanguage["cpp"] + || defaultPathsByLanguage["c"]; includePaths = defaultPaths.includePaths; @@ -83,7 +82,7 @@ PathProbe { versionMajor = version.major; versionMinor = version.minor; versionPatch = version.patch; - found = version && architecture && endianness; + found = !!architecture && !!endianness; } } } diff --git a/share/qbs/imports/qbs/Probes/IncludeProbe.qbs b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs index 3c1059e64..6201cbc55 100644 --- a/share/qbs/imports/qbs/Probes/IncludeProbe.qbs +++ b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs @@ -32,6 +32,8 @@ PathProbe { platformSearchPaths: qbs.targetOS.contains("unix") ? [ "/usr/include", "/usr/local/include", + "/include", + "/app/include", ] : [] platformEnvironmentPaths: { if (qbs.toolchain.contains('msvc')) diff --git a/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs index 2c06a6a0b..87475cb53 100644 --- a/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs +++ b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs @@ -35,18 +35,25 @@ PathProbe { property var version configure: { - var keySuffix = "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1"; var keys = [ - "HKEY_LOCAL_MACHINE\\SOFTWARE\\" + keySuffix, - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\" + keySuffix + "HKEY_LOCAL_MACHINE\\SOFTWARE\\", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\" ]; - for (var i in keys) { - var v = Utilities.getNativeSetting(keys[i], "DisplayVersion"); - if (v) { - path = Utilities.getNativeSetting(keys[i], "InstallLocation"); - version = v; - found = path && version; - return; + var keySuffixes = [ + "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1", + "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 6_is1" + ]; + for (var i = 0; i < keys.length; ++i) { + for (var j = 0; j < keySuffixes.length; ++j) { + var key = keys[i] + keySuffixes[j]; + + var v = Utilities.getNativeSetting(key, "DisplayVersion"); + if (v) { + path = Utilities.getNativeSetting(key, "InstallLocation"); + version = v; + found = path && version; + return; + } } } } diff --git a/share/qbs/imports/qbs/Probes/JdkProbe.qbs b/share/qbs/imports/qbs/Probes/JdkProbe.qbs index 1f414b0fa..efb5a5336 100644 --- a/share/qbs/imports/qbs/Probes/JdkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/JdkProbe.qbs @@ -30,14 +30,14 @@ ****************************************************************************/ import qbs.Environment +import qbs.Host import "../../../modules/java/utils.js" as JavaUtils PathProbe { // Inputs - property stringList hostOS: qbs.hostOS property string architecture: !_androidCrossCompiling ? qbs.architecture : undefined property bool _androidCrossCompiling: qbs.targetOS.contains("android") - && !qbs.hostOS.contains("android") + && !Host.os().contains("android") environmentPaths: Environment.getEnv("JAVA_HOME") platformSearchPaths: [ @@ -47,7 +47,8 @@ PathProbe { ] configure: { - path = JavaUtils.findJdkPath(hostOS, architecture, environmentPaths, platformSearchPaths); + path = JavaUtils.findJdkPath(Host.os(), architecture, environmentPaths, + platformSearchPaths); found = !!path; } } diff --git a/share/qbs/imports/qbs/Probes/KeilProbe.qbs b/share/qbs/imports/qbs/Probes/KeilProbe.qbs index 34afecb64..b123584ad 100644 --- a/share/qbs/imports/qbs/Probes/KeilProbe.qbs +++ b/share/qbs/imports/qbs/Probes/KeilProbe.qbs @@ -29,23 +29,22 @@ ****************************************************************************/ import qbs.File +import qbs.Host import "../../../modules/cpp/keil.js" as KEIL PathProbe { // Inputs - property string compilerFilePath; - property stringList enableDefinesByLanguage; - - property string _nullDevice: qbs.nullDevice + property string compilerFilePath + property stringList enableDefinesByLanguage // Outputs - property string architecture; - property string endianness; - property int versionMajor; - property int versionMinor; - property int versionPatch; - property stringList includePaths; - property var compilerDefinesByLanguage; + property string architecture + property string endianness + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage configure: { compilerDefinesByLanguage = {}; @@ -62,7 +61,7 @@ PathProbe { for (var i = 0; i < languages.length; ++i) { var tag = languages[i]; compilerDefinesByLanguage[tag] = KEIL.dumpMacros( - compilerFilePath, tag, _nullDevice); + compilerFilePath, tag, Host.nullDevice()); } var macros = compilerDefinesByLanguage["c"] @@ -72,7 +71,7 @@ PathProbe { endianness = KEIL.guessEndianness(macros); var defaultPaths = KEIL.dumpDefaultPaths( - compilerFilePath, architecture); + compilerFilePath, Host.nullDevice()); includePaths = defaultPaths.includePaths; @@ -81,7 +80,7 @@ PathProbe { versionMajor = version.major; versionMinor = version.minor; versionPatch = version.patch; - found = version.found && architecture && endianness; + found = !!architecture && !!endianness; } } } diff --git a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs index 26787d1b4..d67a81372 100644 --- a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Ivan Komissarov. +** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com) ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -28,29 +28,40 @@ ** ****************************************************************************/ +import qbs.PathTools + PathProbe { - nameSuffixes: { - if (qbs.targetOS.contains("windows")) - return [".lib"]; - if (qbs.targetOS.contains("macos")) - return [".dylib", ".a"]; - return [".so", ".a"]; - } - platformSearchPaths: qbs.targetOS.contains("unix") ? [ - "/usr/lib", - "/usr/local/lib", - ] : [] - nameFilter: { + property string endianness + nameSuffixes: PathTools.librarySuffixes(qbs.targetOS, ["shared", "static"], true) + platformSearchPaths: { + var result = []; if (qbs.targetOS.contains("unix")) { - return function(name) { - return "lib" + name; - } - } else { - return function(name) { - return name; + if (qbs.targetOS.contains("linux") && qbs.architecture) { + if (qbs.architecture === "armv7") + result = ["/usr/lib/arm-linux-gnueabihf"] + else if (qbs.architecture === "arm64") + result = ["/usr/lib/aarch64-linux-gnu"] + else if (qbs.architecture === "mips" && endianness === "big") + result = ["/usr/lib/mips-linux-gnu"] + else if (qbs.architecture === "mips" && endianness === "little") + result = ["/usr/lib/mipsel-linux-gnu"] + else if (qbs.architecture === "mips64") + result = ["/usr/lib/mips64el-linux-gnuabi64"] + else if (qbs.architecture === "ppc") + result = ["/usr/lib/powerpc-linux-gnu"] + else if (qbs.architecture === "ppc64") + result = ["/usr/lib/powerpc64le-linux-gnu"] + else if (qbs.architecture === "x86_64") + result = ["/usr/lib64", "/usr/lib/x86_64-linux-gnu"] + else if (qbs.architecture === "x86") + result = ["/usr/lib32", "/usr/lib/i386-linux-gnu"] } + result = result.concat(["/usr/lib", "/usr/local/lib", "/lib", "/app/lib"]); } + + return result; } + nameFilter: PathTools.libraryNameFilter(qbs.targetOS) platformEnvironmentPaths: { if (qbs.targetOS.contains("windows")) return [ "PATH" ]; diff --git a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs index 2d5faecdd..d3624e010 100644 --- a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs +++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs @@ -38,6 +38,7 @@ PathProbe { property string compilerFilePath property stringList enableDefinesByLanguage property string preferredArchitecture + property string winSdkVersion // Outputs property string architecture @@ -54,9 +55,9 @@ PathProbe { languages = ["c"]; var info = languages.contains("c") - ? Utilities.msvcCompilerInfo(compilerFilePath, "c") : {}; + ? Utilities.msvcCompilerInfo(compilerFilePath, "c", winSdkVersion) : {}; var infoCpp = languages.contains("cpp") - ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp") : {}; + ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp", winSdkVersion) : {}; found = (!languages.contains("c") || (!!info && !!info.macros && !!info.buildEnvironment)) && (!languages.contains("cpp") || diff --git a/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs index b0162c715..520e57f56 100644 --- a/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs +++ b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs @@ -30,12 +30,13 @@ import qbs.Environment import qbs.FileInfo +import qbs.Host BinaryProbe { names: ["node", "nodejs"] platformSearchPaths: { var paths = base; - if (qbs.hostOS.contains("windows")) { + if (Host.os().contains("windows")) { var env32 = Environment.getEnv("PROGRAMFILES(X86)"); var env64 = Environment.getEnv("PROGRAMFILES"); if (env64 === env32 && env64.endsWith(" (x86)")) diff --git a/share/qbs/imports/qbs/Probes/NpmProbe.qbs b/share/qbs/imports/qbs/Probes/NpmProbe.qbs index f6a99e826..08490065a 100644 --- a/share/qbs/imports/qbs/Probes/NpmProbe.qbs +++ b/share/qbs/imports/qbs/Probes/NpmProbe.qbs @@ -28,6 +28,8 @@ ** ****************************************************************************/ +import qbs.FileInfo +import qbs.Host import qbs.ModUtils import "path-probe.js" as PathProbeConfigure import "../../../modules/nodejs/nodejs.js" as NodeJs @@ -50,25 +52,30 @@ NodeJsProbe { var selectors; var results = PathProbeConfigure.configure( selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, - pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, - pathListSeparator); + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths); - var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator, - hostOS.contains("windows")); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), + Host.os().contains("windows")); v.prepend(interpreterPath); - var result = results.files[0]; - result.npmBin = results.found - ? NodeJs.findLocation(result.filePath, "bin", v.value) - : undefined; - result.npmRoot = results.found - ? NodeJs.findLocation(result.filePath, "root", v.value) - : undefined; - result.npmPrefix = results.found - ? NodeJs.findLocation(result.filePath, "prefix", v.value) - : undefined; + var resultsMapper = function(result) { + result.npmBin = result.found + ? NodeJs.findLocation(result.filePath, "bin", v.value) + : undefined; + result.npmRoot = result.found + ? NodeJs.findLocation(result.filePath, "root", v.value) + : undefined; + result.npmPrefix = result.found + ? NodeJs.findLocation(result.filePath, "prefix", v.value) + : undefined; + return result; + }; + results.files = results.files.map(resultsMapper); found = results.found; + allResults = results.files; + + var result = results.files[0]; candidatePaths = result.candidatePaths; path = result.path; filePath = result.filePath; diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs index d0edea682..dc3b32ab7 100644 --- a/share/qbs/imports/qbs/Probes/PathProbe.qbs +++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ import "path-probe.js" as PathProbeConfigure +import qbs.Host import qbs.ModUtils Probe { @@ -38,15 +39,11 @@ Probe { property var nameFilter property var candidateFilter property varList selectors - property pathList pathPrefixes property pathList searchPaths property stringList pathSuffixes - property pathList platformSearchPaths: hostOS.contains("unix") ? ['/usr', '/usr/local'] : [] - property pathList platformPaths + property pathList platformSearchPaths: Host.os().contains("unix") ? ['/usr', '/usr/local'] : [] property stringList environmentPaths property stringList platformEnvironmentPaths - property stringList hostOS: qbs.hostOS - property string pathListSeparator: qbs.pathListSeparator // Output property stringList candidatePaths @@ -57,23 +54,19 @@ Probe { property varList allResults configure: { - if (pathPrefixes) - console.warn("PathProbe.pathPrefixes is deprecated, use searchPaths instead"); - if (platformPaths) - console.warn("PathProbe.platformPaths is deprecated, use platformSearchPaths instead"); - var _searchPaths = ModUtils.concatAll(pathPrefixes, searchPaths); - var _platformSearchPaths = ModUtils.concatAll(platformPaths, platformSearchPaths); var results = PathProbeConfigure.configure(selectors, names, nameSuffixes, nameFilter, - candidateFilter, _searchPaths, pathSuffixes, - _platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator); + candidateFilter, searchPaths, pathSuffixes, + platformSearchPaths, environmentPaths, + platformEnvironmentPaths); found = results.found; allResults = results.files; - var result = allResults[0]; - candidatePaths = result.candidatePaths; - path = result.path; - filePath = result.filePath; - fileName = result.fileName; + if (allResults.length === 1) { + var result = allResults[0]; + candidatePaths = result.candidatePaths; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + } } } diff --git a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs index b295c7441..0fe81c3cc 100644 --- a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs +++ b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs @@ -28,6 +28,7 @@ ** ****************************************************************************/ +import qbs.Host import qbs.Process import qbs.FileInfo @@ -42,7 +43,6 @@ Probe { property string maxVersion property bool forStaticBuild: false property stringList libDirs // Full, non-sysrooted paths, mirroring the environment variable - property string pathListSeparator: qbs.pathListSeparator // Output property stringList cflags // Unmodified --cflags output @@ -60,6 +60,7 @@ Probe { if (!packageNames || packageNames.length === 0) throw 'PkgConfigProbe.packageNames must be specified.'; var p = new Process(); + var stdout; try { var libDirsToSet = libDirs; if (sysroot) { @@ -72,7 +73,7 @@ Probe { } } if (libDirsToSet) - p.setEnv("PKG_CONFIG_LIBDIR", libDirsToSet.join(pathListSeparator)); + p.setEnv("PKG_CONFIG_LIBDIR", libDirsToSet.join(FileInfo.pathListSeparator())); var versionArgs = []; if (minVersion !== undefined) versionArgs.push("--atleast-version=" + minVersion); @@ -84,16 +85,22 @@ Probe { && p.exec(executable, versionArgs.concat(packageNames)) !== 0) { return; } + + // protobuf is reserved as qbs module name, which depends on the protobuflib module + packageNames = packageNames.map(function(name) { + return name === "protobuflib" ? "protobuf" : name; + }); + var args = packageNames; if (p.exec(executable, args.concat([ '--cflags' ])) === 0) { - cflags = p.readStdOut().trim(); - cflags = cflags ? cflags.split(/\s/) : []; + stdout = p.readStdOut().trim(); + cflags = stdout ? stdout.split(/\s/): []; var libsArgs = args.concat("--libs"); if (forStaticBuild) libsArgs.push("--static"); if (p.exec(executable, libsArgs) === 0) { - libs = p.readStdOut().trim(); - libs = libs ? libs.split(/\s/) : []; + stdout = p.readStdOut().trim(); + libs = stdout ? stdout.split(/\s/): []; if (p.exec(executable, [packageNames[0]].concat([ '--modversion' ])) === 0) { modversion = p.readStdOut().trim(); found = true; diff --git a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs new file mode 100644 index 000000000..5acec6bc0 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import "qbs-pkg-config-probe.js" as PkgConfigProbeConfigure + +Probe { + // Inputs + + property string _executableFilePath + property stringList _extraPaths + property stringList _libDirs + property bool _staticMode: false + property bool _definePrefix: false + + property path _sysroot + + // Output + property var packages + property var packagesByModuleName + property var brokenPackages + property stringList qmakePaths + + configure: { + var result = PkgConfigProbeConfigure.configure( + _executableFilePath, + _extraPaths, + _libDirs, + _staticMode, + _definePrefix, + _sysroot); + packages = result.packages; + packagesByModuleName = result.packagesByModuleName; + brokenPackages = result.brokenPackages; + qmakePaths = result.qmakePaths; + found = true; + } +} diff --git a/share/qbs/imports/qbs/Probes/QmakeProbe.qbs b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs new file mode 100644 index 000000000..c50c6c851 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs @@ -0,0 +1,41 @@ + +/**************************************************************************** +** +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import "qmake-probe.js" as QmakeProbeConfigure + +Probe { + property stringList qmakePaths + property varList qtInfos + + configure: { + qtInfos = QmakeProbeConfigure.configure(qmakePaths); + } +} diff --git a/share/qbs/imports/qbs/Probes/SdccProbe.qbs b/share/qbs/imports/qbs/Probes/SdccProbe.qbs index 3595bb158..ae616fa27 100644 --- a/share/qbs/imports/qbs/Probes/SdccProbe.qbs +++ b/share/qbs/imports/qbs/Probes/SdccProbe.qbs @@ -33,17 +33,18 @@ import "../../../modules/cpp/sdcc.js" as SDCC PathProbe { // Inputs - property string compilerFilePath; - property string preferredArchitecture; + property string compilerFilePath + property stringList enableDefinesByLanguage + property string preferredArchitecture // Outputs - property string architecture; - property string endianness; - property int versionMajor; - property int versionMinor; - property int versionPatch; - property stringList includePaths; - property var compilerDefinesByLanguage; + property string architecture + property string endianness + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage configure: { compilerDefinesByLanguage = {}; @@ -53,9 +54,22 @@ PathProbe { return; } + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + // SDCC compiler support only the C-language. + if (!languages.contains("c")) { + found = false; + return; + } + var macros = SDCC.dumpMacros(compilerFilePath, preferredArchitecture); + if (!macros) { + found = false; + return; + } - // SDCC it is only the C language compiler. compilerDefinesByLanguage["c"] = macros; architecture = SDCC.guessArchitecture(macros); @@ -65,9 +79,11 @@ PathProbe { includePaths = defaultPaths.includePaths; var version = SDCC.guessVersion(macros); - versionMajor = version.major; - versionMinor = version.minor; - versionPatch = version.patch; - found = version.found; + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!architecture && !!endianness; + } } } diff --git a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs index a35e555cc..f494d6012 100644 --- a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs +++ b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import "path-probe.js" as PathProbeConfigure import "../../../modules/typescript/typescript.js" as TypeScript @@ -60,23 +61,29 @@ BinaryProbe { var selectors; var results = PathProbeConfigure.configure( selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, - pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, - pathListSeparator); + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths); - var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator, - hostOS.contains("windows")); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), + Host.os().contains("windows")); v.prepend(interpreterPath); - var result = results.files[0]; - result.version = results.found - ? TypeScript.findTscVersion(result.filePath, v.value) - : undefined; - if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path || - !File.exists(FileInfo.fromNativeSeparators(packageManagerRootPath, "typescript"))) { - result = { found: false }; - } + var resultsMapper = function(result) { + result.version = result.found + ? TypeScript.findTscVersion(result.filePath, v.value) + : undefined; + if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path || + !File.exists( + FileInfo.fromNativeSeparators(packageManagerRootPath, "typescript"))) { + result = { found: false }; + } + return result; + }; + results.files = results.files.map(resultsMapper); found = results.found; + allResults = results.files; + + var result = results.files[0]; candidatePaths = result.candidatePaths; path = result.path; filePath = result.filePath; diff --git a/share/qbs/imports/qbs/Probes/WatcomProbe.qbs b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs new file mode 100644 index 000000000..3ea22acb3 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import qbs.Host +import qbs.ModUtils +import "../../../modules/cpp/watcom.js" as WATCOM + +PathProbe { + // Inputs + property string compilerFilePath + property stringList enableDefinesByLanguage + + property string _pathListSeparator + property string _toolchainInstallPath + property string _targetPlatform + property string _targetArchitecture + + // Outputs + property string architecture + property string endianness + property string targetPlatform + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var compilerDefinesByLanguage + property var environment + + configure: { + compilerDefinesByLanguage = {}; + + if (!File.exists(compilerFilePath)) { + found = false; + return; + } + + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + environment = WATCOM.guessEnvironment(Host.os(), _targetPlatform, _targetArchitecture, + _toolchainInstallPath, _pathListSeparator); + + includePaths = environment["INCLUDE"].split(_pathListSeparator).filter(function(path) { + return File.exists(path); + }); + + for (var i = 0; i < languages.length; ++i) { + var tag = languages[i]; + compilerDefinesByLanguage[tag] = WATCOM.dumpMacros( + environment, compilerFilePath, + _targetPlatform, _targetArchitecture, tag); + } + + var macros = compilerDefinesByLanguage["c"] + || compilerDefinesByLanguage["cpp"]; + + endianness = macros["__BIG_ENDIAN"] ? "big" : "little"; + architecture = ModUtils.guessArchitecture(macros); + targetPlatform = ModUtils.guessTargetPlatform(macros); + + var version = WATCOM.guessVersion(macros); + if (version) { + versionMajor = version.major; + versionMinor = version.minor; + versionPatch = version.patch; + found = !!architecture && !!targetPlatform; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs new file mode 100644 index 000000000..3401315f7 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.Process + +Probe { + property path developerPath + configure: { + var p = new Process(); + try { + p.exec("/usr/bin/xcode-select", ["--print-path"], true); + developerPath = p.readStdOut().trim(); + } catch (e) { + developerPath = "/Applications/Xcode.app/Contents/Developer"; + } finally { + p.close(); + found = true; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs index e0ed99346..9a0d01072 100644 --- a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs +++ b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs @@ -48,6 +48,7 @@ Probe { // Outputs property var architectureSettings property var availableSdks + property var platformSettings property string xcodeVersion configure: { @@ -88,7 +89,7 @@ Probe { architectureSettings = {}; var archSpecsPath = Xcode.archsSpecsPath(xcodeVersion, targetOS, platformType, - platformPath, devicePlatformPath); + platformPath, devicePlatformPath, developerPath); var archSpecsReader = new Xcode.XcodeArchSpecsReader(archSpecsPath); archSpecsReader.getArchitectureSettings().map(function (setting) { var val = archSpecsReader.getArchitectureSettingValue(setting); @@ -97,6 +98,8 @@ Probe { }); availableSdks = Xcode.sdkInfoList(sdksPath); + var platformInfoPlist = FileInfo.joinPaths(platformPath, "Info.plist"); + platformSettings = Xcode.platformInfo(platformInfoPlist); found = !!xcodeVersion; } } diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js index a997f77f2..1858f5222 100644 --- a/share/qbs/imports/qbs/Probes/path-probe.js +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -36,16 +36,16 @@ var ModUtils = require("qbs.ModUtils"); function asStringList(key, value) { if (typeof(value) === "string") return [value]; - if (Array.isArray(value)) + if (value instanceof Array) return value; throw key + " must be a string or a stringList"; } -function canonicalSelectors(selectors) { +function canonicalSelectors(selectors, nameSuffixes) { var mapper = function(selector) { if (typeof(selector) === "string") return {names : [selector]}; - if (Array.isArray(selector)) + if (selector instanceof Array) return {names : selector}; // dict if (!selector.names) @@ -53,14 +53,27 @@ function canonicalSelectors(selectors) { selector.names = asStringList("names", selector.names); if (selector.nameSuffixes) selector.nameSuffixes = asStringList("nameSuffixes", selector.nameSuffixes); + else + selector.nameSuffixes = nameSuffixes; return selector; }; return selectors.map(mapper); } +function pathsFromEnvs(envs, pathListSeparator) { + envs = envs || []; + var result = []; + for (var i = 0; i < envs.length; ++i) { + var value = Environment.getEnv(envs[i]) || ''; + if (value.length > 0) + result = result.concat(value.split(pathListSeparator)); + } + return result; +} + function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, pathSuffixes, platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator) { + platformEnvironmentPaths) { var result = { found: false, files: [] }; if (!selectors && !names) throw '"names" or "selectors" must be specified'; @@ -70,7 +83,7 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, {names: names, nameSuffixes: nameSuffixes} ]; } else { - selectors = canonicalSelectors(selectors); + selectors = canonicalSelectors(selectors, nameSuffixes); } if (nameFilter) { @@ -88,14 +101,11 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, }); // FIXME: Suggest how to obtain paths from system - var _paths = ModUtils.concatAll(searchPaths, platformSearchPaths); - // FIXME: Add getenv support - var envs = ModUtils.concatAll(platformEnvironmentPaths, environmentPaths); - for (var i = 0; i < envs.length; ++i) { - var value = Environment.getEnv(envs[i]) || ''; - if (value.length > 0) - _paths = _paths.concat(value.split(pathListSeparator)); - } + var _paths = ModUtils.concatAll( + pathsFromEnvs(environmentPaths, FileInfo.pathListSeparator()), + searchPaths, + pathsFromEnvs(platformEnvironmentPaths, FileInfo.pathListSeparator()), + platformSearchPaths); var _suffixes = ModUtils.concatAll('', pathSuffixes); _paths = _paths.map(function(p) { return FileInfo.fromNativeSeparators(p); }); _suffixes = _suffixes.map(function(p) { return FileInfo.fromNativeSeparators(p); }); diff --git a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js new file mode 100644 index 000000000..d382dfb02 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var PkgConfig = require("qbs.PkgConfig"); +var ProviderUtils = require("qbs.ProviderUtils"); +var Process = require("qbs.Process"); + +function getQmakePaths(pkg) { + var packageName = pkg.baseFileName; + if (packageName === "QtCore" + || packageName === "Qt5Core" + || packageName === "Qt6Core") { + var binDir = pkg.variables["bindir"] || pkg.variables["host_bins"]; + if (!binDir) { + if (packageName === "QtCore") { // Qt4 does not have host_bins + var mocLocation = pkg.variables["moc_location"]; + if (!mocLocation) { + console.warn("No moc_location variable in " + packageName); + return; + } + binDir = FileInfo.path(mocLocation); + } else { + console.warn("No 'bindir' or 'host_bins' variable in " + packageName); + return; + } + } + var suffix = FileInfo.executableSuffix(); + return [FileInfo.joinPaths(binDir, "qmake" + suffix)]; + } +} + +function configure( + executableFilePath, extraPaths, libDirs, staticMode, definePrefix, sysroot) { + + var result = {}; + result.packages = []; + result.packagesByModuleName = {}; + result.brokenPackages = []; + result.qtInfos = []; + + var options = {}; + options.libDirs = libDirs; + options.sysroot = sysroot; + options.definePrefix = definePrefix; + if (options.sysroot) + options.allowSystemLibraryPaths = true; + options.staticMode = staticMode; + options.extraPaths = extraPaths; + if (options.sysroot && !options.libDirs) { + options.libDirs = [ + options.sysroot + "/usr/lib/pkgconfig", + options.sysroot + "/usr/share/pkgconfig" + ]; + } + if (!options.libDirs) { + // if we have pkg-config/pkgconf installed, let's ask it for its search paths (since + // built-in search paths can differ between platforms) + if (executableFilePath) { + var p = new Process() + if (p.exec(executableFilePath, ['pkg-config', '--variable=pc_path']) === 0) { + var stdout = p.readStdOut().trim(); + options.libDirs = stdout ? stdout.split(FileInfo.pathListSeparator()): []; + var installDir = FileInfo.path(executableFilePath); + options.libDirs = options.libDirs.map(function(path){ + if (FileInfo.isAbsolutePath(path)) + return path; + return FileInfo.cleanPath(FileInfo.joinPaths(installDir, path)); + }); + } + } + } + var pkgConfig = new PkgConfig(options); + result.packages = pkgConfig.packages(); + for (var packageName in result.packages) { + var pkg = result.packages[packageName]; + var moduleName = ProviderUtils.pkgConfigToModuleName(packageName); + result.packagesByModuleName[moduleName] = pkg; + + if (packageName.startsWith("Qt")) { + if (!sysroot) { + var qmakePaths = getQmakePaths(pkg); + if (qmakePaths !== undefined) + result.qmakePaths = qmakePaths; + } + } + } + return result; +} diff --git a/share/qbs/imports/qbs/Probes/qmake-probe.js b/share/qbs/imports/qbs/Probes/qmake-probe.js new file mode 100644 index 000000000..746914e20 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qmake-probe.js @@ -0,0 +1,1256 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); +var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } + +function getQmakeFilePaths(qmakeFilePaths) { + if (qmakeFilePaths && qmakeFilePaths.length > 0) + return qmakeFilePaths; + console.info("Detecting Qt installations..."); + var filePaths = []; + var pathValue = Environment.getEnv("PATH"); + if (pathValue) { + var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); + for (var i = 0; i < dirs.length; ++i) { + var candidate = FileInfo.joinPaths(dirs[i], "qmake" + FileInfo.executableSuffix()); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") + candidate = canonicalCandidate; + if (!filePaths.contains(candidate)) { + console.info("Found Qt at '" + FileInfo.toNativeSeparators(candidate) + "'."); + filePaths.push(candidate); + } + } + } + if (filePaths.length === 0) { + console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " + + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " + + "to point to a qmake executable."); + } + return filePaths; +} + +function queryQmake(qmakeFilePath) { + var qmakeProcess = new Process; + qmakeProcess.exec(qmakeFilePath, ["-query"]); + if (qmakeProcess.exitCode() !== 0) { + throw "The qmake executable '" + FileInfo.toNativeSeparators(qmakeFilePath) + + "' failed with exit code " + qmakeProcess.exitCode() + "."; + } + var queryResult = {}; + while (!qmakeProcess.atEnd()) { + var line = qmakeProcess.readLine(); + var index = (line || "").indexOf(":"); + if (index !== -1) + queryResult[line.slice(0, index)] = line.slice(index + 1).trim(); + } + return queryResult; +} + +function pathQueryValue(queryResult, key) { + var p = queryResult[key]; + if (p) + return FileInfo.fromNativeSeparators(p); +} + +function readFileContent(filePath) { + var f = new TextFile(filePath, TextFile.ReadOnly); + var content = f.readAll(); + f.close(); + return content; +} + +// TODO: Don't do the split every time... +function configVariable(configContent, key) { + var configContentLines = configContent.split('\n'); + var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)"); + for (var i = 0; i < configContentLines.length; ++i) { + var line = configContentLines[i]; + var match = regexp.exec(line); + if (match) + return match[1].trim(); + } +} + +function configVariableItems(configContent, key) { + return splitNonEmpty(configVariable(configContent, key), ' '); +} + +function msvcCompilerVersionForYear(year) { + var mapping = { + "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", + "2017": "19.1", "2019": "19.2" + }; + return mapping[year]; +} + +function msvcCompilerVersionFromMkspecName(mkspecName) { + return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length)); +} + +function addQtBuildVariant(qtProps, buildVariantName) { + if (qtProps.qtConfigItems.contains(buildVariantName)) + qtProps.buildVariant.push(buildVariantName); +} + +function checkForStaticBuild(qtProps) { + if (qtProps.qtMajorVersion >= 5) + return qtProps.qtConfigItems.contains("static"); + if (qtProps.frameworkBuild) + return false; // there are no Qt4 static frameworks + var isWin = qtProps.mkspecName.startsWith("win"); + var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath; + var coreLibFiles = File.directoryEntries(libDir, File.Files) + .filter(function(fp) { return fp.contains("Core"); }); + if (coreLibFiles.length === 0) + throw "Could not determine whether Qt is a static build."; + for (var i = 0; i < coreLibFiles.length; ++i) { + if (Utilities.isSharedLibrary(coreLibFiles[i])) + return false; + } + return true; +} + +function guessMinimumWindowsVersion(qtProps) { + if (qtProps.mkspecName.startsWith("winrt-")) + return "10.0"; + if (!ProviderUtils.isDesktopWindowsQt(qtProps)) + return ""; + if (qtProps.qtMajorVersion >= 6) + return "10.0"; + if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64") + return "5.2" + var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/); + if (match) { + var msvcVersion = match[1]; + if (msvcVersion < 2012) + return "5.0"; + return "5.1"; + } + return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1"; +} + +function fillEntryPointLibs(qtProps, debug) { + result = []; + var isMinGW = ProviderUtils.isMinGwQt(qtProps); + + // Some Linux distributions rename the qtmain library. + var qtMainCandidates = ["qtmain"]; + if (isMinGW && qtProps.qtMajorVersion === 5) + qtMainCandidates.push("qt5main"); + if (qtProps.qtMajorVersion === 6) + qtMainCandidates.push("Qt6EntryPoint"); + + for (var i = 0; i < qtMainCandidates.length; ++i) { + var baseNameCandidate = qtMainCandidates[i]; + var qtmain = qtProps.libraryPath + '/'; + if (isMinGW) + qtmain += "lib"; + qtmain += baseNameCandidate + qtProps.qtLibInfix; + if (debug && ProviderUtils.qtNeedsDSuffix(qtProps)) + qtmain += 'd'; + if (isMinGW) { + qtmain += ".a"; + } else { + qtmain += ".lib"; + if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0) + result.push("Shell32.lib"); + } + if (File.exists(qtmain)) { + result.push(qtmain); + break; + } + } + if (result.length === 0) { + console.warn("Could not find the qtmain library at '" + + FileInfo.toNativeSeparators(qtProps.libraryPath) + + "'. You will not be able to link Qt applications."); + } + return result; +} + +function getQtProperties(qmakeFilePath) { + var queryResult = queryQmake(qmakeFilePath); + var qtProps = {}; + qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS"); + qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS"); + qtProps.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS"); + qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") + || pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.installPath = pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS"); + qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML"); + qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS"); + qtProps.qtVersion = queryResult.QT_VERSION; + + var mkspecsBaseSrcPath; + if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) { + qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"), + "mkspecs"); + mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"), + "mkspecs"); + } else { + qtProps.mkspecBasePath = FileInfo.joinPaths + (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs"); + } + + if (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) { + qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS") + || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); + } + + // QML tools were only moved in Qt 6.2. + qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + // qhelpgenerator was only moved in Qt 6.3. + qtProps.helpGeneratorLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.3") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + if (!File.exists(qtProps.mkspecBasePath)) + throw "Cannot extract the mkspecs directory."; + + var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qconfig.pri")); + qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION")); + qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION")); + qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION")); + qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE"); + qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || ""; + qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH") + || configVariable(qconfigContent, "QT_ARCH") || "x86"; + qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); + qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); + + // retrieve the mkspec + if (qtProps.qtMajorVersion >= 5) { + qtProps.mkspecName = queryResult.QMAKE_XSPEC; + qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName); + if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath)) + qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName); + } else { + if (Host.os().contains("windows")) { + var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default"); + var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf")); + qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL"); + if (!File.exists(qtProps.mkspecPath)) { + // Work around QTBUG-28792. + // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? + var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m); + if (match) { + qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths( + baseDirPath, match[1])); + } + } + } else { + qtProps.mkspecPath = FileInfo.canonicalPath( + FileInfo.joinPaths(qtProps.mkspecBasePath, "default")); + } + + // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: + // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ + qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath); + + qtProps.mkspecName = qtProps.mkspecPath; + var idx = qtProps.mkspecName.lastIndexOf('/'); + if (idx !== -1) + qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1); + } + if (!File.exists(qtProps.mkspecPath)) + throw "mkspec '" + FileInfo.toNativeSeparators(qtProps.mkspecPath) + "' does not exist"; + + // Starting with qt 5.14, android sdk provides multi-abi + if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 + && qtProps.mkspecPath.contains("android")) { + var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qdevice.pri")); + qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); + } + + // determine MSVC version + if (ProviderUtils.isMsvcQt(qtProps)) { + var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION"); + var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION"); + var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION"); + if (msvcMajor && msvcMinor && msvcPatch) + qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch; + else + qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName); + } + + // determine whether we have a framework build + qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx") + && qtProps.configItems.contains("qt_framework"); + + // determine whether Qt is built with debug, release or both + qtProps.buildVariant = []; + addQtBuildVariant(qtProps, "debug"); + addQtBuildVariant(qtProps, "release"); + + qtProps.staticBuild = checkForStaticBuild(qtProps); + + // determine whether user apps require C++11 + if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild) + qtProps.configItems.push("c++11"); + + // Set the minimum operating system versions appropriate for this Qt version + qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps); + if (qtProps.windowsVersion) { // Is target OS Windows? + if (qtProps.buildVariant.contains("debug")) + qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true); + if (qtProps.buildVariant.contains("release")) + qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false); + } else if (qtProps.mkspecPath.contains("macx")) { + if (qtProps.qtMajorVersion >= 5) { + // Since Qt 6.7.1, QMAKE_MACOSX|IOS_DEPLOYMENT_TARGET is no longer present in + // qmake.conf. But it is also present in qconfig.pri, so first try to read it from there + qtProps.macosVersion = configVariable(qconfigContent, "QMAKE_MACOSX_DEPLOYMENT_TARGET"); + qtProps.iosVersion = configVariable(qconfigContent, "QMAKE_IOS_DEPLOYMENT_TARGET"); + + // Next, we override the value from qmake.conf, if present there + // Note, that TVOS/WATCHOS variables are only present in qmake.conf (as of Qt 6.7.1) + var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, + "qmake.conf")); + for (var i = 0; i < lines.length; ++i) { + var line = lines[i].trim(); + match = line.match + (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/); + if (match) { + var platform = match[1]; + var version = match[2]; + if (platform === "MACOSX") + qtProps.macosVersion = version; + else if (platform === "IOS") + qtProps.iosVersion = version; + else if (platform === "TVOS") + qtProps.tvosVersion = version; + else if (platform === "WATCHOS") + qtProps.watchosVersion = version; + } + } + var isMac = qtProps.mkspecName !== "macx-ios-clang" + && qtProps.mkspecName !== "macx-tvos-clang" + && qtProps.mkspecName !== "macx-watchos-clang"; + if (isMac) { + // Qt 5.0.x placed the minimum version in a different file + if (!qtProps.macosVersion) + qtProps.macosVersion = "10.6"; + + // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 + if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0 + && qtProps.qtConfigItems.contains("c++11")) { + qtProps.macosVersion = "10.7"; + } + } + } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) { + var qconfigDir = qtProps.frameworkBuild + ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers") + : FileInfo.joinPaths(qtProps.includePath, "Qt"); + try { + var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"), + TextFile.ReadOnly); + var qtCocoaBuild = false; + var ok = true; + do { + line = qconfig.readLine(); + if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) { + qtCocoaBuild = true; + break; + } + } while (!qconfig.atEof()); + qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4"; + } + catch (e) {} + finally { + if (qconfig) + qconfig.close(); + } + if (!qtProps.macosVersion) { + throw "Could not determine whether Qt is using Cocoa or Carbon from '" + + FileInfo.toNativeSeparators(qconfig.filePath()) + "'."; + } + } + } else if (qtProps.mkspecPath.contains("android")) { + if (qtProps.qtMajorVersion >= 5) + qtProps.androidVersion = "2.3"; + else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8) + qtProps.androidVersion = "1.6"; // Necessitas + } + return qtProps; +} + +function makePluginData() { + var pluginData = {}; + pluginData.type = undefined; + pluginData.className = undefined; + pluginData.autoLoad = true; + pluginData["extends"] = []; + return pluginData; +} + +function makeQtModuleInfo(name, qbsName, deps) { + var moduleInfo = {}; + moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files. + if (moduleInfo.name === undefined) + moduleInfo.name = ""; + moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix. + moduleInfo.dependencies = deps || []; // qbs names. + if (moduleInfo.qbsName && moduleInfo.qbsName !== "core" + && !moduleInfo.dependencies.contains("core")) { + moduleInfo.dependencies.unshift("core"); + } + moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private"); + moduleInfo.hasLibrary = !moduleInfo.isPrivate; + moduleInfo.isStaticLibrary = false; + moduleInfo.isPlugin = false; + moduleInfo.mustExist = true; + moduleInfo.modulePrefix = ""; // empty value means "Qt". + moduleInfo.version = undefined; + moduleInfo.includePaths = []; + moduleInfo.compilerDefines = []; + moduleInfo.staticLibrariesDebug = []; + moduleInfo.staticLibrariesRelease = []; + moduleInfo.dynamicLibrariesDebug = []; + moduleInfo.dynamicLibrariesRelease = []; + moduleInfo.linkerFlagsDebug = []; + moduleInfo.linkerFlagsRelease = []; + moduleInfo.libFilePathDebug = undefined; + moduleInfo.libFilePathRelease = undefined; + moduleInfo.frameworksDebug = []; + moduleInfo.frameworksRelease = []; + moduleInfo.frameworkPathsDebug = []; + moduleInfo.frameworkPathsRelease = []; + moduleInfo.libraryPaths = []; + moduleInfo.libDir = ""; + moduleInfo.config = []; + moduleInfo.supportedPluginTypes = []; + moduleInfo.pluginData = makePluginData(); + return moduleInfo; +} + +function frameworkHeadersPath(qtModuleInfo, qtProps) { + return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers"); +} + +function qt4ModuleIncludePaths(qtModuleInfo, qtProps) { + var paths = []; + if (ProviderUtils.qtIsFramework(qtModuleInfo, qtProps)) + paths.push(frameworkHeadersPath(qtModuleInfo, qtProps)); + else + paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name)); + return paths; +} + +// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users +// for that. +function addTestModule(modules) { + var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]); + testModule.hasLibrary = false; + modules.push(testModule); +} + +// See above. +function addDesignerComponentsModule(modules) { + var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents", + ["designercomponents-private"]); + module.hasLibrary = false; + modules.push(module); +} + +function guessLibraryFilePath(prlFilePath, libDir, qtProps) { + var baseName = FileInfo.baseName(prlFilePath); + var prefixCandidates = ["", "lib"]; + var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"]; + for (var i = 0; i < prefixCandidates.length; ++i) { + var prefix = prefixCandidates[i]; + for (var j = 0; j < suffixCandidates.length; ++j) { + var suffix = suffixCandidates[j]; + var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix); + if (File.exists(candidate)) + return candidate; + } + } +} + +function doReplaceQtLibNamesWithFilePath(namePathMap, libList) { + for (var i = 0; i < libList.length; ++i) { + var lib = libList[i]; + var path = namePathMap[lib]; + if (path) + libList[i] = path; + } +} + +function replaceQtLibNamesWithFilePath(modules, qtProps) { + // We don't want to add the libraries for Qt modules via "-l", because of the + // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, + // we pull them in using the full file path. + var linkerNamesToFilePathsDebug = {}; + var linkerNamesToFilePathsRelease = {}; + for (var i = 0; i < modules.length; ++i) { + var m = modules[i]; + linkerNamesToFilePathsDebug[ + ProviderUtils.qtLibNameForLinker(m, qtProps, true)] = m.libFilePathDebug; + linkerNamesToFilePathsRelease[ + ProviderUtils.qtLibNameForLinker(m, qtProps, false)] = m.libFilePathRelease; + } + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.dynamicLibrariesRelease); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.staticLibrariesRelease); + } +} + +function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) { + if (!modInfo.hasLibrary) + return; // Can happen for Qt4 convenience modules, like "widgets". + + if (debugBuild) { + if (!qtProps.buildVariant.contains("debug")) + return; + var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"]; + for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) { + var m = modulesNeverBuiltAsDebug[i]; + if (modInfo.qbsName === m || modInfo.qbsName === m + "-private") + return; + } + } else if (!qtProps.buildVariant.contains("release")) { + return; + } + + var libs = modInfo.isStaticLibrary + ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease) + : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease); + var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease; + var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease; + var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease; + var libFilePath; + + if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) { + libs.push("z", "m"); + if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { + var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport"); + libs.push(ProviderUtils.qtLibNameForLinker(platformSupportModule, qtProps, debugBuild)); + } + if (modInfo.name === "qios") { + flags.push("-force_load", FileInfo.joinPaths( + qtProps.pluginPath, "platforms", + ProviderUtils.qtLibBaseName( + modInfo, "libqios", debugBuild, qtProps) + ".a")); + } + } + var prlFilePath = modInfo.isPlugin + ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) + : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); + var libDir = prlFilePath; + if (ProviderUtils.qtIsFramework(modInfo, qtProps)) { + prlFilePath = FileInfo.joinPaths( + prlFilePath, + ProviderUtils.qtLibraryBaseName(modInfo, qtProps, false) + ".framework"); + libDir = prlFilePath; + if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) + prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); + } + var baseName = ProviderUtils.qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (!qtProps.mkspecName.startsWith("win") && !ProviderUtils.qtIsFramework(modInfo, qtProps)) + baseName = "lib" + baseName; + prlFilePath = FileInfo.joinPaths(prlFilePath, baseName); + var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win") + && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; + if (isNonStaticQt4OnWindows) + prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... + // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file + if (androidAbi.length > 0 + && modInfo.name !== "QtBootstrap" + && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" + && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) + && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 + || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { + prlFilePath += "_"; + prlFilePath += androidAbi; + } + + prlFilePath += ".prl"; + + try { + var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); + while (!prlFile.atEof()) { + var line = prlFile.readLine().trim(); + var equalsOffset = line.indexOf('='); + if (equalsOffset === -1) + continue; + if (line.startsWith("QMAKE_PRL_TARGET")) { + var isMingw = qtProps.mkspecName.startsWith("win") + && qtProps.mkspecName.contains("g++"); + var isQtVersionBefore56 = qtProps.qtMajorVersion < 5 + || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6); + + // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. + // Of course, the exception has an exception too: For static libs, mingw *does* + // have the "lib" prefix. + var libFileName = ""; + if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw + && !modInfo.isStaticLibrary) { + libFileName += "lib"; + } + + libFileName += line.slice(equalsOffset + 1).trim(); + if (isNonStaticQt4OnWindows) + libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET... + if (isQtVersionBefore56) { + if (qtProps.mkspecName.contains("msvc")) { + libFileName += ".lib"; + } else if (isMingw) { + libFileName += ".a"; + if (!File.exists(FileInfo.joinPaths(libDir, libFileName))) + libFileName = libFileName.slice(0, -2) + ".dll"; + } + } + libFilePath = FileInfo.joinPaths(libDir, libFileName); + continue; + } + if (line.startsWith("QMAKE_PRL_CONFIG")) { + modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); + continue; + } + if (!line.startsWith("QMAKE_PRL_LIBS =")) + continue; + + var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); + for (i = 0; i < parts.length; ++i) { + var part = parts[i]; + part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + part = part.replace("$$[QT_INSTALL_PLUGINS]", qtProps.pluginPath); + part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath); + if (part.startsWith("-l")) { + libs.push(part.slice(2)); + } else if (part.startsWith("-L")) { + modInfo.libraryPaths.push(part.slice(2)); + } else if (part.startsWith("-F")) { + frameworkPaths.push(part.slice(2)); + } else if (part === "-framework") { + if (++i < parts.length) + frameworks.push(parts[i]); + } else if (part === "-pthread") { + // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread + // functionality is included in libc. + if (androidAbi.length === 0) + libs.push("pthread"); + } else if (part.startsWith('-')) { // Some other option + console.debug("QMAKE_PRL_LIBS contains non-library option '" + part + + "' in file '" + prlFilePath + "'"); + flags.push(part); + } else if (part.startsWith("/LIBPATH:")) { + libraryPaths.push(part.slice(9).replace(/\\/g, '/')); + } else { // Assume it's a file path/name. + libs.push(part.replace(/\\/g, '/')); + } + } + } + } catch (e) { + // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file. + // So the pri file variable QMAKE_LIBS_LIBX points to the library + if (modInfo.isExternal) { + libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] : + modInfo.staticLibrariesRelease[0]; + } + if (!libFilePath || !File.exists(libFilePath)) + libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); + if (nonExistingPrlFiles.contains(prlFilePath)) + return; + nonExistingPrlFiles.push(prlFilePath); + if (modInfo.mustExist) { + console.warn("Could not open prl file '" + + FileInfo.toNativeSeparators(prlFilePath) + "' for module '" + + modInfo.name + + "' (" + e + "), and failed to deduce the library file path. " + + " This module will likely not be usable by qbs."); + } + } + finally { + if (prlFile) + prlFile.close(); + } + + if (debugBuild) + modInfo.libFilePathDebug = libFilePath; + else + modInfo.libFilePathRelease = libFilePath; +} + +function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) { + doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); + doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); +} + +function allQt4Modules(qtProps) { + // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. + var modules = []; + + var core = makeQtModuleInfo("QtCore", "core"); + core.compilerDefines.push("QT_CORE_LIB"); + if (qtProps.qtNameSpace) + core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace); + modules.push(core, + makeQtModuleInfo("QtCore", "core-private", ["core"]), + makeQtModuleInfo("QtGui", "gui"), + makeQtModuleInfo("QtGui", "gui-private", ["gui"]), + makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]), + makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]), + makeQtModuleInfo("QtNetwork", "network"), + makeQtModuleInfo("QtNetwork", "network-private", ["network"]), + makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]), + makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]), + makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]), + makeQtModuleInfo("QtScript", "script"), + makeQtModuleInfo("QtScript", "script-private", ["script"]), + makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]), + makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]), + makeQtModuleInfo("QtSql", "sql"), + makeQtModuleInfo("QtSql", "sql-private", ["sql"]), + makeQtModuleInfo("QtSvg", "svg", ["gui"]), + makeQtModuleInfo("QtSvg", "svg-private", ["svg"]), + makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]), + makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]), + makeQtModuleInfo("QtXml", "xml"), + makeQtModuleInfo("QtXml", "xml-private", ["xml"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]), + makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]), + makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]), + makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]), + makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]), + makeQtModuleInfo("QtUiTools", "uitools"), + makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]), + makeQtModuleInfo("QtHelp", "help", ["network", "sql"]), + makeQtModuleInfo("QtHelp", "help-private", ["help"]), + makeQtModuleInfo("QtTest", "testlib"), + makeQtModuleInfo("QtTest", "testlib-private", ["testlib"])); + if (qtProps.mkspecName.startsWith("win")) { + var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer"); + axcontainer.modulePrefix = "Q"; + axcontainer.isStaticLibrary = true; + axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axcontainer); + + var axserver = makeQtModuleInfo("QAxServer", "axserver"); + axserver.modulePrefix = "Q"; + axserver.isStaticLibrary = true; + axserver.compilerDefines.push("QAXSERVER"); + axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axserver); + } else { + modules.push(makeQtModuleInfo("QtDBus", "dbus")); + modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"])); + } + + var designerComponentsPrivate = makeQtModuleInfo( + "QtDesignerComponents", "designercomponents-private", + ["gui-private", "designer-private"]); + designerComponentsPrivate.hasLibrary = true; + modules.push(designerComponentsPrivate); + + var phonon = makeQtModuleInfo("Phonon", "phonon"); + phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps); + modules.push(phonon); + + // Set up include paths that haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + if (module.includePaths.length > 0) + continue; + module.includePaths = qt4ModuleIncludePaths(module, qtProps); + } + + // Set up compiler defines haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + if (module.compilerDefines.length > 0) + continue; + module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB"); + } + + // These are for the convenience of project file authors. It allows them + // to add a dependency to e.g. "Qt.widgets" without a version check. + var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "concurrent"); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + + addTestModule(modules); + addDesignerComponentsModule(modules); + + var modulesThatCanBeDisabled = [ + "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools", + "declarative", "gui", "dbus", "opengl", "openvg"]; + var nonExistingPrlFiles = []; + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + var name = module.qbsName; + var privateIndex = name.indexOf("-private"); + if (privateIndex !== -1) + name = name.slice(0, privateIndex); + if (modulesThatCanBeDisabled.contains(name)) + module.mustExist = false; + if (qtProps.staticBuild) + module.isStaticLibrary = true; + setupLibraries(module, qtProps, nonExistingPrlFiles, ""); + } + replaceQtLibNamesWithFilePath(modules, qtProps); + + return modules; +} + +function getFileContentsRecursively(filePath) { + var file = new TextFile(filePath, TextFile.ReadOnly); + var lines = splitNonEmpty(file.readAll(), '\n'); + for (var i = 0; i < lines.length; ++i) { + var includeString = "include("; + var line = lines[i].trim(); + if (!line.startsWith(includeString)) + continue; + var offset = includeString.length; + var closingParenPos = line.indexOf(')', offset); + if (closingParenPos === -1) { + console.warn("Invalid include statement in '" + + FileInfo.toNativeSeparators(filePath) + "'"); + continue; + } + var includedFilePath = line.slice(offset, closingParenPos); + if (!FileInfo.isAbsolutePath(includedFilePath)) + includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); + var includedContents = getFileContentsRecursively(includedFilePath); + var j = i; + for (var k = 0; k < includedContents.length; ++k) + lines.splice(++j, 0, includedContents[k]); + lines.splice(i--, 1); + } + file.close(); + return lines; +} + +function extractPaths(rhs, filePath) { + var paths = []; + var startIndex = 0; + for (;;) { + while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ') + ++startIndex; + if (startIndex >= rhs.length) + break; + var endIndex; + if (rhs.charAt(startIndex) === '"') { + ++startIndex; + endIndex = rhs.indexOf('"', startIndex); + if (endIndex === -1) { + console.warn("Unmatched quote in file '" + + FileInfo.toNativeSeparators(filePath) + "'"); + break; + } + } else { + endIndex = rhs.indexOf(' ', startIndex + 1); + if (endIndex === -1) + endIndex = rhs.length; + } + paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) + .replace("$$PWD", FileInfo.path(filePath)))); + startIndex = endIndex + 1; + } + return paths; +} + +function removeDuplicatedDependencyLibs(modules) { + var revDeps = {}; + var currentPath = []; + var getLibraries; + var getLibFilePath; + + function setupReverseDependencies(modules) { + var moduleByName = {}; + for (var i = 0; i < modules.length; ++i) + moduleByName[modules[i].qbsName] = modules[i]; + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + for (var j = 0; j < module.dependencies.length; ++j) { + var depmod = moduleByName[module.dependencies[j]]; + if (!depmod) + continue; + if (!revDeps[depmod.qbsName]) + revDeps[depmod.qbsName] = []; + revDeps[depmod.qbsName].push(module); + } + } + } + + function roots(modules) { + var result = []; + for (i = 0; i < modules.length; ++i) { + var module = modules[i] + if (module.dependencies.length === 0) + result.push(module); + } + return result; + } + + function traverse(module, libs) { + if (currentPath.contains(module)) + return; + currentPath.push(module); + var moduleLibraryLists = getLibraries(module); + for (var i = 0; i < moduleLibraryLists.length; ++i) { + var modLibList = moduleLibraryLists[i]; + for (j = modLibList.length - 1; j >= 0; --j) { + if (libs.contains(modLibList[j])) + modLibList.splice(j, 1); + } + } + + var libFilePath = getLibFilePath(module); + if (libFilePath) + libs.push(libFilePath); + for (i = 0; i < moduleLibraryLists.length; ++i) + libs = libs.concat(moduleLibraryLists[i]); + libs.sort(); + + var deps = revDeps[module.qbsName]; + for (i = 0; i < (deps || []).length; ++i) + traverse(deps[i], libs); + + currentPath.pop(); + } + + setupReverseDependencies(modules); + + // Traverse the debug variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesDebug, module.staticLibrariesDebug]; + }; + getLibFilePath = function(module) { return module.libFilePathDebug; }; + var rootModules = roots(modules); + for (var i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); + + // Traverse the release variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesRelease, module.staticLibrariesRelease]; + }; + getLibFilePath = function(module) { return module.libFilePathRelease; }; + for (i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); +} + +function allQt5Modules(qtProps, androidAbi) { + var nonExistingPrlFiles = []; + var modules = []; + var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules"); + var modulePriFiles = File.directoryEntries(modulesDir, File.Files); + for (var i = 0; i < modulePriFiles.length; ++i) { + var priFileName = modulePriFiles[i]; + var priFilePath = FileInfo.joinPaths(modulesDir, priFileName); + var externalFileNamePrefix = "qt_ext_"; + var moduleFileNamePrefix = "qt_lib_"; + var pluginFileNamePrefix = "qt_plugin_"; + var moduleFileNameSuffix = ".pri"; + var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix); + var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix); + var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); + if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix + || !priFileName.endsWith(moduleFileNameSuffix)) { + continue; + } + var moduleInfo = makeQtModuleInfo(); + moduleInfo.isPlugin = fileHasPluginPrefix; + moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix; + var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal + ? externalFileNamePrefix : moduleFileNamePrefix; + moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length); + if (moduleInfo.isPlugin) { + moduleInfo.name = moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + } + var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") + + '.' + moduleInfo.qbsName + '.'; + moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private"); + var hasV2 = false; + var hasModuleEntry = false; + var lines = getFileContentsRecursively(priFilePath); + if (moduleInfo.isExternal) { + moduleInfo.name = "qt" + moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + for (var k = 0; k < lines.length; ++k) { + var extLine = lines[k].trim(); + var extFirstEqualsOffset = extLine.indexOf('='); + if (extFirstEqualsOffset === -1) + continue; + var extKey = extLine.slice(0, extFirstEqualsOffset).trim(); + var extValue = extLine.slice(extFirstEqualsOffset + 1).trim(); + if (!extKey.startsWith("QMAKE_") || !extValue) + continue; + + var elements = extKey.split('_'); + if (elements.length >= 3) { + if (elements[1] === "LIBS") { + extValue = extValue.replace("/home/qt/work/qt/qtbase/lib", + qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath); + if (elements.length === 4 ) { + if (elements[3] === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements.length === 5 ) { + // That's for "x86_64" + var abi = elements[3] + '_' + elements[4]; + if (abi === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements[1] === "INCDIR") { + moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]", + qtProps.includePath)); + } + } + } + moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB"); + moduleInfo.mustExist = false; + } else { + for (var j = 0; j < lines.length; ++j) { + var line = lines[j].trim(); + var firstEqualsOffset = line.indexOf('='); + if (firstEqualsOffset === -1) + continue; + var key = line.slice(0, firstEqualsOffset).trim(); + var value = line.slice(firstEqualsOffset + 1).trim(); + if (!key.startsWith(moduleKeyPrefix) || !value) + continue; + if (key.endsWith(".name")) { + moduleInfo.name = value; + } else if (key.endsWith(".module")) { + hasModuleEntry = true; + } else if (key.endsWith(".depends")) { + moduleInfo.dependencies = splitNonEmpty(value, ' '); + for (var k = 0; k < moduleInfo.dependencies.length; ++k) { + moduleInfo.dependencies[k] + = moduleInfo.dependencies[k].replace("_private", "-private"); + } + } else if (key.endsWith(".module_config")) { + var elems = splitNonEmpty(value, ' '); + for (k = 0; k < elems.length; ++k) { + var elem = elems[k]; + if (elem === "no_link") + moduleInfo.hasLibrary = false; + else if (elem === "staticlib") + moduleInfo.isStaticLibrary = true; + else if (elem === "internal_module") + moduleInfo.isPrivate = true; + else if (elem === "v2") + hasV2 = true; + } + } else if (key.endsWith(".includes")) { + moduleInfo.includePaths = extractPaths(value, priFilePath); + for (k = 0; k < moduleInfo.includePaths.length; ++k) { + moduleInfo.includePaths[k] = moduleInfo.includePaths[k] + .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath) + .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } + } else if (key.endsWith(".libs")) { + var libDirs = extractPaths(value, priFilePath); + if (libDirs.length === 1) { + moduleInfo.libDir = libDirs[0] + .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } else { + moduleInfo.libDir = qtProps.libraryPath; + } + } else if (key.endsWith(".DEFINES")) { + moduleInfo.compilerDefines = splitNonEmpty(value, ' '); + } else if (key.endsWith(".VERSION")) { + moduleInfo.version = value; + } else if (key.endsWith(".plugin_types")) { + moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' '); + } else if (key.endsWith(".TYPE")) { + moduleInfo.pluginData.type = value; + } else if (key.endsWith(".EXTENDS")) { + moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' '); + for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) { + if (moduleInfo.pluginData["extends"][k] === "-") { + moduleInfo.pluginData["extends"].splice(k, 1); + moduleInfo.pluginData.autoLoad = false; + break; + } + } + } else if (key.endsWith(".CLASS_NAME")) { + moduleInfo.pluginData.className = value; + } + } + } + if (hasV2 && !hasModuleEntry && !moduleInfo.isStaticLibrary) + moduleInfo.hasLibrary = false; + + // Fix include paths for Apple frameworks. + // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. + if (!hasV2 && ProviderUtils.qtIsFramework(moduleInfo, qtProps)) { + moduleInfo.includePaths = []; + var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps); + if (moduleInfo.isPrivate) { + baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version); + moduleInfo.includePaths.push(baseIncDir, + FileInfo.joinPaths(baseIncDir, moduleInfo.name)); + } else { + moduleInfo.includePaths.push(baseIncDir); + } + } + + setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi); + + modules.push(moduleInfo); + if (moduleInfo.qbsName === "testlib") + addTestModule(modules); + if (moduleInfo.qbsName === "designercomponents-private") + addDesignerComponentsModule(modules); + } + + replaceQtLibNamesWithFilePath(modules, qtProps); + removeDuplicatedDependencyLibs(modules); + + return modules; +} + +function getQtInfo(qmakeFilePath) { + if (!File.exists(qmakeFilePath)) { + throw "The specified qmake file path '" + + FileInfo.toNativeSeparators(qmakeFilePath) + "' does not exist."; + } + var qtProps = getQtProperties(qmakeFilePath); + var androidAbis = []; + if (qtProps.androidAbis !== undefined) { + // Multiple androidAbis detected: Qt >= 5.14 + androidAbis = qtProps.androidAbis; + } else { + // Single abi detected: Qt < 5.14 + androidAbis.push(''); + } + if (androidAbis.length > 1) + console.info("Qt with multiple abi detected: '" + androidAbis + "'"); + + var result = {}; + result.qtProps = qtProps; + result.abiInfos = []; + result.qmakeFilePath = qmakeFilePath; + for (a = 0; a < androidAbis.length; ++a) { + var abiInfo = {}; + if (androidAbis.length > 1) + console.info("Found abi '" + androidAbis[a] + "'..."); + abiInfo.androidAbi = androidAbis[a]; + var allModules = qtProps.qtMajorVersion < 5 + ? allQt4Modules(qtProps) : allQt5Modules(qtProps, androidAbis[a]);; + abiInfo.modules = {}; + for (var i = 0; i < allModules.length; ++i) { + var module = allModules[i]; + abiInfo.modules[module.qbsName] = module; + } + abiInfo.pluginsByType = {}; + abiInfo.nonEssentialPlugins = []; + for (var moduleName in abiInfo.modules) { + var m = abiInfo.modules[moduleName]; + if (m.isPlugin) { + if (!abiInfo.pluginsByType[m.pluginData.type]) + abiInfo.pluginsByType[m.pluginData.type] = []; + abiInfo.pluginsByType[m.pluginData.type].push(m.name); + if (!m.pluginData.autoLoad) + abiInfo.nonEssentialPlugins.push(m.name); + } + } + + result.abiInfos.push(abiInfo); + } + return result; +} + +function configure(qmakeFilePaths) { + var result = []; + qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths); + if (!qmakeFilePaths || qmakeFilePaths.length === 0) + return result; + for (var i = 0; i < qmakeFilePaths.length; ++i) { + var qtInfo = {}; + try { + console.info("Getting info about Qt at '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "'..."); + qtInfo = getQtInfo(qmakeFilePaths[i]); + result.push(qtInfo); + } catch (e) { + console.warn("Error getting info about Qt for '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "': " + e); + throw e; + } + } + return result; +} diff --git a/share/qbs/imports/qbs/ProviderUtils/provider-utils.js b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js new file mode 100644 index 000000000..06e703a7a --- /dev/null +++ b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Utilities = require("qbs.Utilities"); + +function pkgConfigToModuleName(packageName) { + return packageName.replace(/\./g, '-'); +} + +function msvcPrefix() { return "win32-msvc"; } + +function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } + +function isMinGwQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); +} + +function isDesktopWindowsQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-") || isMinGwQt(qtProps); +} + +function qtNeedsDSuffix(qtProps) { + return !isMinGwQt(qtProps) + || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 + || qtProps.configItems.contains("debug_and_release"); +} + +function qtIsFramework(modInfo, qtProps) { + if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) + return false; + var modulesNeverBuiltAsFrameworks = [ + "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" + ]; + + if (qtProps.qtMajorVersion <= 5) { + modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 + } + + return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); +} + +function qtLibBaseName(modInfo, libName, debugBuild, qtProps) { + var name = libName; + if (qtProps.mkspecName.startsWith("win")) { + if (debugBuild && qtNeedsDSuffix(qtProps)) + name += 'd'; + if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5) + name += qtProps.qtMajorVersion; + } + if (qtProps.mkspecName.contains("macx") + || qtProps.mkspecName.contains("ios") + || qtProps.mkspecName.contains("darwin")) { + if (!qtIsFramework(modInfo, qtProps) + && qtProps.buildVariant.contains("debug") + && (!qtProps.buildVariant.contains("release") || debugBuild)) { + name += "_debug"; + } + } + return name; +} + +function qtModuleNameWithoutPrefix(modInfo) { + if (modInfo.name === "Phonon") + return "phonon"; + if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt")) + return modInfo.name.slice(2); // Strip off "Qt". + if (modInfo.name.startsWith(modInfo.modulePrefix)) + return modInfo.name.slice(modInfo.modulePrefix.length); + return modInfo.name; +} + +function qtLibraryBaseName(modInfo, qtProps, debugBuild) { + if (modInfo.isPlugin) + return qtLibBaseName(modInfo, modInfo.name, debugBuild, qtProps); + + // Some modules use a different naming scheme, so it doesn't get boring. + var libNameBroken = modInfo.name === "Enginio" + || modInfo.name === "DataVisualization" + || modInfo.name === "Phonon"; + + var libName = ""; + if (!modInfo.isExternal) { + libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; + if (qtProps.qtMajorVersion >= 5 && !qtIsFramework(modInfo, qtProps) && !libNameBroken) + libName += qtProps.qtMajorVersion; + } + libName += qtModuleNameWithoutPrefix(modInfo); + if (!modInfo.isExternal) + libName += qtProps.qtLibInfix; + return qtLibBaseName(modInfo, libName, debugBuild, qtProps); +} + +function qtLibNameForLinker(modInfo, qtProps, debugBuild) { + if (!modInfo.hasLibrary) + return undefined; + var libName = qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (qtProps.mkspecName.contains("msvc")) + libName += ".lib"; + return libName; +} diff --git a/share/qbs/imports/qbs/base/AndroidApk.qbs b/share/qbs/imports/qbs/base/AndroidApk.qbs index 5f86d8457..70cf6aa93 100644 --- a/share/qbs/imports/qbs/base/AndroidApk.qbs +++ b/share/qbs/imports/qbs/base/AndroidApk.qbs @@ -32,7 +32,7 @@ import qbs.File import qbs.FileInfo Product { - type: ["android.apk"] + type: ["android.package"] qbs.targetPlatform: "android" Depends { name: "Android.sdk" } } diff --git a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs index 134f4dee0..462b96b7c 100644 --- a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs +++ b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs @@ -34,7 +34,8 @@ import qbs.ModUtils AppleDiskImage { property string sourceBase: "/Applications" - readonly property string absoluteSourceBase: FileInfo.joinPaths(qbs.installRoot, sourceBase) + readonly property string absoluteSourceBase: + FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, sourceBase) property stringList symlinks: ["/Applications:Applications"] readonly property string stageDirectory: FileInfo.joinPaths(destinationDirectory, "Volumes", dmg.volumeName) @@ -56,7 +57,7 @@ AppleDiskImage { prepare: Array.prototype.map.call(outputs["dmg.input"], function (symlink) { var cmd = new Command("ln", ["-sfn", symlink.dmg.symlinkTarget, symlink.filePath]); cmd.workingDirectory = product.stageDirectory; - cmd.description = "symlinking " + symlink.fileName + " => " + symlink.dmg.symlinkTarget; + cmd.description = "symlinking " + symlink.fileName + " to " + symlink.dmg.symlinkTarget; return cmd; }); } diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs index 694cfb83b..76536c68e 100644 --- a/share/qbs/imports/qbs/base/Application.qbs +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -29,7 +29,7 @@ ****************************************************************************/ NativeBinary { - type: isForAndroid && !consoleApplication ? ["android.apk"] : ["application"] + type: isForAndroid && !consoleApplication ? ["android.package"] : ["application"] property bool usesNativeCode @@ -60,10 +60,18 @@ NativeBinary { installDir: isBundle ? "Applications" : "bin" Group { - condition: install - fileTagsFilter: isBundle ? "bundle.content" : "application"; + condition: install && _installable + fileTagsFilter: isBundle ? "bundle.content" : "application" qbs.install: true qbs.installDir: installDir qbs.installSourceBase: isBundle ? destinationDirectory : outer } + + Group { + condition: installDebugInformation && _installable + fileTagsFilter: ["debuginfo_app"] + qbs.install: true + qbs.installDir: debugInformationInstallDir + qbs.installSourceBase: destinationDirectory + } } diff --git a/share/qbs/imports/qbs/base/ApplicationExtension.qbs b/share/qbs/imports/qbs/base/ApplicationExtension.qbs index 140475909..3d25d5f73 100644 --- a/share/qbs/imports/qbs/base/ApplicationExtension.qbs +++ b/share/qbs/imports/qbs/base/ApplicationExtension.qbs @@ -34,14 +34,21 @@ XPCService { type: base.concat(["applicationextension"]) property bool _useLegacyExtensionLibraries: - qbs.targetOS.contains("macos") && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 || - qbs.targetOS.contains("ios") && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9 + qbs.targetOS.contains("macos") + && xcode.present + && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 + || qbs.targetOS.contains("ios") + && xcode.present + && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9 cpp.entryPoint: "_NSExtensionMain" + cpp.frameworkPaths: base.concat(_useLegacyExtensionLibraries + ? qbs.sysroot + "/System/Library/PrivateFrameworks/" + : []) cpp.frameworks: { var frameworks = base.concat(["Foundation"]); if (_useLegacyExtensionLibraries) - frameworks.push(qbs.sysroot + "/System/Library/PrivateFrameworks/PlugInKit.framework"); + frameworks.push("PlugInKit"); return frameworks; } diff --git a/share/qbs/imports/qbs/base/AutotestRunner.qbs b/share/qbs/imports/qbs/base/AutotestRunner.qbs index 62ba7740b..30921eb38 100644 --- a/share/qbs/imports/qbs/base/AutotestRunner.qbs +++ b/share/qbs/imports/qbs/base/AutotestRunner.qbs @@ -94,7 +94,7 @@ Product { .concat([commandFilePath]) .concat(arguments); var cmd = new Command(fullCommandLine[0], fullCommandLine.slice(1)); - cmd.description = "Running test " + input.fileName; + cmd.description = "running test " + input.fileName; cmd.environment = product.environment; cmd.workingDirectory = workingDir; cmd.timeout = timeout; diff --git a/share/qbs/imports/qbs/base/DynamicLibrary.qbs b/share/qbs/imports/qbs/base/DynamicLibrary.qbs index 267519a42..818665e38 100644 --- a/share/qbs/imports/qbs/base/DynamicLibrary.qbs +++ b/share/qbs/imports/qbs/base/DynamicLibrary.qbs @@ -30,24 +30,4 @@ Library { type: ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []) - - installDir: isBundle ? "Library/Frameworks" : qbs.targetOS.contains("windows") - ? "bin" : "lib" - property bool installImportLib: false - property string importLibInstallDir: "lib" - - Group { - condition: install - fileTagsFilter: isBundle ? "bundle.content" : ["dynamiclibrary", "dynamiclibrary_symlink"] - qbs.install: true - qbs.installDir: installDir - qbs.installSourceBase: isBundle ? destinationDirectory : outer - } - - Group { - condition: installImportLib - fileTagsFilter: "dynamiclibrary_import" - qbs.install: true - qbs.installDir: importLibInstallDir - } } diff --git a/share/qbs/imports/qbs/base/InnoSetup.qbs b/share/qbs/imports/qbs/base/InnoSetup.qbs index 5ea076eb3..e40a627f5 100644 --- a/share/qbs/imports/qbs/base/InnoSetup.qbs +++ b/share/qbs/imports/qbs/base/InnoSetup.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ Product { - Depends { name: "innosetup"; condition: qbs.targetOS.contains("windows") } + condition: qbs.targetOS.contains("windows") && innosetup.present + Depends { name: "innosetup"; condition: qbs.targetOS.contains("windows"); required: false } type: ["innosetup.exe"] } diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs index 615c2319f..914d79bcb 100644 --- a/share/qbs/imports/qbs/base/Library.qbs +++ b/share/qbs/imports/qbs/base/Library.qbs @@ -34,4 +34,61 @@ NativeBinary { return ["staticlibrary"]; return ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []); } + + readonly property bool isDynamicLibrary: type.contains("dynamiclibrary") + readonly property bool isStaticLibrary: type.contains("staticlibrary") + readonly property bool isLoadableModule: type.contains("loadablemodule") + + installDir: { + if (isBundle) + return "Library/Frameworks"; + if (isDynamicLibrary) + return qbs.targetOS.contains("windows") ? "bin" : "lib"; + if (isStaticLibrary) + return "lib"; + } + + property bool installImportLib: false + property string importLibInstallDir: "lib" + + Group { + condition: install && _installable + fileTagsFilter: { + if (isBundle) + return ["bundle.content"]; + if (isDynamicLibrary) + return ["dynamiclibrary", "dynamiclibrary_symlink"]; + if (isStaticLibrary) + return ["staticlibrary"]; + if (isLoadableModule) + return ["loadablemodule"]; + return []; + } + qbs.install: true + qbs.installDir: installDir + qbs.installSourceBase: isBundle ? destinationDirectory : outer + } + + Group { + condition: installImportLib + && type.contains("dynamiclibrary") + && _installable + fileTagsFilter: "dynamiclibrary_import" + qbs.install: true + qbs.installDir: importLibInstallDir + } + + Group { + condition: installDebugInformation && _installable + fileTagsFilter: { + if (isDynamicLibrary) + return ["debuginfo_dll"]; + else if (isLoadableModule) + return ["debuginfo_loadablemodule"]; + return []; + } + qbs.install: true + qbs.installDir: debugInformationInstallDir + qbs.installSourceBase: destinationDirectory + } } diff --git a/share/qbs/imports/qbs/base/NativeBinary.qbs b/share/qbs/imports/qbs/base/NativeBinary.qbs index 3597f348f..900ed9faa 100644 --- a/share/qbs/imports/qbs/base/NativeBinary.qbs +++ b/share/qbs/imports/qbs/base/NativeBinary.qbs @@ -35,6 +35,12 @@ Product { property bool install: false property string installDir + // Product artifacts should be installed if it's not multiplexed or aggregated, + // or if it is multiplexed and it's the aggregate product + readonly property bool _installable: !multiplexed || !aggregate || !multiplexConfigurationId + + property bool installDebugInformation: install + property string debugInformationInstallDir: installDir Depends { name: "bundle"; condition: isForDarwin } diff --git a/share/qbs/imports/qbs/base/QtGuiApplication.qbs b/share/qbs/imports/qbs/base/QtGuiApplication.qbs index 61bc69752..7b2abf017 100644 --- a/share/qbs/imports/qbs/base/QtGuiApplication.qbs +++ b/share/qbs/imports/qbs/base/QtGuiApplication.qbs @@ -28,6 +28,6 @@ ** ****************************************************************************/ -CppApplication { +QtApplication { Depends { name: "Qt.gui" } } diff --git a/share/qbs/imports/qbs/base/QtModule.qbs b/share/qbs/imports/qbs/base/QtModule.qbs new file mode 100644 index 000000000..35421436f --- /dev/null +++ b/share/qbs/imports/qbs/base/QtModule.qbs @@ -0,0 +1,87 @@ +import qbs.FileInfo + +Module { + condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild) + && (!qbs.architecture + || architectures.length === 0 + || architectures.contains(qbs.architecture)) + + readonly property bool isCombinedUIKitBuild: ["ios", "tvos", "watchos"].contains(targetPlatform) + && ["x86", "x86_64"].contains(qbs.architecture) + && qbs.targetPlatform === targetPlatform + "-simulator" + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + + Depends { name: "Qt.plugin_support" } + property stringList pluginTypes + Qt.plugin_support.pluginTypes: pluginTypes + Depends { + condition: Qt.core.staticBuild && !isPlugin + name: "Qt"; + submodules: { + // We have to pull in all plugins here, because dependency resolving happens + // before module merging, and we don't know yet if someone set + // Qt.pluginSupport.pluginsByType in the product. + // TODO: We might be able to use Qt.pluginSupport.pluginsByType now. + // The real filtering is done later by the plugin module files themselves. + var list = []; + var allPlugins = Qt.plugin_support.allPluginsByType; + for (var i = 0; i < (pluginTypes || []).length; ++i) + Array.prototype.push.apply(list, allPlugins[pluginTypes[i]]) + return list; + } + } + + property string qtModuleName + property path binPath: Qt.core.binPath + property path incPath: Qt.core.incPath + property path libPath: Qt.core.libPath + property string qtLibInfix: Qt.core.libInfix + property string libNameForLinkerDebug + property string libNameForLinkerRelease + property string libNameForLinker: Qt.core.qtBuildVariant === "debug" + ? libNameForLinkerDebug : libNameForLinkerRelease + property string libFilePathDebug + property string libFilePathRelease + property string libFilePath: Qt.core.qtBuildVariant === "debug" + ? libFilePathDebug : libFilePathRelease + version: Qt.core.version + property bool hasLibrary: true + property bool isStaticLibrary: false + property bool isPlugin: false + + property stringList architectures + property string targetPlatform + property stringList staticLibsDebug + property stringList staticLibsRelease + property stringList dynamicLibsDebug + property stringList dynamicLibsRelease + property stringList linkerFlagsDebug + property stringList linkerFlagsRelease + property stringList staticLibs: Qt.core.qtBuildVariant === "debug" + ? staticLibsDebug : staticLibsRelease + property stringList dynamicLibs: Qt.core.qtBuildVariant === "debug" + ? dynamicLibsDebug : dynamicLibsRelease + property stringList frameworksDebug + property stringList frameworksRelease + property stringList frameworkPathsDebug + property stringList frameworkPathsRelease + property stringList mFrameworks: Qt.core.qtBuildVariant === "debug" + ? frameworksDebug : frameworksRelease + property stringList mFrameworkPaths: Qt.core.qtBuildVariant === "debug" + ? frameworkPathsDebug: frameworkPathsRelease + cpp.linkerFlags: Qt.core.qtBuildVariant === "debug" + ? linkerFlagsDebug : linkerFlagsRelease + property bool enableLinking: qtModuleName != undefined && hasLibrary + property stringList moduleConfig + + Properties { + condition: enableLinking + cpp.staticLibraries: staticLibs + cpp.dynamicLibraries: dynamicLibs + cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild + ? [libNameForLinker] : []) + cpp.systemFrameworkPaths: mFrameworkPaths + } +} diff --git a/share/qbs/imports/qbs/base/QtPlugin.qbs b/share/qbs/imports/qbs/base/QtPlugin.qbs new file mode 100644 index 000000000..883e34465 --- /dev/null +++ b/share/qbs/imports/qbs/base/QtPlugin.qbs @@ -0,0 +1,51 @@ +import qbs.FileInfo +import qbs.TextFile + +QtModule { + isPlugin: true + + property string className + property stringList extendsModules + + enableLinking: { + if (!base) + return false; + if (!isStaticLibrary) + return false; + if (!Qt.plugin_support.linkPlugins) + return false; + if (!(Qt.plugin_support.enabledPlugins || []).contains(qtModuleName)) + return false; + if (!extendsModules || extendsModules.length === 0) + return true; + for (var i = 0; i < extendsModules.length; ++i) { + var moduleName = extendsModules[i]; + if (product.Qt[moduleName] && product.Qt[moduleName].present) + return true; + } + return false; + } + + Rule { + condition: enableLinking + multiplex: true + Artifact { + filePath: product.targetName + "_qt_plugin_import_" + + product.moduleProperty(product.moduleName, "qtModuleName") + ".cpp" + fileTags: "cpp" + } + + prepare: { + var cmd = new JavaScriptCommand(); + var pluginName = product.moduleProperty(product.moduleName, "qtModuleName"); + cmd.description = "creating static import for plugin '" + pluginName + "'"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + var className = product.moduleProperty(product.moduleName, "className"); + f.writeLine("#include <QtPlugin>\n\nQ_IMPORT_PLUGIN(" + className + ")"); + f.close(); + }; + return cmd; + } + } +} diff --git a/share/qbs/imports/qbs/base/StaticLibrary.qbs b/share/qbs/imports/qbs/base/StaticLibrary.qbs index 4eea3c991..5a78a83b0 100644 --- a/share/qbs/imports/qbs/base/StaticLibrary.qbs +++ b/share/qbs/imports/qbs/base/StaticLibrary.qbs @@ -30,13 +30,4 @@ Library { type: ["staticlibrary"] - - installDir: isBundle ? "Library/Frameworks" : "lib" - Group { - condition: install - fileTagsFilter: isBundle ? "bundle.content" : "staticlibrary"; - qbs.install: true - qbs.installDir: installDir - qbs.installSourceBase: isBundle ? destinationDirectory : outer - } } |