diff options
Diffstat (limited to 'share')
142 files changed, 4967 insertions, 2314 deletions
diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 2891f7272..e47bd74f0 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -8,22 +8,8 @@ endif() # below we copy some files that are required to run qbs from the build directory # emulating the same layout we will have after the installation -# copy & install dmgbuild -install( - PROGRAMS ../src/3rdparty/python/bin/dmgbuild - DESTINATION "${QBS_LIBEXEC_INSTALL_DIR}" - ) -set(_DMGBUILD_INSTALL_PATH ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIBEXEC_INSTALL_DIR}/dmgbuild) -add_custom_command( - OUTPUT ${_DMGBUILD_INSTALL_PATH} - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/bin/dmgbuild - ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIBEXEC_INSTALL_DIR} - COMMENT "Copying dmgbuild script" - ) - # copy & install python packages -set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python2.7/site-packages/) +set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python3.9/site-packages/) file(GLOB_RECURSE _SITE_PACKAGES_RELATIVE RELATIVE "${_SITE_PACKAGES_DIR}" "${_SITE_PACKAGES_DIR}/*.py") set(_SITE_PACKAGES_SRC ${_SITE_PACKAGES_RELATIVE}) @@ -85,7 +71,7 @@ add_custom_command( -f ${PROJECT_SOURCE_DIR}/qbs.qbs -d ${PROJECT_BINARY_DIR}/ config:resources-build - qbs.installPrefix:undefined + qbs.installPrefix:"" project.withCode:false project.withDocumentation:false profile:none @@ -102,7 +88,6 @@ add_custom_command( add_custom_target( BuildQbsResources ALL DEPENDS - "${_DMGBUILD_INSTALL_PATH}" ${_SITE_PACKAGES_DST} ${_QBS_RESOURCES_DST} "${_QMLTYPES_INSTALL_PATH}" diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index f88f6ff52..9f7d92e59 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -182,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) { @@ -194,40 +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) { - // TODO: XCode supports multiple formatters separated by a comma - var varFormatterLower = varFormatter.toLowerCase(); - if (varFormatterLower === "rfc1034identifier") - varValue = Utilities.rfc1034Identifier(varValue); - if (varValue === "" && varFormatterLower.startsWith("default=")) - varValue = varFormatter.split("=")[1]; - } - - 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, []); diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index f01ee1070..a1ede5344 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -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__"])) @@ -592,6 +597,8 @@ function guessArchitecture(m) { architecture = "hcs12"; } else if (hasAnyOf(m, ["__e2k__"])) { architecture = "e2k"; + } else if (hasAnyOf(m, ["__hppa__"])) { + architecture = "hppa"; } } @@ -610,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"; diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js index a857a7139..c928c33e4 100644 --- a/share/qbs/imports/qbs/PathTools/path-tools.js +++ b/share/qbs/imports/qbs/PathTools/path-tools.js @@ -232,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 index b1b77ca36..3b3959017 100644 --- a/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs @@ -42,7 +42,7 @@ BinaryProbe { var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, pathSuffixes, platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator); + platformEnvironmentPaths); if (!results.found) { var msvcs = Utilities.installedMSVCs(preferredArchitecture); if (msvcs.length >= 1) { diff --git a/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs index 3ead109a0..8dc01d376 100644 --- a/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs @@ -42,7 +42,7 @@ BinaryProbe { var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, pathSuffixes, platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator); + platformEnvironmentPaths); var compilerPath; if (results.found) compilerPath = results.files[0].filePath; diff --git a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs index 658da8a9f..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 @@ -40,8 +41,6 @@ PathProbe { property string vcvarsallFilePath property stringList enableDefinesByLanguage property string preferredArchitecture - property string _nullDevice: qbs.nullDevice - property string _pathListSeparator: qbs.pathListSeparator property string winSdkVersion // Outputs @@ -95,8 +94,8 @@ 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 index cd722933c..097b1b6f1 100644 --- a/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs @@ -39,10 +39,11 @@ Probe { property stringList additionalArguments: [] property path conanfilePath property path packageReference - property path executable: "conan" + (qbs.hostOS.contains("windows") ? ".exe": "") + property path executable: "conan" + FileInfo.executableSuffix() property stringList generators: ["json"] property var options property var settings + property bool verbose: false // Output property var dependencies @@ -104,11 +105,25 @@ Probe { args = args.concat(["-if", generatedFilesPath]); var p = new Process(); - try { - p.exec(executable, args, true); - } finally { + 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")) diff --git a/share/qbs/imports/qbs/Probes/DmcProbe.qbs b/share/qbs/imports/qbs/Probes/DmcProbe.qbs index 0375805b5..6a8723a3b 100644 --- a/share/qbs/imports/qbs/Probes/DmcProbe.qbs +++ b/share/qbs/imports/qbs/Probes/DmcProbe.qbs @@ -37,6 +37,10 @@ PathProbe { 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 @@ -62,7 +66,11 @@ PathProbe { for (var i = 0; i < languages.length; ++i) { var tag = languages[i]; compilerDefinesByLanguage[tag] = DMC.dumpMacros( - compilerFilePath, tag); + compilerFilePath, + _targetPlatform, + _targetArchitecture, + _targetExtender, + tag); var paths = DMC.dumpDefaultPaths(compilerFilePath, tag); defaultPathsByLanguage[tag] = paths; } @@ -83,7 +91,7 @@ PathProbe { versionMajor = version.major; versionMinor = version.minor; versionPatch = version.patch; - found = !!architecture && !!endianness; + found = !!architecture; } } } diff --git a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs index 9081f5efb..0872e6cc0 100644 --- a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs @@ -1,5 +1,6 @@ import qbs.Environment import qbs.FileInfo +import qbs.Host import "path-probe.js" as PathProbeConfigure BinaryProbe { @@ -13,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; @@ -51,8 +52,7 @@ BinaryProbe { var selectors; var results = PathProbeConfigure.configure( selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, - pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, - pathListSeparator); + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths); found = results.found; if (!found) 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 3a65c208f..a0008be47 100644 --- a/share/qbs/imports/qbs/Probes/IarProbe.qbs +++ b/share/qbs/imports/qbs/Probes/IarProbe.qbs @@ -36,8 +36,6 @@ PathProbe { property string compilerFilePath property stringList enableDefinesByLanguage - property string _nullDevice: qbs.nullDevice - // Outputs property string architecture property string endianness 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 ba39691fb..b123584ad 100644 --- a/share/qbs/imports/qbs/Probes/KeilProbe.qbs +++ b/share/qbs/imports/qbs/Probes/KeilProbe.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ import qbs.File +import qbs.Host import "../../../modules/cpp/keil.js" as KEIL PathProbe { @@ -36,8 +37,6 @@ PathProbe { property string compilerFilePath property stringList enableDefinesByLanguage - property string _nullDevice: qbs.nullDevice - // Outputs property string architecture property string endianness @@ -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, _nullDevice); + compilerFilePath, Host.nullDevice()); includePaths = defaultPaths.includePaths; diff --git a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs index 7631eb441..d67a81372 100644 --- a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs @@ -28,15 +28,11 @@ ** ****************************************************************************/ +import qbs.PathTools + PathProbe { property string endianness - nameSuffixes: { - if (qbs.targetOS.contains("windows")) - return [".lib"]; - if (qbs.targetOS.contains("macos")) - return [".dylib", ".a"]; - return [".so", ".a"]; - } + nameSuffixes: PathTools.librarySuffixes(qbs.targetOS, ["shared", "static"], true) platformSearchPaths: { var result = []; if (qbs.targetOS.contains("unix")) { @@ -65,17 +61,7 @@ PathProbe { return result; } - nameFilter: { - if (qbs.targetOS.contains("unix")) { - return function(name) { - return "lib" + name; - } - } else { - return function(name) { - return name; - } - } - } + nameFilter: PathTools.libraryNameFilter(qbs.targetOS) platformEnvironmentPaths: { if (qbs.targetOS.contains("windows")) return [ "PATH" ]; 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 3ca6a96c2..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,11 +52,10 @@ 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 resultsMapper = function(result) { diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs index 768defd87..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 { @@ -40,11 +41,9 @@ Probe { property varList selectors property pathList searchPaths property stringList pathSuffixes - property pathList platformSearchPaths: hostOS.contains("unix") ? ['/usr', '/usr/local'] : [] + 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 @@ -58,7 +57,7 @@ Probe { var results = PathProbeConfigure.configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, pathSuffixes, platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator); + platformEnvironmentPaths); found = results.found; allResults = results.files; diff --git a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs index 2ed772227..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 @@ -73,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); @@ -85,6 +85,12 @@ 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) { stdout = p.readStdOut().trim(); 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/TypeScriptProbe.qbs b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs index 6a854a2e6..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,11 +61,10 @@ 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 resultsMapper = function(result) { 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/XcodeProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs index edd67433b..9a0d01072 100644 --- a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs +++ b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs @@ -89,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); diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js index 1f55dcf32..1858f5222 100644 --- a/share/qbs/imports/qbs/Probes/path-probe.js +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -73,7 +73,7 @@ function pathsFromEnvs(envs, pathListSeparator) { 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'; @@ -102,9 +102,9 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, // FIXME: Suggest how to obtain paths from system var _paths = ModUtils.concatAll( - pathsFromEnvs(environmentPaths, pathListSeparator), + pathsFromEnvs(environmentPaths, FileInfo.pathListSeparator()), searchPaths, - pathsFromEnvs(platformEnvironmentPaths, pathListSeparator), + pathsFromEnvs(platformEnvironmentPaths, FileInfo.pathListSeparator()), platformSearchPaths); var _suffixes = ModUtils.concatAll('', pathSuffixes); _paths = _paths.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/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/ApplicationExtension.qbs b/share/qbs/imports/qbs/base/ApplicationExtension.qbs index eae5145ce..3d25d5f73 100644 --- a/share/qbs/imports/qbs/base/ApplicationExtension.qbs +++ b/share/qbs/imports/qbs/base/ApplicationExtension.qbs @@ -34,8 +34,12 @@ 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 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/NativeBinary.qbs b/share/qbs/imports/qbs/base/NativeBinary.qbs index c51f132ee..900ed9faa 100644 --- a/share/qbs/imports/qbs/base/NativeBinary.qbs +++ b/share/qbs/imports/qbs/base/NativeBinary.qbs @@ -39,7 +39,7 @@ Product { // or if it is multiplexed and it's the aggregate product readonly property bool _installable: !multiplexed || !aggregate || !multiplexConfigurationId - property bool installDebugInformation: false + property bool installDebugInformation: install property string debugInformationInstallDir: installDir Depends { name: "bundle"; condition: isForDarwin } diff --git a/share/qbs/module-providers/Qt/templates/QtModule.qbs b/share/qbs/imports/qbs/base/QtModule.qbs index aa7c1d15a..35421436f 100644 --- a/share/qbs/module-providers/Qt/templates/QtModule.qbs +++ b/share/qbs/imports/qbs/base/QtModule.qbs @@ -23,6 +23,7 @@ Module { // 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; @@ -81,6 +82,6 @@ Module { cpp.dynamicLibraries: dynamicLibs cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild ? [libNameForLinker] : []) - cpp.frameworkPaths: mFrameworkPaths + cpp.systemFrameworkPaths: mFrameworkPaths } } diff --git a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs b/share/qbs/imports/qbs/base/QtPlugin.qbs index 88bfa5a65..883e34465 100644 --- a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs +++ b/share/qbs/imports/qbs/base/QtPlugin.qbs @@ -38,7 +38,7 @@ QtModule { prepare: { var cmd = new JavaScriptCommand(); var pluginName = product.moduleProperty(product.moduleName, "qtModuleName"); - cmd.description = "Creating static import for plugin '" + pluginName + "'."; + 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"); diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs index 33083c51d..0d036c04d 100644 --- a/share/qbs/module-providers/Qt/provider.qbs +++ b/share/qbs/module-providers/Qt/provider.qbs @@ -1,6 +1,14 @@ import "setup-qt.js" as SetupQt +import qbs.Probes ModuleProvider { + Probes.QmakeProbe { + id: probe + qmakePaths: qmakeFilePaths + } property stringList qmakeFilePaths - relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path, qbs) + readonly property varList _qtInfos: probe.qtInfos + condition: moduleName.startsWith("Qt.") + isEager: false + relativeSearchPaths: SetupQt.doSetup(moduleName, _qtInfos, outputBaseDir, path) } diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index 5298f8984..9a314822b 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -37,1181 +37,13 @@ ** ****************************************************************************/ -var Environment = require("qbs.Environment"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); var ModUtils = require("qbs.ModUtils"); -var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); -function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } -function toNative(p) { return FileInfo.toNativeSeparators(p); } -function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; } - -function getQmakeFilePaths(qmakeFilePaths, qbs) { - if (qmakeFilePaths && qmakeFilePaths.length > 0) - return qmakeFilePaths; - console.info("Detecting Qt installations..."); - var filePaths = []; - var pathValue = Environment.getEnv("PATH"); - if (pathValue) { - var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator); - var suffix = exeSuffix(qbs); - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "qmake" + suffix); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") - candidate = canonicalCandidate; - if (!filePaths.contains(candidate)) { - console.info("Found Qt at '" + toNative(candidate) + "'."); - filePaths.push(candidate); - } - } - } - if (filePaths.length === 0) { - console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " - + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " - + "to point a qmake executable."); - } - return filePaths; -} - -function queryQmake(qmakeFilePath) { - var qmakeProcess = new Process; - qmakeProcess.exec(qmakeFilePath, ["-query"]); - if (qmakeProcess.exitCode() !== 0) { - throw "The qmake executable '" + toNative(qmakeFilePath) + "' failed with exit code " - + qmakeProcess.exitCode() + "."; - } - var queryResult = {}; - while (!qmakeProcess.atEnd()) { - var line = qmakeProcess.readLine(); - var index = (line || "").indexOf(":"); - if (index !== -1) - queryResult[line.slice(0, index)] = line.slice(index + 1).trim(); - } - return queryResult; -} - -function pathQueryValue(queryResult, key) { - var p = queryResult[key]; - if (p) - return FileInfo.fromNativeSeparators(p); -} - -function readFileContent(filePath) { - var f = new TextFile(filePath, TextFile.ReadOnly); - var content = f.readAll(); - f.close(); - return content; -} - -// TODO: Don't do the split every time... -function configVariable(configContent, key) { - var configContentLines = configContent.split('\n'); - var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)"); - for (var i = 0; i < configContentLines.length; ++i) { - var line = configContentLines[i]; - var match = regexp.exec(line); - if (match) - return match[1].trim(); - } -} - -function configVariableItems(configContent, key) { - return splitNonEmpty(configVariable(configContent, key), ' '); -} - -function msvcPrefix() { return "win32-msvc"; } - -function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } - -function msvcCompilerVersionForYear(year) { - var mapping = { - "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", - "2017": "19.1", "2019": "19.2" - }; - return mapping[year]; -} - -function msvcCompilerVersionFromMkspecName(mkspecName) { - return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length)); -} - -function addQtBuildVariant(qtProps, buildVariantName) { - if (qtProps.qtConfigItems.contains(buildVariantName)) - qtProps.buildVariant.push(buildVariantName); -} - -function checkForStaticBuild(qtProps) { - if (qtProps.qtMajorVersion >= 5) - return qtProps.qtConfigItems.contains("static"); - if (qtProps.frameworkBuild) - return false; // there are no Qt4 static frameworks - var isWin = qtProps.mkspecName.startsWith("win"); - var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath; - var coreLibFiles = File.directoryEntries(libDir, File.Files) - .filter(function(fp) { return fp.contains("Core"); }); - if (coreLibFiles.length === 0) - throw "Could not determine whether Qt is a static build."; - for (var i = 0; i < coreLibFiles.length; ++i) { - if (Utilities.isSharedLibrary(coreLibFiles[i])) - return false; - } - return true; -} - -function isForMinGw(qtProps) { - return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); -} - -function targetsDesktopWindows(qtProps) { - return qtProps.mkspecName.startsWith("win32-") || isForMinGw(qtProps); -} - -function guessMinimumWindowsVersion(qtProps) { - if (qtProps.mkspecName.startsWith("winrt-")) - return "10.0"; - if (!targetsDesktopWindows(qtProps)) - return ""; - if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64") - return "5.2" - var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/); - if (match) { - var msvcVersion = match[1]; - if (msvcVersion < 2012) - return "5.0"; - return "5.1"; - } - return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1"; -} - -function needsDSuffix(qtProps) { - return !isForMinGw(qtProps) || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 - || qtProps.configItems.contains("debug_and_release"); -} - -function fillEntryPointLibs(qtProps, debug) { - result = []; - var isMinGW = isForMinGw(qtProps); - - // Some Linux distributions rename the qtmain library. - var qtMainCandidates = ["qtmain"]; - if (isMinGW && qtProps.qtMajorVersion === 5) - qtMainCandidates.push("qt5main"); - if (qtProps.qtMajorVersion === 6) - qtMainCandidates.push("Qt6EntryPoint"); - - for (var i = 0; i < qtMainCandidates.length; ++i) { - var baseNameCandidate = qtMainCandidates[i]; - var qtmain = qtProps.libraryPath + '/'; - if (isMinGW) - qtmain += "lib"; - qtmain += baseNameCandidate + qtProps.qtLibInfix; - if (debug && needsDSuffix(qtProps)) - qtmain += 'd'; - if (isMinGW) { - qtmain += ".a"; - } else { - qtmain += ".lib"; - if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0) - result.push("Shell32.lib"); - } - if (File.exists(qtmain)) { - result.push(qtmain); - break; - } - } - if (result.length === 0) { - console.warn("Could not find the qtmain library at '" + toNative(qtProps.libraryPath) - + "'. You will not be able to link Qt applications."); - } - return result; -} - -function abiToArchitecture(abi) { - switch (abi) { - case "armeabi-v7a": - return "armv7a"; - case "arm64-v8a": - return "arm64"; - case "x86": - case "x86_64": - default: - return abi; - } -} - -function getQtProperties(qmakeFilePath, qbs) { - var queryResult = queryQmake(qmakeFilePath); - var qtProps = {}; - qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX"); - qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); - qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS"); - qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS"); - qtProps.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 - - if (!File.exists(qtProps.mkspecBasePath)) - throw "Cannot extract the mkspecs directory."; - - var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, - "qconfig.pri")); - qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION")); - qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION")); - qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION")); - qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE"); - qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || ""; - qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH") - || configVariable(qconfigContent, "QT_ARCH") || "x86"; - qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); - qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); - - // retrieve the mkspec - if (qtProps.qtMajorVersion >= 5) { - qtProps.mkspecName = queryResult.QMAKE_XSPEC; - qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName); - if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath)) - qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName); - } else { - if (qbs.hostOS.contains("windows")) { - var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default"); - var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf")); - qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL"); - if (!File.exists(qtProps.mkspecPath)) { - // Work around QTBUG-28792. - // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? - var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m); - if (match) { - qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths( - baseDirPath, match[1])); - } - } - } else { - qtProps.mkspecPath = FileInfo.canonicalPath( - FileInfo.joinPaths(qtProps.mkspecBasePath, "default")); - } - - // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: - // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ - qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath); - - qtProps.mkspecName = qtProps.mkspecPath; - var idx = qtProps.mkspecName.lastIndexOf('/'); - if (idx !== -1) - qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1); - } - if (!File.exists(qtProps.mkspecPath)) - throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist"; - - // Starting with qt 5.14, android sdk provides multi-abi - if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 - && qtProps.mkspecPath.contains("android")) { - var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, - "qdevice.pri")); - qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); - } - - // determine MSVC version - if (isMsvcQt(qtProps)) { - var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION"); - var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION"); - var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION"); - if (msvcMajor && msvcMinor && msvcPatch) - qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch; - else - qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName); - } - - // determine whether we have a framework build - qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx") - && qtProps.configItems.contains("qt_framework"); - - // determine whether Qt is built with debug, release or both - qtProps.buildVariant = []; - addQtBuildVariant(qtProps, "debug"); - addQtBuildVariant(qtProps, "release"); - - qtProps.staticBuild = checkForStaticBuild(qtProps); - - // determine whether user apps require C++11 - if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild) - qtProps.configItems.push("c++11"); - - // Set the minimum operating system versions appropriate for this Qt version - qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps); - if (qtProps.windowsVersion) { // Is target OS Windows? - if (qtProps.buildVariant.contains("debug")) - qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true); - if (qtProps.buildVariant.contains("release")) - qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false); - } else if (qtProps.mkspecPath.contains("macx")) { - if (qtProps.qtMajorVersion >= 5) { - var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, - "qmake.conf")); - for (var i = 0; i < lines.length; ++i) { - var line = lines[i].trim(); - match = line.match - (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/); - if (match) { - var platform = match[1]; - var version = match[2]; - if (platform === "MACOSX") - qtProps.macosVersion = version; - else if (platform === "IOS") - qtProps.iosVersion = version; - else if (platform === "TVOS") - qtProps.tvosVersion = version; - else if (platform === "WATCHOS") - qtProps.watchosVersion = version; - } - } - var isMac = qtProps.mkspecName !== "macx-ios-clang" - && qtProps.mkspecName !== "macx-tvos-clang" - && qtProps.mkspecName !== "macx-watchos-clang"; - if (isMac) { - // Qt 5.0.x placed the minimum version in a different file - if (!qtProps.macosVersion) - qtProps.macosVersion = "10.6"; - - // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 - if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0 - && qtProps.qtConfigItems.contains("c++11")) { - qtProps.macosVersion = "10.7"; - } - } - } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) { - var qconfigDir = qtProps.frameworkBuild - ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers") - : FileInfo.joinPaths(qtProps.includePath, "Qt"); - try { - var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"), - TextFile.ReadOnly); - var qtCocoaBuild = false; - var ok = true; - do { - line = qconfig.readLine(); - if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) { - qtCocoaBuild = true; - break; - } - } while (!qconfig.atEof()); - qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4"; - } - catch (e) {} - finally { - if (qconfig) - qconfig.close(); - } - if (!qtProps.macosVersion) { - throw "Could not determine whether Qt is using Cocoa or Carbon from '" - + toNative(qconfig.filePath()) + "'."; - } - } - } else if (qtProps.mkspecPath.contains("android")) { - if (qtProps.qtMajorVersion >= 5) - qtProps.androidVersion = "2.3"; - else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8) - qtProps.androidVersion = "1.6"; // Necessitas - } - return qtProps; -} - -function makePluginData() { - var pluginData = {}; - pluginData.type = undefined; - pluginData.className = undefined; - pluginData.autoLoad = true; - pluginData["extends"] = []; - return pluginData; -} - -function makeQtModuleInfo(name, qbsName, deps) { - var moduleInfo = {}; - moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files. - if (moduleInfo.name === undefined) - moduleInfo.name = ""; - moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix. - moduleInfo.dependencies = deps || []; // qbs names. - if (moduleInfo.qbsName && moduleInfo.qbsName !== "core" - && !moduleInfo.dependencies.contains("core")) { - moduleInfo.dependencies.unshift("core"); - } - moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private"); - moduleInfo.hasLibrary = !moduleInfo.isPrivate; - moduleInfo.isStaticLibrary = false; - moduleInfo.isPlugin = false; - moduleInfo.mustExist = true; - moduleInfo.modulePrefix = ""; // empty value means "Qt". - moduleInfo.version = undefined; - moduleInfo.includePaths = []; - moduleInfo.compilerDefines = []; - moduleInfo.staticLibrariesDebug = []; - moduleInfo.staticLibrariesRelease = []; - moduleInfo.dynamicLibrariesDebug = []; - moduleInfo.dynamicLibrariesRelease = []; - moduleInfo.linkerFlagsDebug = []; - moduleInfo.linkerFlagsRelease = []; - moduleInfo.libFilePathDebug = undefined; - moduleInfo.libFilePathRelease = undefined; - moduleInfo.frameworksDebug = []; - moduleInfo.frameworksRelease = []; - moduleInfo.frameworkPathsDebug = []; - moduleInfo.frameworkPathsRelease = []; - moduleInfo.libraryPaths = []; - moduleInfo.libDir = ""; - moduleInfo.config = []; - moduleInfo.supportedPluginTypes = []; - moduleInfo.pluginData = makePluginData(); - return moduleInfo; -} - -function frameworkHeadersPath(qtModuleInfo, qtProps) { - return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers"); -} - -function qt4ModuleIncludePaths(qtModuleInfo, qtProps) { - var paths = []; - if (isFramework(qtModuleInfo, qtProps)) - paths.push(frameworkHeadersPath(qtModuleInfo, qtProps)); - else - paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name)); - return paths; -} - -// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users -// for that. -function addTestModule(modules) { - var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]); - testModule.hasLibrary = false; - modules.push(testModule); -} - -// See above. -function addDesignerComponentsModule(modules) { - var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents", - ["designercomponents-private"]); - module.hasLibrary = false; - modules.push(module); -} - -function isFramework(modInfo, qtProps) { - if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) - return false; - var modulesNeverBuiltAsFrameworks = [ - "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" - ]; - - if (qtProps.qtMajorVersion <= 5) { - modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 - } - - return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); -} - -function libBaseName(modInfo, libName, debugBuild, qtProps) { - var name = libName; - if (qtProps.mkspecName.startsWith("win")) { - if (debugBuild && needsDSuffix(qtProps)) - name += 'd'; - if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5) - name += qtProps.qtMajorVersion; - } - if (qtProps.mkspecName.contains("macx") - || qtProps.mkspecName.contains("ios") - || qtProps.mkspecName.contains("darwin")) { - if (!isFramework(modInfo, qtProps) - && qtProps.buildVariant.contains("debug") - && (!qtProps.buildVariant.contains("release") || debugBuild)) { - name += "_debug"; - } - } - return name; -} - -function moduleNameWithoutPrefix(modInfo) { - if (modInfo.name === "Phonon") - return "phonon"; - if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt")) - return modInfo.name.slice(2); // Strip off "Qt". - if (modInfo.name.startsWith(modInfo.modulePrefix)) - return modInfo.name.slice(modInfo.modulePrefix.length); - return modInfo.name; -} - -function libraryBaseName(modInfo, qtProps, debugBuild) { - if (modInfo.isPlugin) - return libBaseName(modInfo, modInfo.name, debugBuild, qtProps); - - // Some modules use a different naming scheme, so it doesn't get boring. - var libNameBroken = modInfo.name === "Enginio" - || modInfo.name === "DataVisualization" - || modInfo.name === "Phonon"; - - var libName = ""; - if (!modInfo.isExternal) { - libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; - if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken) - libName += qtProps.qtMajorVersion; - } - libName += moduleNameWithoutPrefix(modInfo); - if (!modInfo.isExternal) - libName += qtProps.qtLibInfix; - return libBaseName(modInfo, libName, debugBuild, qtProps); -} - -function libNameForLinker(modInfo, qtProps, debugBuild) { - if (!modInfo.hasLibrary) - return undefined; - var libName = libraryBaseName(modInfo, qtProps, debugBuild); - if (qtProps.mkspecName.contains("msvc")) - libName += ".lib"; - return libName; -} - -function guessLibraryFilePath(prlFilePath, libDir, qtProps) { - var baseName = FileInfo.baseName(prlFilePath); - var prefixCandidates = ["", "lib"]; - var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"]; - for (var i = 0; i < prefixCandidates.length; ++i) { - var prefix = prefixCandidates[i]; - for (var j = 0; j < suffixCandidates.length; ++j) { - var suffix = suffixCandidates[j]; - var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix); - if (File.exists(candidate)) - return candidate; - } - } -} - -function doReplaceQtLibNamesWithFilePath(namePathMap, libList) { - for (var i = 0; i < libList.length; ++i) { - var lib = libList[i]; - var path = namePathMap[lib]; - if (path) - libList[i] = path; - } -} - -function replaceQtLibNamesWithFilePath(modules, qtProps) { - // We don't want to add the libraries for Qt modules via "-l", because of the - // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, - // we pull them in using the full file path. - var linkerNamesToFilePathsDebug = {}; - var linkerNamesToFilePathsRelease = {}; - for (var i = 0; i < modules.length; ++i) { - var m = modules[i]; - linkerNamesToFilePathsDebug[libNameForLinker(m, qtProps, true)] = m.libFilePathDebug; - linkerNamesToFilePathsRelease[libNameForLinker(m, qtProps, false)] = m.libFilePathRelease; - } - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - module.dynamicLibrariesRelease); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - module.staticLibrariesRelease); - } -} - -function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) { - if (!modInfo.hasLibrary) - return; // Can happen for Qt4 convenience modules, like "widgets". - - if (debugBuild) { - if (!qtProps.buildVariant.contains("debug")) - return; - var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"]; - for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) { - var m = modulesNeverBuiltAsDebug[i]; - if (modInfo.qbsName === m || modInfo.qbsName === m + "-private") - return; - } - } else if (!qtProps.buildVariant.contains("release")) { - return; - } - - var libs = modInfo.isStaticLibrary - ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease) - : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease); - var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease; - var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease; - var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease; - var libFilePath; - - if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) { - libs.push("z", "m"); - if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { - var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport"); - libs.push(libNameForLinker(platformSupportModule, qtProps, debugBuild)); - } - if (modInfo.name === "qios") { - flags.push("-force_load", FileInfo.joinPaths( - qtProps.pluginPath, "platforms", - libBaseName(modInfo, "libqios", debugBuild, qtProps) + ".a")); - } - } - var prlFilePath = modInfo.isPlugin - ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) - : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); - var libDir = prlFilePath; - if (isFramework(modInfo, qtProps)) { - prlFilePath = FileInfo.joinPaths(prlFilePath, - libraryBaseName(modInfo, qtProps, false) + ".framework"); - libDir = prlFilePath; - if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) - prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); - } - var baseName = libraryBaseName(modInfo, qtProps, debugBuild); - if (!qtProps.mkspecName.startsWith("win") && !isFramework(modInfo, qtProps)) - baseName = "lib" + baseName; - prlFilePath = FileInfo.joinPaths(prlFilePath, baseName); - var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win") - && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; - if (isNonStaticQt4OnWindows) - prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... - // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file - if (androidAbi.length > 0 - && modInfo.name !== "QtBootstrap" - && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" - && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) - && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 - || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { - prlFilePath += "_"; - prlFilePath += androidAbi; - } - - prlFilePath += ".prl"; - - try { - var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); - while (!prlFile.atEof()) { - var line = prlFile.readLine().trim(); - var equalsOffset = line.indexOf('='); - if (equalsOffset === -1) - continue; - if (line.startsWith("QMAKE_PRL_TARGET")) { - var isMingw = qtProps.mkspecName.startsWith("win") - && qtProps.mkspecName.contains("g++"); - var isQtVersionBefore56 = qtProps.qtMajorVersion < 5 - || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6); - - // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. - // Of course, the exception has an exception too: For static libs, mingw *does* - // have the "lib" prefix. - var libFileName = ""; - if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw - && !modInfo.isStaticLibrary) { - libFileName += "lib"; - } - - libFileName += line.slice(equalsOffset + 1).trim(); - if (isNonStaticQt4OnWindows) - libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET... - if (isQtVersionBefore56) { - if (qtProps.mkspecName.contains("msvc")) { - libFileName += ".lib"; - } else if (isMingw) { - libFileName += ".a"; - if (!File.exists(FileInfo.joinPaths(libDir, libFileName))) - libFileName = libFileName.slice(0, -2) + ".dll"; - } - } - libFilePath = FileInfo.joinPaths(libDir, libFileName); - continue; - } - if (line.startsWith("QMAKE_PRL_CONFIG")) { - modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); - continue; - } - if (!line.startsWith("QMAKE_PRL_LIBS =")) - continue; - - var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); - for (i = 0; i < parts.length; ++i) { - var part = parts[i]; - part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); - 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) { - libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); - if (nonExistingPrlFiles.contains(prlFilePath)) - return; - nonExistingPrlFiles.push(prlFilePath); - if (modInfo.mustExist) { - console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '" - + modInfo.name - + "' (" + e + "), and failed to deduce the library file path. " - + " This module will likely not be usable by qbs."); - } - } - finally { - if (prlFile) - prlFile.close(); - } - - if (debugBuild) - modInfo.libFilePathDebug = libFilePath; - else - modInfo.libFilePathRelease = libFilePath; -} - -function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) { - doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); - doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); -} - -function allQt4Modules(qtProps) { - // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. - var modules = []; - - var core = makeQtModuleInfo("QtCore", "core"); - core.compilerDefines.push("QT_CORE_LIB"); - if (qtProps.qtNameSpace) - core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace); - modules.push(core, - makeQtModuleInfo("QtCore", "core-private", ["core"]), - makeQtModuleInfo("QtGui", "gui"), - makeQtModuleInfo("QtGui", "gui-private", ["gui"]), - makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]), - makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]), - makeQtModuleInfo("QtNetwork", "network"), - makeQtModuleInfo("QtNetwork", "network-private", ["network"]), - makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]), - makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]), - makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]), - makeQtModuleInfo("QtScript", "script"), - makeQtModuleInfo("QtScript", "script-private", ["script"]), - makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]), - makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]), - makeQtModuleInfo("QtSql", "sql"), - makeQtModuleInfo("QtSql", "sql-private", ["sql"]), - makeQtModuleInfo("QtSvg", "svg", ["gui"]), - makeQtModuleInfo("QtSvg", "svg-private", ["svg"]), - makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]), - makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]), - makeQtModuleInfo("QtXml", "xml"), - makeQtModuleInfo("QtXml", "xml-private", ["xml"]), - makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]), - makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]), - makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]), - makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]), - makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]), - makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]), - makeQtModuleInfo("QtUiTools", "uitools"), - makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]), - makeQtModuleInfo("QtHelp", "help", ["network", "sql"]), - makeQtModuleInfo("QtHelp", "help-private", ["help"]), - makeQtModuleInfo("QtTest", "testlib"), - makeQtModuleInfo("QtTest", "testlib-private", ["testlib"])); - if (qtProps.mkspecName.startsWith("win")) { - var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer"); - axcontainer.modulePrefix = "Q"; - axcontainer.isStaticLibrary = true; - axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); - modules.push(axcontainer); - - var axserver = makeQtModuleInfo("QAxServer", "axserver"); - axserver.modulePrefix = "Q"; - axserver.isStaticLibrary = true; - axserver.compilerDefines.push("QAXSERVER"); - axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); - modules.push(axserver); - } else { - modules.push(makeQtModuleInfo("QtDBus", "dbus")); - modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"])); - } - - var designerComponentsPrivate = makeQtModuleInfo( - "QtDesignerComponents", "designercomponents-private", - ["gui-private", "designer-private"]); - designerComponentsPrivate.hasLibrary = true; - modules.push(designerComponentsPrivate); - - var phonon = makeQtModuleInfo("Phonon", "phonon"); - phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps); - modules.push(phonon); - - // Set up include paths that haven't been set up before this point. - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - if (module.includePaths.length > 0) - continue; - module.includePaths = qt4ModuleIncludePaths(module, qtProps); - } - - // Set up compiler defines haven't been set up before this point. - for (i = 0; i < modules.length; ++i) { - module = modules[i]; - if (module.compilerDefines.length > 0) - continue; - module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB"); - } - - // These are for the convenience of project file authors. It allows them - // to add a dependency to e.g. "Qt.widgets" without a version check. - var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "concurrent"); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - - addTestModule(modules); - addDesignerComponentsModule(modules); - - var modulesThatCanBeDisabled = [ - "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools", - "declarative", "gui", "dbus", "opengl", "openvg"]; - var nonExistingPrlFiles = []; - for (i = 0; i < modules.length; ++i) { - module = modules[i]; - var name = module.qbsName; - var privateIndex = name.indexOf("-private"); - if (privateIndex !== -1) - name = name.slice(0, privateIndex); - if (modulesThatCanBeDisabled.contains(name)) - module.mustExist = false; - if (qtProps.staticBuild) - module.isStaticLibrary = true; - setupLibraries(module, qtProps, nonExistingPrlFiles, ""); - } - replaceQtLibNamesWithFilePath(modules, qtProps); - - return modules; -} - -function getFileContentsRecursively(filePath) { - var file = new TextFile(filePath, TextFile.ReadOnly); - var lines = splitNonEmpty(file.readAll(), '\n'); - for (var i = 0; i < lines.length; ++i) { - var includeString = "include("; - var line = lines[i].trim(); - if (!line.startsWith(includeString)) - continue; - var offset = includeString.length; - var closingParenPos = line.indexOf(')', offset); - if (closingParenPos === -1) { - console.warn("Invalid include statement in '" + toNative(filePath) + "'"); - continue; - } - var includedFilePath = line.slice(offset, closingParenPos); - if (!FileInfo.isAbsolutePath(includedFilePath)) - includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); - var includedContents = getFileContentsRecursively(includedFilePath); - var j = i; - for (var k = 0; k < includedContents.length; ++k) - lines.splice(++j, 0, includedContents[k]); - lines.splice(i--, 1); - } - file.close(); - return lines; -} - -function extractPaths(rhs, filePath) { - var paths = []; - var startIndex = 0; - for (;;) { - while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ') - ++startIndex; - if (startIndex >= rhs.length) - break; - var endIndex; - if (rhs.charAt(startIndex) === '"') { - ++startIndex; - endIndex = rhs.indexOf('"', startIndex); - if (endIndex === -1) { - console.warn("Unmatched quote in file '" + toNative(filePath) + "'"); - break; - } - } else { - endIndex = rhs.indexOf(' ', startIndex + 1); - if (endIndex === -1) - endIndex = rhs.length; - } - paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) - .replace("$$PWD", FileInfo.path(filePath)))); - startIndex = endIndex + 1; - } - return paths; -} - -function removeDuplicatedDependencyLibs(modules) { - var revDeps = {}; - var currentPath = []; - var getLibraries; - var getLibFilePath; - - function setupReverseDependencies(modules) { - var moduleByName = {}; - for (var i = 0; i < modules.length; ++i) - moduleByName[modules[i].qbsName] = modules[i]; - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - for (var j = 0; j < module.dependencies.length; ++j) { - var depmod = moduleByName[module.dependencies[j]]; - if (!depmod) - continue; - if (!revDeps[depmod.qbsName]) - revDeps[depmod.qbsName] = []; - revDeps[depmod.qbsName].push(module); - } - } - } - - function roots(modules) { - var result = []; - for (i = 0; i < modules.length; ++i) { - var module = modules[i] - if (module.dependencies.length === 0) - result.push(module); - } - return result; - } - - function traverse(module, libs) { - if (currentPath.contains(module)) - return; - currentPath.push(module); - var moduleLibraryLists = getLibraries(module); - for (var i = 0; i < moduleLibraryLists.length; ++i) { - var modLibList = moduleLibraryLists[i]; - for (j = modLibList.length - 1; j >= 0; --j) { - if (libs.contains(modLibList[j])) - modLibList.splice(j, 1); - } - } - - var libFilePath = getLibFilePath(module); - if (libFilePath) - libs.push(libFilePath); - for (i = 0; i < moduleLibraryLists.length; ++i) - libs = libs.concat(moduleLibraryLists[i]); - libs.sort(); - - var deps = revDeps[module.qbsName]; - for (i = 0; i < (deps || []).length; ++i) - traverse(deps[i], libs); - - currentPath.pop(); - } - - setupReverseDependencies(modules); - - // Traverse the debug variants of modules. - getLibraries = function(module) { - return [module.dynamicLibrariesDebug, module.staticLibrariesDebug]; - }; - getLibFilePath = function(module) { return module.libFilePathDebug; }; - var rootModules = roots(modules); - for (var i = 0; i < rootModules.length; ++i) - traverse(rootModules[i], []); - - // Traverse the release variants of modules. - getLibraries = function(module) { - return [module.dynamicLibrariesRelease, module.staticLibrariesRelease]; - }; - getLibFilePath = function(module) { return module.libFilePathRelease; }; - for (i = 0; i < rootModules.length; ++i) - traverse(rootModules[i], []); -} - -function allQt5Modules(qtProps, androidAbi) { - var nonExistingPrlFiles = []; - var modules = []; - var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules"); - var modulePriFiles = File.directoryEntries(modulesDir, File.Files); - for (var i = 0; i < modulePriFiles.length; ++i) { - var priFileName = modulePriFiles[i]; - var priFilePath = FileInfo.joinPaths(modulesDir, priFileName); - var genericFileNamePrefix = "qt_"; - var moduleFileNamePrefix = "qt_lib_"; - var pluginFileNamePrefix = "qt_plugin_"; - var moduleFileNameSuffix = ".pri"; - var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); - if (!fileHasPluginPrefix && !priFileName.startsWith(genericFileNamePrefix) - || !priFileName.endsWith(moduleFileNameSuffix)) { - continue; - } - var moduleInfo = makeQtModuleInfo(); - moduleInfo.isPlugin = fileHasPluginPrefix; - moduleInfo.isExternal = !moduleInfo.isPlugin - && !priFileName.startsWith(moduleFileNamePrefix); - var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal - ? genericFileNamePrefix : 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); - 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.hasLibrary = false; - - // Fix include paths for Apple frameworks. - // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. - if (!hasV2 && isFramework(moduleInfo, qtProps)) { - moduleInfo.includePaths = []; - var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps); - if (moduleInfo.isPrivate) { - baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version); - moduleInfo.includePaths.push(baseIncDir, - FileInfo.joinPaths(baseIncDir, moduleInfo.name)); - } else { - moduleInfo.includePaths.push(baseIncDir); - } - } - - setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi); - - modules.push(moduleInfo); - if (moduleInfo.qbsName === "testlib") - addTestModule(modules); - if (moduleInfo.qbsName === "designercomponents-private") - addDesignerComponentsModule(modules); - } - - replaceQtLibNamesWithFilePath(modules, qtProps); - removeDuplicatedDependencyLibs(modules); - return modules; -} - function extractQbsArchs(module, qtProps) { if (qtProps.mkspecName.startsWith("macx-")) { var archs = []; @@ -1273,7 +105,7 @@ function qbsTargetPlatformFromQtMkspec(qtProps) { return "solaris"; if (mkspec.startsWith("vxworks-")) return "vxworks"; - if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-")) + if (ProviderUtils.isDesktopWindowsQt(qtProps) || mkspec.startsWith("winrt-")) return "windows"; } @@ -1329,7 +161,7 @@ function defaultQpaPlugin(module, qtProps) { function libraryFileTag(module, qtProps) { if (module.isStaticLibrary) return "staticlibrary"; - return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") + return ProviderUtils.isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") ? "dynamiclibrary_import" : "dynamiclibrary"; } @@ -1354,7 +186,20 @@ function findVariable(content, start) { } function minVersionJsString(minVersion) { - return !minVersion ? "original" : ModUtils.toJSLiteral(minVersion); + return !minVersion ? "" : ModUtils.toJSLiteral(minVersion); +} + +function abiToArchitecture(abi) { + switch (abi) { + case "armeabi-v7a": + return "armv7a"; + case "arm64-v8a": + return "arm64"; + case "x86": + case "x86_64": + default: + return abi; + } } function replaceSpecialValues(content, module, qtProps, abi) { @@ -1370,12 +215,14 @@ function replaceSpecialValues(content, module, qtProps, abi) { qtConfig: ModUtils.toJSLiteral(qtProps.qtConfigItems), binPath: ModUtils.toJSLiteral(qtProps.binaryPath), installPath: ModUtils.toJSLiteral(qtProps.installPath), + installPrefixPath: ModUtils.toJSLiteral(qtProps.installPrefixPath), libPath: ModUtils.toJSLiteral(qtProps.libraryPath), libExecPath: ModUtils.toJSLiteral(qtProps.libExecPath), qmlLibExecPath: ModUtils.toJSLiteral(qtProps.qmlLibExecPath), pluginPath: ModUtils.toJSLiteral(qtProps.pluginPath), incPath: ModUtils.toJSLiteral(qtProps.includePath), docPath: ModUtils.toJSLiteral(qtProps.documentationPath), + helpGeneratorLibExecPath: ModUtils.toJSLiteral(qtProps.helpGeneratorLibExecPath), mkspecName: ModUtils.toJSLiteral(qtProps.mkspecName), mkspecPath: ModUtils.toJSLiteral(qtProps.mkspecPath), version: ModUtils.toJSLiteral(qtProps.qtVersion), @@ -1383,7 +230,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { availableBuildVariants: ModUtils.toJSLiteral(qtProps.buildVariant), staticBuild: ModUtils.toJSLiteral(qtProps.staticBuild), frameworkBuild: ModUtils.toJSLiteral(qtProps.frameworkBuild), - name: ModUtils.toJSLiteral(moduleNameWithoutPrefix(module)), + name: ModUtils.toJSLiteral(ProviderUtils.qtModuleNameWithoutPrefix(module)), has_library: ModUtils.toJSLiteral(module.hasLibrary), dependencies: ModUtils.toJSLiteral(module.dependencies), includes: ModUtils.toJSLiteral(module.includePaths), @@ -1400,18 +247,20 @@ function replaceSpecialValues(content, module, qtProps, abi) { frameworksRelease: ModUtils.toJSLiteral(module.frameworksRelease), libFilePathDebug: ModUtils.toJSLiteral(module.libFilePathDebug), libFilePathRelease: ModUtils.toJSLiteral(module.libFilePathRelease), - libNameForLinkerDebug: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, true)), + libNameForLinkerDebug: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, true)), pluginTypes: ModUtils.toJSLiteral(module.supportedPluginTypes), moduleConfig: ModUtils.toJSLiteral(module.config), - libNameForLinkerRelease: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, false)), + libNameForLinkerRelease: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, false)), entryPointLibsDebug: ModUtils.toJSLiteral(qtProps.entryPointLibsDebug), entryPointLibsRelease: ModUtils.toJSLiteral(qtProps.entryPointLibsRelease), - minWinVersion: minVersionJsString(qtProps.windowsVersion), - minMacVersion: minVersionJsString(qtProps.macosVersion), - minIosVersion: minVersionJsString(qtProps.iosVersion), - minTvosVersion: minVersionJsString(qtProps.tvosVersion), - minWatchosVersion: minVersionJsString(qtProps.watchosVersion), - minAndroidVersion: minVersionJsString(qtProps.androidVersion), + minWinVersion_optional: minVersionJsString(qtProps.windowsVersion), + minMacVersion_optional: minVersionJsString(qtProps.macosVersion), + minIosVersion_optional: minVersionJsString(qtProps.iosVersion), + minTvosVersion_optional: minVersionJsString(qtProps.tvosVersion), + minWatchosVersion_optional: minVersionJsString(qtProps.watchosVersion), + minAndroidVersion_optional: minVersionJsString(qtProps.androidVersion), }; var additionalContent = ""; @@ -1463,16 +312,15 @@ function replaceSpecialValues(content, module, qtProps, abi) { + indent + indent + "fileTags: [\"qt.core.metatypes\"]\n" + indent + "}"; } - if (module.hasLibrary && !isFramework(module, qtProps)) { + if (module.hasLibrary && !ProviderUtils.qtIsFramework(module, qtProps)) { if (additionalContent) additionalContent += "\n" + indent; additionalContent += "Group {\n"; if (module.isPlugin) { additionalContent += indent + indent - + "condition: Qt[\"" + module.qbsName + "\"].enableLinking\n"; + + "condition: enableLinking\n"; } - additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName + "\"]" - + ".libFilePath]\n" + additionalContent += indent + indent + "files: libFilePath\n" + indent + indent + "filesAreTargets: true\n" + indent + indent + "fileTags: [\"" + libraryFileTag(module, qtProps) + "\"]\n" @@ -1482,9 +330,21 @@ function replaceSpecialValues(content, module, qtProps, abi) { for (var pos = findVariable(content, 0); pos[0] !== -1; pos = findVariable(content, pos[0])) { - var replacement = dict[content.slice(pos[0] + 1, pos[1])] || ""; - content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1); - pos[0] += replacement.length; + var varName = content.slice(pos[0] + 1, pos[1]); + var replacement = dict[varName] || ""; + if (!replacement && varName.endsWith("_optional")) { + var prevNewline = content.lastIndexOf('\n', pos[0]); + if (prevNewline === -1) + prevNewline = 0; + var nextNewline = content.indexOf('\n', pos[0]); + if (nextNewline === -1) + prevNewline = content.length; + content = content.slice(0, prevNewline) + content.slice(nextNewline); + pos[0] = prevNewline; + } else { + content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1); + pos[0] += replacement.length; + } } return content; } @@ -1493,7 +353,7 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all pluginMap, nonEssentialPlugins) { if (!File.makePath(targetDirectory)) { - throw "Cannot create directory '" + toNative(targetDirectory) + "'."; + throw "Cannot create directory '" + FileInfo.toNativeSeparators(targetDirectory) + "'."; } var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName), TextFile.ReadOnly); @@ -1515,67 +375,64 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all targetFile.close(); } -function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { - if (!File.exists(qmakeFilePath)) - throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist."; - var qtProps = getQtProperties(qmakeFilePath, qbs); - var androidAbis = []; - if (qtProps.androidAbis !== undefined) - // Multiple androidAbis detected: Qt >= 5.14 - androidAbis = qtProps.androidAbis; - else - // Single abi detected: Qt < 5.14 - androidAbis.push(''); - if (androidAbis.length > 1) - console.info("Qt with multiple abi detected: '" + androidAbis + "'"); +function setupOneQt(moduleName, qtInfo, outputBaseDir, uniquify, location) { + var qtProps = qtInfo.qtProps; var relativeSearchPaths = []; - for (a = 0; a < androidAbis.length; ++a) { - if (androidAbis.length > 1) - console.info("Configuring abi '" + androidAbis[a] + "'..."); - var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : - allQt5Modules(qtProps,androidAbis[a]); - var pluginsByType = {}; - var nonEssentialPlugins = []; - for (var i = 0; i < modules.length; ++i) { - var m = modules[i]; - if (m.isPlugin) { - if (!pluginsByType[m.pluginData.type]) - pluginsByType[m.pluginData.type] = []; - pluginsByType[m.pluginData.type].push(m.name); - if (!m.pluginData.autoLoad) - nonEssentialPlugins.push(m.name); - } - } - - var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : ""; - relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbis[a]); + for (a = 0; a < qtInfo.abiInfos.length; ++a) { + var abiInfo = qtInfo.abiInfos[a]; + var androidAbi = abiInfo.androidAbi; + if (qtInfo.abiInfos.length > 1) + console.info("Configuring abi '" + androidAbi + "'..."); + + var relativeSearchPath = uniquify ? Utilities.getHash(qtInfo.qmakeFilePath) : ""; + relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbi); var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt"); - if (File.exists(qbsQtModuleBaseDir)) - File.remove(qbsQtModuleBaseDir); + // TODO: + // if (File.exists(qbsQtModuleBaseDir)) + // File.remove(qbsQtModuleBaseDir); var allFiles = []; - copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("plugin_support.qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, - androidAbis[a], location, allFiles, undefined, pluginsByType, - nonEssentialPlugins); + if (moduleName === "plugin_support") { + copyTemplateFile("plugin_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, + androidAbi, location, allFiles, undefined, abiInfo.pluginsByType, + abiInfo.nonEssentialPlugins); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "android_support") { + // Note that it's not strictly necessary to copy this one, as it has no variable content. + // But we do it anyway for consistency. + copyTemplateFile("android_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"), + qtProps, androidAbi, location, allFiles); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "qmlcache") { + var qmlcacheStr = "qmlcache"; + if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, + "qmlcachegen" + FileInfo.executableSuffix()))) { + copyTemplateFile(qmlcacheStr + ".qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, + androidAbi, location, allFiles); + } + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; + if (abiInfo.modules[moduleName] !== undefined) { + var module = abiInfo.modules[moduleName]; var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName); var moduleTemplateFileName; + if (module.qbsName === "core") { moduleTemplateFileName = "core.qbs"; - copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "gui") { moduleTemplateFileName = "gui.qbs"; @@ -1583,62 +440,51 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { moduleTemplateFileName = "scxml.qbs"; } else if (module.qbsName === "dbus") { moduleTemplateFileName = "dbus.qbs"; - copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "qml") { moduleTemplateFileName = "qml.qbs"; - copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - var qmlcacheStr = "qmlcache"; - if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, - "qmlcachegen" + exeSuffix(qbs)))) { - copyTemplateFile(qmlcacheStr + ".qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, - androidAbis[a], location, allFiles); - } } else if (module.qbsName === "quick") { moduleTemplateFileName = "quick.qbs"; - copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.isPlugin) { moduleTemplateFileName = "plugin.qbs"; } else { moduleTemplateFileName = "module.qbs"; } - copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbis[a], + copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbi, location, allFiles, module); + relativeSearchPaths.push(relativeSearchPath); } - - // Note that it's not strictly necessary to copy this one, as it has no variable content. - // But we do it anyway for consistency. - copyTemplateFile("android_support.qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"), - qtProps, androidAbis[a], location, allFiles); - relativeSearchPaths.push(relativeSearchPath) } return relativeSearchPaths; } -function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) { - qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths, qbs); - if (!qmakeFilePaths || qmakeFilePaths.length === 0) +function doSetup(moduleName, qtInfos, outputBaseDir, location) { + if (!qtInfos || qtInfos.length === 0) return []; - var uniquifySearchPath = qmakeFilePaths.length > 1; + var uniquifySearchPath = qtInfos.length > 1; var allSearchPaths = []; - for (var i = 0; i < qmakeFilePaths.length; ++i) { + moduleName = moduleName.substring(3); + for (var i = 0; i < qtInfos.length; ++i) { try { - console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'..."); - var searchPaths = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath, - location, qbs); + console.info("Setting up Qt module '" + moduleName + "' for Qt located at '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "'."); + var searchPaths = setupOneQt(moduleName, qtInfos[i], outputBaseDir, uniquifySearchPath, + location); if (searchPaths.length > 0) { for (var j = 0; j < searchPaths.length; ++j ) allSearchPaths.push(searchPaths[j]); - console.info("Qt was set up successfully."); } } catch (e) { - console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e); + console.warn("Error setting up Qt module '" + moduleName + "' for '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "': " + e); + throw e; } } return allSearchPaths; diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index ea19bba4a..6d548f194 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -10,6 +10,7 @@ Module { version: @version@ property string qmlRootDir: product.sourceDirectory property stringList extraPrefixDirs + property stringList qmlImportPaths property stringList deploymentDependencies // qmake: ANDROID_DEPLOYMENT_DEPENDENCIES property stringList extraPlugins // qmake: ANDROID_EXTRA_PLUGINS property stringList extraLibs // qmake: ANDROID_EXTRA_LIBS @@ -19,17 +20,9 @@ Module { property string rccFilePath property string _qtBinaryDir property string _qtInstallDir - // TODO: Remove in 1.21 - // From 1.21 product property used from an export item will point to the - // importingProduct property. So using the importingProduct property will be useless - // and the change will be reverted - property var _importingProduct: (typeof importingProduct !== "undefined") ? importingProduct : - product - property bool _enableSdkSupport: _importingProduct.type - && _importingProduct.type.contains("android.package") - && !consoleApplication - property bool _enableNdkSupport: !_importingProduct.aggregate - || _importingProduct.multiplexConfigurationId + property bool _enableSdkSupport: product.type && product.type.contains("android.package") + && !product.consoleApplication + property bool _enableNdkSupport: !product.aggregate || product.multiplexConfigurationId property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android") property string _deployQtOutDir: FileInfo.joinPaths(product.buildDirectory, "deployqt_out") @@ -110,6 +103,7 @@ Module { var nativeLibs = inputs["android.nativelibrary"]; var architectures = []; var triples = []; + var qtInstallDirectories = []; var hostArch; var targetArchitecture; if (nativeLibs.length === 1) { @@ -119,6 +113,7 @@ Module { if (product.Qt.android_support._multiAbi) { architectures.push(theBinary.Android.ndk.abi); triples.push(theBinary.cpp.toolchainTriple); + qtInstallDirectories.push(theBinary.Qt.android_support._qtInstallDir); } } else { for (i = 0; i < nativeLibs.length; ++i) { @@ -127,6 +122,8 @@ Module { if (candidate.product.name === product.name) { architectures.push(candidate.Android.ndk.abi); triples.push(candidate.cpp.toolchainTriple); + qtInstallDirectories.push( + candidate.Qt.android_support._qtInstallDir); hostArch = candidate.Android.ndk.hostArch; targetArchitecture = candidate.Android.ndk.abi; theBinary = candidate; @@ -162,7 +159,20 @@ Module { f.writeLine("{"); f.writeLine('"description": "This file was generated by qbs to be read by ' + 'androiddeployqt and should not be modified by hand.",'); - f.writeLine('"qt": "' + product.Qt.android_support._qtInstallDir + '",'); + if (Utilities.versionCompare(product.Qt.core.version, "6.3.0") >= 0) { + var line = '"qt": {'; + for (var i = 0; i < qtInstallDirectories.length && i < architectures.length; + ++i) { + line = line + '"' + architectures[i] + '":"' + + qtInstallDirectories[i] + '"'; + if (i < qtInstallDirectories.length-1 || i < architectures.length-1) + line = line + ','; + } + line = line + "},"; + f.writeLine(line); + } else { + f.writeLine('"qt": "' + product.Qt.android_support._qtInstallDir + '",'); + } f.writeLine('"sdk": "' + product.Android.sdk.sdkDir + '",'); f.writeLine('"sdkBuildToolsRevision": "' + product.Android.sdk.buildToolsVersion + '",'); @@ -203,13 +213,18 @@ Module { var prefixDirs = product.Qt.android_support.extraPrefixDirs; if (prefixDirs && prefixDirs.length > 0) f.writeLine('"extraPrefixDirs": ' + JSON.stringify(prefixDirs) + ','); - if ((product.qmlImportPaths instanceof Array) && product.qmlImportPaths.length > 0) + var qmlImportPaths = product.Qt.android_support.qmlImportPaths; + if (qmlImportPaths && qmlImportPaths.length > 0) + f.writeLine('"qml-import-paths": "' + qmlImportPaths.join(',') + '",'); + else if ((product.qmlImportPaths instanceof Array) && product.qmlImportPaths.length > 0) f.writeLine('"qml-import-paths": "' + product.qmlImportPaths.join(',') + '",'); if (Utilities.versionCompare(product.Qt.android_support.version, "6.0") >= 0) { - f.writeLine('"qml-importscanner-binary": "' + - product.Qt.core.qmlImportScannerFilePath + '",'); - f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath + '",'); + f.writeLine('"qml-importscanner-binary": "' + + product.Qt.core.qmlImportScannerFilePath + FileInfo.executableSuffix() + + '",'); + f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath + + FileInfo.executableSuffix() + '",'); if (inputs["qrc"] && inputs["qrc"].length > 0) { var qrcFiles = []; diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index c3b26eb50..485402716 100644 --- a/share/qbs/module-providers/Qt/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs @@ -18,6 +18,7 @@ Module { && qbs.targetPlatform === targetPlatform + "-simulator" Depends { name: "cpp" } + Depends { name: "Sanitizers.address"; condition: config.contains("sanitize_address") } Depends { name: "Qt.android_support"; condition: qbs.targetOS.contains("android") } Properties { @@ -43,6 +44,7 @@ Module { property path installPath: @installPath@ property path incPath: @incPath@ property path libPath: @libPath@ + property path installPrefixPath: @installPrefixPath@ property path libExecPath: @libExecPath@ property path qmlLibExecPath: @qmlLibExecPath@ property path pluginPath: @pluginPath@ @@ -55,6 +57,7 @@ Module { property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3" property stringList qdocEnvironment property path docPath: @docPath@ + property string helpGeneratorLibExecPath: @helpGeneratorLibExecPath@ property stringList helpGeneratorArgs: versionMajor >= 5 ? ["-platform", "minimal"] : [] property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] property int versionMajor: versionParts[0] @@ -73,6 +76,8 @@ Module { property string qtBuildVariant: { if (availableBuildVariants.contains(qbs.buildVariant)) return qbs.buildVariant; + if (qbs.buildVariant === "profiling" && availableBuildVariants.contains("release")) + return "release"; return availableBuildVariants.length > 0 ? availableBuildVariants[0] : ""; } @@ -104,6 +109,7 @@ Module { property string libFilePathRelease: @libFilePathRelease@ property string libFilePath: qtBuildVariant === "debug" ? libFilePathDebug : libFilePathRelease + property bool useRPaths: qbs.targetOS.contains("linux") && !qbs.targetOS.contains("android") property stringList coreLibPaths: @libraryPaths@ property bool hasLibrary: true @@ -117,6 +123,7 @@ Module { property bool lreleaseMultiplexMode: false property stringList moduleConfig: @moduleConfig@ + Properties { condition: moduleConfig.contains("use_gold_linker") cpp.linkerVariant: "gold" @@ -145,13 +152,13 @@ Module { if (Utilities.versionCompare(version, "5.6.0") < 0) defines.push("main=qtmn"); } + if (qbs.toolchain.contains("msvc")) + defines.push("_ENABLE_EXTENDED_ALIGNED_STORAGE"); return defines; } cpp.driverFlags: { var flags = []; if (qbs.toolchain.contains("gcc")) { - if (config.contains("sanitize_address")) - flags.push("-fsanitize=address"); if (config.contains("sanitize_undefined")) flags.push("-fsanitize=undefined"); if (config.contains("sanitize_thread")) @@ -161,11 +168,8 @@ Module { } return flags; } - cpp.includePaths: { - var paths = @includes@; - paths.push(mkspecPath, generatedHeadersDir); - return paths; - } + cpp.includePaths: generatedHeadersDir + cpp.systemIncludePaths: @includes@.concat(mkspecPath) cpp.libraryPaths: { var libPaths = [libPath]; if (staticBuild && pluginPath) @@ -184,7 +188,7 @@ Module { } cpp.dynamicLibraries: dynamicLibs cpp.linkerFlags: coreLinkerFlags - cpp.frameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : []) + cpp.systemFrameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : []) cpp.frameworks: { var frameworks = coreFrameworks if (frameworkBuild) @@ -195,19 +199,21 @@ Module { return undefined; return frameworks; } - cpp.rpaths: qbs.targetOS.contains('linux') && !qbs.targetOS.contains("android") ? [libPath] : - undefined + cpp.rpaths: useRPaths ? libPath : undefined cpp.runtimeLibrary: qbs.toolchain.contains("msvc") ? config.contains("static_runtime") ? "static" : "dynamic" : original - cpp.positionIndependentCode: versionMajor >= 5 ? true : undefined + cpp.positionIndependentCode: versionMajor >= 5 ? true : original cpp.cxxFlags: { var flags = []; if (qbs.toolchain.contains('msvc')) { if (versionMajor < 5) flags.push('/Zc:wchar_t-'); + if (Utilities.versionCompare(version, "6.3") >= 0 + && Utilities.versionCompare(cpp.compilerVersion, "19.10") >= 0) { + flags.push("/permissive-"); + } } - return flags; } cpp.cxxStandardLibrary: { @@ -216,12 +222,12 @@ Module { return "libc++"; return original; } - cpp.minimumWindowsVersion: @minWinVersion@ - cpp.minimumMacosVersion: @minMacVersion@ - cpp.minimumIosVersion: @minIosVersion@ - cpp.minimumTvosVersion: @minTvosVersion@ - cpp.minimumWatchosVersion: @minWatchosVersion@ - cpp.minimumAndroidVersion: @minAndroidVersion@ + cpp.minimumWindowsVersion: @minWinVersion_optional@ + cpp.minimumMacosVersion: @minMacVersion_optional@ + cpp.minimumIosVersion: @minIosVersion_optional@ + cpp.minimumTvosVersion: @minTvosVersion_optional@ + cpp.minimumWatchosVersion: @minWatchosVersion_optional@ + cpp.minimumAndroidVersion: @minAndroidVersion_optional@ // Universal Windows Platform support cpp.windowsApiFamily: mkspecName.startsWith("winrt-") ? "pc" : undefined @@ -502,7 +508,7 @@ Module { var args = ['-silent', '-qm', output.filePath].concat(inputFilePaths); var cmd = new Command(product.Qt.core.binPath + '/' + product.Qt.core.lreleaseName, args); - cmd.description = 'Creating ' + output.fileName; + cmd.description = 'creating ' + output.fileName; cmd.highlight = 'filegen'; return cmd; } @@ -542,7 +548,8 @@ Module { args = args.concat(product.Qt.core.helpGeneratorArgs); args.push("-o"); args.push(output.filePath); - var cmd = new Command(product.Qt.core.binPath + "/qhelpgenerator", args); + var cmd = new Command( + product.Qt.core.helpGeneratorLibExecPath + "/qhelpgenerator", args); cmd.description = 'qhelpgenerator ' + input.fileName; cmd.highlight = 'filegen'; cmd.stdoutFilterFunction = function(output) { diff --git a/share/qbs/module-providers/Qt/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs index 90bbe2864..bbda5a4f2 100644 --- a/share/qbs/module-providers/Qt/templates/dbus.qbs +++ b/share/qbs/module-providers/Qt/templates/dbus.qbs @@ -1,6 +1,5 @@ import qbs.FileInfo import qbs.ModUtils -import "../QtModule.qbs" as QtModule import "dbus.js" as DBus QtModule { @@ -62,7 +61,7 @@ QtModule { moduleConfig: @moduleConfig@ cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ @additionalContent@ diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs index 1d45c6f1e..db491eafe 100644 --- a/share/qbs/module-providers/Qt/templates/gui.qbs +++ b/share/qbs/module-providers/Qt/templates/gui.qbs @@ -1,7 +1,6 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Utilities -import '../QtModule.qbs' as QtModule QtModule { qtModuleName: "Gui" @@ -28,7 +27,7 @@ QtModule { : product.Qt.core.libExecPath + '/' + product.Qt.gui.uicName; var cmd = new Command(uicPath, [input.filePath, '-o', output.filePath]); - cmd.description = 'uic ' + input.fileName; + cmd.description = 'generating ' + output.fileName; cmd.highlight = 'codegen'; return cmd; } @@ -59,7 +58,7 @@ QtModule { : undefined cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ Properties { diff --git a/share/qbs/module-providers/Qt/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs index b09f79a87..9f0313ab5 100644 --- a/share/qbs/module-providers/Qt/templates/module.qbs +++ b/share/qbs/module-providers/Qt/templates/module.qbs @@ -1,5 +1,3 @@ -import '../QtModule.qbs' as QtModule - QtModule { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} @@ -24,7 +22,7 @@ QtModule { pluginTypes: @pluginTypes@ moduleConfig: @moduleConfig@ cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ @additionalContent@ } diff --git a/share/qbs/module-providers/Qt/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs index e73e2a4d9..34d4f4153 100644 --- a/share/qbs/module-providers/Qt/templates/plugin.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin.qbs @@ -1,5 +1,3 @@ -import '../QtPlugin.qbs' as QtPlugin - QtPlugin { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} diff --git a/share/qbs/module-providers/Qt/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js index ea8293f2d..38462dcf7 100644 --- a/share/qbs/module-providers/Qt/templates/qml.js +++ b/share/qbs/module-providers/Qt/templates/qml.js @@ -39,16 +39,32 @@ function getPrlRhs(line) return line.split('=')[1].trim(); } -function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDir) +function getLibsForPlugin(pluginData, product) { + var targetOS = product.qbs.targetOS; + var toolchain = product.qbs.toolchain; + var buildVariant = product.Qt.core.qtBuildVariant; + var qtLibDir = product.Qt.core.libPath; + var qtPluginDir = product.Qt.core.pluginPath; + var qtDir = product.Qt.core.installPrefixPath; + var qtQmlPath = product.Qt.qml.qmlPath; + if (!pluginData.path) return ""; var prlFileName = ""; if (!targetOS.contains("windows")) prlFileName += "lib"; prlFileName += pluginData.plugin; - if (buildVariant === "debug" && targetOS.contains("windows")) - prlFileName += "d"; + if (buildVariant === "debug") { + if (targetOS.contains("windows")) { + prlFileName += "d"; + } else if (product.Qt.core.versionMajor >= 6 && + (targetOS.contains("ios") + || targetOS.contains("tvos") + || targetOS.contains("watchos"))) { + prlFileName += "_debug"; + } + } prlFileName += ".prl"; var prlFilePath = FileInfo.joinPaths(pluginData.path, prlFileName); if (!File.exists(prlFilePath)) { @@ -59,7 +75,7 @@ function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDi var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); try { var pluginLib; - var otherLibs = ""; + var otherLibs = []; var line; while (!prlFile.atEof()) { line = prlFile.readLine().trim(); @@ -74,12 +90,15 @@ function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDi otherLibsLine = otherLibsLine.replace(/-l([^ ]+)/g, "$1" + ".lib"); } otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_LIBS\]/g, qtLibDir); - otherLibs += otherLibsLine + '\n'; + otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PLUGINS\]/g, qtPluginDir); + otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PREFIX\]/g, qtDir); + otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_QML\]/g, qtQmlPath); + otherLibs = otherLibs.concat(otherLibsLine.split(' ').map(FileInfo.cleanPath)); } } if (!pluginLib) throw "Malformed prl file '" + prlFilePath + "'."; - return pluginLib + ' ' + otherLibs; + return [pluginLib].concat(otherLibs); } finally { prlFile.close(); } diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs index 9b4dfa36c..0a938e58d 100644 --- a/share/qbs/module-providers/Qt/templates/qml.qbs +++ b/share/qbs/module-providers/Qt/templates/qml.qbs @@ -1,5 +1,6 @@ +import qbs.FileInfo +import qbs.Host import qbs.TextFile -import '../QtModule.qbs' as QtModule import "qml.js" as Qml QtModule { @@ -46,7 +47,7 @@ QtModule { pluginTypes: @pluginTypes@ moduleConfig: @moduleConfig@ cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ @additionalContent@ @@ -135,7 +136,7 @@ QtModule { prepare: { var cmd = new JavaScriptCommand(); if (inputs["qt.qml.qml"]) - cmd.description = "Creating " + outputs["cpp"][0].fileName; + cmd.description = "creating " + outputs["cpp"][0].fileName; else cmd.silent = true; cmd.sourceCode = function() { @@ -144,7 +145,7 @@ QtModule { qmlInputs = []; var scannerData = Qml.scannerData(product.Qt.qml.qmlImportScannerFilePath, qmlInputs.map(function(inp) { return inp.filePath; }), - product.Qt.qml.qmlPath, product.qbs.hostOS); + product.Qt.qml.qmlPath, Host.os()); var cppFile; var listFile; try { @@ -155,6 +156,7 @@ QtModule { if (cppFile) cppFile.writeLine("#include <QtPlugin>"); var plugins = { }; + var libsWithUniqueObjects = []; for (var p in scannerData) { var plugin = scannerData[p].plugin; if (!plugin || plugins[plugin]) @@ -167,13 +169,17 @@ QtModule { } if (cppFile) cppFile.writeLine("Q_IMPORT_PLUGIN(" + className + ")"); - var libs = Qml.getLibsForPlugin(scannerData[p], - product.Qt.core.qtBuildVariant, - product.qbs.targetOS, - product.qbs.toolchain, - product.Qt.core.libPath); - listFile.write(libs + ' '); + var libs = Qml.getLibsForPlugin(scannerData[p], product); + for (var i = 0; i < libs.length; ++i) { + var lib = libs[i]; + if (!lib.endsWith(product.cpp.objectSuffix) + || (!libsWithUniqueObjects.contains(lib) + && !product.cpp.staticLibraries.contains(FileInfo.cleanPath(lib)))) { + libsWithUniqueObjects.push(lib); + } + } } + listFile.write(libsWithUniqueObjects.join("\n")); } finally { if (cppFile) cppFile.close(); diff --git a/share/qbs/module-providers/Qt/templates/qmlcache.qbs b/share/qbs/module-providers/Qt/templates/qmlcache.qbs index 38338d106..7047884c0 100644 --- a/share/qbs/module-providers/Qt/templates/qmlcache.qbs +++ b/share/qbs/module-providers/Qt/templates/qmlcache.qbs @@ -10,7 +10,7 @@ Module { throw "qmlcachegen unsupported for this target"; } property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.qmlLibExecPath, "qmlcachegen") - + (qbs.hostOS.contains("windows") ? ".exe" : "") + + FileInfo.executableSuffix() property bool supportsAllArchitectures: Utilities.versionCompare(Qt.core.version, "5.11") >= 0 property string installDir diff --git a/share/qbs/module-providers/Qt/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js index ec1402345..d7e58984f 100644 --- a/share/qbs/module-providers/Qt/templates/quick.js +++ b/share/qbs/module-providers/Qt/templates/quick.js @@ -37,7 +37,7 @@ function scanQrc(product, qrcFilePath) { var result = []; var process = new Process(); try { - var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + product.cpp.executableSuffix); + var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + FileInfo.executableSuffix()); var exitCode = process.exec(rcc, ["--list", qrcFilePath], true); for (;;) { var line = process.readLine(); @@ -70,7 +70,7 @@ function contentFromQrc(product, qrcFilePath) { var supportsFiltering = product.Qt.quick._supportsQmlJsFiltering; var filesInQrc = scanQrc(product, qrcFilePath); var qmlJsFiles = filesInQrc.filter(function (filePath) { - return (/\.(js|qml)$/).test(filePath); + return (/\.(mjs|js|qml)$/).test(filePath); } ); var content = {}; if (!supportsFiltering || filesInQrc.length - qmlJsFiles.length > 0) { diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs index 00174150e..5fc4aa349 100644 --- a/share/qbs/module-providers/Qt/templates/quick.qbs +++ b/share/qbs/module-providers/Qt/templates/quick.qbs @@ -33,7 +33,6 @@ import qbs.FileInfo import qbs.Process import qbs.TextFile import qbs.Utilities -import '../QtModule.qbs' as QtModule import 'quick.js' as QC QtModule { @@ -60,7 +59,7 @@ QtModule { pluginTypes: @pluginTypes@ moduleConfig: @moduleConfig@ cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ @additionalContent@ @@ -71,9 +70,12 @@ QtModule { readonly property string _generatedLoaderFileName: _compilerIsQmlCacheGen ? "qmlcache_loader.cpp" : "qtquickcompiler_loader.cpp" + property string _compilerBaseDir: _compilerIsQmlCacheGen ? Qt.core.qmlLibExecPath + : Qt.core.binPath property string compilerBaseName: (_compilerIsQmlCacheGen ? "qmlcachegen" : "qtquickcompiler") - property string compilerFilePath: FileInfo.joinPaths(Qt.core.binPath, - compilerBaseName + product.cpp.executableSuffix) + property string compilerFilePath: FileInfo.joinPaths(_compilerBaseDir, + compilerBaseName + FileInfo.executableSuffix()) + property bool compilerAvailable: File.exists(compilerFilePath); property bool useCompiler: compilerAvailable && !_compilerIsQmlCacheGen diff --git a/share/qbs/module-providers/Qt/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs index 7125ec53c..757041b52 100644 --- a/share/qbs/module-providers/Qt/templates/scxml.qbs +++ b/share/qbs/module-providers/Qt/templates/scxml.qbs @@ -1,10 +1,11 @@ import qbs.FileInfo import qbs.Utilities -import "../QtModule.qbs" as QtModule QtModule { qtModuleName: "Scxml" + property string _qscxmlcDir: Utilities.versionCompare(Qt.core.version, "6.3") >= 0 + ? Qt.core.libExecPath : Qt.core.binPath property string qscxmlcName: "qscxmlc" property string className property string namespace @@ -26,8 +27,7 @@ QtModule { prepare: { var compilerName = product.moduleProperty("Qt.scxml", "qscxmlcName"); - var compilerPath = FileInfo.joinPaths(input.moduleProperty("Qt.core", "binPath"), - compilerName); + var compilerPath = FileInfo.joinPaths(input.Qt.scxml._qscxmlcDir, compilerName); var args = ["--header", outputs["hpp"][0].filePath, "--impl", outputs["cpp"][0].filePath]; var cxx98 = input.moduleProperty("cpp", "cxxLanguageVersion") === "c++98"; @@ -73,7 +73,7 @@ QtModule { moduleConfig: @moduleConfig@ cpp.defines: @defines@ - cpp.includePaths: @includes@ + cpp.systemIncludePaths: @includes@ cpp.libraryPaths: @libraryPaths@ @additionalContent@ diff --git a/share/qbs/module-providers/__fallback/fallback.qbs b/share/qbs/module-providers/__fallback/fallback.qbs index 52a3f080a..349af3a52 100644 --- a/share/qbs/module-providers/__fallback/fallback.qbs +++ b/share/qbs/module-providers/__fallback/fallback.qbs @@ -46,6 +46,8 @@ Module { property string theName: FileInfo.completeBaseName(filePath) + readonly property bool __fallback: true // Hack, please look away + Probes.PkgConfigProbe { id: pkgConfigProbe condition: pkgconfig.present diff --git a/share/qbs/module-providers/conan.js b/share/qbs/module-providers/conan.js new file mode 100644 index 000000000..a44af7eec --- /dev/null +++ b/share/qbs/module-providers/conan.js @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Kai Dohmen +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com). +** 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 File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathProbe = require("../imports/qbs/Probes/path-probe.js") +var PathTools = require("qbs.PathTools"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +const architectureMap = { + 'x86': 'x86', + 'x86_64': 'x86_64', + 'ppc32be': 'ppc', + 'ppc32': 'ppc', + 'ppc64le': 'ppc64', + 'ppc64': 'ppc64', + 'armv4': 'arm', + 'armv4i': 'arm', + 'armv5el': 'arm', + 'armv5hf': 'arm', + 'armv6': 'arm', + 'armv7': 'arm', + 'armv7hf': 'arm', + 'armv7s': 'arm', + 'armv7k': 'arm', + 'armv8': 'arm64', + 'armv8_32': 'arm64', + 'armv8.3': 'arm64', + 'sparc': 'sparc', + 'sparcv9': 'sparc64', + 'mips': 'mips', + 'mips64': 'mips64', + 'avr': 'avr', + 's390': 's390x', + 's390x': 's390x', + 'sh4le': 'sh' +} + +const platformMap = { + 'Windows': 'windows', + 'WindowsStore': 'windows', + 'WindowsCE': 'windows', + 'Linux': 'linux', + 'Macos': 'macos', + 'Android': 'android', + 'iOS': 'ios', + 'watchOS': 'watchos', + 'tvOS': 'tvos', + 'FreeBSD': 'freebsd', + 'SunOS': 'solaris', + 'AIX': 'aix', + 'Emscripten': undefined, + 'Arduino': 'none', + 'Neutrino': 'qnx', + 'baremetal': 'none', + 'VxWorks': 'vxworks', +} + +function findLibs(libnames, libdirs, libtypes, targetOS, forImport) { + var result = []; + if (libnames === null || libdirs === null) + return result; + libnames.forEach(function(libraryName) { + const suffixes = PathTools.librarySuffixes(targetOS, libtypes, forImport); + const lib = PathProbe.configure( + undefined, // selectors + [libraryName], + suffixes, + PathTools.libraryNameFilter(targetOS), + undefined, // candidateFilter + libdirs, // searchPaths + undefined, // pathSuffixes + [], // platformSearchPaths + undefined, // environmentPaths + undefined // platformEnvironmentPaths + ); + if (lib.found) + result.push(lib.files[0].filePath); + }); + return result; +} + +function getLibraryTypes(options) { + if (options !== undefined && options !== null) { + if (options.shared === undefined) + return ["shared", "static"]; + else if (options.shared === "True") + return ["shared"]; + else if (options.shared === "False") + return ["static"]; + } + return ["shared", "static"]; +} + +function configure(installDirectory, moduleName, outputBaseDir, jsonProbe) { + const moduleMapping = {"protobuflib": "protobuf"} + const realModuleName = moduleMapping[moduleName] || moduleName; + + const moduleFilePath = + FileInfo.joinPaths(installDirectory, "conan-qbs-deps", realModuleName + ".json") + if (!File.exists(moduleFilePath)) + return []; + + var reverseMapping = {} + for (var key in moduleMapping) + reverseMapping[moduleMapping[key]] = key + + console.info("Setting up Conan module '" + moduleName + "'"); + + var moduleFile = new TextFile(moduleFilePath, TextFile.ReadOnly); + const moduleInfo = JSON.parse(moduleFile.readAll()); + + const outputDir = FileInfo.joinPaths(outputBaseDir, "modules", moduleName.replace(".", "/")); + File.makePath(outputDir); + const outputFilePath = FileInfo.joinPaths(outputDir, "module.qbs"); + + const cppInfo = moduleInfo.cpp_info; + + var moduleContent = ""; + + // function write(data) { moduleContent = moduleContent + data } + function writeLine(data) { moduleContent = moduleContent + data + "\n"; } + + function writeProperty(propertyName, propertyValue) { + if (propertyValue === undefined || propertyValue === null) + propertyValue = []; + writeLine(" readonly property stringList " + propertyName + + ": " + ModUtils.toJSLiteral(propertyValue)); + } + function writeCppProperty(propertyName, propertyValue) { + writeProperty(propertyName, propertyValue); + // skip empty props for simplicity of the module file + if (propertyValue !== undefined && propertyValue != null && propertyValue.length !== 0) { + writeLine(" cpp." + propertyName + ": " + propertyName); + } + } + + writeLine("Module {"); + + writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + + const architecture = architectureMap[moduleInfo.settings.arch]; + const platform = platformMap[moduleInfo.settings.os]; + const targetOS = Utilities.canonicalPlatform(platform); + + writeLine(" readonly property string architecture: " + ModUtils.toJSLiteral(architecture)); + writeLine(" readonly property string platform: " + ModUtils.toJSLiteral(platform)); + writeLine(" condition: true"); + if (architecture !== undefined) { + writeLine(" && (!qbs.architecture || qbs.architecture === architecture)"); + } + if (platform !== undefined) { + if (["ios", "tvos", "watchos"].includes(platform)) { + writeLine(" && (qbs.targetPlatform === platform || qbs.targetPlatform === platform + \"-simulator\")"); + } else { + writeLine(" && qbs.targetPlatform === platform"); + } + } + + writeLine(" Depends { name: 'cpp' }"); + + moduleInfo.dependencies.forEach(function(dep) { + const realDepName = reverseMapping[dep.name] || dep.name; + writeLine(" Depends { name: " + ModUtils.toJSLiteral(realDepName) + + "; version: " + ModUtils.toJSLiteral(dep.version) + "}"); + }); + + writeLine(" readonly property stringList hostBinDirs: (" + ModUtils.toJSLiteral(moduleInfo.build_bindirs) + ")"); + // target bindirs + writeLine(" readonly property stringList binDirs: (" + ModUtils.toJSLiteral(cppInfo.bindirs) + ")"); + + // TODO: there's a weird issue with system include dirs with xcode-less clang - incorrect include order? + writeCppProperty("includePaths", cppInfo.includedirs); + writeCppProperty("frameworkPaths", cppInfo.frameworkdirs); + writeCppProperty("frameworks", cppInfo.frameworks); + writeCppProperty("defines", cppInfo.defines); + writeCppProperty("cFlags", cppInfo.cflags); + writeCppProperty("cxxFlags", cppInfo.cxxflags); + writeCppProperty("linkerFlags", (cppInfo.sharedlinkflags || []).concat(cppInfo.exelinkflags || [])); + + writeProperty("resources", cppInfo.resdirs); + + const isForImport = targetOS.includes("windows") + const libraryTypes = getLibraryTypes(moduleInfo.options); + libraryTypes.forEach(function(libraryType){ + const cppInfoLibs = cppInfo.libs === null ? [] : cppInfo.libs; + const libs = findLibs(cppInfoLibs, cppInfo.libdirs, libraryType, targetOS, isForImport) + .concat(cppInfo.system_libs === null ? [] : cppInfo.system_libs); + const property = libraryType === "shared" ? "dynamicLibraries" : "staticLibraries"; + writeCppProperty(property, libs); + }); + + writeLine("}"); + + var moduleFile = new TextFile(outputFilePath, TextFile.WriteOnly); + moduleFile.write(moduleContent); + moduleFile.close(); + + return ""; +} diff --git a/share/qbs/module-providers/conan.qbs b/share/qbs/module-providers/conan.qbs new file mode 100644 index 000000000..6391eeb65 --- /dev/null +++ b/share/qbs/module-providers/conan.qbs @@ -0,0 +1,12 @@ +import "conan.js" as ConanHelper + +ModuleProvider { + /* input */ + property path installDirectory + + isEager: false + + relativeSearchPaths: { + return ConanHelper.configure(installDirectory, moduleName, outputBaseDir); + } +} diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs index 52344d0b8..885a6da7a 100644 --- a/share/qbs/module-providers/qbspkgconfig.qbs +++ b/share/qbs/module-providers/qbspkgconfig.qbs @@ -40,46 +40,54 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.PkgConfig +import qbs.ProviderUtils +import qbs.Probes import qbs.Process import qbs.TextFile +import "Qt/setup-qt.js" as SetupQt + ModuleProvider { - property string executableFilePath + property stringList executableNames: ["pkgconf", "pkg-config"] + property string executableFilePath: pkgConfigProbe.filePath property stringList extraPaths property stringList libDirs property bool staticMode: false - property path sysroot: { - if (qbs.targetOS.contains("macos")) - return ""; - return qbs.sysroot; + property bool definePrefix: Host.os().includes("windows") + + // We take the sysroot default from qbs.sysroot, except for Xcode toolchains, where + // the sysroot points into the Xcode installation and does not contain .pc files. + property path sysroot: qbs.toolchain && qbs.toolchain.includes("xcode") + ? undefined : qbs.sysroot + + Probes.BinaryProbe { + id: pkgConfigProbe + condition: !executableFilePath + names: executableNames } - property bool mergeDependencies: true - relativeSearchPaths: { + Probes.QbsPkgConfigProbe { + id: theProbe + _executableFilePath: executableFilePath + _extraPaths: extraPaths + _sysroot: sysroot + _libDirs: libDirs + _staticMode: staticMode + _definePrefix: definePrefix + } - // we need Probes in Providers... - function getPkgConfigExecutable(qbs) { - function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } - function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; } - - var pathValue = Environment.getEnv("PATH"); - if (!pathValue) - return undefined; - var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator); - var suffix = exeSuffix(qbs); - var filePaths = []; - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "pkg-config" + suffix); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - return canonicalCandidate; - } - return undefined; - } + Probes.QmakeProbe { + id: qmakeProbe + condition: moduleName.startsWith("Qt") && theProbe.qmakePaths + qmakePaths: theProbe.qmakePaths + } + + isEager: false + relativeSearchPaths: { function getModuleInfo(pkg, staticMode) { var result = {}; @@ -121,14 +129,12 @@ ModuleProvider { return result; } - function getModuleName(packageName) { return packageName.replace('.', '-'); } - function getModuleDependencies(pkg, staticMode) { var mapper = function(p) { var result = {}; for (var key in p) result[key] = p[key]; - result.name = getModuleName(result.name); + result.name = ProviderUtils.pkgConfigToModuleName(result.name); return result; } var result = pkg.requires.map(mapper); @@ -137,95 +143,83 @@ ModuleProvider { return result; } - console.debug("Running pkgconfig provider.") + console.debug("Running pkgconfig provider for " + moduleName + "."); var outputDir = FileInfo.joinPaths(outputBaseDir, "modules"); File.makePath(outputDir); - var options = {}; - options.libDirs = libDirs; - options.sysroot = sysroot; - options.staticMode = staticMode; - options.mergeDependencies = mergeDependencies; - options.extraPaths = extraPaths; - if (options.sysroot && !options.libDirs) { - options.libDirs = [ - sysroot + "/usr/lib/pkgconfig", - sysroot + "/usr/share/pkgconfig" - ]; + var moduleMapping = { + "protobuf": "protobuflib" } - if (!options.libDirs) { - // if we have pkg-config installed, let's ask it for its search paths (since - // built-in search paths can differ between platforms) - var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(qbs); - if (executable) { - var p = new Process() - if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { - var stdout = p.readStdOut().trim(); - // TODO: qbs.pathListSeparator? depends on what pkg-config prints on Windows - options.libDirs = stdout ? stdout.split(':'): []; - } + var reverseMapping = {} + for (var key in moduleMapping) + reverseMapping[moduleMapping[key]] = key + + if (moduleName.startsWith("Qt")) { + function setupQt(packageName, qtInfos) { + if (qtInfos === undefined || qtInfos.length === 0) + return []; + var qtProviderDir = FileInfo.joinPaths(path, "Qt"); + return SetupQt.doSetup(packageName, qtInfos, outputBaseDir, qtProviderDir); } - } - var pkgConfig = new PkgConfig(options); - - var brokenPackages = []; - var packages = pkgConfig.packages(); - for (var packageName in packages) { - var pkg = packages[packageName]; - if (pkg.isBroken) { - brokenPackages.push(pkg); - continue; - } - var moduleName = getModuleName(packageName); - var moduleInfo = getModuleInfo(pkg, staticMode); - var deps = getModuleDependencies(pkg, staticMode); - - var moduleDir = FileInfo.joinPaths(outputDir, moduleName); - File.makePath(moduleDir); - var module = - new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); - module.writeLine("Module {"); - module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); - module.writeLine(" Depends { name: 'cpp' }"); - deps.forEach(function(dep) { - module.write(" Depends { name: '" + dep.name + "'"); - for (var k in dep) { - if (k === "name") - continue; - module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); - } - module.writeLine(" }"); - }) - function writeProperty(propertyName) { - var value = moduleInfo[propertyName]; - if (value.length !== 0) { // skip empty props for simplicity of the module file - module.writeLine( - " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); - } + if (!sysroot) { + return setupQt(moduleName, qmakeProbe.qtInfos); } - writeProperty("includePaths"); - writeProperty("systemIncludePaths"); - writeProperty("defines"); - writeProperty("commonCompilerFlags"); - writeProperty("dynamicLibraries"); - writeProperty("staticLibraries"); - writeProperty("libraryPaths"); - writeProperty("frameworks"); - writeProperty("frameworkPaths"); - writeProperty("driverLinkerFlags"); - module.writeLine("}"); - module.close(); + return []; } - if (brokenPackages.length !== 0) { - console.warn("Failed to load some pkg-config packages:"); - for (var i = 0; i < brokenPackages.length; ++i) { - console.warn(" " + brokenPackages[i].filePath - + ": " + brokenPackages[i].errorText); + var pkg; + pkg = theProbe.packages[reverseMapping[moduleName]]; + if (pkg === undefined) + pkg = theProbe.packagesByModuleName[moduleName]; + if (pkg === undefined) + return []; + + if (pkg.isBroken) { + console.warn("Failed to load " + moduleName + " as it's pkg-config package is broken"); + return []; + } + var moduleInfo = getModuleInfo(pkg, staticMode); + var deps = getModuleDependencies(pkg, staticMode); + + var moduleDir = FileInfo.joinPaths(outputDir, moduleName); + File.makePath(moduleDir); + var module = + new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); + module.writeLine("Module {"); + module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + module.writeLine(" Depends { name: 'cpp' }"); + deps.forEach(function(dep) { + var depName = ProviderUtils.pkgConfigToModuleName( + moduleMapping[dep.name] ? moduleMapping[dep.name] : dep.name); + module.write(" Depends { name: '" + depName + "'"); + for (var k in dep) { + if (k === "name") + continue; + module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); + } + module.writeLine(" }"); + }) + function writeProperty(propertyName) { + var value = moduleInfo[propertyName]; + if (value.length !== 0) { // skip empty props for simplicity of the module file + module.writeLine( + " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); } } + writeProperty("includePaths"); + writeProperty("systemIncludePaths"); + writeProperty("defines"); + writeProperty("commonCompilerFlags"); + writeProperty("dynamicLibraries"); + writeProperty("staticLibraries"); + writeProperty("libraryPaths"); + writeProperty("frameworks"); + writeProperty("frameworkPaths"); + writeProperty("driverLinkerFlags"); + module.writeLine("}"); + module.close(); return ""; } diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index c52b54664..440448dcf 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -52,7 +52,7 @@ Module { Probes.PathProbe { id: bundletoolProbe - platformSearchPaths: [Android.sdk.sdkDir] + platformSearchPaths: [sdkDir] names: ["bundletool-all"] nameSuffixes: ["-0.11.0.jar", "-0.12.0.jar", "-0.13.0.jar", "-0.13.3.jar", "-0.13.4.jar", "-0.14.0.jar", "-0.15.0.jar", "-1.0.0.jar", "-1.1.0.jar", "-1.2.0.jar", "-1.3.0.jar", @@ -102,36 +102,36 @@ Module { Group { name: "java sources" - condition: Android.sdk.automaticSources - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.sourcesDir + '/') + condition: product.Android.sdk.automaticSources + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.sourcesDir + '/') files: "**/*.java" } Group { name: "android resources" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.resources"] - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.resourcesDir + '/') + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.resourcesDir + '/') files: "**/*" } Group { name: "android assets" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.assets"] - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.assetsDir + '/') + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.assetsDir + '/') files: "**/*" } Group { name: "manifest" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.manifest"] - files: Android.sdk.manifestFile - && Android.sdk.manifestFile !== Android.sdk.defaultManifestFile - ? [Android.sdk.manifestFile] - : (File.exists(Android.sdk.defaultManifestFile) - ? [Android.sdk.defaultManifestFile] : []) + files: product.Android.sdk.manifestFile + && product.Android.sdk.manifestFile !== product.Android.sdk.defaultManifestFile + ? [product.Android.sdk.manifestFile] + : (File.exists(product.Android.sdk.defaultManifestFile) + ? [product.Android.sdk.defaultManifestFile] : []) } @@ -196,8 +196,8 @@ Module { java.languageVersion: platformJavaVersion java.runtimeVersion: platformJavaVersion java.bootClassPaths: androidJarFilePath - codesign.apksignerFilePath: Android.sdk.apksignerFilePath - codesign._packageName: Android.sdk.apkBaseName + (_generateAab ? ".aab" : ".apk") + codesign.apksignerFilePath: apksignerFilePath + codesign._packageName: apkBaseName + (_generateAab ? ".aab" : ".apk") codesign.useApksigner: !_generateAab } @@ -256,14 +256,14 @@ Module { args.push("-I" + aidlSearchPaths[i]); args.push(input.filePath, output.filePath); var cmd = new Command(aidl, args); - cmd.description = "Processing " + input.fileName; + cmd.description = "processing " + input.fileName; return [cmd]; } } property bool customManifestProcessing: false Group { - condition: !Android.sdk.customManifestProcessing + condition: !product.Android.sdk.customManifestProcessing fileTagsFilter: "android.manifest_processed" fileTags: "android.manifest_final" } @@ -276,7 +276,7 @@ Module { } prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Ensuring correct package name in Android manifest file"; + cmd.description = "ensuring correct package name in Android manifest file"; cmd.sourceCode = function() { var manifestData = new Xml.DomDocument(); manifestData.load(input.filePath); @@ -421,7 +421,7 @@ Module { prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Generating BuildConfig.java"; + cmd.description = "generating BuildConfig.java"; cmd.sourceCode = function() { var debugValue = product.qbs.buildVariant === "debug" ? "true" : "false"; var ofile = new TextFile(output.filePath, TextFile.WriteOnly); diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index d217d113b..232ee5a30 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -84,13 +84,20 @@ function prepareDex(project, product, inputs, outputs, input, output, explicitly return; dep.artifacts["java.jar"].forEach(function(artifact) { - if (!jarFiles.contains(artifact.filePath)) + if (!jarFiles.includes(artifact.filePath)) jarFiles.push(artifact.filePath); }); dep.dependencies.forEach(traverseJarDeps); } product.dependencies.forEach(traverseJarDeps); + if (typeof product.artifacts["java.jar"] !== "undefined") { + product.artifacts["java.jar"].forEach(function(artifact) { + if (!jarFiles.includes(artifact.filePath)) + jarFiles.push(artifact.filePath); + }); + } + args = args.concat(jarFiles); var cmd; @@ -159,7 +166,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output, throw "File '" + resources[i].filePath + "' is tagged as an Android resource, " + "but is not located under a directory called 'res'."; } - if (!resourceDirs.contains(resDir)) + if (!resourceDirs.includes(resDir)) resourceDirs.push(resDir); } } @@ -174,7 +181,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output, throw "File '" + assets[i].filePath + "' is tagged as an Android asset, " + "but is not located under a directory called 'assets'."; } - if (!assetDirs.contains(assetDir)) + if (!assetDirs.includes(assetDir)) assetDirs.push(assetDir); } } @@ -258,7 +265,7 @@ function prepareAapt2Link(project, product, inputs, outputs, input, output, expl throw "File '" + assets[i].filePath + "' is tagged as an Android asset, " + "but is not located under a directory called 'assets'."; } - if (!assetDirs.contains(assetDir)) + if (!assetDirs.includes(assetDir)) assetDirs.push(assetDir); } } @@ -422,7 +429,7 @@ function stlDeploymentData(product, inputs, type) return data; for (var i = 0; i < theInputs.length; ++i) { var currentInput = theInputs[i]; - if (uniqueFilePaths.contains(currentInput.filePath)) + if (uniqueFilePaths.includes(currentInput.filePath)) continue; uniqueFilePaths.push(currentInput.filePath); data.uniqueInputs.push(currentInput); diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.js b/share/qbs/modules/Exporter/cmake/cmakeexporter.js new file mode 100644 index 000000000..093032f2e --- /dev/null +++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.js @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com> +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var ExporterHelpers = require("../exporter.js"); + +function tagListToString(tagList) +{ + return JSON.stringify(tagList); +} + +function collectAutodetectedData(project, topLevelProduct, outputs) +{ + var packageName = topLevelProduct.Exporter.cmake.packageName; + + var data = {}; + data.packageName = packageName; + data.installPrefixDir = "_" + packageName.toUpperCase() + "_INSTALL_PREFIX"; + data.packages = []; + + function quote(value) + { + return "\"" + value + "\""; + } + + function quoteAndPrefixify(propName) + { + function quoteAndPrefixifyHelper(value) { + var prefixToStrip = + ExporterHelpers.getPrefixToStrip(project, topLevelProduct, propName, value); + if (typeof value !== "string" + || !prefixToStrip + || (value.length > prefixToStrip.length + && value[prefixToStrip.length] !== '/')) { + return quote(value); + } + return quote("${" + data.installPrefixDir + "}" + value.slice(prefixToStrip.length)); + } + return quoteAndPrefixifyHelper; + } + + var installedOutputFilePath = ModUtils.artifactInstalledFilePath( + outputs["Exporter.cmake.configFile"][0]); + var installedOutputPathName = FileInfo.path(installedOutputFilePath); + + var installRootPath = FileInfo.joinPaths(topLevelProduct.qbs.installRoot, topLevelProduct.qbs.installPrefix); + data.installPrefix = FileInfo.relativePath(installedOutputPathName, installRootPath); + + var libArtifacts; + var libImportArtifacts; + var isProduct = !topLevelProduct.present; + var considerFramework = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("bundle.content")) + && topLevelProduct.bundle + && topLevelProduct.bundle.isBundle + && topLevelProduct.qbs.targetOS.includes("darwin"); + var considerDynamicLibs = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("dynamiclibrary")); + var considerStaticLibs = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("staticlibrary")); + if (considerFramework) { + libArtifacts = topLevelProduct.artifacts["bundle.symlink.executable"]; + if (considerDynamicLibs) + data.type = "SHARED"; + else if (considerStaticLibs) + data.type = "STATIC"; + else + data.type = "INTERFACE"; + } else if (considerDynamicLibs) { + libArtifacts = topLevelProduct.artifacts.dynamiclibrary; + libImportArtifacts = topLevelProduct.artifacts.dynamiclibrary_import; + data.type = "SHARED"; + } else if (considerStaticLibs) { + libArtifacts = topLevelProduct.artifacts.staticlibrary; + data.type = "STATIC"; + } else { + data.type = "INTERFACE"; + } + + for (var i = 0; i < (libArtifacts || []).length; ++i) { + var libArtifact = libArtifacts[i]; + var libImportArtifact = (libImportArtifacts || [])[i]; + if (libArtifact.qbs.install) { + var installPath = ModUtils.artifactInstalledFilePath(libArtifact); + data.importedLocation = quoteAndPrefixify("installRoot")(installPath); + data.soName = topLevelProduct.targetName; + if (libImportArtifact && libImportArtifact.qbs.install) { + installPath = ModUtils.artifactInstalledFilePath(libImportArtifact); + data.importedImplibLocation = quoteAndPrefixify("installRoot")(installPath); + } + break; + } + } + var cpp = topLevelProduct.exports.cpp; + if (cpp) { + data.libraryPaths = (cpp.libraryPaths || []).map(quoteAndPrefixify("cpp.libraryPaths")); + + data.linkLibraries = []; + data.linkLibraries = data.linkLibraries.concat(cpp.dynamicLibraries || []); + data.linkLibraries = data.linkLibraries.concat(cpp.staticLibraries || []); + data.linkLibraries = data.linkLibraries.map(quoteAndPrefixify("cpp.dynamicLibraries")); + + data.linkOptions = []; + data.linkOptions = data.linkOptions.concat(cpp.driverLinkerFlags || []); + if ((cpp.linkerFlags || []).length > 0) { + data.linkOptions = + data.linkOptions.concat("LINKER:" + (cpp.linkerFlags || []).join(",")); + } + data.linkOptions = data.linkOptions.map(quote); + + data.includeDirectories = + (cpp.includePaths || []).map(quoteAndPrefixify("cpp.includePaths")); + data.compileDefinitions = (cpp.defines || []).map(quote); + + data.compileOptions = []; + data.compileOptions = data.compileOptions.concat(cpp.commonCompilerFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.driverFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.cxxFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.cFlags || []); + data.compileOptions = data.compileOptions.map(quote); + } + + function gatherDeps(dep) { + if (dep.name === "Exporter.cmake") + return; + var depHasExporter = dep.Exporter && dep.Exporter.cmake; + if (!depHasExporter) + return; + data.packages.push(dep.Exporter.cmake.packageName); + } + + var exportedDeps = topLevelProduct.exports ? topLevelProduct.exports.dependencies : []; + exportedDeps.forEach(gatherDeps); + + return data; +} + +function writeConfigFile(project, product, outputs) +{ + var autoDetectedData = collectAutodetectedData(project, product, outputs); + var packageName = autoDetectedData.packageName; + + function writeCommand(command, lines) + { + if ((lines || []).length === 0) + return; + cmakeConfigFile.writeLine(command + "(" + packageName + " INTERFACE"); + for (i = 0; i < lines.length; i++) { + cmakeConfigFile.writeLine(" " + lines[i]); + } + cmakeConfigFile.writeLine(")"); + } + + var cmakeConfigFile = new TextFile(outputs["Exporter.cmake.configFile"][0].filePath, + TextFile.WriteOnly); + cmakeConfigFile.writeLine("# Generated by Qbs"); + + cmakeConfigFile.writeLine("cmake_minimum_required(VERSION 3.5)"); + + cmakeConfigFile.writeLine("if(TARGET " + packageName + ")"); + cmakeConfigFile.writeLine(" return()"); + cmakeConfigFile.writeLine("endif()"); + + cmakeConfigFile.writeLine("set(" + autoDetectedData.installPrefixDir + + " \"${CMAKE_CURRENT_LIST_DIR}/" + + autoDetectedData.installPrefix + "\")"); + + autoDetectedData.packages.forEach(function(packageName) { + cmakeConfigFile.writeLine("find_package(" + packageName + " REQUIRED SILENT)"); + }); + cmakeConfigFile.writeLine( + "add_library(" + packageName + " " + autoDetectedData.type + " IMPORTED)"); + var configuration = (product.qbs.buildVariant) ? + product.qbs.buildVariant.toUpperCase() : "NONE"; + cmakeConfigFile.writeLine("set_property(TARGET " + packageName + + " APPEND PROPERTY IMPORTED_CONFIGURATIONS " + + configuration + ")"); + + cmakeConfigFile.writeLine("set_target_properties(" + packageName + " PROPERTIES"); + cmakeConfigFile.writeLine(" IMPORTED_LINK_INTERFACE_LANGUAGES_" + configuration + + " CXX"); + if (autoDetectedData.type !== "INTERFACE") { + cmakeConfigFile.writeLine(" IMPORTED_LOCATION_" + configuration + " " + + autoDetectedData.importedLocation); + } + if (autoDetectedData.importedImplibLocation) { + cmakeConfigFile.writeLine(" IMPORTED_IMPLIB_" + configuration + " " + + autoDetectedData.importedImplibLocation); + } + cmakeConfigFile.writeLine(")"); + + writeCommand("target_link_directories", autoDetectedData.libraryPaths); + writeCommand("target_link_libraries", + autoDetectedData.linkLibraries.concat(autoDetectedData.packages)); + writeCommand("target_link_options", autoDetectedData.linkOptions); + writeCommand("target_include_directories", autoDetectedData.includeDirectories); + writeCommand("target_compile_definitions", autoDetectedData.compileDefinitions); + writeCommand("target_compile_options", autoDetectedData.compileOptions); + + cmakeConfigFile.close(); +} + +function writeVersionFile(product, outputs) +{ + var cmakeVersionFile = new TextFile( + outputs["Exporter.cmake.versionFile"][0].filePath, TextFile.WriteOnly); + cmakeVersionFile.writeLine("# Generated by Qbs"); + cmakeVersionFile.writeLine("set(PACKAGE_VERSION \"" + product.version + "\")"); + cmakeVersionFile.close(); +} diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs new file mode 100644 index 000000000..a578e938b --- /dev/null +++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com> +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** 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$ +** +****************************************************************************/ + +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile + +import "cmakeexporter.js" as HelperFunctions + +Module { + property string configFileName: packageName + "Config.cmake" + property string versionFileName: packageName + "ConfigVersion.cmake" + property string packageName: product.targetName + + additionalProductTypes: ["Exporter.cmake.package"] + + Rule { + multiplex: true + requiresInputs: false + + auxiliaryInputs: { + if (product.type.includes("staticlibrary")) + return ["staticlibrary"]; + if (product.type.includes("dynamiclibrary")) + return ["dynamiclibrary"]; + } + + Artifact { + filePath: product.Exporter.cmake.configFileName + fileTags: ["Exporter.cmake.package", "Exporter.cmake.configFile"] + } + Artifact { + filePath: product.Exporter.cmake.versionFileName + fileTags: ["Exporter.cmake.package", "Exporter.cmake.versionFile"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generate cmake package files"; + cmd.sourceCode = function() { + HelperFunctions.writeConfigFile(project, product, outputs); + HelperFunctions.writeVersionFile(product, outputs); + } + return [cmd]; + } + } +} diff --git a/share/qbs/modules/Exporter/exporter.js b/share/qbs/modules/Exporter/exporter.js new file mode 100644 index 000000000..65a632ac8 --- /dev/null +++ b/share/qbs/modules/Exporter/exporter.js @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of the 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. +** +****************************************************************************/ + +function getPrefixToStrip(project, product, propName, value) +{ + function checkValuePrefix(forbiddenPrefix, prefixDescription) + { + if (value.startsWith(forbiddenPrefix)) { + throw "Value '" + value + "' for exported property '" + propName + "' in product '" + + product.name + "' points into " + prefixDescription + ".\n" + + "Did you forget to set the prefixMapping property in an Export item?"; + } + } + + // Catch user oversights: Paths that point into the project source or build directories + // make no sense in the module. + if (!value.startsWith(product.qbs.installRoot)) { + checkValuePrefix(project.buildDirectory, "project build directory"); + checkValuePrefix(project.sourceDirectory, "project source directory"); + } + + // Adapt file paths pointing into the install dir, that is, make them relative to the + // module file for relocatability. We accept them with or without the install root. + // The latter form will typically be a result of applying the prefixMapping property, + // while the first one could be an untransformed path, for instance if the project + // file is written in such a way that include paths are picked up from the installed + // location rather than the source directory. + var result; + var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix); + if (fullInstallPrefix.length > 1 && value.startsWith(fullInstallPrefix)) { + result = fullInstallPrefix; + } else { + var installPrefix = FileInfo.joinPaths("/", product.qbs.installPrefix); + if (installPrefix.length > 1 && value.startsWith(installPrefix)) + result = installPrefix; + } + return result; +} diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js index 52b4dffe3..50bba536c 100644 --- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js +++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js @@ -33,7 +33,7 @@ var ModUtils = require("qbs.ModUtils"); function quote(value) { - if (value.contains(" ") || value.contains("'") || value.contains('"')) { + if (value.includes(" ") || value.includes("'") || value.includes('"')) { return '"' + value.replace(/(["'\\])/g, "\\$1") + '"'; } return value; @@ -105,12 +105,12 @@ function collectAutodetectedData(topLevelProduct) var libArtifacts; var isProduct = !productOrModule.present; var considerDynamicLibs = !isProduct || (productOrModule.type - && productOrModule.type.contains("dynamiclibrary")); + && productOrModule.type.includes("dynamiclibrary")); if (considerDynamicLibs) { libArtifacts = productOrModule.artifacts.dynamiclibrary; } else { var considerStaticLibs = !isProduct || (productOrModule.type - && productOrModule.type.contains("staticlibrary")); + && productOrModule.type.includes("staticlibrary")); if (considerStaticLibs) libArtifacts = productOrModule.artifacts.staticlibrary; } @@ -186,7 +186,7 @@ function collectAutodetectedData(topLevelProduct) exportedDepNames.push(exportedDeps[i].name); for (i = 0; i < (productOrModule.dependencies || []).length; ++i) { var dep = productOrModule.dependencies[i]; - if (exportedDepNames.contains(dep.name)) + if (exportedDepNames.includes(dep.name)) continue; privateDeps.push(dep); } @@ -197,22 +197,22 @@ function collectAutodetectedData(topLevelProduct) var depHasPkgConfig = dep.Exporter && dep.Exporter.pkgconfig; if (depHasPkgConfig) { var entry = FileInfo.completeBaseName(dep.Exporter.pkgconfig.fileName); - if (excludedDeps.contains(entry)) + if (excludedDeps.includes(entry)) return; - if (isPrivateDep && !data.requiresPrivate.contains(entry) - && !explicitRequiresPrivate.contains(entry)) { + if (isPrivateDep && !data.requiresPrivate.includes(entry) + && !explicitRequiresPrivate.includes(entry)) { data.requiresPrivate.push(entry); } - if (!isPrivateDep && !data.requires.contains(entry) - && !explicitRequires.contains(entry)) { + if (!isPrivateDep && !data.requires.includes(entry) + && !explicitRequires.includes(entry)) { data.requires.push(entry); } } else { - if (excludedDeps.contains(dep.name)) + if (excludedDeps.includes(dep.name)) return; - if (isPrivateDep && explicitRequiresPrivate.contains(dep.name)) + if (isPrivateDep && explicitRequiresPrivate.includes(dep.name)) return; - if (!isPrivateDep && explicitRequires.contains(dep.name)) + if (!isPrivateDep && explicitRequires.includes(dep.name)) return; collectAutodetectedDataRecursive(dep, isPrivateDep); } diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs index 8cc55f885..feb0e017c 100644 --- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs +++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs @@ -34,9 +34,9 @@ Module { auxiliaryInputs: { if (!autoDetect) return undefined; - if (product.type.contains("staticlibrary")) + if (product.type.includes("staticlibrary")) return ["staticlibrary"]; - if (product.type.contains("dynamiclibrary")) + if (product.type.includes("dynamiclibrary")) return ["dynamiclibrary"]; } @@ -46,7 +46,7 @@ Module { } prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Creating " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.sourceCode = function() { var f = new TextFile(output.filePath, TextFile.WriteOnly); if (product.Exporter.pkgconfig._usePrefix) diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.js b/share/qbs/modules/Exporter/qbs/qbsexporter.js index be46372c3..145474a4b 100644 --- a/share/qbs/modules/Exporter/qbs/qbsexporter.js +++ b/share/qbs/modules/Exporter/qbs/qbsexporter.js @@ -30,6 +30,7 @@ var FileInfo = require("qbs.FileInfo"); var ModUtils = require("qbs.ModUtils"); +var ExporterHelpers = require("../exporter.js"); function tagListToString(tagList) { @@ -74,7 +75,7 @@ function writeTargetArtifactGroups(product, output, moduleFile) var tag = product.Exporter.qbs._artifactTypes[i]; var artifactsForTag = product.artifacts[tag] || []; for (var j = 0; j < artifactsForTag.length; ++j) { - if (!relevantArtifacts.contains(artifactsForTag[j])) + if (!relevantArtifacts.includes(artifactsForTag[j])) relevantArtifacts.push(artifactsForTag[j]); } } @@ -82,7 +83,7 @@ function writeTargetArtifactGroups(product, output, moduleFile) var artifactCount = relevantArtifacts ? relevantArtifacts.length : 0; for (i = 0; i < artifactCount; ++i) { var artifact = relevantArtifacts[i]; - if (!artifact.fileTags.contains("installable")) + if (!artifact.fileTags.includes("installable")) continue; // Put all artifacts with the same set of file tags into the same group, so we don't @@ -102,15 +103,6 @@ function writeTargetArtifactGroups(product, output, moduleFile) } } -function checkValuePrefix(name, value, forbiddenPrefix, prefixDescription) -{ - if (value.startsWith(forbiddenPrefix)) { - throw "Value '" + value + "' for exported property '" + name + "' in product '" - + product.name + "' points into " + prefixDescription + ".\n" - + "Did you forget to set the prefixMapping property in an Export item?"; - } -} - function stringifyValue(project, product, moduleInstallDir, prop, value) { if (value instanceof Array) { @@ -128,29 +120,9 @@ function stringifyValue(project, product, moduleInstallDir, prop, value) return value; } - // Catch user oversights: Paths that point into the project source or build directories - // make no sense in the module. - if (!value.startsWith(product.qbs.installRoot)) { - checkValuePrefix(prop.name, value, project.buildDirectory, "project build directory"); - checkValuePrefix(prop.name, value, project.sourceDirectory, "project source directory"); - } - - // Adapt file paths pointing into the install dir, that is, make them relative to the - // module file for relocatability. We accept them with or without the install root. - // The latter form will typically be a result of applying the prefixMapping property, - // while the first one could be an untransformed path, for instance if the project - // file is written in such a way that include paths are picked up from the installed - // location rather than the source directory. - var valuePrefixToStrip; - var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix); - if (fullInstallPrefix.length > 1 && value.startsWith(fullInstallPrefix)) { - valuePrefixToStrip = fullInstallPrefix; - } else { - var installPrefix = FileInfo.joinPaths("/", product.qbs.installPrefix); - if (installPrefix.length > 1 && value.startsWith(installPrefix)) - valuePrefixToStrip = installPrefix; - } + var valuePrefixToStrip = ExporterHelpers.getPrefixToStrip(project, product, prop.name, value); if (valuePrefixToStrip) { + var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix); var deployedModuleInstallDir = moduleInstallDir.slice(fullInstallPrefix.length); return "FileInfo.cleanPath(FileInfo.joinPaths(path, FileInfo.relativePath(" + JSON.stringify(deployedModuleInstallDir) + ", " @@ -172,7 +144,7 @@ function writeProperty(project, product, moduleInstallDir, prop, indentation, co var moduleName; if (isModuleProperty) { moduleName = prop.name.slice(0, separatorIndex); - if ((product.Exporter.qbs.excludedDependencies || []).contains(moduleName)) + if ((product.Exporter.qbs.excludedDependencies || []).includes(moduleName)) return; } line += prop.name + ": "; @@ -244,7 +216,7 @@ function isExcludedDependency(product, childItem) for (var i = 0; i < childItem.properties.length; ++i) { var prop = childItem.properties[i]; var unquotedRhs = prop.sourceCode.slice(1, -1); - if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.contains(unquotedRhs)) + if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.includes(unquotedRhs)) return true; } return false; @@ -264,7 +236,7 @@ function writeImportStatements(product, moduleFile) var imports = product.exports.imports; // We potentially use FileInfo ourselves when transforming paths in stringifyValue(). - if (!imports.contains("import qbs.FileInfo")) + if (!imports.includes("import qbs.FileInfo")) imports.push("import qbs.FileInfo"); for (var i = 0; i < imports.length; ++i) diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.qbs b/share/qbs/modules/Exporter/qbs/qbsexporter.qbs index b08639e38..1e7cb02d5 100644 --- a/share/qbs/modules/Exporter/qbs/qbsexporter.qbs +++ b/share/qbs/modules/Exporter/qbs/qbsexporter.qbs @@ -59,7 +59,7 @@ Module { } prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Creating " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.sourceCode = function() { var f = new TextFile(output.filePath, TextFile.WriteOnly); HelperFunctions.writeImportStatements(product, f); diff --git a/share/qbs/modules/Sanitizers/address/asan.qbs b/share/qbs/modules/Sanitizers/address/asan.qbs new file mode 100644 index 000000000..9d8f5b97e --- /dev/null +++ b/share/qbs/modules/Sanitizers/address/asan.qbs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.Utilities + +Module { + Depends { name: "cpp" } + + property bool enabled: true + readonly property bool _supported: qbs.toolchain.includes("gcc") + || qbs.toolchain.includes("clang-cl") + || (qbs.toolchain.includes("msvc") + && Utilities.versionCompare(cpp.compilerVersion, "19.28.29500.0") >= 0) + readonly property bool _enabled: enabled && _supported + + property string detectUseAfterReturn: "always" + PropertyOptions { + name: "detectUseAfterReturn" + description: "Whether to detect problems with stack use after return from a function" + allowedValues: ["never", "runtime", "always"] + } + + property bool detectUseAfterScope: true + + cpp.driverFlags: { + var flags = []; + if (!_enabled) + return flags; + if (qbs.toolchain.includes("msvc") && !qbs.toolchain.includes("clang-cl")) { + flags.push("/fsanitize=address"); + if (detectUseAfterReturn !== "never") + flags.push("/fsanitize-address-use-after-return"); + return flags; + } + flags.push("-fsanitize=address", "-fno-omit-frame-pointer"); + if (detectUseAfterScope) + flags.push("-fsanitize-address-use-after-scope"); + if (detectUseAfterReturn) { + if (qbs.toolchain.includes("llvm")) { + var minVersion = qbs.toolchain.contains("xcode") ? "14" : "13"; + if (Utilities.versionCompare(cpp.compilerVersion, minVersion) >= 0) + flags.push("-fsanitize-address-use-after-return=" + detectUseAfterReturn); + } else if (detectUseAfterReturn === "never") { + flags.push("--param", "asan-use-after-return=0"); + } + } + return flags; + } +} diff --git a/share/qbs/modules/archiver/archiver.qbs b/share/qbs/modules/archiver/archiver.qbs index 6ae53dd37..069e76717 100644 --- a/share/qbs/modules/archiver/archiver.qbs +++ b/share/qbs/modules/archiver/archiver.qbs @@ -30,6 +30,7 @@ import qbs.Environment import qbs.File +import qbs.Host import qbs.FileInfo import qbs.Probes @@ -53,7 +54,7 @@ Module { names: ["7z"] platformSearchPaths: { var paths = base; - if (qbs.hostOS.contains("windows")) { + if (Host.os().includes("windows")) { var env32 = Environment.getEnv("PROGRAMFILES(X86)"); var env64 = Environment.getEnv("PROGRAMFILES"); if (env64 === env32 && env64.endsWith(" (x86)")) @@ -211,7 +212,7 @@ Module { args.push("-0"); } else { compression = compression === "bz2" ? "bzip2" : compression; - if (["store", "deflate", "bzip2"].contains(compression)) + if (["store", "deflate", "bzip2"].includes(compression)) args.push("-Z", compression); if (compressionLevel) @@ -220,7 +221,7 @@ Module { args.push("-r", output.filePath, ".", "-i@" + input.filePath); args = args.concat(product.moduleProperty("archiver", "flags")); - } else if (["tar", "zip", "jar"].contains(binaryName)) { + } else if (["tar", "zip", "jar"].includes(binaryName)) { throw binaryName + ": unrecognized archive type: '" + type + "'"; } else if (binaryName) { throw "unrecognized archive tool: '" + binaryName + "'"; @@ -229,7 +230,7 @@ Module { } var archiverCommand = new Command(binary, args); - archiverCommand.description = "Creating archive file " + output.fileName; + archiverCommand.description = "creating archive file " + output.fileName; archiverCommand.highlight = "linker"; archiverCommand.workingDirectory = product.moduleProperty("archiver", "workingDirectory"); diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index 2568f3435..f640fd55f 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -33,6 +33,7 @@ import qbs.DarwinTools import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.PropertyList import qbs.TextFile @@ -46,13 +47,13 @@ Module { Probe { id: bundleSettingsProbe - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") property string xcodeDeveloperPath: xcode.developerPath property var xcodeArchSettings: xcode._architectureSettings property string productTypeIdentifier: _productTypeIdentifier - property bool useXcodeBuildSpecs: _useXcodeBuildSpecs - property bool isMacOs: qbs.targetOS.contains("macos") + property bool useXcodeBuildSpecs: !useBuiltinXcodeBuildSpecs + property bool isMacOs: qbs.targetOS.includes("macos") property bool xcodePresent: xcode.present property string xcodeVersion: xcode.version @@ -73,9 +74,9 @@ Module { "LOCAL_LIBRARY_DIR": Environment.getEnv("HOME") + "/Library", // actually, this is cpp.targetAbi, but XCode does not set it for non-simulator builds // while Qbs set it to "macho". - "LLVM_TARGET_TRIPLE_SUFFIX": qbs.targetOS.contains("simulator") ? "-simulator" : "", + "LLVM_TARGET_TRIPLE_SUFFIX": qbs.targetOS.includes("simulator") ? "-simulator" : "", "SWIFT_PLATFORM_TARGET_PREFIX": isMacOs ? "macos" - : qbs.targetOS.contains("ios") ? "ios" : "", + : qbs.targetOS.includes("ios") ? "ios" : "", "TARGET_BUILD_DIR": product.buildDirectory, "WRAPPER_NAME": bundleName, "WRAPPER_EXTENSION": extension @@ -117,7 +118,7 @@ Module { additionalProductTypes: !(product.multiplexed || product.aggregate) || !product.multiplexConfigurationId ? ["bundle.content"] : [] - property bool isBundle: !product.consoleApplication && qbs.targetOS.contains("darwin") + property bool isBundle: !product.consoleApplication && qbs.targetOS.includes("darwin") readonly property bool isShallow: bundleSettingsProbe.xcodeSettings["SHALLOW_BUNDLE"] === "YES" @@ -151,7 +152,7 @@ Module { property var infoPlist property bool processInfoPlist: true property bool embedInfoPlist: product.consoleApplication && !isBundle - property string infoPlistFormat: qbs.targetOS.contains("macos") ? "same-as-input" : "binary1" + property string infoPlistFormat: qbs.targetOS.includes("macos") ? "same-as-input" : "binary1" property string localizedResourcesFolderSuffix: ".lproj" @@ -186,11 +187,18 @@ Module { readonly property string unlocalizedResourcesFolderPath: bundleSettingsProbe.xcodeSettings["UNLOCALIZED_RESOURCES_FOLDER_PATH"] readonly property string versionsFolderPath: bundleSettingsProbe.xcodeSettings["VERSIONS_FOLDER_PATH"] + property bool useBuiltinXcodeBuildSpecs: !_useXcodeBuildSpecs // true to use ONLY the qbs build specs + // private properties property string _productTypeIdentifier: Bundle.productTypeIdentifier(product.type) property stringList _productTypeIdentifierChain: bundleSettingsProbe.productTypeIdentifierChain - property bool _useXcodeBuildSpecs: true // false to use ONLY the qbs build specs + readonly property path _developerPath: xcode.developerPath + readonly property path _platformInfoPlist: xcode.platformInfoPlist + readonly property path _sdkSettingsPlist: xcode.sdkSettingsPlist + readonly property path _toolchainInfoPlist: xcode.toolchainInfoPlist + + property bool _useXcodeBuildSpecs: true // TODO: remove in 1.25 property var extraEnv: ({ "PRODUCT_BUNDLE_IDENTIFIER": identifier @@ -224,7 +232,7 @@ Module { } validate: { - if (!qbs.targetOS.contains("darwin")) + if (!qbs.targetOS.includes("darwin")) return; if (!bundleSettingsProbe.found) { var error = "Bundle product type " + _productTypeIdentifier + " is not supported."; @@ -270,7 +278,7 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true requiresInputs: false // TODO: The resources property should probably be a tag instead. inputs: ["infoplist", "partial_infoplist"] @@ -278,8 +286,8 @@ Module { outputFileTags: ["bundle.input", "aggregate_infoplist"] outputArtifacts: { var artifacts = []; - var embed = ModUtils.moduleProperty(product, "embedInfoPlist"); - if (ModUtils.moduleProperty(product, "isBundle") || embed) { + var embed = product.bundle.embedInfoPlist; + if (product.bundle.isBundle || embed) { artifacts.push({ filePath: FileInfo.joinPaths( product.destinationDirectory, product.name + "-Info.plist"), @@ -287,7 +295,7 @@ Module { bundle: { _bundleFilePath: FileInfo.joinPaths( product.destinationDirectory, - ModUtils.moduleProperty(product, "infoPlistPath")), + product.bundle.infoPlistPath), } }); } @@ -298,20 +306,21 @@ Module { var cmd = new JavaScriptCommand(); cmd.description = "generating Info.plist for " + product.name; cmd.highlight = "codegen"; - cmd.infoPlist = ModUtils.moduleProperty(product, "infoPlist") || {}; - cmd.processInfoPlist = ModUtils.moduleProperty(product, "processInfoPlist"); - cmd.infoPlistFormat = ModUtils.moduleProperty(product, "infoPlistFormat"); - cmd.extraEnv = ModUtils.moduleProperty(product, "extraEnv"); - cmd.qmakeEnv = ModUtils.moduleProperty(product, "qmakeEnv"); + cmd.infoPlist = product.bundle.infoPlist || {}; + cmd.processInfoPlist = product.bundle.processInfoPlist; + cmd.infoPlistFormat = product.bundle.infoPlistFormat; + cmd.extraEnv = product.bundle.extraEnv; + cmd.qmakeEnv = product.bundle.qmakeEnv; + // TODO: bundle module should know nothing about cpp module cmd.buildEnv = product.moduleProperty("cpp", "buildEnv"); - cmd.developerPath = product.moduleProperty("xcode", "developerPath"); - cmd.platformInfoPlist = product.moduleProperty("xcode", "platformInfoPlist"); - cmd.sdkSettingsPlist = product.moduleProperty("xcode", "sdkSettingsPlist"); - cmd.toolchainInfoPlist = product.moduleProperty("xcode", "toolchainInfoPlist"); + cmd.developerPath = product.bundle._developerPath; + cmd.platformInfoPlist = product.bundle._platformInfoPlist; + cmd.sdkSettingsPlist = product.bundle._sdkSettingsPlist; + cmd.toolchainInfoPlist = product.bundle._toolchainInfoPlist; - cmd.osBuildVersion = product.moduleProperty("qbs", "hostOSBuildVersion"); + cmd.osBuildVersion = product.qbs.hostOSBuildVersion; cmd.sourceCode = function() { var plist, process, key, i; @@ -340,7 +349,7 @@ Module { if (processInfoPlist) { // Add default values to the aggregate plist if the corresponding keys // for those values are not already present - var defaultValues = ModUtils.moduleProperty(product, "defaultInfoPlist"); + var defaultValues = product.bundle.defaultInfoPlist; for (key in defaultValues) { if (defaultValues.hasOwnProperty(key) && !(key in aggregatePlist)) aggregatePlist[key] = defaultValues[key]; @@ -452,7 +461,7 @@ Module { infoPlistFormat = "xml1"; var validFormats = [ "xml1", "binary1", "json" ]; - if (!validFormats.contains(infoPlistFormat)) + if (!validFormats.includes(infoPlistFormat)) throw("Invalid Info.plist format " + infoPlistFormat + ". " + "Must be in [xml1, binary1, json]."); @@ -470,18 +479,18 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true inputs: ["aggregate_infoplist"] outputFileTags: ["bundle.input", "pkginfo"] outputArtifacts: { var artifacts = []; - if (ModUtils.moduleProperty(product, "isBundle") && ModUtils.moduleProperty(product, "generatePackageInfo")) { + if (product.bundle.isBundle && product.bundle.generatePackageInfo) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, "PkgInfo"), fileTags: ["bundle.input", "pkginfo"], - bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "pkgInfoPath")) } + bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.pkgInfoPath) } }); } return artifacts; @@ -511,7 +520,7 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true inputs: ["bundle.input", "aggregate_infoplist", "pkginfo", "hpp", @@ -532,12 +541,12 @@ Module { "bundle.code-signature"] outputArtifacts: { var i, artifacts = []; - if (ModUtils.moduleProperty(product, "isBundle")) { + if (product.bundle.isBundle) { for (i in inputs["bundle.input"]) { - var fp = inputs["bundle.input"][i].moduleProperty("bundle", "_bundleFilePath"); + var fp = inputs["bundle.input"][i].bundle._bundleFilePath; if (!fp) throw("Artifact " + inputs["bundle.input"][i].filePath + " has no associated bundle file path"); - var extraTags = inputs["bundle.input"][i].fileTags.contains("application") + var extraTags = inputs["bundle.input"][i].fileTags.includes("application") ? ["bundle.application-executable"] : []; artifacts.push({ filePath: fp, @@ -549,20 +558,19 @@ Module { for (i in provprofiles) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, - ModUtils.moduleProperty(product, - "contentsFolderPath"), + product.bundle.contentsFolderPath, provprofiles[i].fileName), fileTags: ["bundle.provisioningprofile", "bundle.content"] }); } - var packageType = ModUtils.moduleProperty(product, "packageType"); - var isShallow = ModUtils.moduleProperty(product, "isShallow"); + var packageType = product.bundle.packageType; + var isShallow = product.bundle.isShallow; if (packageType === "FMWK" && !isShallow) { - var publicHeaders = ModUtils.moduleProperty(product, "publicHeaders"); + var publicHeaders = product.bundle.publicHeaders; if (publicHeaders && publicHeaders.length) { artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Headers"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Headers"), fileTags: ["bundle.symlink.headers", "bundle.content"] }); } @@ -570,23 +578,23 @@ Module { var privateHeaders = ModUtils.moduleProperty(product, "privateHeaders"); if (privateHeaders && privateHeaders.length) { artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "PrivateHeaders"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "PrivateHeaders"), fileTags: ["bundle.symlink.private-headers", "bundle.content"] }); } artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Resources"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Resources"), fileTags: ["bundle.symlink.resources", "bundle.content"] }); artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), product.targetName), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, product.targetName), fileTags: ["bundle.symlink.executable", "bundle.content"] }); artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "versionsFolderPath"), "Current"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.versionsFolderPath, "Current"), fileTags: ["bundle.symlink.version", "bundle.content"] }); } @@ -603,7 +611,7 @@ Module { } } - sources = ModUtils.moduleProperty(product, "resources"); + sources = product.bundle.resources; for (i in sources) { destination = BundleTools.destinationDirectoryForResource(product, {baseDir: FileInfo.path(sources[i]), fileName: FileInfo.fileName(sources[i])}); artifacts.push({ @@ -614,11 +622,11 @@ Module { var wrapperPath = FileInfo.joinPaths( product.destinationDirectory, - ModUtils.moduleProperty(product, "bundleName")); + product.bundle.bundleName); for (var i = 0; i < artifacts.length; ++i) artifacts[i].bundle = { wrapperPath: wrapperPath }; - if (product.qbs.hostOS.contains("darwin") && product.codesign + if (Host.os().includes("darwin") && product.codesign && product.codesign.enableCodeSigning) { artifacts.push({ filePath: FileInfo.joinPaths(product.bundle.contentsFolderPath, "_CodeSignature/CodeResources"), @@ -631,7 +639,7 @@ Module { prepare: { var i, cmd, commands = []; - var packageType = ModUtils.moduleProperty(product, "packageType"); + var packageType = product.bundle.packageType; var bundleType = "bundle"; if (packageType === "APPL") @@ -649,7 +657,7 @@ Module { var symlinks = outputs["bundle.symlink.version"]; for (i in symlinks) { - cmd = new Command("ln", ["-sfn", ModUtils.moduleProperty(product, "frameworkVersion"), + cmd = new Command("ln", ["-sfn", product.bundle.frameworkVersion, symlinks[i].filePath]); cmd.silent = true; commands.push(cmd); @@ -696,8 +704,8 @@ Module { } var bundleInputs = sortedArtifactList(inputs["bundle.input"], function (a, b) { - return a.moduleProperty("bundle", "_bundleFilePath").localeCompare( - b.moduleProperty("bundle", "_bundleFilePath")); + return a.bundle._bundleFilePath.localeCompare( + b.bundle._bundleFilePath); }); var bundleContents = sortedArtifactList(outputs["bundle.content.copied"]); for (i in bundleContents) { @@ -730,8 +738,8 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying public headers"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "publicHeaders"); - cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "publicHeadersFolderPath")); + cmd.sources = product.bundle.publicHeaders; + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.publicHeadersFolderPath); cmd.sourceCode = function() { var i; for (var i in sources) { @@ -744,8 +752,8 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying private headers"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "privateHeaders"); - cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "privateHeadersFolderPath")); + cmd.sources = product.bundle.privateHeaders; + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.privateHeadersFolderPath); cmd.sourceCode = function() { var i; for (var i in sources) { @@ -758,7 +766,7 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying resources"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "resources"); + cmd.sources = product.bundle.resources; cmd.sourceCode = function() { var i; for (var i in sources) { @@ -769,15 +777,17 @@ Module { if (cmd.sources && cmd.sources.length) commands.push(cmd); - if (product.moduleProperty("qbs", "hostOS").contains("darwin")) { + if (product.qbs.hostOS.includes("darwin")) { Array.prototype.push.apply(commands, Codesign.prepareSign( project, product, inputs, outputs, input, output)); if (bundleType === "application" - && product.moduleProperty("qbs", "targetOS").contains("macos")) { - cmd = new Command(ModUtils.moduleProperty(product, "lsregisterPath"), - ["-f", product.bundle.bundleName]); - cmd.description = "register " + ModUtils.moduleProperty(product, "bundleName"); + && product.qbs.targetOS.includes("macos")) { + var bundlePath = FileInfo.joinPaths( + product.destinationDirectory, product.bundle.bundleName); + cmd = new Command(product.bundle.lsregisterPath, + ["-f", bundlePath]); + cmd.description = "registering " + product.bundle.bundleName; commands.push(cmd); } } diff --git a/share/qbs/modules/bundle/bundle.js b/share/qbs/modules/bundle/bundle.js index 293f53225..cf765f7be 100644 --- a/share/qbs/modules/bundle/bundle.js +++ b/share/qbs/modules/bundle/bundle.js @@ -83,7 +83,7 @@ var _productTypeIdentifiers = { function productTypeIdentifier(productType) { for (var k in _productTypeIdentifiers) { - if (productType.contains(k)) + if (productType.includes(k)) return _productTypeIdentifiers[k]; } return "com.apple.package-type.wrapper"; @@ -152,13 +152,21 @@ function _assign(target, source) { function macOSSpecsPaths(version, developerPath) { var result = []; - if (Utilities.versionCompare(version, "12.5") >= 0) { + if (Utilities.versionCompare(version, "15.3") >= 0) { + result.push(FileInfo.joinPaths( + developerPath, "..", "SharedFrameworks", "XCBuild.framework", "PlugIns", + "XCBBuildService.bundle", "Contents", "PlugIns", "XCBSpecifications.ideplugin", + "Contents", "Resources")); + } + if (Utilities.versionCompare(version, "14.3") >= 0) { + result.push(FileInfo.joinPaths( + developerPath, "Library", "Xcode", "Plug-ins", "XCBSpecifications.ideplugin", + "Contents", "Resources")); + } else if (Utilities.versionCompare(version, "12.5") >= 0) { result.push(FileInfo.joinPaths( developerPath, "..", "PlugIns", "XCBSpecifications.ideplugin", "Contents", "Resources")); - } - - if (Utilities.versionCompare(version, "12") >= 0) { + } else if (Utilities.versionCompare(version, "12") >= 0) { result.push(FileInfo.joinPaths( developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode", "PrivatePlugIns", "IDEOSXSupportCore.ideplugin", "Contents", "Resources")); @@ -181,15 +189,15 @@ var XcodeBuildSpecsReader = (function () { var i, j; for (i = 0; i < specsPaths.length; ++i) { var specsPath = specsPaths[i]; - var names = ["Darwin", "MacOSX"]; + var names = ["", "Darwin", "MacOSX"]; for (j = 0; j < names.length; ++j) { var name = names[j]; var plist = new PropertyList2(); var plist2 = new PropertyList2(); try { - var plistName = [name, "Package", "Types.xcspec"].join(separator); - var plistName2 = [name, "Product", "Types.xcspec"].join(separator); + var plistName = [name, "Package", "Types.xcspec"].join(name ? separator : ""); + var plistName2 = [name, "Product", "Types.xcspec"].join(name ? separator : ""); var plistPath = FileInfo.joinPaths(specsPath, plistName); var plistPath2 = FileInfo.joinPaths(specsPath, plistName2); if (File.exists(plistPath)) { @@ -311,7 +319,8 @@ var XcodeBuildSpecsReader = (function () { }; XcodeBuildSpecsReader.prototype.expandedSetting = function (typeIdentifier, baseSettings, settingName) { - var obj = baseSettings || {}; + var obj = {}; + _assign(obj, baseSettings); // todo: copy recursively obj = _assign(obj, this.settings(typeIdentifier, true)); if (obj) { for (var x in this._additionalSettings) { diff --git a/share/qbs/modules/capnproto/capnprotobase.qbs b/share/qbs/modules/capnproto/capnprotobase.qbs index e84d39433..916e53a51 100644 --- a/share/qbs/modules/capnproto/capnprotobase.qbs +++ b/share/qbs/modules/capnproto/capnprotobase.qbs @@ -28,6 +28,7 @@ ** ****************************************************************************/ +import qbs.FileInfo import qbs.Probes import "capnproto.js" as HelperFunctions @@ -40,16 +41,20 @@ Module { property pathList importPaths: [] - readonly property string outputDir: product.buildDirectory + "/capnp" + property string outputDir: product.buildDirectory + "/capnp" + + property var _searchPaths Probes.BinaryProbe { id: compilerProbe names: compilerName ? [compilerName] : [] + searchPaths: _searchPaths } Probes.BinaryProbe { id: pluginProbe names: pluginName ? [pluginName] : [] + searchPaths: _searchPaths } FileTagger { diff --git a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs index f33bc9a48..83192b7de 100644 --- a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs +++ b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs @@ -39,6 +39,8 @@ CapnProtoBase { Depends { name: "capnp-rpc"; condition: useRpc } pluginName: "capnpc-c++" + version: capnp.version + _searchPaths: capnp.hostBinDirs cpp.systemIncludePaths: outputDir cpp.cxxLanguageVersion: "c++14" diff --git a/share/qbs/modules/cli/CLIModule.qbs b/share/qbs/modules/cli/CLIModule.qbs index cea4c09ca..3b12284ed 100644 --- a/share/qbs/modules/cli/CLIModule.qbs +++ b/share/qbs/modules/cli/CLIModule.qbs @@ -1,12 +1,13 @@ // base for Common Language Infrastructure modules import qbs.FileInfo +import qbs.Host import qbs.ModUtils import "cli.js" as CLI Module { - Depends { name: "bundle"; condition: qbs.targetOS.contains("darwin") } + Depends { name: "bundle"; condition: qbs.targetOS.includes("darwin") } Properties { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") bundle.isBundle: false } @@ -87,8 +88,8 @@ Module { } setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, - product.qbs.hostOS.contains("windows")); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), + Host.os().includes("windows")); v.prepend(product.cli.toolchainInstallPath); v.set(); } diff --git a/share/qbs/modules/cli/cli.js b/share/qbs/modules/cli/cli.js index 38833ac51..faa812201 100644 --- a/share/qbs/modules/cli/cli.js +++ b/share/qbs/modules/cli/cli.js @@ -59,16 +59,16 @@ function prepareCompiler(product, inputs, output) { "cli.fsharp": fsharpCompilerPath }; - var pathFunction = product.moduleProperty("qbs", "hostOS").contains("windows") + var pathFunction = product.moduleProperty("qbs", "hostOS").includes("windows") ? FileInfo.toWindowsSeparators : function (path) { return path; }; var outputDescription = "assembly"; - if (output.fileTags.contains("application")) { + if (output.fileTags.includes("application")) { args.push("/target:" + (product.consoleApplication === false ? "winexe" : "exe")); - } else if (output.fileTags.contains("dynamiclibrary")) { + } else if (output.fileTags.includes("dynamiclibrary")) { args.push("/target:library"); - } else if (output.fileTags.contains("cli.netmodule")) { + } else if (output.fileTags.includes("cli.netmodule")) { args.push("/target:module"); outputDescription = "netmodule"; } @@ -77,7 +77,7 @@ function prepareCompiler(product, inputs, output) { var keys = Object.keys(inputs); var language; for (i in keys) { - if (Object.keys(compilers).contains(keys[i])) { + if (Object.keys(compilers).includes(keys[i])) { if (language) throw("You cannot compile source files in more than one CLI language into a single " + outputDescription + "."); language = keys[i]; diff --git a/share/qbs/modules/cli/mono.qbs b/share/qbs/modules/cli/mono.qbs index f04956a5e..f16387dd7 100644 --- a/share/qbs/modules/cli/mono.qbs +++ b/share/qbs/modules/cli/mono.qbs @@ -1,8 +1,9 @@ import qbs.File +import qbs.Host import qbs.Probes CLIModule { - condition: qbs.toolchain && qbs.toolchain.contains("mono") + condition: qbs.toolchain && qbs.toolchain.includes("mono") debugInfoSuffix: ".mdb" csharpCompilerName: "mcs" @@ -14,9 +15,9 @@ CLIModule { names: ["mono"] platformSearchPaths: { var paths = []; - if (qbs.hostOS.contains("macos")) + if (Host.os().includes("macos")) paths.push("/Library/Frameworks/Mono.framework/Commands"); - if (qbs.hostOS.contains("unix")) + if (Host.os().includes("unix")) paths.push("/usr/bin"); return paths; } diff --git a/share/qbs/modules/cli/windows-dotnet.qbs b/share/qbs/modules/cli/windows-dotnet.qbs index 6fde50b85..a4d27c207 100644 --- a/share/qbs/modules/cli/windows-dotnet.qbs +++ b/share/qbs/modules/cli/windows-dotnet.qbs @@ -1,7 +1,7 @@ import qbs.Utilities CLIModule { - condition: qbs.toolchain && qbs.toolchain.contains("dotnet") + condition: qbs.toolchain && qbs.toolchain.includes("dotnet") debugInfoSuffix: ".pdb" csharpCompilerName: "csc" diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs index f4535aa9c..b1811dcfd 100644 --- a/share/qbs/modules/codesign/android.qbs +++ b/share/qbs/modules/codesign/android.qbs @@ -31,12 +31,13 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Probes import "codesign.js" as CodeSign CodeSignModule { - condition: qbs.targetOS.contains("android") + condition: qbs.targetOS.includes("android") priority: 1 enableCodeSigning: true @@ -54,7 +55,7 @@ CodeSignModule { property string keytoolName: "keytool" property string debugKeystorePath: FileInfo.joinPaths( - Environment.getEnv(qbs.hostOS.contains("windows") + Environment.getEnv(Host.os().includes("windows") ? "USERPROFILE" : "HOME"), ".android", "debug.keystore") readonly property string debugKeystorePassword: "android" diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs index 4340e3037..0d1335d92 100644 --- a/share/qbs/modules/codesign/apple.qbs +++ b/share/qbs/modules/codesign/apple.qbs @@ -34,6 +34,7 @@ import qbs.DarwinTools import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.PropertyList import qbs.Probes @@ -42,14 +43,14 @@ import "codesign.js" as CodeSign import "../xcode/xcode.js" as XcodeUtils CodeSignModule { - Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") } Probes.BinaryProbe { id: codesignProbe names: [codesignName] } - condition: qbs.hostOS.contains("macos") && qbs.targetOS.contains("darwin") + condition: Host.os().includes("macos") && qbs.targetOS.includes("darwin") priority: 0 enableCodeSigning: _codeSigningRequired @@ -77,15 +78,15 @@ CodeSignModule { var isDebug = qbs.buildVariant !== "release"; - if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("tvos") - || qbs.targetOS.contains("watchos")) { + if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("tvos") + || qbs.targetOS.includes("watchos")) { switch (signingType) { case "app-store": return isDebug ? "iPhone Developer" : "iPhone Distribution"; } } - if (qbs.targetOS.contains("macos")) { + if (qbs.targetOS.includes("macos")) { switch (signingType) { case "app-store": return isDebug ? "Mac Developer" : "3rd Party Mac Developer Application"; @@ -160,27 +161,27 @@ CodeSignModule { readonly property bool _provisioningProfileAllowed: product.bundle && product.bundle.isBundle - && product.type.contains("application") + && product.type.includes("application") && xcode.platformType !== "simulator" // Required for tvOS, iOS, and watchOS (not simulators) // PROVISIONING_PROFILE_REQUIRED is specified only in Embedded-Device.xcspec in the // IDEiOSSupportCore IDE plugin, so we'll just write out the logic here manually readonly property bool _provisioningProfileRequired: - _provisioningProfileAllowed && !qbs.targetOS.contains("macos") + _provisioningProfileAllowed && !qbs.targetOS.includes("macos") // Not used on simulator platforms either but provisioning profiles aren't used there anyways readonly property string _provisioningProfilePlatform: { - if (qbs.targetOS.contains("macos")) + if (qbs.targetOS.includes("macos")) return "OSX"; - if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("watchos")) + if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("watchos")) return "iOS"; - if (qbs.targetOS.contains("tvos")) + if (qbs.targetOS.includes("tvos")) return "tvOS"; } readonly property string _embeddedProfileName: - (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] + (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] || "embedded.mobileprovision" setupBuildEnvironment: { var prefixes = product.xcode ? [ diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js index 463e7cbb7..482225ea2 100644 --- a/share/qbs/modules/codesign/codesign.js +++ b/share/qbs/modules/codesign/codesign.js @@ -43,10 +43,12 @@ function findSigningIdentities(searchString, team) { var matchedIdentities = {}; for (var key in identities) { var identity = identities[key]; - if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].contains(team)) + if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].includes(team)) continue; - if (searchString === key || identity.subjectInfo.CN.startsWith(searchString)) + if (searchString === key + || (identity.subjectInfo.CN && identity.subjectInfo.CN.startsWith(searchString))) { matchedIdentities[key] = identity; + } } return matchedIdentities; } @@ -103,7 +105,7 @@ function findBestProvisioningProfile(product, files) { // Provisioning profiles are not normally used with ad-hoc code signing or non-apps // We do these checks down here only for the automatic selection but not above because // if the user explicitly selects a provisioning profile it should be used no matter what - if (actualSigningIdentity.SHA1 === "-" || !product.type.contains("application")) + if (actualSigningIdentity.SHA1 === "-" || !product.type.includes("application")) return undefined; // Filter out any provisioning profiles we know to be unsuitable from the start @@ -114,7 +116,7 @@ function findBestProvisioningProfile(product, files) { var certCommonNames = (data["DeveloperCertificates"] || []).map(function (cert) { return Utilities.certificateInfo(cert).subjectInfo.CN; }); - if (!certCommonNames.contains(actualSigningIdentity.subjectInfo.CN)) { + if (!certCommonNames.includes(actualSigningIdentity.subjectInfo.CN)) { console.log("Skipping provisioning profile with no matching certificate names for '" + actualSigningIdentity.subjectInfo.CN + "' (found " + certCommonNames.join(", ") + "): " @@ -124,7 +126,7 @@ function findBestProvisioningProfile(product, files) { } var platforms = data["Platform"] || []; - if (platforms.length > 0 && profilePlatform && !platforms.contains(profilePlatform)) { + if (platforms.length > 0 && profilePlatform && !platforms.includes(profilePlatform)) { console.log("Skipping provisioning profile for platform " + platforms.join(", ") + " (current platform " + profilePlatform + ")" + ": " + profile.filePath); @@ -132,7 +134,7 @@ function findBestProvisioningProfile(product, files) { } if (teamIdentifier - && !data["TeamIdentifier"].contains(teamIdentifier) + && !data["TeamIdentifier"].includes(teamIdentifier) && data["TeamName"] !== teamIdentifier) { console.log("Skipping provisioning profile for team " + data["TeamIdentifier"] + " (" + data["TeamName"] + ") (current team " + teamIdentifier + ")" @@ -223,7 +225,7 @@ function findBestSignToolSearchPaths(arch) { }); function addSearchPath(searchPath) { - if (File.exists(searchPath) && !searchPaths.contains(searchPath)) { + if (File.exists(searchPath) && !searchPaths.includes(searchPath)) { searchPaths.push(searchPath); return true; } @@ -278,20 +280,21 @@ function prepareSign(project, product, inputs, outputs, input, output) { return cmds; var isBundle = "bundle.content" in outputs; - var outputFilePath = isBundle - ? FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName) - : outputs["codesign.signed_artifact"][0].filePath; - var outputFileName = isBundle - ? product.bundle.bundleName - : outputs["codesign.signed_artifact"][0].fileName; - var isProductBundle = product.bundle && product.bundle.isBundle; - // If the product is a bundle, just sign the bundle - // instead of signing the bundle and executable separately + var artifacts = []; + if (isBundle) { + artifacts = [{ + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName), + fileName: product.bundle.bundleName + }]; + } else { + artifacts = outputs["codesign.signed_artifact"]; + } + var isProductBundle = product.bundle && product.bundle.isBundle; var shouldSignArtifact = !isProductBundle || isBundle; var enableCodeSigning = product.codesign.enableCodeSigning; - if (enableCodeSigning && shouldSignArtifact) { + if (enableCodeSigning) { var actualSigningIdentity = product.codesign._actualSigningIdentity; if (!actualSigningIdentity) { throw "No codesigning identities (i.e. certificate and private key pairs) matching “" @@ -308,36 +311,53 @@ function prepareSign(project, product, inputs, outputs, input, output) { } } - var args = ["--force", "--sign", actualSigningIdentity.SHA1]; - - // If signingTimestamp is undefined or empty, do not specify the flag at all - - // this uses the system-specific default behavior - var signingTimestamp = product.codesign.signingTimestamp; - if (signingTimestamp) { - // 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; - args.push(flag); + // The codesign tool behaves weirdly. It can sign a bundle with a single artifact, but if + // say debug build variant is present, it starts complaining that it is not signed. + // We could always sign everything, but again, in case of a framework (but not in case of + // app or loadable bundle), codesign produces a warning that artifact is already signed. + // So, we skip signing the release artifact and only sign if other build variants present. + if (!shouldSignArtifact && artifacts.length == 1) { + artifacts = []; } + for (var i = 0; i < artifacts.length; ++i) { + if (!shouldSignArtifact + && artifacts[i].qbs && artifacts[i].qbs.buildVariant === "release") { + continue; + } + var outputFilePath = artifacts[i].filePath; + var outputFileName = artifacts[i].fileName; + + var args = ["--force", "--sign", actualSigningIdentity.SHA1]; + + // If signingTimestamp is undefined or empty, do not specify the flag at all - + // this uses the system-specific default behavior + var signingTimestamp = product.codesign.signingTimestamp; + if (signingTimestamp) { + // 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; + args.push(flag); + } - for (var j in inputs["codesign.xcent"]) { - args.push("--entitlements", inputs["codesign.xcent"][j].filePath); - break; // there should only be one - } + for (var j in inputs["codesign.xcent"]) { + args.push("--entitlements", inputs["codesign.xcent"][j].filePath); + break; // there should only be one + } - args = args.concat(product.codesign.codesignFlags || []); + args = args.concat(product.codesign.codesignFlags || []); - args.push(outputFilePath + subpath); - cmd = new Command(product.codesign.codesignPath, args); - cmd.description = "codesign " + outputFileName - + " (" + actualSigningIdentity.subjectInfo.CN + ")"; - cmd.outputFilePath = outputFilePath; - cmd.stderrFilterFunction = function(stderr) { - return stderr.replace(outputFilePath + ": replacing existing signature\n", ""); - }; - cmds.push(cmd); + args.push(outputFilePath + subpath); + cmd = new Command(product.codesign.codesignPath, args); + cmd.description = "codesign " + outputFileName + + " (" + actualSigningIdentity.subjectInfo.CN + ")"; + cmd.outputFilePath = outputFilePath; + cmd.stderrFilterFunction = function(stderr) { + return stderr.replace(outputFilePath + ": replacing existing signature\n", ""); + }; + cmds.push(cmd); + } } if (isBundle) { diff --git a/share/qbs/modules/codesign/signtool.qbs b/share/qbs/modules/codesign/signtool.qbs index afc91e7fc..111f0a307 100644 --- a/share/qbs/modules/codesign/signtool.qbs +++ b/share/qbs/modules/codesign/signtool.qbs @@ -29,21 +29,22 @@ ****************************************************************************/ import qbs.File +import qbs.Host import qbs.ModUtils import qbs.Probes import "codesign.js" as CODESIGN CodeSignModule { - condition: qbs.targetOS.contains("windows") - && qbs.hostOS.contains("windows") - && qbs.toolchain.contains("msvc") + condition: qbs.targetOS.includes("windows") + && Host.os().includes("windows") + && qbs.toolchain.includes("msvc") _canSignArtifacts: true Probes.BinaryProbe { id: signtoolProbe names: [codesignName] - searchPaths: CODESIGN.findBestSignToolSearchPaths(qbs.hostArchitecture) + searchPaths: CODESIGN.findBestSignToolSearchPaths(Host.architecture()) } codesignName: "signtool" @@ -82,7 +83,7 @@ CodeSignModule { description: "Path to the signing certificate PFX file." } - property path certificatePassword + property string certificatePassword PropertyOptions { name: "certificatePassword" description: "Password to use when opening a certificate PFX file." diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 0a3050370..bd1eaa242 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ // base for Cpp modules +import qbs.FileInfo import qbs.ModUtils import qbs.Utilities import qbs.WindowsUtils @@ -216,7 +217,7 @@ Module { property stringList knownArchitectures: [] property var toolchainDetails - property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + property string compilerExtension: FileInfo.executableSuffix() property string linkerMode: "automatic" PropertyOptions { diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 158c3c061..e20973acc 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -43,7 +43,7 @@ import "gcc.js" as Gcc UnixGCC { condition: false - Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") } Probes.BinaryProbe { id: lipoProbe @@ -76,7 +76,7 @@ UnixGCC { dynamicLibrarySuffix: ".dylib" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && (!product.aggregate || !!product.multiplexConfigurationId) && qbs.buildVariant !== "release" @@ -107,32 +107,42 @@ UnixGCC { property var defaultInfoPlist: { var dict = {}; - if (qbs.targetOS.contains("macos")) { + if (qbs.targetOS.includes("macos")) { dict["NSPrincipalClass"] = "NSApplication"; // needed for Retina display support + // QBS-1670: set this flag by default to avoid extensive GPU usage + dict["NSSupportsAutomaticGraphicsSwitching"] = true; + if (minimumMacosVersion) dict["LSMinimumSystemVersion"] = minimumMacosVersion; } + if (qbs.targetOS.includes("ios") && minimumIosVersion) + dict["MinimumOSVersion"] = minimumIosVersion; + else if (qbs.targetOS.includes("tvos") && minimumTvosVersion) + dict["MinimumOSVersion"] = minimumTvosVersion; + else if (qbs.targetOS.includes("watchos") && minimumWatchosVersion) + dict["MinimumOSVersion"] = minimumWatchosVersion; + if (qbs.targetOS.containsAny(["ios", "tvos"])) { dict["LSRequiresIPhoneOS"] = true; if (xcode.platformType === "device") { - if (qbs.targetOS.contains("ios")) { + if (qbs.targetOS.includes("ios")) { if (qbs.architecture === "arm64") dict["UIRequiredDeviceCapabilities"] = ["arm64"]; else dict["UIRequiredDeviceCapabilities"] = ["armv7"]; } - if (qbs.targetOS.contains("tvos")) + if (qbs.targetOS.includes("tvos")) dict["UIRequiredDeviceCapabilities"] = ["arm64"]; } } if (xcode.present) { var targetDevices = DarwinTools.targetedDeviceFamily(xcode.targetDevices); - if (qbs.targetOS.contains("ios")) + if (qbs.targetOS.includes("ios")) dict["UIDeviceFamily"] = targetDevices; if (qbs.targetOS.containsAny(["ios", "watchos"])) { @@ -143,13 +153,13 @@ UnixGCC { "UIInterfaceOrientationLandscapeRight" ]; - if (targetDevices.contains("ipad")) + if (targetDevices.includes("ipad")) dict["UISupportedInterfaceOrientations~ipad"] = orientations; - if (targetDevices.contains("watch")) + if (targetDevices.includes("watch")) dict["UISupportedInterfaceOrientations"] = orientations.slice(0, 2); - if (targetDevices.contains("iphone")) { + if (targetDevices.includes("iphone")) { orientations.splice(1, 1); dict["UISupportedInterfaceOrientations"] = orientations; } @@ -189,13 +199,13 @@ UnixGCC { // Set the corresponding environment variable even if the minimum OS version is undefined, // because this indicates the default deployment target for that OS - if (qbs.targetOS.contains("ios") && minimumIosVersion) + if (qbs.targetOS.includes("ios") && minimumIosVersion) env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion; - if (qbs.targetOS.contains("macos") && minimumMacosVersion) + if (qbs.targetOS.includes("macos") && minimumMacosVersion) env["MACOSX_DEPLOYMENT_TARGET"] = minimumMacosVersion; - if (qbs.targetOS.contains("watchos") && minimumWatchosVersion) + if (qbs.targetOS.includes("watchos") && minimumWatchosVersion) env["WATCHOS_DEPLOYMENT_TARGET"] = minimumWatchosVersion; - if (qbs.targetOS.contains("tvos") && minimumTvosVersion) + if (qbs.targetOS.includes("tvos") && minimumTvosVersion) env["TVOS_DEPLOYMENT_TARGET"] = minimumTvosVersion; if (xcode.present) @@ -208,7 +218,7 @@ UnixGCC { property string minimumDarwinVersionCompilerFlag property string minimumDarwinVersionLinkerFlag - property bool libcxxAvailable: qbs.toolchain.contains("clang") && cxxLanguageVersion !== "c++98" + property bool libcxxAvailable: qbs.toolchain.includes("clang") && cxxLanguageVersion !== "c++98" Rule { condition: enableAggregationRules @@ -261,7 +271,7 @@ UnixGCC { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true Artifact { diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 5060af2d6..f84028940 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.PathTools import qbs.Probes @@ -42,7 +43,7 @@ import 'cpp.js' as Cpp import 'gcc.js' as Gcc CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("gcc") + condition: qbs.toolchain && qbs.toolchain.includes("gcc") priority: -100 Depends { name: "codesign" } @@ -122,9 +123,9 @@ CppModule { precompiledHeaderSuffix: ".gch" - property bool compilerHasTargetOption: qbs.toolchain.contains("clang") + property bool compilerHasTargetOption: qbs.toolchain.includes("clang") && Utilities.versionCompare(compilerVersion, "3.1") >= 0 - property bool assemblerHasTargetOption: qbs.toolchain.contains("xcode") + property bool assemblerHasTargetOption: qbs.toolchain.includes("xcode") && Utilities.versionCompare(compilerVersion, "7") >= 0 property string target: targetArch ? [targetArch, targetVendor, targetSystem, targetAbi].join("-") @@ -169,7 +170,7 @@ CppModule { property string linkerVariant PropertyOptions { name: "linkerVariant" - allowedValues: ["bfd", "gold", "lld"] + allowedValues: ["bfd", "gold", "lld", "mold"] description: "Allows to specify the linker variant. Maps to gcc's and clang's -fuse-ld " + "option." } @@ -181,9 +182,9 @@ CppModule { property string toolchainPathPrefix: Gcc.pathPrefix(toolchainInstallPath, toolchainPrefix) property string binutilsPathPrefix: Gcc.pathPrefix(binutilsPath, toolchainPrefix) - property string cCompilerName: (qbs.toolchain.contains("clang") ? "clang" : "gcc") + property string cCompilerName: (qbs.toolchain.includes("clang") ? "clang" : "gcc") + compilerExtension - property string cxxCompilerName: (qbs.toolchain.contains("clang") ? "clang++" : "g++") + property string cxxCompilerName: (qbs.toolchain.includes("clang") ? "clang++" : "g++") + compilerExtension compilerPathByLanguage: ({ @@ -215,7 +216,7 @@ CppModule { linkerScriptFlag: "-T" readonly property bool shouldCreateSymlinks: { - return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat); + return createSymlinks && internalVersion && ["macho", "elf"].includes(imageFormat); } readonly property bool shouldSignArtifacts: codesign._canSignArtifacts @@ -250,18 +251,18 @@ CppModule { property var buildEnv: { var env = {}; - if (qbs.toolchain.contains("mingw")) - env.PATH = [toolchainInstallPath]; // For libwinpthread etc + if (qbs.toolchain.includes("mingw")) + env.PATH = toolchainInstallPath; // For libwinpthread etc return env; } exceptionHandlingModel: { - if (qbs.toolchain.contains("mingw")) { + if (qbs.toolchain.includes("mingw")) { // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html claims // __USING_SJLJ_EXCEPTIONS__ is defined as 1 when using SJLJ exceptions, but there don't // seem to be defines for the other models, so use the presence of the DLLs for now. var prefix = toolchainInstallPath; - if (!qbs.hostOS.contains("windows")) + if (!Host.os().includes("windows")) prefix = FileInfo.joinPaths(toolchainInstallPath, "..", "lib", "gcc", toolchainPrefix, [compilerVersionMajor, compilerVersionMinor].join(".")); @@ -313,7 +314,7 @@ CppModule { if (gccProbe.targetPlatform) { // Can't differentiate Darwin OSes at the compiler level alone if (gccProbe.targetPlatform === "darwin" - ? !qbs.targetOS.contains("darwin") + ? !qbs.targetOS.includes("darwin") : qbs.targetPlatform !== gccProbe.targetPlatform) isWrongTriple = true; } else if (qbs.targetPlatform) { @@ -350,7 +351,7 @@ CppModule { var validateFlagsFunction = function (value) { if (value) { for (var i = 0; i < value.length; ++i) { - if (["-target", "-triple", "-arch"].contains(value[i])) + if (["-target", "-triple", "-arch"].includes(value[i])) return false; } } @@ -402,7 +403,7 @@ CppModule { inputs: { var tags = ["obj", "res", "linkerscript", "versionscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; @@ -482,9 +483,9 @@ CppModule { var objCount = objs ? objs.length : 0; for (var i = 0; i < objCount; ++i) { var ft = objs[i].fileTags; - if (ft.contains("c_obj")) + if (ft.includes("c_obj")) tags.push("c_staticlibrary"); - if (ft.contains("cpp_obj")) + if (ft.includes("cpp_obj")) tags.push("cpp_staticlibrary"); } return [{ @@ -520,7 +521,7 @@ CppModule { inputs: { var tags = ["obj", "res", "linkerscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; @@ -563,7 +564,7 @@ CppModule { inputs: { var tags = ["obj", "res", "linkerscript"]; if (product.bundle && product.bundle.embedInfoPlist - && product.qbs.targetOS.contains("darwin")) { + && product.qbs.targetOS.includes("darwin")) { tags.push("aggregate_infoplist"); } return tags; diff --git a/share/qbs/modules/cpp/LinuxGCC.qbs b/share/qbs/modules/cpp/LinuxGCC.qbs index 14fb0a7e9..4b594a0aa 100644 --- a/share/qbs/modules/cpp/LinuxGCC.qbs +++ b/share/qbs/modules/cpp/LinuxGCC.qbs @@ -28,11 +28,12 @@ ** ****************************************************************************/ +import qbs.Host import qbs.Process UnixGCC { - condition: qbs.targetOS.contains('linux') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('linux') && + qbs.toolchain && qbs.toolchain.includes('gcc') priority: 1 targetVendor: "pc" @@ -41,7 +42,7 @@ UnixGCC { Probe { id: runPathsProbe - condition: !_skipAllChecks && qbs.targetPlatform === qbs.hostPlatform + condition: !_skipAllChecks && qbs.targetPlatform === Host.platform() property stringList systemRunPaths: [] configure: { var paths = []; diff --git a/share/qbs/modules/cpp/MingwBaseModule.qbs b/share/qbs/modules/cpp/MingwBaseModule.qbs index 831512c5c..c9cfe9bd8 100644 --- a/share/qbs/modules/cpp/MingwBaseModule.qbs +++ b/share/qbs/modules/cpp/MingwBaseModule.qbs @@ -44,9 +44,10 @@ GenericGCC { windowsApiCharacterSet: "unicode" platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) .concat("WIN32") + runtimeLibrary: "dynamic" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && qbs.buildVariant !== "release" && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) @@ -95,9 +96,9 @@ GenericGCC { var tf; try { tf = new TextFile(outputFilePath, TextFile.WriteOnly); - if (productType.contains("application")) + if (productType.includes("application")) tf.write("1 "); // CREATEPROCESS_MANIFEST_RESOURCE_ID - else if (productType.contains("dynamiclibrary")) + else if (productType.includes("dynamiclibrary")) tf.write("2 "); // ISOLATIONAWARE_MANIFEST_RESOURCE_ID tf.write("24 "); // RT_MANIFEST tf.writeLine(Utilities.cStringQuote(inputFilePath)); diff --git a/share/qbs/modules/cpp/UnixGCC.qbs b/share/qbs/modules/cpp/UnixGCC.qbs index 94dfb1907..6f377c5c8 100644 --- a/share/qbs/modules/cpp/UnixGCC.qbs +++ b/share/qbs/modules/cpp/UnixGCC.qbs @@ -31,8 +31,8 @@ import qbs.File GenericGCC { - condition: qbs.toolchain && qbs.toolchain.contains("gcc") - && qbs.targetOS.contains("unix") + condition: qbs.toolchain && qbs.toolchain.includes("gcc") + && qbs.targetOS.includes("unix") priority: -50 dynamicLibraryPrefix: "lib" diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs index 6e19a0e7d..7c45d3c6b 100644 --- a/share/qbs/modules/cpp/android-gcc.qbs +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -39,7 +39,7 @@ import 'gcc.js' as Gcc LinuxGCC { Depends { name: "Android.ndk" } - condition: qbs.targetOS.contains("android") && qbs.toolchain && qbs.toolchain.contains("llvm") + condition: qbs.targetOS.includes("android") && qbs.toolchain && qbs.toolchain.includes("llvm") priority: 2 rpaths: [] @@ -58,7 +58,7 @@ LinuxGCC { return "i686"; } } - property string targetDir: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + property string targetDir: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "") property string triple: [archLibsDir, targetSystem, targetDir].join("-") property string libsDir: FileInfo.joinPaths(sysroot, "usr", "lib", triple); @@ -128,7 +128,7 @@ LinuxGCC { // When using ndk r19c, llvm doesn't add sysroot/usr/include/c++/v1 to the path // But it works starting with ndk r20b systemIncludePaths: (Utilities.versionCompare(Android.ndk.version, "20") < 0) ? - FileInfo.joinPaths(sysroot, "usr", "include", "c++", "v1") : "" + FileInfo.joinPaths(sysroot, "usr", "include", "c++", "v1") : [] defines: ["ANDROID", "__ANDROID__"] @@ -155,7 +155,7 @@ LinuxGCC { target: [targetArch, targetSystem, targetAbi].join("-") targetSystem: "linux" - targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + + targetAbi: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "") + Android.ndk.platformVersion endianness: "little" @@ -170,7 +170,7 @@ LinuxGCC { prepare: { var stripArgs = ["--strip-all", "-o", output.filePath, input.filePath]; var stripCmd = new Command(product.cpp.stripPath, stripArgs); - stripCmd.description = "Stripping unneeded symbols from " + input.fileName; + stripCmd.description = "stripping unneeded symbols from " + input.fileName; return stripCmd; } } diff --git a/share/qbs/modules/cpp/cosmic.js b/share/qbs/modules/cpp/cosmic.js index 50aea6e25..4979f33f7 100644 --- a/share/qbs/modules/cpp/cosmic.js +++ b/share/qbs/modules/cpp/cosmic.js @@ -387,6 +387,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli cmd.workingDirectory = product.buildDirectory; cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; cmds.push(cmd); cmds.push(renameObjectFile(project, product, inputs, outputs, input, output)); @@ -415,6 +416,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl cmd.workingDirectory = product.buildDirectory; cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; cmds.push(cmd); return cmds; } @@ -426,6 +428,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { cmd.workingDirectory = product.buildDirectory; cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } @@ -433,7 +436,8 @@ function prepareArchiver(project, product, inputs, outputs, input, output) { var args = archiverFlags(project, product, inputs, outputs); var cmd = new Command(product.cpp.archiverPath, args); cmd.workingDirectory = product.buildDirectory; - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } diff --git a/share/qbs/modules/cpp/cosmic.qbs b/share/qbs/modules/cpp/cosmic.qbs index 46c904ac5..8bf0f22a3 100644 --- a/share/qbs/modules/cpp/cosmic.qbs +++ b/share/qbs/modules/cpp/cosmic.qbs @@ -37,7 +37,7 @@ import "cosmic.js" as COSMIC import "cpp.js" as Cpp CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("cosmic") + condition: qbs.toolchain && qbs.toolchain.includes("cosmic") Probes.BinaryProbe { id: compilerPathProbe diff --git a/share/qbs/modules/cpp/cpp.js b/share/qbs/modules/cpp/cpp.js index 315b902ee..9f907580a 100644 --- a/share/qbs/modules/cpp/cpp.js +++ b/share/qbs/modules/cpp/cpp.js @@ -42,7 +42,7 @@ function languageVersion(versionArray, knownValues, lang) { return versions[0]; for (var i = 0; i < knownValues.length; ++i) { var candidate = knownValues[i]; - if (versions.contains(candidate)) + if (versions.includes(candidate)) return candidate; } var version = versions[0]; @@ -51,9 +51,15 @@ function languageVersion(versionArray, knownValues, lang) { return version; } -function extractMacros(output) { +function extractMacros(output, regexp) { var m = {}; - output.trim().split(/\r?\n/g).map(function (line) { + output.trim().split(/\r?\n/g).map(function(line) { + if (regexp) { + var match = regexp.exec(line); + if (!match) + return; + line = match[1]; + } var prefix = "#define "; if (!line.startsWith(prefix)) return; @@ -128,7 +134,7 @@ function assemblerOutputArtifacts(input) { } function compilerOutputArtifacts(input, inputs) { - var objTags = input.fileTags.contains("cpp_intermediate_object") + var objTags = input.fileTags.includes("cpp_intermediate_object") ? ["intermediate_obj"] : ["obj"]; if (inputs) { @@ -214,6 +220,7 @@ function precompiledHeaderOutputArtifacts(input, product, lang, generateObjects) function collectLibraryDependencies(product) { var seen = {}; + var seenObjectFiles = []; var result = []; function addFilePath(filePath, wholeArchive, productName) { @@ -226,7 +233,7 @@ function collectLibraryDependencies(product) { var artifactFilePaths = artifacts.map(function(a) { return a.filePath; }); var wholeArchive = dep.parameters.cpp && dep.parameters.cpp.linkWholeArchive; var artifactsAreImportLibs = artifacts.length > 0 - && artifacts[0].fileTags.contains("dynamiclibrary_import"); + && artifacts[0].fileTags.includes("dynamiclibrary_import"); for (var i = 0; i < artifactFilePaths.length; ++i) { addFilePath(artifactFilePaths[i], wholeArchive, artifactsAreImportLibs ? dep.name : undefined); @@ -242,16 +249,23 @@ function collectLibraryDependencies(product) { function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); } - function handleExternalLibraries(tag, suffix) { + function handleExternalLibraries(tag, libSuffix, objSuffix) { var externalLibs = sanitizedModuleListProperty(obj, "cpp", tag) || []; externalLibs.forEach(function(libName) { - if (!libName.endsWith(suffix) && !libName.startsWith('@')) - libName += suffix; + var isObjectFile = objSuffix && libName.endsWith(objSuffix); + if (isObjectFile) { + if (seenObjectFiles.includes(libName)) + return; + seenObjectFiles.push(libName); + } + if (!libName.endsWith(libSuffix) && !isObjectFile && !libName.startsWith('@')) + libName += libSuffix; addFilePath(libName, false); }); } handleExternalLibraries("staticLibraries", - obj.moduleProperty("cpp", "staticLibrarySuffix")); + obj.moduleProperty("cpp", "staticLibrarySuffix"), + obj.moduleProperty("cpp", "objectSuffix")); handleExternalLibraries("dynamicLibraries", obj.moduleProperty("cpp", "dynamicLibraryImportSuffix")); } @@ -313,7 +327,10 @@ function collectIncludePaths(input) { var includePaths = input.cpp.includePaths; if (includePaths) allIncludePaths = allIncludePaths.uniqueConcat(includePaths); - return allIncludePaths; + var builtIns = input.cpp.compilerIncludePaths; + return allIncludePaths.filter(function(p) { + return !builtIns.includes(p); + }); } function collectSystemIncludePaths(input) { @@ -324,7 +341,10 @@ function collectSystemIncludePaths(input) { var distributionIncludePaths = input.cpp.distributionIncludePaths; if (distributionIncludePaths) allIncludePaths = allIncludePaths.uniqueConcat(distributionIncludePaths); - return allIncludePaths; + var builtIns = input.cpp.compilerIncludePaths; + return allIncludePaths.filter(function(p) { + return !builtIns.includes(p); + }); } function collectPreincludePaths(input) { diff --git a/share/qbs/modules/cpp/darwin.js b/share/qbs/modules/cpp/darwin.js index e4f740dbe..0223c5ed3 100644 --- a/share/qbs/modules/cpp/darwin.js +++ b/share/qbs/modules/cpp/darwin.js @@ -73,7 +73,7 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) { // approach for all bundle types. var defaultVariant; if (!buildVariants.some(function (x) { return x.name === "release"; }) - && product.multiplexByQbsProperties.contains("buildVariants") + && product.multiplexByQbsProperties.includes("buildVariants") && product.qbs.buildVariants && product.qbs.buildVariants.length > 1) { var defaultBuildVariant = product.qbs.defaultBuildVariant; buildVariants.map(function (variant) { @@ -137,7 +137,7 @@ function prepareLipo(project, product, inputs, outputs, input, output) { for (var p in inputs) inputs[p] = inputs[p].filter(function(inp) { return inp.product.name === product.name; }); var allInputs = [].concat.apply([], Object.keys(inputs).map(function (tag) { - return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].contains(tag) + return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].includes(tag) ? inputs[tag] : []; })); diff --git a/share/qbs/modules/cpp/dmc.js b/share/qbs/modules/cpp/dmc.js index 7303c67fc..ea7cd7bb5 100644 --- a/share/qbs/modules/cpp/dmc.js +++ b/share/qbs/modules/cpp/dmc.js @@ -38,7 +38,47 @@ var TemporaryDir = require("qbs.TemporaryDir"); var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); -function dumpMacros(compilerFilePath, tag) { +function targetFlags(platform, architecture, extender, consoleApp, type) { + if (platform === "dos") { + if (architecture === "x86_16") { + if (extender === "dosz") + return ["-mz"]; + else if (extender === "dosr") + return ["-mr"]; + return ["-mc"]; + } else if (architecture === "x86") { + if (extender === "dosx") + return ["-mx"]; + else if (extender === "dosp") + return ["-mp"]; + } + } else if (platform === "windows") { + var flags = []; + if (architecture === "x86_16") { + flags.push("-ml"); + if (type.includes("application") && !consoleApp) + flags.push("-WA"); + else if (type.includes("dynamiclibrary")) + flags.push("-WD"); + } else if (architecture === "x86") { + flags.push("-mn"); + if (type.includes("application")) + flags.push("-WA"); + else if (type.includes("dynamiclibrary")) + flags.push("-WD"); + } + return flags; + } + return []; +} + +function languageFlags(tag) { + if (tag === "cpp") + return ["-cpp"]; + return []; +} + +function dumpMacros(compilerPath, platform, architecture, extender, tag) { // Note: The DMC compiler does not support the predefined/ macros dumping. So, we do it // with the following trick, where we try to create and compile a special temporary file // and to parse the console output with the own magic pattern: #define <key> <value>. @@ -55,7 +95,8 @@ function dumpMacros(compilerFilePath, tag) { "_MSDOS", "MSDOS", // Prepare the OS/2 target macros. "__OS2__", - "WIN32", "_WIN32", + // Prepare the Windows target macros. + "WIN32", "_WIN32", "__NT__", // Prepare extended the 32 and 16 bit DOS target macros. "DOS386", "DOS16RM", // Prepare the memory model macros. @@ -96,9 +137,10 @@ function dumpMacros(compilerFilePath, tag) { var process = new Process(); process.setWorkingDirectory(outputDirectory.path()); - var args = ["-c"].concat((tag === "cpp") ? ["-cpp"] : [], - FileInfo.toWindowsSeparators(outputFilePath)); - process.exec(compilerFilePath, args, false); + var lang = languageFlags(tag); + var target = targetFlags(platform, architecture, extender, false, ["application"]); + var args = ["-c"].concat(lang, target, FileInfo.toWindowsSeparators(outputFilePath)); + process.exec(compilerPath, args, false); File.remove(outputFilePath); var out = process.readStdOut(); return Cpp.extractMacros(out); @@ -166,6 +208,12 @@ function depsOutputArtifacts(input, product) { function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { var args = ["-c"]; + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + args = args.concat(languageFlags(tag)); + args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture, + product.cpp.extenderName, product.consoleApplication, + product.type)); + // Input. args.push(FileInfo.toWindowsSeparators(input.filePath)); // Output. @@ -178,7 +226,6 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { // Defines. args = args.concat(Cpp.collectDefinesArguments(input)); - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); if (tag === "cpp") { // We need to add the paths to the STL includes, because the DMC compiler does // not handle it by default (because the STL libraries is a separate port). @@ -226,8 +273,6 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("-wx"); if (tag === "cpp") { - args.push("-cpp"); - // Exceptions flag. if (input.cpp.enableExceptions) args.push("-Ae"); @@ -285,6 +330,10 @@ function linkerFlags(project, product, inputs, outputs) { var useCompilerDriver = useCompilerDriverLinker(product); if (useCompilerDriver) { + args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture, + product.cpp.extenderName, product.consoleApplication, + product.type)); + // Input objects. args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { return FileInfo.toWindowsSeparators(path); @@ -301,19 +350,24 @@ function linkerFlags(project, product, inputs, outputs) { })); // Output. - if (product.type.contains("application")) { + if (product.type.includes("application")) { args.push("-o" + FileInfo.toWindowsSeparators(outputs.application[0].filePath)); - args.push("-WA"); - args.push("/SUBSYSTEM:" + (product.consoleApplication ? "CONSOLE" : "WINDOWS")); - } else if (product.type.contains("dynamiclibrary")) { + args.push("-L/" + (product.cpp.generateLinkerMapFile ? "MAP" : "NOMAP")); + if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86") + args.push("-L/SUBSYSTEM:" + (product.consoleApplication ? "CONSOLE" : "WINDOWS")); + } else if (product.type.includes("dynamiclibrary")) { args.push("-o" + FileInfo.toWindowsSeparators(outputs.dynamiclibrary[0].filePath)); - args.push("-WD"); + if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86") { + args.push("kernel32.lib"); + args.push("-L/IMPLIB:" + FileInfo.toWindowsSeparators( + outputs.dynamiclibrary_import[0].filePath)); + } } if (product.cpp.debugInformation) - args.push("/DEBUG"); + args.push("-L/DEBUG"); - args.push("/NOLOGO", "/SILENT"); + args.push("-L/NOLOGO", "-L/SILENT"); } // Misc flags. @@ -383,20 +437,10 @@ function renameLinkerMapFile(project, product, inputs, outputs, input, output) { cmd.newMapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix); cmd.oldMapFilePath = buildLinkerMapFilePath(target, ".map"); cmd.silent = true; - cmd.sourceCode = function() { File.move(oldMapFilePath, newMapFilePath); }; - return cmd; -} - -// It is a workaround to generate the import library file from the dynamic library. -// Because the DMC compiler use the separate `implib.exe` tool for that. -function createImportLib(project, product, inputs, outputs, input, output) { - var args = [ - FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath), - FileInfo.toWindowsSeparators(outputs.dynamiclibrary[0].filePath) - ]; - var cmd = new Command(input.cpp.implibPath, args); - cmd.workingDirectory = product.buildDirectory; - cmd.silent = true; + cmd.sourceCode = function() { + if (oldMapFilePath !== newMapFilePath) + File.move(oldMapFilePath, newMapFilePath); + }; return cmd; } @@ -406,6 +450,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli cmd.workingDirectory = product.buildDirectory; cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; return [cmd]; } @@ -415,6 +460,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl cmd.workingDirectory = product.buildDirectory; cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; return [cmd]; } @@ -428,18 +474,14 @@ function prepareLinker(project, product, inputs, outputs, input, output) { cmd.workingDirectory = product.buildDirectory; cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; cmds.push(cmd); - if (outputs.dynamiclibrary - || (outputs.application && !product.cpp.generateLinkerMapFile)) { - if (outputs.dynamiclibrary) - cmds.push(createImportLib(project, product, inputs, outputs, input, output)); - cmds.push(removeLinkerMapFile(project, product, inputs, outputs, input, output)); - } else if (outputs.application - && product.cpp.generateLinkerMapFile - && (product.cpp.linkerMapSuffix !== ".map")) { + if (product.cpp.generateLinkerMapFile) cmds.push(renameLinkerMapFile(project, product, inputs, outputs, input, output)); - } + else + cmds.push(removeLinkerMapFile(project, product, inputs, outputs, input, output)); + return cmds; } @@ -447,8 +489,9 @@ function prepareArchiver(project, product, inputs, outputs, input, output) { var args = archiverFlags(project, product, inputs, outputs); var cmd = new Command(product.cpp.archiverPath, args); cmd.workingDirectory = product.buildDirectory; - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } @@ -458,5 +501,6 @@ function prepareRccCompiler(project, product, inputs, outputs, input, output) { cmd.workingDirectory = product.buildDirectory; cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; return [cmd]; } diff --git a/share/qbs/modules/cpp/dmc.qbs b/share/qbs/modules/cpp/dmc.qbs index 68a0033e0..ac89550e8 100644 --- a/share/qbs/modules/cpp/dmc.qbs +++ b/share/qbs/modules/cpp/dmc.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.PathTools import qbs.Probes import qbs.Utilities @@ -37,12 +38,12 @@ import "dmc.js" as DMC import "cpp.js" as Cpp CppModule { - condition: qbs.hostOS.contains("windows") && qbs.toolchain && qbs.toolchain.contains("dmc") + condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("dmc") Probes.BinaryProbe { id: compilerPathProbe condition: !toolchainInstallPath && !_skipAllChecks - names: ["cxcorm"] + names: ["dmc"] } Probes.DmcProbe { @@ -50,10 +51,13 @@ CppModule { condition: !_skipAllChecks compilerFilePath: compilerPath enableDefinesByLanguage: enableCompilerDefinesByLanguage + _targetPlatform: qbs.targetPlatform + _targetArchitecture: qbs.architecture + _targetExtender: extenderName } qbs.architecture: dmcProbe.found ? dmcProbe.architecture : original - qbs.targetPlatform: dmcProbe.targetPlatform + qbs.targetPlatform: dmcProbe.found ? dmcProbe.targetPlatform : original compilerVersionMajor: dmcProbe.versionMajor compilerVersionMinor: dmcProbe.versionMinor @@ -80,11 +84,21 @@ CppModule { property string archiverName: "lib.exe" property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName) - property string implibName: "implib.exe" - property string implibPath: FileInfo.joinPaths(toolchainInstallPath, implibName) property string rccCompilerName: "rcc.exe" property string rccCompilerPath: FileInfo.joinPaths(toolchainInstallPath, rccCompilerName) + property string extenderName + PropertyOptions { + name: "extenderName" + allowedValues: [undefined, "dosz", "dosr", "dosx", "dosp"] + description: "Specifies the DOS memory extender to compile with:\n" + + " - \"dosz\" is the ZPM 16 bit DOS Extender\n" + + " - \"dosr\" is the Rational 16 bit DOS Extender\n" + + " - \"dosx\" is the DOSX 32 bit DOS Extender\n" + + " - \"dosp\" is the Pharlap 32 bit DOS Extender\n" + ; + } + runtimeLibrary: "dynamic" staticLibrarySuffix: ".lib" @@ -92,7 +106,12 @@ CppModule { executableSuffix: ".exe" objectSuffix: ".obj" - imageFormat: "pe" + imageFormat: { + if (qbs.targetPlatform === "dos") + return "mz"; + else if (qbs.targetPlatform === "windows") + return "pe"; + } defineFlag: "-D" includeFlag: "-I" @@ -100,7 +119,7 @@ CppModule { preincludeFlag: "-HI" libraryPathFlag: "-L/packcode" - knownArchitectures: ["x86"] + knownArchitectures: ["x86", "x86_16"] Rule { id: assembler diff --git a/share/qbs/modules/cpp/freebsd-gcc.qbs b/share/qbs/modules/cpp/freebsd-gcc.qbs index 929c4e557..e04ed7f2b 100644 --- a/share/qbs/modules/cpp/freebsd-gcc.qbs +++ b/share/qbs/modules/cpp/freebsd-gcc.qbs @@ -29,13 +29,14 @@ ****************************************************************************/ import "freebsd.js" as FreeBSD +import qbs.Host UnixGCC { - condition: qbs.targetOS.contains("freebsd") && - qbs.toolchain && qbs.toolchain.contains("gcc") + condition: qbs.targetOS.includes("freebsd") && + qbs.toolchain && qbs.toolchain.includes("gcc") priority: 1 - targetSystem: "freebsd" + (qbs.hostOS.contains("freebsd") ? FreeBSD.hostKernelRelease() : "") + targetSystem: "freebsd" + (Host.os().includes("freebsd") ? FreeBSD.hostKernelRelease() : "") distributionIncludePaths: ["/usr/local/include"] distributionLibraryPaths: ["/usr/local/lib"] diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index 13cdb4a3d..90f8fc933 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -32,6 +32,7 @@ var Codesign = require("../codesign/codesign.js"); var Cpp = require("cpp.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); var DarwinTools = require("qbs.DarwinTools"); var ModUtils = require("qbs.ModUtils"); var PathTools = require("qbs.PathTools"); @@ -77,6 +78,7 @@ function useCompilerDriverLinker(product, inputs) { function collectLibraryDependencies(product, isDarwin) { var publicDeps = {}; + var privateDeps = {}; var objects = []; var objectByFilePath = {}; var tagForLinkingAgainstSharedLib = product.cpp.imageFormat === "pe" @@ -169,6 +171,8 @@ function collectLibraryDependencies(product, isDarwin) { && typeof dep.artifacts[tagForLinkingAgainstSharedLib] !== "undefined"; if (!isStaticLibrary && !isDynamicLibrary) return; + if (isBelowIndirectDynamicLib && privateDeps[dep.name]) + return; var nextIsBelowIndirectDynamicLib = isBelowIndirectDynamicLib || isDynamicLibrary; dep.dependencies.forEach(function(depdep) { @@ -192,6 +196,7 @@ function collectLibraryDependencies(product, isDarwin) { publicDeps[dep.name] = true; } else { addArtifactFilePaths(dep, tagForLinkingAgainstSharedLib, addPrivateFilePath); + privateDeps[dep.name] = true; } } } @@ -203,7 +208,6 @@ function collectLibraryDependencies(product, isDarwin) { product.dependencies.forEach(traverseDirectDependency); addExternalLibs(product); - var seenRPathLinkDirs = {}; var result = { libraries: [], rpath_link: [] }; objects.forEach( function (obj) { @@ -214,10 +218,7 @@ function collectLibraryDependencies(product, isDarwin) { framework: obj.framework }); } else { var dirPath = FileInfo.path(obj.filePath); - if (!seenRPathLinkDirs.hasOwnProperty(dirPath)) { - seenRPathLinkDirs[dirPath] = true; - result.rpath_link.push(dirPath); - } + result.rpath_link.push(dirPath); } }); return result; @@ -229,7 +230,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { if (useCompilerDriverLinker(product, inputs)) { var sep = ","; - var useXlinker = linkerFlags.some(function (f) { return f.contains(sep); }); + var useXlinker = linkerFlags.some(function (f) { return f.includes(sep); }); if (useXlinker) { // One or more linker arguments contain the separator character itself // Use -Xlinker to handle these @@ -246,7 +247,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { return xlinkerFlags; } - if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.contains("-Xlinker")) { + if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.includes("-Xlinker")) { console.warn("Encountered -Xlinker linker flag escape sequence. This may cause the " + "target to fail to link. Please do not escape these flags manually; " + "qbs does that for you."); @@ -261,7 +262,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) { } function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPath) { - var isDarwin = product.qbs.targetOS.contains("darwin"); + var isDarwin = product.qbs.targetOS.includes("darwin"); var libraryDependencies = collectLibraryDependencies(product, isDarwin); var frameworks = product.cpp.frameworks; var weakFrameworks = product.cpp.weakFrameworks; @@ -274,7 +275,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var escapableLinkerFlags = []; - if (primaryOutput.fileTags.contains("dynamiclibrary")) { + if (primaryOutput.fileTags.includes("dynamiclibrary")) { if (isDarwin) { args.push((function () { var tags = ["c", "cpp", "objc", "objcpp", "asm_cpp"]; @@ -299,7 +300,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat } } - if (primaryOutput.fileTags.contains("loadablemodule")) + if (primaryOutput.fileTags.includes("loadablemodule")) args.push(isDarwin ? "-bundle" : "-shared"); if (primaryOutput.fileTags.containsAny(["dynamiclibrary", "loadablemodule"])) { @@ -322,7 +323,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var sysroot = product.cpp.syslibroot; if (sysroot) { - if (product.qbs.toolchain.contains("qcc")) + if (product.qbs.toolchain.includes("qcc")) escapableLinkerFlags.push("--sysroot=" + sysroot); else if (isDarwin) escapableLinkerFlags.push("-syslibroot", sysroot); @@ -339,7 +340,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat function fixupRPath(rpath) { // iOS, tvOS, watchOS, and others, are fine - if (!product.qbs.targetOS.contains("macos")) + if (!product.qbs.targetOS.includes("macos")) return rpath; // ...as are newer versions of macOS @@ -355,11 +356,11 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat } function isNotSystemRunPath(p) { - return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.contains(p) - && !canonicalSystemRunPaths.contains(File.canonicalFilePath(p))); + return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.includes(p) + && !canonicalSystemRunPaths.includes(File.canonicalFilePath(p))); }; - if (!product.qbs.targetOS.contains("windows")) { + if (!product.qbs.targetOS.includes("windows")) { for (i in rpaths) { if (isNotSystemRunPath(rpaths[i])) escapableLinkerFlags.push("-rpath", fixupRPath(rpaths[i])); @@ -369,13 +370,16 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat if (product.cpp.entryPoint) escapableLinkerFlags.push("-e", product.cpp.entryPoint); - if (product.qbs.toolchain.contains("mingw")) { + if (product.qbs.toolchain.includes("mingw")) { if (product.consoleApplication !== undefined) escapableLinkerFlags.push("-subsystem", product.consoleApplication ? "console" : "windows"); var minimumWindowsVersion = product.cpp.minimumWindowsVersion; if (minimumWindowsVersion) { + // workaround for QBS-1724, mingw seems to be broken + if (Utilities.versionCompare(minimumWindowsVersion, "6.2") > 0) + minimumWindowsVersion = "6.2"; var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem'); if (subsystemVersion) { var major = subsystemVersion.split('.')[0]; @@ -397,13 +401,13 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var stdlib = isLinkingCppObjects ? product.cpp.cxxStandardLibrary : undefined; - if (stdlib && product.qbs.toolchain.contains("clang")) + if (stdlib && product.qbs.toolchain.includes("clang")) args.push("-stdlib=" + stdlib); // Flags for library search paths var allLibraryPaths = Cpp.collectLibraryPaths(product); - if (systemRunPaths.length > 0) - allLibraryPaths = allLibraryPaths.filter(isNotSystemRunPath); + var builtIns = product.cpp.compilerLibraryPaths + allLibraryPaths = allLibraryPaths.filter(function(p) { return !builtIns.includes(p); }); args = args.concat(allLibraryPaths.map(function(path) { return product.cpp.libraryPathFlag + path })); escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs)); @@ -471,9 +475,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat var symbolLinkMode = dep.symbolLinkMode; if (isDarwin && symbolLinkMode) { - if (!["lazy", "reexport", "upward", "weak"].contains(symbolLinkMode)) + if (!["lazy", "reexport", "upward", "weak"].includes(symbolLinkMode)) throw new Error("unknown value '" + symbolLinkMode + "' for cpp.symbolLinkMode"); + } + var supportsLazyMode = Utilities.versionCompare(product.cpp.compilerVersion, "15.0.0") < 0; + if (isDarwin && symbolLinkMode && (symbolLinkMode !== "lazy" || supportsLazyMode)) { if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@')) escapableLinkerFlags.push("-" + symbolLinkMode + "_library", lib); else if (dep.framework) @@ -529,6 +536,8 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat Array.prototype.push.apply(escapedLinkerFlags, args); if (useCompilerDriver) escapedLinkerFlags = escapedLinkerFlags.concat(Cpp.collectMiscLinkerArguments(product)); + if (product.qbs.toolchain.includes("mingw") && product.cpp.runtimeLibrary === "static") + escapedLinkerFlags = ['-static-libgcc', '-static-libstdc++'].concat(escapedLinkerFlags); return escapedLinkerFlags; } @@ -574,7 +583,7 @@ function languageTagFromFileExtension(toolchain, fileName) { "s" : "asm", "S" : "asm_cpp" }; - if (!toolchain.contains("clang")) + if (!toolchain.includes("clang")) m["sx"] = "asm_cpp"; // clang does NOT recognize .sx return m[fileName.substring(i + 1)]; } @@ -582,7 +591,7 @@ function languageTagFromFileExtension(toolchain, fileName) { // Older versions of the QNX SDK have C and C++ compilers whose filenames differ only by case, // which won't work in case insensitive environments like Win32+NTFS, HFS+ and APFS function isLegacyQnxSdk(config) { - return config.qbs.toolchain.contains("qcc") && config.qnx && !config.qnx.qnx7; + return config.qbs.toolchain.includes("qcc") && config.qnx && !config.qnx.qnx7; } function effectiveCompilerInfo(toolchain, input, output) { @@ -590,7 +599,7 @@ function effectiveCompilerInfo(toolchain, input, output) { var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); // Whether we're compiling a precompiled header or normal source file - var pchOutput = output.fileTags.contains(tag + "_pch"); + var pchOutput = output.fileTags.includes(tag + "_pch"); var compilerPathByLanguage = input.cpp.compilerPathByLanguage; if (compilerPathByLanguage) @@ -598,7 +607,7 @@ function effectiveCompilerInfo(toolchain, input, output) { if (!compilerPath || tag !== languageTagFromFileExtension(toolchain, input.fileName) || isLegacyQnxSdk(input)) { - if (input.qbs.toolchain.contains("qcc")) + if (input.qbs.toolchain.includes("qcc")) language = qnxLangArgs(input, tag); else language = ["-x", languageName(tag) + (pchOutput ? '-header' : '')]; @@ -692,6 +701,22 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers {"name": "gcc", "version": "4.7"} ] }, + "c17": { + "fallback": "c11", + "toolchains": [ + {"name": "xcode", "version": "10.2"}, + {"name": "clang", "version": "7.0"}, + {"name": "gcc", "version": "8.1"} + ] + }, + "c2x": { + "fallback": "c17", + "toolchains": [ + {"name": "xcode", "version": "11.4"}, + {"name": "clang", "version": "9.0"}, + {"name": "gcc", "version": "9.0"} + ] + }, "c++14": { "fallback": "c++1y", "toolchains": [ @@ -711,7 +736,7 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers "c++20": { "fallback": "c++2a", "toolchains": [ - {"name": "xcode"}, // ?? + {"name": "xcode", "version": "12.5"}, {"name": "clang", "version": "11.0"}, {"name": "gcc", "version": "10.1"} ] @@ -730,7 +755,7 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers if (m) { for (var idx = 0; idx < m.toolchains.length; ++idx) { var tc = m.toolchains[idx]; - if (toolchain.contains(tc.name)) { + if (toolchain.includes(tc.name)) { // If we found our toolchain and it doesn't yet support the language standard // we're requesting, or we're using an older version that only supports the // preliminary flag, use that. @@ -752,7 +777,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { // Determine which C-language we're compiling var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); - if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].contains(tag)) + if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].includes(tag)) throw ("unsupported source language: " + tag); var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain, @@ -785,12 +810,12 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { args = args.concat(configFlags(input)); - if (!input.qbs.toolchain.contains("qcc")) + if (!input.qbs.toolchain.includes("qcc")) args.push('-pipe'); if (input.cpp.enableReproducibleBuilds) { var toolchain = product.qbs.toolchain; - if (!toolchain.contains("clang")) { + if (!toolchain.includes("clang")) { var hashString = FileInfo.relativePath(project.sourceDirectory, input.filePath); var hash = Utilities.getHash(hashString); args.push("-frandom-seed=0x" + hash.substring(0, 8)); @@ -798,8 +823,8 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { var major = product.cpp.compilerVersionMajor; var minor = product.cpp.compilerVersionMinor; - if ((toolchain.contains("clang") && (major > 3 || (major === 3 && minor >= 5))) || - (toolchain.contains("gcc") && (major > 4 || (major === 4 && minor >= 9)))) { + if ((toolchain.includes("clang") && (major > 3 || (major === 3 && minor >= 5))) || + (toolchain.includes("gcc") && (major > 4 || (major === 4 && minor >= 9)))) { args.push("-Wdate-time"); } } @@ -827,7 +852,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { } var visibility = input.cpp.visibility; - if (!product.qbs.toolchain.contains("mingw")) { + if (!product.qbs.toolchain.includes("mingw")) { if (visibility === 'hidden' || visibility === 'minimal') args.push('-fvisibility=hidden'); if ((visibility === 'hiddenInlines' || visibility === 'minimal') && tag === 'cpp') @@ -843,7 +868,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { args = args.concat(Cpp.collectMiscCompilerArguments(input, tag)); var pchTag = compilerInfo.tag + "_pch"; - var pchOutput = output.fileTags.contains(pchTag); + var pchOutput = output.fileTags.includes(pchTag); var pchInputs = explicitlyDependsOn[pchTag]; if (!pchOutput && pchInputs && pchInputs.length === 1 && ModUtils.moduleProperty(input, 'usePrecompiledHeader', tag)) { @@ -856,7 +881,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { args = args.concat(Cpp.collectPreincludePathsArguments(input)); var positionIndependentCode = input.cpp.positionIndependentCode; - if (positionIndependentCode && !product.qbs.targetOS.contains("windows")) + if (positionIndependentCode && !product.qbs.targetOS.includes("windows")) args.push('-fPIC'); var cppFlags = input.cpp.cppFlags; @@ -868,7 +893,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { args = args.concat(Cpp.collectSystemIncludePathsArguments(input)); var minimumWindowsVersion = input.cpp.minimumWindowsVersion; - if (minimumWindowsVersion && product.qbs.targetOS.contains("windows")) { + if (minimumWindowsVersion && product.qbs.targetOS.includes("windows")) { var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex'); if (hexVersion) { var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ]; @@ -881,7 +906,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { switch (tag) { case "c": case "objc": - var knownValues = ["c11", "c99", "c90", "c89"]; + var knownValues = ["c2x", "c17", "c11", "c99", "c90", "c89"]; return Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C"); case "cpp": case "objcpp": @@ -902,7 +927,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) { if (tag === "cpp" || tag === "objcpp") { var cxxStandardLibrary = product.cpp.cxxStandardLibrary; - if (cxxStandardLibrary && product.qbs.toolchain.contains("clang")) { + if (cxxStandardLibrary && product.qbs.toolchain.includes("clang")) { args.push("-stdlib=" + cxxStandardLibrary); } } @@ -917,7 +942,7 @@ function additionalCompilerAndLinkerFlags(product) { var args = [] var requireAppExtensionSafeApi = product.cpp.requireAppExtensionSafeApi; - if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.contains("darwin")) { + if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.includes("darwin")) { args.push(requireAppExtensionSafeApi ? "-fapplication-extension" : "-fno-application-extension"); } @@ -968,7 +993,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output) { function compilerEnvVars(config, compilerInfo) { - if (config.qbs.toolchain.contains("qcc")) + if (config.qbs.toolchain.includes("qcc")) return ["QCC_CONF_PATH"]; var list = ["CPATH", "TMPDIR"]; @@ -980,15 +1005,15 @@ function compilerEnvVars(config, compilerInfo) list.push("OBJC_INCLUDE_PATH"); else if (compilerInfo.tag === "objccpp") list.push("OBJCPLUS_INCLUDE_PATH"); - if (config.qbs.targetOS.contains("macos")) + if (config.qbs.targetOS.includes("macos")) list.push("MACOSX_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("ios")) + else if (config.qbs.targetOS.includes("ios")) list.push("IPHONEOS_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("tvos")) + else if (config.qbs.targetOS.includes("tvos")) list.push("TVOS_DEPLOYMENT_TARGET"); - else if (config.qbs.targetOS.contains("watchos")) + else if (config.qbs.targetOS.includes("watchos")) list.push("WATCHOS_DEPLOYMENT_TARGET"); - if (config.qbs.toolchain.contains("clang")) { + if (config.qbs.toolchain.includes("clang")) { list.push("TEMP", "TMP"); } else { list.push("LANG", "LC_CTYPE", "LC_MESSAGES", "LC_ALL", "GCC_COMPARE_DEBUG", @@ -999,7 +1024,7 @@ function compilerEnvVars(config, compilerInfo) function linkerEnvVars(config, inputs) { - if (config.qbs.toolchain.contains("clang") || config.qbs.toolchain.contains("qcc")) + if (config.qbs.toolchain.includes("clang") || config.qbs.toolchain.includes("qcc")) return []; var list = ["GNUTARGET", "LDEMULATION", "COLLECT_NO_DEMANGLE"]; if (useCompilerDriverLinker(config, inputs)) @@ -1009,7 +1034,7 @@ function linkerEnvVars(config, inputs) function setResponseFileThreshold(command, product) { - if (product.qbs.targetOS.contains("windows") && product.qbs.hostOS.contains("windows")) + if (product.qbs.targetOS.includes("windows") && Host.os().includes("windows")) command.responseFileThreshold = 10000; } @@ -1017,7 +1042,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain, input, output); var compilerPath = compilerInfo.path; - var pchOutput = output.fileTags.contains(compilerInfo.tag + "_pch"); + var pchOutput = output.fileTags.includes(compilerInfo.tag + "_pch"); var args = compilerFlags(project, product, input, output, explicitlyDependsOn); var wrapperArgsLength = 0; @@ -1107,7 +1132,7 @@ function getSymbolInfo(product, inputFile) // construct the list of defined symbols by subtracting. var undefinedGlobalSymbols = collectStdoutLines(command, args.concat(["-u", inputFile])); result.definedGlobalSymbols = result.allGlobalSymbols.filter(function(line) { - return !undefinedGlobalSymbols.contains(line); }); + return !undefinedGlobalSymbols.includes(line); }); result.success = true; } catch (e) { console.debug("Failed to collect symbols for shared library: nm command '" @@ -1207,8 +1232,13 @@ function createSymbolCheckingCommands(product, outputs) { oldSymbols = oldNmResult.allGlobalSymbols; newSymbols = newNmResult.allGlobalSymbols; } else { - oldSymbols = oldNmResult.definedGlobalSymbols; - newSymbols = newNmResult.definedGlobalSymbols; + var weakFilter = function(line) { + var symbolType = line.split(/\s+/)[1]; + return symbolType != "v" && symbolType != "V" + && symbolType != "w" && symbolType != "W"; + }; + oldSymbols = oldNmResult.definedGlobalSymbols.filter(weakFilter); + newSymbols = newNmResult.definedGlobalSymbols.filter(weakFilter); } if (oldSymbols.length !== newSymbols.length) { console.debug("List of relevant symbols differs for '" + libFilePath + "'."); @@ -1314,7 +1344,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var responseFileArgumentIndex = wrapperArgsLength; // qcc doesn't properly handle response files, so we have to do it manually - var useQnxResponseFileHack = product.qbs.toolchain.contains("qcc") + var useQnxResponseFileHack = product.qbs.toolchain.includes("qcc") && useCompilerDriverLinker(product, inputs); if (useQnxResponseFileHack) { // qcc needs to see at least one object/library file to think it has something to do, @@ -1334,7 +1364,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { setResponseFileThreshold(cmd, product); commands.push(cmd); - if (product.qbs.targetOS.contains("darwin")) { + if (product.qbs.targetOS.includes("darwin")) { if (!product.aggregate) { commands = commands.concat(separateDebugInfoCommandsDarwin( product, outputs, [primaryOutput])); @@ -1468,7 +1498,7 @@ function dumpDefaultPaths(env, compilerFilePath, args, nullDevice, pathListSepar if (libraryPaths.length === 0) libraryPaths.push(sysroot + "/lib", sysroot + "/usr/lib"); - if (frameworkPaths.length === 0 && targetOS.contains("darwin")) + if (frameworkPaths.length === 0 && targetOS.includes("darwin")) frameworkPaths.push(sysroot + "/System/Library/Frameworks"); return { @@ -1496,9 +1526,9 @@ function targetLinkerFlags(targetArch, targetOS) { "x86_64": "elf_x86_64", } }; - if (targetOS.contains("windows")) + if (targetOS.includes("windows")) return linkerFlags["windows"][targetArch]; - else if (targetOS.contains("freebsd")) + else if (targetOS.includes("freebsd")) return linkerFlags["freebsd"][targetArch]; return linkerFlags["other"][targetArch]; } diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js index 3848761f5..ed58ac262 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -450,6 +450,7 @@ function dumpMacros(compilerFilePath, tag) { args.push(cppLanguageOption(compilerFilePath)); var p = new Process(); + p.setWorkingDirectory(tempDir.path()); p.exec(compilerFilePath, args, true); var outFile = new TextFile(outFilePath, TextFile.ReadOnly); return Cpp.extractMacros(outFile.readAll()); @@ -471,6 +472,7 @@ function dumpCompilerIncludePaths(compilerFilePath, tag) { var includePaths = []; var p = new Process(); + p.setWorkingDirectory(tempDir.path()); // It is possible that the process can return an error code in case the // compiler does not support the `--IDE3` flag. So, don't throw an error in this case. p.exec(compilerFilePath, args, false); @@ -573,7 +575,7 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("--c89"); break; default: - // Default C language version is C11/C99 that + // Default C language version is C18/C11/C99 that // depends on the IAR version. break; } @@ -682,7 +684,7 @@ function linkerFlags(project, product, inputs, outputs) { args = args.concat(Cpp.collectLibraryDependenciesArguments(product)); // Linker scripts. - args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs)); + args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true)); // Silent output generation flag. args.push(product.cpp.linkerSilentFlag); @@ -729,6 +731,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var cmd = new Command(compilerPath, args); cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; return [cmd]; } @@ -738,6 +741,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; return [cmd]; } @@ -748,6 +752,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var cmd = new Command(linkerPath, args); cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } @@ -755,8 +760,9 @@ function prepareArchiver(project, product, inputs, outputs, input, output) { var args = archiverFlags(project, product, inputs, outputs); var archiverPath = product.cpp.archiverPath; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; cmd.stdoutFilterFunction = function(output) { return ""; }; diff --git a/share/qbs/modules/cpp/iar.qbs b/share/qbs/modules/cpp/iar.qbs index f02c033ce..558635ef5 100644 --- a/share/qbs/modules/cpp/iar.qbs +++ b/share/qbs/modules/cpp/iar.qbs @@ -37,7 +37,7 @@ import "cpp.js" as Cpp import "iar.js" as IAR CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("iar") + condition: qbs.toolchain && qbs.toolchain.includes("iar") Probes.BinaryProbe { id: compilerPathProbe diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs index 2d73c8125..968b873da 100644 --- a/share/qbs/modules/cpp/ios-gcc.qbs +++ b/share/qbs/modules/cpp/ios-gcc.qbs @@ -31,16 +31,17 @@ import qbs.DarwinTools import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('ios') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('ios') && + qbs.toolchain && qbs.toolchain.includes('gcc') minimumIosVersion: { - if (qbs.architecture == "armv7a") + if (Host.architecture() == "armv7a") return "6.0"; // XCode 12 requres version (at least 8.0) to be present in the -target triplet // when compiling for ios-simulator @@ -50,10 +51,10 @@ DarwinGCC { targetSystem: "ios" + (minimumIosVersion || "") minimumDarwinVersion: minimumIosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("ios-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("ios-simulator") ? "-mios-simulator-version-min" : "-miphoneos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("ios-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("ios-simulator") ? "-ios_simulator_version_min" : "-iphoneos_version_min" @@ -68,13 +69,13 @@ DarwinGCC { readonly property stringList simulatorObjcFlags: { // default in Xcode and also required for building 32-bit Simulator binaries with ARC // since the default ABI version is 0 for 32-bit targets - return qbs.targetOS.contains("ios-simulator") + return qbs.targetOS.includes("ios-simulator") ? ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"] : []; } Rule { - condition: !product.qbs.targetOS.contains("ios-simulator") + condition: !product.qbs.targetOS.includes("ios-simulator") inputsFromDependencies: ["bundle.content"] Artifact { diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 5a5e165c8..8f3297aa2 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -818,7 +818,7 @@ function linkerFlags(project, product, inputs, outputs) { args = args.concat(Cpp.collectLibraryDependenciesArguments(product)); // Linker scripts. - args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs)); + args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true)); // Map file generation flag. if (product.cpp.generateLinkerMapFile) @@ -917,6 +917,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var cmd = new Command(compilerPath, args); cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; if (isMcsArchitecture(architecture)) { cmd.maxExitCode = 1; cmd.stdoutFilterFunction = filterMcsOutput; @@ -944,6 +945,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; if (isMcsArchitecture(architecture)) { cmd.maxExitCode = 1; cmd.stdoutFilterFunction = filterMcsOutput; @@ -962,6 +964,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var cmd = new Command(linkerPath, args); cmd.description = "linking " + primaryOutput.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; if (isMcsArchitecture(architecture)) { cmd.maxExitCode = 1; cmd.stdoutFilterFunction = filterMcsOutput; @@ -977,8 +980,9 @@ function prepareArchiver(project, product, inputs, outputs, input, output) { var archiverPath = product.cpp.archiverPath; var architecture = product.cpp.architecture; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; if (isMcsArchitecture(architecture)) { cmd.stdoutFilterFunction = filterMcsOutput; } else if (isC166Architecture(architecture)) { diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs index c66e95a47..5922ad4e2 100644 --- a/share/qbs/modules/cpp/keil.qbs +++ b/share/qbs/modules/cpp/keil.qbs @@ -30,12 +30,13 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.Probes import "cpp.js" as Cpp import "keil.js" as KEIL CppModule { - condition: qbs.hostOS.contains("windows") && qbs.toolchain && qbs.toolchain.contains("keil") + condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("keil") Probes.BinaryProbe { id: compilerPathProbe diff --git a/share/qbs/modules/cpp/macos-gcc.qbs b/share/qbs/modules/cpp/macos-gcc.qbs index 612b46ae8..c667bbc29 100644 --- a/share/qbs/modules/cpp/macos-gcc.qbs +++ b/share/qbs/modules/cpp/macos-gcc.qbs @@ -33,8 +33,8 @@ import qbs.Utilities DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('macos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('macos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "macosx" + (minimumMacosVersion || "") diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index b206bd4ba..1700c21b3 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -79,8 +79,8 @@ function hasCxx17Option(input) { // Probably this is not the earliest version to support the flag, but we have tested this one // and it's a pain to find out the exact minimum. - return Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0 - || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 7); + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 7) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0; } function hasZCplusPlusOption(input) @@ -92,18 +92,24 @@ function hasZCplusPlusOption(input) // clang-cl supports this option starting around-ish versions 8/9, but it // ignores this option, so this doesn't really matter // https://reviews.llvm.org/D45877 - return Utilities.versionCompare(input.cpp.compilerVersion, "19.14.26433") >= 0 - || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 9); + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 9) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.14.26433") >= 0; } function hasCxx20Option(input) { - return Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30133.0") >= 0 - || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 13); + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30133.0") >= 0; +} + +function hasCVerOption(input) +{ + return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13) + || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30138.0") >= 0; } function supportsExternalIncludesOption(input) { - if (input.qbs.toolchain.contains("clang-cl")) + if (input.qbs.toolchain.includes("clang-cl")) return false; // Exclude clang-cl. // This option was introcuded since MSVC 2017 v15.6 (aka _MSC_VER 19.13). // But due to some MSVC bugs: @@ -112,16 +118,15 @@ function supportsExternalIncludesOption(input) { return Utilities.versionCompare(input.cpp.compilerVersion, "19.16") >= 0; } -function addLanguageVersionFlag(input, args) { +function addCxxLanguageVersionFlag(input, args) { var cxxVersion = Cpp.languageVersion(input.cpp.cxxLanguageVersion, ["c++23", "c++20", "c++17", "c++14", "c++11", "c++98"], "C++"); if (!cxxVersion) return; - // Visual C++ 2013, Update 3 - var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0 - // or clang-cl - || input.qbs.toolchain.contains("clang-cl"); + // Visual C++ 2013, Update 3 or clang-cl + var hasStdOption = input.qbs.toolchain.includes("clang-cl") + || Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0; if (!hasStdOption) return; @@ -138,8 +143,27 @@ function addLanguageVersionFlag(input, args) { args.push(flag); } +function addCLanguageVersionFlag(input, args) { + var cVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, + ["c17", "c11"], "C"); + if (!cVersion) + return; + + var hasStdOption = hasCVerOption(input); + if (!hasStdOption) + return; + + var flag; + if (cVersion === "c17") + flag = "/std:c17"; + else if (cVersion === "c11") + flag = "/std:c11"; + if (flag) + args.push(flag); +} + function handleClangClArchitectureFlags(product, architecture, flags) { - if (product.qbs.toolchain.contains("clang-cl")) { + if (product.qbs.toolchain.includes("clang-cl")) { if (architecture === "x86") flags.push("-m32"); else if (architecture === "x86_64") @@ -156,7 +180,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli // Determine which C-language we're compiling var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(Object.keys(outputs))); - if (!["c", "cpp"].contains(tag)) + if (!["c", "cpp"].includes(tag)) throw ("unsupported source language"); var enableExceptions = input.cpp.enableExceptions; @@ -225,14 +249,17 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path); })); - var includeFlag = input.cpp.includeFlag; - if (supportsExternalIncludesOption(input)) { - args.push("/experimental:external"); - var enforcesSlashW = - Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30037") >= 0 - if (enforcesSlashW) - args.push("/external:W0") - includeFlag = input.cpp.systemIncludeFlag; + var includeFlag = input.qbs.toolchain.includes("clang-cl") + ? input.cpp.systemIncludeFlag : input.cpp.includeFlag; + if (!input.qbs.toolchain.includes("clang-cl")) { + if (supportsExternalIncludesOption(input)) { + args.push("/experimental:external"); + var enforcesSlashW = + Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30037") >= 0 + if (enforcesSlashW) + args.push("/external:W0") + includeFlag = input.cpp.systemIncludeFlag; + } } var systemIncludePaths = Cpp.collectSystemIncludePaths(input); args = args.concat([].uniqueConcat(systemIncludePaths).map(function(path) { @@ -277,9 +304,10 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli // Language if (tag === "cpp") { args.push("/TP"); - addLanguageVersionFlag(input, args); + addCxxLanguageVersionFlag(input, args); } else if (tag === "c") { args.push("/TC"); + addCLanguageVersionFlag(input, args); } // Whether we're compiling a precompiled header or normal source file @@ -287,7 +315,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var pchInputs = explicitlyDependsOn[tag + "_pch"]; if (pchOutput) { // create PCH - if (input.qbs.toolchain.contains("clang-cl")) { + if (input.qbs.toolchain.includes("clang-cl")) { // clang-cl does not support /Yc flag without filename args.push("/Yc" + FileInfo.toWindowsSeparators(input.filePath)); // clang-cl complains when pch file is not included @@ -345,7 +373,8 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli function linkerSupportsWholeArchive(product) { - return Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0 + return product.qbs.toolchainType.includes("clang-cl") || + Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0 } function handleDiscardProperty(product, flags) { @@ -361,16 +390,16 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var linkDLL = (outputs.dynamiclibrary ? true : false) var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0]) var debugInformation = product.cpp.debugInformation; - var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"], + var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"] || [], function (a) { return a.filePath; }); - var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"], + var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"] || [], function (a) { return a.filePath; }); var generateManifestFiles = !linkDLL && product.cpp.generateManifestFile; - var useClangCl = product.qbs.toolchain.contains("clang-cl"); + var useClangCl = product.qbs.toolchain.includes("clang-cl"); var canEmbedManifest = useClangCl || product.cpp.compilerVersionMajor >= 17 // VS 2012 var linkerPath = effectiveLinkerPath(product, inputs); diff --git a/share/qbs/modules/cpp/qnx-qcc.qbs b/share/qbs/modules/cpp/qnx-qcc.qbs index a39a6a998..69288ddd8 100644 --- a/share/qbs/modules/cpp/qnx-qcc.qbs +++ b/share/qbs/modules/cpp/qnx-qcc.qbs @@ -33,8 +33,8 @@ import qbs.FileInfo UnixGCC { Depends { name: "qnx" } - condition: qbs.targetOS.contains("qnx") && - qbs.toolchain && qbs.toolchain.contains("qcc") + condition: qbs.targetOS.includes("qnx") && + qbs.toolchain && qbs.toolchain.includes("qcc") priority: 1 distributionIncludePaths: FileInfo.joinPaths(qnx.targetDir, "usr", "include") @@ -79,7 +79,7 @@ UnixGCC { toolchainPrefix: target + "-" - targetVendor: ["x86", "x86_64"].contains(qbs.architecture) ? "pc" : base + targetVendor: ["x86", "x86_64"].includes(qbs.architecture) ? "pc" : base targetSystem: "nto" targetAbi: "qnx" + qnx.version + (qnxTargetArchName === "armv7le" ? "eabi" : "") diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index b3dfd92b4..49da8a715 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -33,6 +33,7 @@ var Cpp = require("cpp.js"); var Environment = require("qbs.Environment"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); var PathTools = require("qbs.PathTools"); var Process = require("qbs.Process"); @@ -285,18 +286,18 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { // C language version flags. if (tag === "c") { - var knownValues = ["c11", "c99", "c89"]; + var knownValues = ["c2x", "c17", "c11", "c99", "c89"]; var cLanguageVersion = Cpp.languageVersion( input.cpp.cLanguageVersion, knownValues, "C"); switch (cLanguageVersion) { + case "c17": + cLanguageVersion = "c11"; + // fall through case "c89": - args.push("--std-c89"); - break; case "c99": - args.push("--std-c99"); - break; case "c11": - args.push("--std-c11"); + case "c2x": + args.push("--std-" + cLanguageVersion); break; } } @@ -395,7 +396,7 @@ function buildLinkerMapFilePath(target, suffix) { // We need to replace the '\r\n\' line endings with the'\n' line // endings for each generated object file. function patchObjectFile(project, product, inputs, outputs, input, output) { - var isWindows = input.qbs.hostOS.contains("windows"); + var isWindows = Host.os().includes("windows"); if (isWindows && input.cpp.debugInformation) { var cmd = new JavaScriptCommand(); cmd.objectPath = outputs.obj[0].filePath; @@ -460,10 +461,9 @@ function renameLinkerMapFile(project, product, inputs, outputs, input, output) { // remove a listing files only after the linking completes. function removeCompilerListingFiles(project, product, inputs, outputs, input, output) { var cmd = new JavaScriptCommand(); - cmd.objects = inputs.obj.map(function(a) { return a; }); cmd.silent = true; cmd.sourceCode = function() { - objects.forEach(function(object) { + inputs.obj.forEach(function(object) { if (!object.filePath.endsWith(".c" + object.cpp.objectSuffix)) return; // Skip the assembler generated objects. if (!object.cpp.generateCompilerListingFiles @@ -505,6 +505,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var cmd = new Command(compilerPath, args); cmd.description = "compiling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "compiler"; cmds.push(cmd); cmd = patchObjectFile(project, product, inputs, outputs, input, output); @@ -525,6 +526,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; + cmd.jobPool = "assembler"; cmds.push(cmd); cmd = patchObjectFile(project, product, inputs, outputs, input, output); @@ -541,6 +543,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { var cmd = new Command(linkerPath, args); cmd.description = "linking " + outputs.application[0].fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; cmds.push(cmd); cmd = removeCompilerListingFiles(project, product, inputs, outputs, input, output); @@ -562,7 +565,8 @@ function prepareArchiver(project, product, inputs, outputs, input, output) { var args = archiverFlags(project, product, inputs, outputs); var archiverPath = product.cpp.archiverPath; var cmd = new Command(archiverPath, args); - cmd.description = "linking " + output.fileName; + cmd.description = "creating " + output.fileName; cmd.highlight = "linker"; + cmd.jobPool = "linker"; return [cmd]; } diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs index 4e6f05bc0..24cb6d738 100644 --- a/share/qbs/modules/cpp/sdcc.qbs +++ b/share/qbs/modules/cpp/sdcc.qbs @@ -35,7 +35,7 @@ import "cpp.js" as Cpp import "sdcc.js" as SDCC CppModule { - condition: qbs.toolchain && qbs.toolchain.contains("sdcc") + condition: qbs.toolchain && qbs.toolchain.includes("sdcc") Probes.BinaryProbe { id: compilerPathProbe diff --git a/share/qbs/modules/cpp/setuprunenv.js b/share/qbs/modules/cpp/setuprunenv.js index 550d08d63..2ea331a30 100644 --- a/share/qbs/modules/cpp/setuprunenv.js +++ b/share/qbs/modules/cpp/setuprunenv.js @@ -30,11 +30,12 @@ var FileInfo = require("qbs.FileInfo"); var File = require("qbs.File"); +var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); // TODO: append/prepend functionality should go to qbs.Environment function addNewElement(list, elem) { - if (!list.contains(elem)) + if (!list.includes(elem)) list.push(elem); } @@ -48,7 +49,7 @@ function artifactDir(artifact) function addExternalLibPath(product, list, path) { addNewElement(list, path); - if (product.qbs.hostOS.contains("windows") && FileInfo.fileName(path) === "lib") { + if (Host.os().includes("windows") && FileInfo.fileName(path) === "lib") { var binPath = FileInfo.joinPaths(FileInfo.path(path), "bin"); if (File.exists(binPath)) addNewElement(list, binPath); @@ -57,7 +58,7 @@ function addExternalLibPath(product, list, path) function gatherPaths(product, libPaths, frameworkPaths, seenProducts) { - if (seenProducts.contains(product.name)) + if (seenProducts.includes(product.name)) return; seenProducts.push(product.name); @@ -66,6 +67,8 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts) product.cpp.libraryPaths.forEach(function(p) { addExternalLibPath(product, libPaths, p); }); if (product.cpp && product.cpp.frameworkPaths) product.cpp.frameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); }); + if (product.cpp && product.cpp.systemFrameworkPaths) + product.cpp.systemFrameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); }); // Extract paths from dynamic libraries, if they are given as file paths. if (product.cpp && product.cpp.dynamicLibraries) { @@ -103,10 +106,10 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts) function setupRunEnvironment(product, config) { - if (config.contains("ignore-lib-dependencies")) + if (config.includes("ignore-lib-dependencies")) return; - if (product.qbs.hostPlatform !== product.qbs.targetPlatform) + if (Host.platform() !== product.qbs.targetPlatform) return; var libPaths = []; @@ -117,8 +120,8 @@ function setupRunEnvironment(product, config) if (runPaths && runPaths.length > 0) { var canonicalRunPaths = runPaths.map(function(p) { return File.canonicalFilePath(p); }); var filterFunc = function(libPath) { - return !runPaths.contains(libPath) - && !canonicalRunPaths.contains(File.canonicalFilePath(libPath)); + return !runPaths.includes(libPath) + && !canonicalRunPaths.includes(File.canonicalFilePath(libPath)); }; libPaths = libPaths.filter(filterFunc); frameworkPaths = frameworkPaths.filter(filterFunc); @@ -126,19 +129,19 @@ function setupRunEnvironment(product, config) if (libPaths.length > 0) { var envVarName; - if (product.qbs.targetOS.contains("windows")) + if (product.qbs.targetOS.includes("windows")) envVarName = "PATH"; - else if (product.qbs.targetOS.contains("macos")) + else if (product.qbs.targetOS.includes("macos")) envVarName = "DYLD_LIBRARY_PATH"; else envVarName = "LD_LIBRARY_PATH"; - var envVar = new ModUtils.EnvironmentVariable(envVarName, product.qbs.pathListSeparator, - product.qbs.hostOS.contains("windows")); + var envVar = new ModUtils.EnvironmentVariable(envVarName, FileInfo.pathListSeparator(), + Host.os().includes("windows")); libPaths.forEach(function(p) { envVar.prepend(p); }); envVar.set(); } - if (product.qbs.targetOS.contains("macos") && frameworkPaths.length > 0) { + if (product.qbs.targetOS.includes("macos") && frameworkPaths.length > 0) { envVar = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", ':', false); frameworkPaths.forEach(function(p) { envVar.prepend(p); }); envVar.set(); diff --git a/share/qbs/modules/cpp/tvos-gcc.qbs b/share/qbs/modules/cpp/tvos-gcc.qbs index 19bc9b787..eba3b091e 100644 --- a/share/qbs/modules/cpp/tvos-gcc.qbs +++ b/share/qbs/modules/cpp/tvos-gcc.qbs @@ -30,16 +30,16 @@ DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('tvos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('tvos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "tvos" + (minimumTvosVersion || "") minimumDarwinVersion: minimumTvosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("tvos-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("tvos-simulator") ? "-mtvos-simulator-version-min" : "-mtvos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("tvos-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("tvos-simulator") ? "-tvos_simulator_version_min" : "-tvos_version_min" } diff --git a/share/qbs/modules/cpp/watchos-gcc.qbs b/share/qbs/modules/cpp/watchos-gcc.qbs index 46e4bf874..3a160f6d0 100644 --- a/share/qbs/modules/cpp/watchos-gcc.qbs +++ b/share/qbs/modules/cpp/watchos-gcc.qbs @@ -31,16 +31,16 @@ DarwinGCC { priority: 1 - condition: qbs.targetOS.contains('watchos') && - qbs.toolchain && qbs.toolchain.contains('gcc') + condition: qbs.targetOS.includes('watchos') && + qbs.toolchain && qbs.toolchain.includes('gcc') targetSystem: "watchos" + (minimumWatchosVersion || "") minimumDarwinVersion: minimumWatchosVersion - minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("watchos-simulator") + minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("watchos-simulator") ? "-mwatchos-simulator-version-min" : "-mwatchos-version-min" - minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("watchos-simulator") + minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("watchos-simulator") ? "-watchos_simulator_version_min" : "-watchos_version_min" } diff --git a/share/qbs/modules/cpp/watcom.js b/share/qbs/modules/cpp/watcom.js new file mode 100644 index 000000000..9ae893db9 --- /dev/null +++ b/share/qbs/modules/cpp/watcom.js @@ -0,0 +1,568 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +var Cpp = require("cpp.js"); +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathTools = require("qbs.PathTools"); +var Process = require("qbs.Process"); +var TemporaryDir = require("qbs.TemporaryDir"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function toolchainDetails(qbs) { + var platform = qbs.targetPlatform; + var details = {}; + if (platform === "dos") { + details.imageFormat = "mz"; + details.executableSuffix = ".exe"; + } else if (platform === "os2") { + details.imageFormat = "pe"; + details.executableSuffix = ".exe"; + details.dynamicLibrarySuffix = ".dll"; + } else if (platform === "windows") { + details.imageFormat = "pe"; + details.executableSuffix = ".exe"; + details.dynamicLibrarySuffix = ".dll"; + } else if (platform === "linux") { + details.imageFormat = "elf"; + details.executableSuffix = ""; + details.dynamicLibrarySuffix = ".so"; + } + return details; +} + +function languageFlag(tag) { + if (tag === "c") + return "-xc"; + else if (tag === "cpp") + return "-xc++"; +} + +function targetFlag(platform, architecture, type) { + if (platform === "dos") { + if (architecture === "x86_16") + return "-bdos"; + else if (architecture === "x86") + return "-bdos4g"; + } else if (platform === "os2") { + if (architecture === "x86_16") + return "-bos2"; + else if (architecture === "x86") + return "-bos2v2"; + } else if (platform === "windows") { + if (architecture === "x86_16") { + if (type.includes("dynamiclibrary")) + return "-bwindows_dll"; + return "-bwindows"; + } else if (architecture === "x86") { + if (type.includes("dynamiclibrary")) + return "-bnt_dll"; + return "-bnt"; + } + } else if (platform === "linux") { + return "-blinux"; + } +} + +function guessVersion(macros) { + var version = parseInt(macros["__WATCOMC__"], 10) + || parseInt(macros["__WATCOM_CPLUSPLUS__"], 10); + if (version) { + return { major: parseInt((version - 1100) / 100), + minor: parseInt(version / 10) % 10, + patch: ((version % 10) > 0) ? parseInt(version % 10) : 0 } + } +} + +function guessEnvironment(hostOs, platform, architecture, + toolchainInstallPath, pathListSeparator) { + var toolchainRootPath = FileInfo.path(toolchainInstallPath); + if (!File.exists(toolchainRootPath)) { + throw "Unable to deduce environment due to compiler root directory: '" + + toolchainRootPath + "' does not exist"; + } + + var env = {}; + + function setVariable(key, properties, path, separator) { + var values = []; + for (var i = 0; i < properties.length; ++i) { + if (path) { + var fullpath = FileInfo.joinPaths(path, properties[i]); + values.push(FileInfo.toNativeSeparators(fullpath)); + } else { + values.push(properties[i]); + } + } + env[key] = values.join(separator); + } + + setVariable("WATCOM", [toolchainRootPath], undefined, pathListSeparator); + setVariable("EDPATH", ["eddat"], toolchainRootPath, pathListSeparator); + + if (hostOs.includes("linux")) + setVariable("PATH", ["binl64", "binl"], toolchainRootPath, pathListSeparator); + else if (hostOs.includes("windows")) + setVariable("PATH", ["binnt64", "binnt"], toolchainRootPath, pathListSeparator); + + if (platform === "linux") { + setVariable("INCLUDE", ["lh"], toolchainRootPath, pathListSeparator); + } else { + // Common for DOS, Windows, OS/2. + setVariable("WIPFC", ["wipfc"], toolchainRootPath, pathListSeparator); + setVariable("WHTMLHELP", ["binnt/help"], toolchainRootPath, pathListSeparator); + + var includes = ["h"]; + if (platform === "dos") { + // Same includes as before. + } else if (platform === "os2") { + if (architecture === "x86") + includes = includes.concat(["h/os2"]); + else if (architecture === "x86_16") + includes = includes.concat(["h/os21x"]); + } else if (platform === "windows") { + if (architecture === "x86") + includes = includes.concat(["h/nt", "h/nt/directx", "h/nt/ddk"]); + else if (architecture === "x86_16") + includes = includes.concat(["h/win"]); + } else { + throw "Unable to deduce environment for unsupported target platform: '" + + platform + "'"; + } + + setVariable("INCLUDE", includes, toolchainRootPath, pathListSeparator); + } + + return env; +} + +function dumpMacros(environment, compilerPath, platform, architecture, tag) { + // Note: The Open Watcom compiler does not support the predefined + // macros dumping. So, we do it with the following trick, where we try + // to create and compile a special temporary file and to parse the console + // output with the own magic pattern: #define <key> <value>. + + var outputDirectory = new TemporaryDir(); + var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c"); + var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly); + outputFile.writeLine("#define VALUE_TO_STRING(x) #x"); + outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)"); + outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \"#var\" \"VALUE(var)"); + // Declare all available pre-defined macros of Watcon compiler. + var keys = [ + // Prepare the DOS target macros. + "__DOS__", "_DOS", "MSDOS", + // Prepare the OS/2 target macros. + "__OS2__", + // Prepare the QNX target macros. + "__QNX__", + // Prepare the Netware target macros. + "__NETWARE__", "__NETWARE_386__", + // Prepare the Windows target macros. + "__NT__", "__WINDOWS__", "_WINDOWS", "__WINDOWS_386__", + // Prepare the Linux and Unix target macros. + "__LINUX__", "__UNIX__", + // Prepare the 16-bit target specific macros. + "__I86__", "M_I86", "_M_I86", "_M_IX86", + // Prepare the 32-bit target specific macros. + "__386__", "M_I386", "_M_I386", "_M_IX86", + // Prepare the indicated options macros. + "_MT", "_DLL", "__FPI__", "__CHAR_SIGNED__", "__INLINE_FUNCTIONS__", + "_CPPRTTI", "_CPPUNWIND", "NO_EXT_KEYS", + // Prepare the common memory model macros. + "__FLAT__", "__SMALL__", "__MEDIUM__", + "__COMPACT__", "__LARGE__", "__HUGE__", + // Prepare the 16-bit memory model macros. + "M_I86SM", "_M_I86SM", "M_I86MM", "_M_I86MM", "M_I86CM", + "_M_I86CM", "M_I86LM", "_M_I86LM", "M_I86HM", "_M_I86HM", + // Prepare the 32-bit memory model macros. + "M_386FM", "_M_386FM", "M_386SM", "M_386MM", "_M_386MM", + "M_386CM", "_M_386CM", "M_386LM", "_M_386LM", + // Prepare the compiler macros. + "__X86__", "__cplusplus", "__WATCOMC__", "__WATCOM_CPLUSPLUS__", + "_INTEGRAL_MAX_BITS", "_PUSHPOP_SUPPORTED", "_STDCALL_SUPPORTED", + // Prepare the other macros. + "__3R__", "_based", "_cdecl", "cdecl", "_export", "_far16", "_far", "far", + "_fastcall", "_fortran", "fortran", "_huge", "huge", "_inline", "_interrupt", + "interrupt", "_loadds", "_near", "near", "_pascal", "pascal", "_saveregs", + "_segment", "_segname", "_self", "SOMDLINK", "_STDCALL_SUPPORTED", "__SW_0", + "__SW_3R", "__SW_5", "__SW_FP287", "__SW_FP2", "__SW_FP387", "__SW_FP3", + "__SW_FPI", "__SW_MF", "__SW_MS", "__SW_ZDP", "__SW_ZFP", "__SW_ZGF", + "__SW_ZGP", "_stdcall", "_syscall", "__BIG_ENDIAN" + ]; + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + outputFile.writeLine("#if defined(" + key + ")"); + outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))"); + outputFile.writeLine("#endif"); + } + outputFile.close(); + + var process = new Process(); + process.setWorkingDirectory(outputDirectory.path()); + for (var envkey in environment) + process.setEnv(envkey, environment[envkey]); + + var target = targetFlag(platform, architecture, ["application"]); + var lang = languageFlag(tag); + var args = [ target, lang, outputFilePath ]; + + process.exec(compilerPath, args, false); + var m = Cpp.extractMacros(process.readStdOut(), /"?(#define(\s\w+){1,2})"?$/); + if (tag === "cpp" && m["__cplusplus"] === "1") + return m; + else if (tag === "c") + return m; +} + +function effectiveLinkerPath(product) { + if (product.cpp.linkerMode === "automatic") { + var compilerPath = product.cpp.compilerPath; + if (compilerPath) + return compilerPath; + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + return product.cpp.linkerPath; +} + +function useCompilerDriverLinker(product) { + var linker = effectiveLinkerPath(product); + var compiler = product.cpp.compilerPath; + return linker === compiler; +} + +function escapeLinkerFlags(useCompilerDriver, linkerFlags) { + if (!linkerFlags || linkerFlags.length === 0) + return []; + + if (useCompilerDriver) { + var sep = ","; + return [["-Wl"].concat(linkerFlags).join(sep)]; + } + return linkerFlags; +} + +function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = [FileInfo.toNativeSeparators(input.filePath)]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + + args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) { + return "-fi" + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + var includePaths = [].concat(Cpp.collectIncludePaths(input)).concat( + Cpp.collectSystemIncludePaths(input)); + args = args.concat(includePaths.map(function(path) { + return "-i" + FileInfo.toNativeSeparators(path); + })); + + if (input.cpp.debugInformation) + args.push("-d1"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") + args.push("-w0"); + else if (warnings === "all") + args.push("-wx"); + if (input.cpp.treatWarningsAsErrors) + args.push("-we"); + + args.push("-zq"); // Silent. + args = args.concat(Cpp.collectMiscAssemblerArguments(input)); + return args; +} + +function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { + var args = ["-g" + (input.cpp.debugInformation ? "3" : "0")]; + + var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture, + product.type); + args.push(target); + + if (product.type.includes("application")) { + if (product.qbs.targetPlatform === "windows") { + var consoleApplication = product.consoleApplication; + args.push(consoleApplication ? "-mconsole" : "-mwindows"); + } + } else if (product.type.includes("dynamiclibrary")) { + args.push("-shared"); + } + + var optimization = input.cpp.optimization + if (optimization === "fast") + args.push("-Ot"); + else if (optimization === "small") + args.push("-Os"); + else if (optimization === "none") + args.push("-O0"); + + var warnings = input.cpp.warningLevel + if (warnings === "none") { + args.push("-w"); + } else if (warnings === "all") { + args.push("-Wall"); + args.push("-Wextra"); + } + if (input.cpp.treatWarningsAsErrors) + args.push("-Werror"); + + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); + + var langFlag = languageFlag(tag); + if (langFlag) + args.push(langFlag); + + if (tag === "cpp") { + var enableExceptions = input.cpp.enableExceptions; + if (enableExceptions) { + var ehModel = input.cpp.exceptionHandlingModel; + switch (ehModel) { + case "direct": + args.push("-feh-direct"); + break; + case "table": + args.push("-feh-table"); + break; + default: + args.push("-feh"); + break; + } + } else { + args.push("-fno-eh"); + } + + var enableRtti = input.cpp.enableRtti; + args.push(enableRtti ? "-frtti" : "-fno-rtti"); + } else if (tag === "c") { + var knownValues = ["c99", "c89"]; + var cLanguageVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C"); + switch (cLanguageVersion) { + case "c89": + args.push("-std=c89"); + break; + case "c99": + args.push("-std=c99"); + break; + } + } + + var preincludePaths = Cpp.collectPreincludePaths(input); + for (var i = 0; i < preincludePaths.length; ++i) + args.push(input.cpp.preincludeFlag, preincludePaths[i]); + + args = args.concat(Cpp.collectDefinesArguments(input)); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.systemIncludeFlag + FileInfo.toNativeSeparators(path); + })); + + args = args.concat(Cpp.collectMiscCompilerArguments(input, tag), + Cpp.collectMiscDriverArguments(input)); + + args.push("-o", FileInfo.toNativeSeparators(outputs.obj[0].filePath)); + args.push("-c", FileInfo.toNativeSeparators(input.filePath)); + + return args; +} + +function resourceCompilerFlags(project, product, input, outputs) { + var args = [input.filePath]; + args.push("-fo=" + FileInfo.toNativeSeparators(outputs.res[0].filePath)); + args = args.concat(Cpp.collectDefinesArguments(input, "-d")); + + args = args.concat(Cpp.collectIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) { + return input.cpp.includeFlag + FileInfo.toNativeSeparators(path); + })); + args.push("-q", "-ad", "-r"); + return args; +} + +function linkerFlags(project, product, inputs, outputs) { + var args = []; + var useCompilerDriver = useCompilerDriverLinker(product); + if (useCompilerDriver) { + var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture, + product.type); + args.push(target); + + if (product.type.includes("application")) { + args.push("-o", FileInfo.toNativeSeparators(outputs.application[0].filePath)); + if (product.cpp.generateLinkerMapFile) + args.push("-fm=" + FileInfo.toNativeSeparators(outputs.mem_map[0].filePath)); + } else if (product.type.includes("dynamiclibrary")) { + if (product.qbs.targetPlatform === "windows") { + args.push("-Wl, option implib=" + FileInfo.toNativeSeparators( + outputs.dynamiclibrary_import[0].filePath)); + } + args.push("-o", FileInfo.toNativeSeparators(outputs.dynamiclibrary[0].filePath)) + } + + var escapableLinkerFlags = []; + var targetLinkerFlags = product.cpp.targetLinkerFlags; + if (targetLinkerFlags) + escapableLinkerFlags = escapableLinkerFlags.concat(targetLinkerFlags); + + escapableLinkerFlags = escapableLinkerFlags.concat( + Cpp.collectMiscEscapableLinkerArguments(product)); + + var escapedLinkerFlags = escapeLinkerFlags(useCompilerDriver, escapableLinkerFlags); + if (escapedLinkerFlags) + args = args.concat(escapedLinkerFlags); + + args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) { + return product.cpp.libraryPathFlag + FileInfo.toNativeSeparators(path); + })); + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + })); + + var libraryDependencies = Cpp.collectLibraryDependencies(product); + for (var i = 0; i < libraryDependencies.length; ++i) { + var lib = libraryDependencies[i].filePath; + if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@')) + args.push(FileInfo.toNativeSeparators(lib)); + else + args.push("-Wl, libfile " + lib); + } + + var resourcePaths = Cpp.collectResourceObjectPaths(inputs).map(function(path) { + return FileInfo.toNativeSeparators(path); + }); + if (resourcePaths.length > 0) + args = args.concat("-Wl, resource " + resourcePaths.join(",")); + } + + args = args.concat(Cpp.collectMiscLinkerArguments(product), + Cpp.collectMiscDriverArguments(product)); + return args; +} + +function libraryManagerFlags(project, product, inputs, outputs) { + var args = ["-b", "-n", "-q"]; + args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) { + return "+" + FileInfo.toNativeSeparators(path); + })); + args.push("-o", FileInfo.toNativeSeparators(outputs.staticlibrary[0].filePath)); + return args; +} + +function disassemblerFlags(project, product, inputs, outputs) { + var objectPath = Cpp.relativePath(product.buildDirectory, outputs.obj[0].filePath); + var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath); + var args = []; + args.push(FileInfo.toNativeSeparators(objectPath)); + args.push("-l=" + FileInfo.toNativeSeparators(listingPath)); + args.push("-s", "-a"); + return args; +} + +function generateCompilerListing(project, product, inputs, outputs, input, output) { + var args = disassemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.disassemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.silent = true; + cmd.jobPool = "watcom_job_pool"; + return cmd; +} + +function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = assemblerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.assemblerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "assembling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateAssemblerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; + var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); + var cmd = new Command(input.cpp.compilerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + cmds.push(cmd); + if (input.cpp.generateCompilerListingFiles) + cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output)); + return cmds; +} + +function prepareResourceCompiler(project, product, inputs, outputs, input, output, + explicitlyDependsOn) { + var args = resourceCompilerFlags(project, product, input, outputs); + var cmd = new Command(input.cpp.resourceCompilerPath, args); + // Set working directory to source directory as a workaround + // to make the resources compilable by resource compiler (it is magic). + cmd.workingDirectory = product.sourceDirectory; + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0] + : outputs.application[0]; + var args = linkerFlags(project, product, inputs, outputs); + var linkerPath = effectiveLinkerPath(product); + var cmd = new Command(linkerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} + +function prepareLibraryManager(project, product, inputs, outputs, input, output) { + var args = libraryManagerFlags(project, product, inputs, outputs); + var cmd = new Command(product.cpp.libraryManagerPath, args); + cmd.workingDirectory = product.buildDirectory; + cmd.description = "linking " + outputs.staticlibrary[0].fileName; + cmd.highlight = "linker"; + cmd.jobPool = "watcom_job_pool"; + return [cmd]; +} diff --git a/share/qbs/modules/cpp/watcom.qbs b/share/qbs/modules/cpp/watcom.qbs new file mode 100644 index 000000000..84157b67b --- /dev/null +++ b/share/qbs/modules/cpp/watcom.qbs @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** 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 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import "cpp.js" as Cpp +import "watcom.js" as WATCOM + +CppModule { + condition: qbs.toolchain && qbs.toolchain.includes("watcom") + + Probes.BinaryProbe { + id: compilerPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["owcc"] + } + + Probes.WatcomProbe { + id: watcomProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + _pathListSeparator: qbs.pathListSeparator + _toolchainInstallPath: toolchainInstallPath + _targetPlatform: qbs.targetPlatform + _targetArchitecture: qbs.architecture + } + + qbs.architecture: watcomProbe.found ? watcomProbe.architecture : original + qbs.targetPlatform: watcomProbe.found ? watcomProbe.targetPlatform : original + + compilerVersionMajor: watcomProbe.versionMajor + compilerVersionMinor: watcomProbe.versionMinor + compilerVersionPatch: watcomProbe.versionPatch + endianness: watcomProbe.endianness + + compilerDefinesByLanguage: watcomProbe.compilerDefinesByLanguage + compilerIncludePaths: watcomProbe.includePaths + + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined + + /* Work-around for QtCreator which expects these properties to exist. */ + property string cCompilerName: compilerName + property string cxxCompilerName: compilerName + + compilerName: "owcc" + compilerExtension + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + assemblerName: "wasm" + compilerExtension + assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName) + + linkerName: "wlink" + compilerExtension + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + property string disassemblerName: "wdis" + compilerExtension + property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath, + disassemblerName) + property string resourceCompilerName: "wrc" + compilerExtension + property string resourceCompilerPath: FileInfo.joinPaths(toolchainInstallPath, + resourceCompilerName) + property string libraryManagerName: "wlib" + compilerExtension + property string libraryManagerPath: FileInfo.joinPaths(toolchainInstallPath, + libraryManagerName) + + runtimeLibrary: "dynamic" + + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: toolchainDetails.dynamicLibrarySuffix + executableSuffix: toolchainDetails.executableSuffix + objectSuffix: ".obj" + + imageFormat: toolchainDetails.imageFormat + + defineFlag: "-D" + includeFlag: "-I" + systemIncludeFlag: "-I" + preincludeFlag: "-include" + libraryDependencyFlag: "-l" + libraryPathFlag: "-L" + linkerScriptFlag: "" + + toolchainDetails: WATCOM.toolchainDetails(qbs) + + knownArchitectures: ["x86", "x86_16"] + + property var buildEnv: watcomProbe.environment + setupBuildEnvironment: { + for (var key in product.cpp.buildEnv) { + var v = new ModUtils.EnvironmentVariable(key, product.qbs.pathListSeparator); + v.prepend(product.cpp.buildEnv[key]); + v.set(); + } + } + + Rule { + id: assembler + inputs: ["asm"] + outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles) + outputArtifacts: Cpp.assemblerOutputArtifacts(input) + prepare: WATCOM.prepareAssembler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.asm"] + fileTags: ["asm"] + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles) + outputArtifacts: Cpp.compilerOutputArtifacts(input) + prepare: WATCOM.prepareCompiler.apply(WATCOM, arguments) + } + + Rule { + id: rccCompiler + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + outputFileTags: Cpp.resourceCompilerOutputTags() + outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input) + prepare: WATCOM.prepareResourceCompiler.apply(WATCOM, arguments) + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ["obj", "res", "linkerscript"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile) + outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: dynamicLibraryLinker + condition: qbs.targetOS.includes("windows") + multiplex: true + inputs: ["obj", "res"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.dynamicLibraryLinkerOutputTags(); + outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLinker.apply(WATCOM, arguments) + } + + Rule { + id: libraryManager + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: Cpp.staticLibraryLinkerOutputTags() + outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product) + prepare: WATCOM.prepareLibraryManager.apply(WATCOM, arguments) + } + + JobLimit { + jobPool: "watcom_job_pool" + jobCount: 1 + } +} diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs index e1f4ee425..9aa9d7395 100644 --- a/share/qbs/modules/cpp/windows-clang-cl.qbs +++ b/share/qbs/modules/cpp/windows-clang-cl.qbs @@ -28,15 +28,16 @@ ** ****************************************************************************/ +import qbs.Host import qbs.ModUtils import qbs.Probes import qbs.FileInfo import 'windows-msvc-base.qbs' as MsvcBaseModule MsvcBaseModule { - condition: qbs.hostOS.contains('windows') && - qbs.targetOS.contains('windows') && - qbs.toolchain && qbs.toolchain.contains('clang-cl') + condition: Host.os().includes('windows') && + qbs.targetOS.includes('windows') && + qbs.toolchain && qbs.toolchain.includes('clang-cl') priority: 100 Probes.ClangClBinaryProbe { @@ -84,14 +85,17 @@ MsvcBaseModule { linkerName: "lld-link.exe" linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + systemIncludeFlag: "/imsvc" + validateFunc: { + var baseFunc = base; return function() { if (_skipAllChecks) return; var validator = new ModUtils.PropertyValidator("cpp"); validator.setRequiredProperty("vcvarsallPath", vcvarsallPath); validator.validate(); - base(); + baseFunc(); } } } diff --git a/share/qbs/modules/cpp/windows-clang-mingw.qbs b/share/qbs/modules/cpp/windows-clang-mingw.qbs index 10d56cc85..cfe6f2745 100644 --- a/share/qbs/modules/cpp/windows-clang-mingw.qbs +++ b/share/qbs/modules/cpp/windows-clang-mingw.qbs @@ -30,6 +30,7 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities import "msvc.js" as MSVC @@ -37,8 +38,8 @@ import "msvc.js" as MSVC import "setuprunenv.js" as SetupRunEnv MingwBaseModule { - condition: qbs.targetOS.contains("windows") && - qbs.toolchain && qbs.toolchain.contains("clang") + condition: qbs.targetOS.includes("windows") && + qbs.toolchain && qbs.toolchain.includes("clang") priority: 0 // llvm-as and llvm-objopy are not shipped with the official binaries on Windows at the @@ -68,16 +69,16 @@ MingwBaseModule { "llvm-rc" + compilerExtension) setupBuildEnvironment: { - if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + if (Host.os().includes("windows") && product.qbs.sysroot) { + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin")); v.set(); } } setupRunEnvironment: { - if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + if (Host.os().includes("windows") && product.qbs.sysroot) { + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin")); v.set(); SetupRunEnv.setupRunEnvironment(product, config); diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs index 2a01d81a9..9ba4258c5 100644 --- a/share/qbs/modules/cpp/windows-mingw.qbs +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -37,8 +37,8 @@ import 'cpp.js' as Cpp import "setuprunenv.js" as SetupRunEnv MingwBaseModule { - condition: qbs.targetOS.contains("windows") && - qbs.toolchain && qbs.toolchain.contains("mingw") + condition: qbs.targetOS.includes("windows") && + qbs.toolchain && qbs.toolchain.includes("mingw") priority: 0 probeEnv: buildEnv @@ -52,13 +52,13 @@ MingwBaseModule { } setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(product.cpp.toolchainInstallPath); v.set(); } setupRunEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(product.cpp.toolchainInstallPath); v.set(); SetupRunEnv.setupRunEnvironment(product, config); diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index d188d53f3..9613800e5 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -95,7 +95,7 @@ CppModule { precompiledHeaderSuffix: ".pch" imageFormat: "pe" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && qbs.buildVariant !== "release" && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) @@ -320,12 +320,11 @@ CppModule { outputFileTags: Cpp.assemblerOutputTags(false) outputArtifacts: Cpp.assemblerOutputArtifacts(input) prepare: { - var args = ["/nologo", "/c", - "/Fo" + FileInfo.toWindowsSeparators(output.filePath), - FileInfo.toWindowsSeparators(input.filePath)]; + var args = ["/nologo", "/c", "/Fo" + FileInfo.toWindowsSeparators(output.filePath)]; if (product.cpp.debugInformation) args.push("/Zi"); args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm")); + args.push(FileInfo.toWindowsSeparators(input.filePath)); var cmd = new Command(product.cpp.assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.jobPool = "assembler"; diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index 5e41c76fa..c30cec239 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -29,13 +29,14 @@ ** ****************************************************************************/ +import qbs.Host import qbs.Probes import "windows-msvc-base.qbs" as MsvcBaseModule MsvcBaseModule { - condition: qbs.hostOS.contains('windows') && - qbs.targetOS.contains('windows') && - qbs.toolchain && qbs.toolchain.contains('msvc') + condition: Host.os().includes('windows') && + qbs.targetOS.includes('windows') && + qbs.toolchain && qbs.toolchain.includes('msvc') priority: 50 Probes.ClBinaryProbe { diff --git a/share/qbs/modules/dmg/DMGModule.qbs b/share/qbs/modules/dmg/DMGModule.qbs index c5d097a0b..27a945033 100644 --- a/share/qbs/modules/dmg/DMGModule.qbs +++ b/share/qbs/modules/dmg/DMGModule.qbs @@ -31,15 +31,17 @@ import qbs.DarwinTools import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Process +import qbs.Probes import qbs.TextFile import "dmg.js" as Dmg Module { Depends { name: "xcode"; required: false } - condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin") + condition: Host.os().includes("darwin") && qbs.targetOS.includes("darwin") property string volumeName: product.targetName PropertyOptions { @@ -72,6 +74,12 @@ Module { property string sourceBase + Probes.BinaryProbe { + id: pythonProbe + names: ["python3"] + } + property string pythonExePath: pythonProbe.found ? pythonProbe.filePath : "python3" + readonly property string pythonPath: File.canonicalFilePath(FileInfo.joinPaths(path, "..", "..", "python")) diff --git a/share/qbs/modules/dmg/dmg.js b/share/qbs/modules/dmg/dmg.js index 4d972db9b..06fe7b906 100644 --- a/share/qbs/modules/dmg/dmg.js +++ b/share/qbs/modules/dmg/dmg.js @@ -57,7 +57,7 @@ function dmgbuildSettings(product, inputs) { volumeIcon = volumeIcons[0].filePath; } - var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"], function (a) { + var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"] || [], function (a) { return { "dmg": { "licenseLocale": localizationFromArtifact(a), @@ -97,14 +97,14 @@ function dmgbuildSettings(product, inputs) { }, {}); } - var contentsArray = Array.prototype.map.call(inputs["dmg.input"], function (a) { + var contentsArray = Array.prototype.map.call(inputs["dmg.input"] || [], function (a) { if (a.dmg.sourceBase && !a.filePath.startsWith(a.dmg.sourceBase)) { throw new Error("Cannot install '" + a.filePath + "', " + "because it doesn't start with the value of " + "dmg.sourceBase '" + a.dmg.sourceBase + "'."); } - var isSymlink = a.fileTags.contains("dmg.input.symlink"); + var isSymlink = a.fileTags.includes("dmg.input.symlink"); return { "x": a.dmg.iconX, "y": a.dmg.iconY, @@ -114,7 +114,7 @@ function dmgbuildSettings(product, inputs) { }; }); - Array.prototype.forEach.call(product.dmg.iconPositions, function (obj) { + Array.prototype.forEach.call(product.dmg.iconPositions || [], function (obj) { var existingIndex = -1; Array.prototype.forEach.call(contentsArray, function (contentsItem, i) { if (contentsItem["name"] === obj["path"]) @@ -135,7 +135,7 @@ function dmgbuildSettings(product, inputs) { } }); - return { + var result = { "title": product.dmg.volumeName, "icon": !product.dmg.badgeVolumeIcon ? volumeIcon : undefined, "badge-icon": product.dmg.badgeVolumeIcon ? volumeIcon : undefined, @@ -154,13 +154,16 @@ function dmgbuildSettings(product, inputs) { }, "format": product.dmg.format, "compression-level": product.dmg.compressionLevel, - "license": { + "contents": contentsArray + }; + if (licenseFileObjects.length >= 0) { + result["license"] = { "default-language": product.dmg.defaultLicenseLocale, "licenses": reduceLicensesForKey(licenseFileObjects, "licenses"), "buttons": reduceLicensesForKey(licenseFileObjects, "buttons") - }, - "contents": contentsArray - }; + }; + } + return result; } function prepareLicense(project, product, inputs, outputs, input, output) { @@ -199,8 +202,10 @@ function prepareDmg(project, product, inputs, outputs, input, output) { cmds.push(cmd); // Create the actual DMG via dmgbuild - cmd = new Command(FileInfo.joinPaths(product.qbs.libexecPath, "dmgbuild"), - [product.dmg.volumeName, + cmd = new Command(product.dmg.pythonExePath, + ["-m", + "dmgbuild", + product.dmg.volumeName, output.filePath, "--no-hidpi", // qbs handles this by itself "--settings", settingsJsonFilePath]); diff --git a/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs b/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs new file mode 100644 index 000000000..9f5b2b70c --- /dev/null +++ b/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2024 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 +import qbs.FileInfo + +import "../flatbuffers.js" as Flatbuf +import "../flatbuffersbase.qbs" as FlatbufBase + +FlatbufBase { + Depends { name: "cpp" } + Depends { name: "flatcc" } + + compilerName: "flatcc" + + _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf", "c") + _searchPaths: flatbuffers.hostBinDirs + + cpp.systemIncludePaths: base.concat([_outputDir]) + + Rule { + inputs: ["flatbuf.input"] + outputFileTags: ["hpp"] + outputArtifacts: [Flatbuf.artifactC(input.flatbuf.c, input, "hpp", "_generated.h")] + + prepare: { + return Flatbuf.doPrepareC(input.flatbuf.c, input); + } + } + + validate: { + Flatbuf.validateCompiler(compilerName, compilerPath); + } +} diff --git a/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs b/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs new file mode 100644 index 000000000..4614d4949 --- /dev/null +++ b/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2024 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 +import qbs.FileInfo + +import "../flatbuffers.js" as Flatbuf +import "../flatbuffersbase.qbs" as FlatbufBase + +FlatbufBase { + Depends { name: "cpp" } + Depends { name: "flatbuffers" } + + property string filenameExtension: "h" + property string filenameSuffix: "_generated" + property string includePrefix // TODO: test + property bool keepPrefix: false + + _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf", "cpp") + _searchPaths: flatbuffers.hostBinDirs + + cpp.cxxLanguageVersion: "c++11" + cpp.systemIncludePaths: base.concat([_outputDir]) + + Rule { + inputs: ["flatbuf.input"] + outputFileTags: ["hpp"] + outputArtifacts: { + var module = input.flatbuf.cpp; + var fullSuffix = module.filenameSuffix + "." + module.filenameExtension; + return [ Flatbuf.artifact(module, input, "hpp", fullSuffix) ]; + } + + prepare: { + return Flatbuf.doPrepareCpp(input.flatbuf.cpp, input, output); + } + } + + validate: { + Flatbuf.validateCompiler(compilerName, compilerPath); + } +} diff --git a/share/qbs/modules/flatbuf/flatbuffers.js b/share/qbs/modules/flatbuf/flatbuffers.js new file mode 100644 index 000000000..24fd91168 --- /dev/null +++ b/share/qbs/modules/flatbuf/flatbuffers.js @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2024 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 File = require("qbs.File"); + +function validateCompiler(compilerName, compilerPath) { + if (!File.exists(compilerPath)) { + throw "Can't find '" + compilerName + "' binary. Please set the compilerPath property or " + + "make sure the compiler is found in PATH"; + } +} + +function getOutputDir(module, input) { + var outputDir = module._outputDir; + if (!module.keepPrefix) + return outputDir; + var importPaths = module.importPaths; + if (importPaths !== undefined && importPaths.length !== 0) { + var canonicalInput = File.canonicalFilePath(FileInfo.path(input.filePath)); + for (var i = 0; i < importPaths.length; ++i) { + var path = File.canonicalFilePath(importPaths[i]); + + if (canonicalInput.startsWith(path)) { + return outputDir + "/" + FileInfo.relativePath(path, canonicalInput); + } + } + } + return outputDir; +} + +function artifactC(module, input, tag, suffix) { + var outputDir = module._outputDir; + return { + fileTags: [tag], + filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix, + cpp: { warningLevel: "none"} + }; +} + +function doPrepareC(module, input) +{ + var args = []; + args.push("-a") // write all + args.push("-o", input.flatbuf.c._outputDir) // output dir + + var importPaths = module.importPaths; + importPaths.forEach(function(path) { + args.push("-I", path); + }); + + args.push(input.filePath); + + var cmd = new Command(module.compilerPath, args); + cmd.workingDirectory = FileInfo.path(module._outputDir) + cmd.highlight = "codegen"; + cmd.description = "generating C files for " + input.fileName; + return [cmd]; +} + +function artifact(module, input, tag, suffix) { + var outputDir = getOutputDir(module, input); + return { + fileTags: [tag], + filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix, + cpp: { warningLevel: "none" } + }; +} + +function doPrepareCpp(module, input, output) +{ + var outputDir = FileInfo.path(output.filePath); + var result = []; + + var args = []; + args.push("--cpp") + + var importPaths = module.importPaths; + importPaths.forEach(function(path) { + args.push("-I", path); + }); + + args.push("--filename-ext", module.filenameExtension); + args.push("--filename-suffix", module.filenameSuffix); + + if (module.includePrefix) + args.push("--include-prefix", module.includePrefix); + + if (module.keepPrefix) + args.push("--keep-prefix"); + + args.push(input.filePath); + var cmd = new Command(input.flatbuf.cpp.compilerPath, args); + cmd.workingDirectory = outputDir; + cmd.highlight = "codegen"; + cmd.description = "generating C++ files for " + input.fileName; + result.push(cmd); + + return result; +} diff --git a/share/qbs/modules/flatbuf/flatbuffersbase.qbs b/share/qbs/modules/flatbuf/flatbuffersbase.qbs new file mode 100644 index 000000000..98eb2f344 --- /dev/null +++ b/share/qbs/modules/flatbuf/flatbuffersbase.qbs @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2024 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 +import qbs.File +import qbs.FileInfo +import qbs.Probes + +import "flatbuffers.js" as HelperFunctions + +Module { + property string compilerName: "flatc" + property string compilerPath: compilerProbe.filePath + + property pathList importPaths: [] + + property string _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf") + + property stringList _searchPaths + + FileTagger { + patterns: ["*.fbs"] + fileTags: ["flatbuf.input"]; + } + + Probes.BinaryProbe { + id: compilerProbe + names: [compilerName] + searchPaths: _searchPaths + } +} diff --git a/share/qbs/modules/freedesktop/FreeDesktop.qbs b/share/qbs/modules/freedesktop/FreeDesktop.qbs index 5ff56a9a4..60722c674 100644 --- a/share/qbs/modules/freedesktop/FreeDesktop.qbs +++ b/share/qbs/modules/freedesktop/FreeDesktop.qbs @@ -33,22 +33,21 @@ import qbs.TextFile import "freedesktop.js" as Fdo Module { - id: fdoModule - property string name: product.name + property string appName: name property var desktopKeys readonly property var defaultDesktopKeys: { return { 'Type': 'Application', - 'Name': product.freedesktop.name, + 'Name': product.freedesktop.appName, 'Exec': product.targetName, 'Terminal': 'false', 'Version': '1.1', } } - property bool _fdoSupported: qbs.targetOS.contains("unix") && !qbs.targetOS.contains("darwin") + property bool _fdoSupported: qbs.targetOS.includes("unix") && !qbs.targetOS.includes("darwin") additionalProductTypes: "freedesktop.desktopfile" @@ -70,7 +69,7 @@ Module { prepare: { var cmd = new JavaScriptCommand(); - cmd.description = input.fileName + "->" + output.fileName; + cmd.description = "generating " + output.fileName + " from " + input.fileName; cmd.highlight = "codegen"; cmd.sourceCode = function() { var aggregateDesktopKeys = Fdo.parseDesktopFile(input.filePath); @@ -96,14 +95,14 @@ Module { } Group { - condition: fdoModule._fdoSupported + condition: product.freedesktop._fdoSupported fileTagsFilter: [ "freedesktop.desktopfile" ] qbs.install: true qbs.installDir: "share/applications" } Group { - condition: fdoModule._fdoSupported + condition: product.freedesktop._fdoSupported fileTagsFilter: [ "freedesktop.appIcon" ] qbs.install: true qbs.installDir: "share/icons/hicolor/scalable/apps" @@ -115,7 +114,7 @@ Module { } Group { - condition: fdoModule._fdoSupported + condition: product.freedesktop._fdoSupported fileTagsFilter: [ "freedesktop.appstream" ] qbs.install: true qbs.installDir: "share/metainfo" diff --git a/share/qbs/modules/ib/IBModule.qbs b/share/qbs/modules/ib/IBModule.qbs index 6af5e94e4..e6183e20c 100644 --- a/share/qbs/modules/ib/IBModule.qbs +++ b/share/qbs/modules/ib/IBModule.qbs @@ -31,6 +31,7 @@ import qbs.BundleTools import qbs.DarwinTools import qbs.File +import qbs.Host import qbs.FileInfo import qbs.ModUtils import qbs.Process @@ -49,7 +50,7 @@ Module { } } - condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin") + condition: Host.os().includes("darwin") && qbs.targetOS.includes("darwin") property bool warnings: true property bool errors: true @@ -195,7 +196,7 @@ Module { cmd.highlight = 'compiler'; // May not be strictly needed, but is set by some versions of Xcode - if (input.fileTags.contains("storyboard")) + if (input.fileTags.includes("storyboard")) cmd.environment.push("IBSC_MINIMUM_COMPATIBILITY_VERSION=" + (product.moduleProperty("cpp", "minimumDarwinVersion") || "")); diff --git a/share/qbs/modules/ib/ib.js b/share/qbs/modules/ib/ib.js index ebb615e8e..d6873b261 100644 --- a/share/qbs/modules/ib/ib.js +++ b/share/qbs/modules/ib/ib.js @@ -116,7 +116,7 @@ function ibtooldArguments(product, inputs, input, outputs, overrideOutput) { var outputFormat = ModUtils.moduleProperty(product, "outputFormat"); if (outputFormat) { - if (!["binary1", "xml1", "human-readable-text"].contains(outputFormat)) + if (!["binary1", "xml1", "human-readable-text"].includes(outputFormat)) throw("Invalid ibtoold output format: " + outputFormat + ". " + "Must be in [binary1, xml1, human-readable-text]."); @@ -205,9 +205,9 @@ function ibtooldArguments(product, inputs, input, outputs, overrideOutput) { function ibtoolFileTaggers(fileTags) { var ext; - if (fileTags.contains("nib") && !fileTags.contains("storyboard")) + if (fileTags.includes("nib") && !fileTags.includes("storyboard")) ext = "nib"; - if (fileTags.contains("storyboard") && !fileTags.contains("nib")) + if (fileTags.includes("storyboard") && !fileTags.includes("nib")) ext = "storyboard"; if (!ext) @@ -223,9 +223,9 @@ function ibtoolFileTaggers(fileTags) { function ibtoolCompiledDirSuffix(product, input) { var suffix = input.completeBaseName; - if (input.fileTags.contains("nib")) + if (input.fileTags.includes("nib")) suffix += ModUtils.moduleProperty(product, "compiledNibSuffix"); - else if (input.fileTags.contains("storyboard")) + else if (input.fileTags.includes("storyboard")) suffix += ModUtils.moduleProperty(product, "compiledStoryboardSuffix"); return suffix; } @@ -252,7 +252,7 @@ function ibtoolOutputArtifacts(product, inputs, input) { var artifacts = tracker.artifacts(ibtoolBuildDirectory); if (product.moduleProperty("ib", "ibtoolVersionMajor") >= 6) { - var prefix = input.fileTags.contains("storyboard") ? "SB" : ""; + var prefix = input.fileTags.includes("storyboard") ? "SB" : ""; var path = FileInfo.joinPaths(product.destinationDirectory, input.completeBaseName + "-" + prefix + "PartialInfo.plist"); artifacts.push({ filePath: path, fileTags: ["partial_infoplist"] }); @@ -261,7 +261,7 @@ function ibtoolOutputArtifacts(product, inputs, input) { // Let the output artifacts known the "main" output // This can be either a file or directory so the artifact might already exist in the output list for (var i = 0; i < artifacts.length; ++i) { - if (artifacts[i].fileTags.contains("compiled_ibdoc")) + if (artifacts[i].fileTags.includes("compiled_ibdoc")) artifacts[i].bundle = { _bundleFilePath: artifacts[i].filePath.replace(ibtoolBuildDirectory, main) }; @@ -296,7 +296,7 @@ function actoolOutputArtifacts(product, inputs) { } for (var i = 0; i < artifacts.length; ++i) { - if (artifacts[i].fileTags.contains("compiled_assetcatalog")) { + if (artifacts[i].fileTags.includes("compiled_assetcatalog")) { artifacts[i].bundle = { _bundleFilePath: artifacts[i].filePath.replace( product.buildDirectory + "/actool.dir", diff --git a/share/qbs/modules/ico/ico.js b/share/qbs/modules/ico/ico.js index a61d585ab..dc96eedf3 100644 --- a/share/qbs/modules/ico/ico.js +++ b/share/qbs/modules/ico/ico.js @@ -40,20 +40,20 @@ function prepareIconset(project, product, inputs, outputs, input, output) { ico: {} }; }); - inputs = {"png": inputs.filter(function (a) { return a.fileTags.contains("png"); })}; + inputs = {"png": inputs.filter(function (a) { return a.fileTags.includes("png"); })}; input = undefined; return prepare(project, product, inputs, outputs, input, output); } function prepare(project, product, inputs, outputs, input, output) { var args = ["--create", "--output=" + output.filePath]; - if (output.fileTags.contains("ico")) { + if (output.fileTags.includes("ico")) { args.push("--icon"); if (product.ico.alphaThreshold !== undefined) args.push("--alpha-threshold=" + product.ico.alphaThreshold); } - var isCursor = output.fileTags.contains("cur"); + var isCursor = output.fileTags.includes("cur"); if (isCursor) args.push("--cursor"); diff --git a/share/qbs/modules/innosetup/InnoSetupModule.qbs b/share/qbs/modules/innosetup/InnoSetupModule.qbs index 930d85a9a..9f1018ec4 100644 --- a/share/qbs/modules/innosetup/InnoSetupModule.qbs +++ b/share/qbs/modules/innosetup/InnoSetupModule.qbs @@ -33,7 +33,7 @@ import qbs.ModUtils import qbs.Probes Module { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") Probes.InnoSetupProbe { id: innoSetupProbe diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs index ceb29f36f..343b2f4ba 100644 --- a/share/qbs/modules/java/JavaModule.qbs +++ b/share/qbs/modules/java/JavaModule.qbs @@ -29,6 +29,7 @@ ****************************************************************************/ import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Probes import qbs.Process @@ -122,10 +123,10 @@ Module { } else { paths.push(FileInfo.joinPaths(jdkPath, "include")); - var hostOS = qbs.hostOS.contains("windows") ? qbs.hostOS.concat(["win32"]) : qbs.hostOS; + var hostOS = Host.os().includes("windows") ? Host.os().concat(["win32"]) : Host.os(); var platforms = ["win32", "darwin", "linux", "bsd", "solaris"]; for (var i = 0; i < platforms.length; ++i) { - if (hostOS.contains(platforms[i])) { + if (hostOS.includes(platforms[i])) { // Corresponds to JDK_INCLUDE_SUBDIR in the JDK Makefiles paths.push(FileInfo.joinPaths(jdkPath, "include", platforms[i])); break; @@ -140,7 +141,7 @@ Module { property path classFilesDir: FileInfo.joinPaths(product.buildDirectory, "classes") property path internalClassFilesDir: FileInfo.joinPaths(product.buildDirectory, ".classes") - property bool isAppleJava: qbs.hostOS.contains("darwin") + property bool isAppleJava: Host.os().includes("darwin") && (compilerVersionMajor < 1 || (compilerVersionMajor === 1 && compilerVersionMinor < 7)) @@ -253,7 +254,7 @@ Module { prepare: { var cmd = new Command(ModUtils.moduleProperty(product, "compilerFilePath"), JavaUtils.javacArguments(product, inputs)); - cmd.description = "Compiling Java sources"; + cmd.description = "compiling Java sources"; cmd.highlight = "compiler"; return [cmd]; } diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js index 520511192..a59b8d41d 100644 --- a/share/qbs/modules/java/utils.js +++ b/share/qbs/modules/java/utils.js @@ -79,7 +79,7 @@ function findJdkPath(hostOS, arch, environmentPaths, searchPaths) { return environmentPaths[i]; } - if (hostOS.contains("windows")) { + if (hostOS.includes("windows")) { var rootKey = jdkRootRegistryKey(useWow64Key(arch)); if (rootKey) { var current = Utilities.getNativeSetting(rootKey, "CurrentVersion"); // 1.8 etc. @@ -94,27 +94,55 @@ function findJdkPath(hostOS, arch, environmentPaths, searchPaths) { return undefined; } - if (hostOS.contains("macos")) { + if (hostOS.includes("macos")) { var p = new Process(); try { // We filter by architecture here so that we'll get a compatible JVM for JNI use. var args = []; + var canonicalArch; if (arch) { // Hardcoding apple/macosx/macho here is fine because we know we're on macOS - args.push("--arch", - Utilities.canonicalTargetArchitecture(arch, undefined, - "apple", "macosx", "macho")); + canonicalArch = Utilities.canonicalTargetArchitecture(arch, undefined, "apple", + "macosx", "macho"); + args.push("--arch", canonicalArch); } // --failfast doesn't print the default JVM if nothing matches the filter(s). var status = p.exec("/usr/libexec/java_home", args.concat(["--failfast"])); - return status === 0 ? p.readStdOut().trim() : undefined; + if (status === 0) + return p.readStdOut().trim(); + + // It has been obvserved that java_home fails for any architecture that is passed, + // so try without the filter and look up the JDK architecture manually. + if (!canonicalArch) + return undefined; + + if (p.exec("/usr/libexec/java_home", ["--failfast"]) !== 0) + return undefined; + var jdkPath = p.readStdOut().trim(); + var releaseFile = new TextFile(jdkPath + "/release", TextFile.ReadOnly); + var line; + while ((line = releaseFile.readLine())) { + if (!line.startsWith("OS_ARCH=")) + continue; + var firstQuote = line.indexOf('"'); + if (firstQuote === -1) + break; + var secondQuote = line.indexOf('"', firstQuote + 1); + if (secondQuote === -1) + break; + var archFromFile = line.substring(firstQuote + 1, secondQuote); + if (archFromFile !== canonicalArch) + break; + return jdkPath; + } + return undefined; } finally { p.close(); } } - if (hostOS.contains("unix")) { + if (hostOS.includes("unix")) { var requiredTools = ["javac", "java", "jar"]; for (i = 0; i < searchPaths.length; ++i) { function fullToolPath(tool) { @@ -193,7 +221,7 @@ function javacArguments(product, inputs, overrides) { classPaths.push(inputs["java.jar"][i].filePath); var debugArg = product.moduleProperty("qbs", "buildVariant") === "debug" ? "-g" : "-g:none"; - var pathListSeparator = product.moduleProperty("qbs", "pathListSeparator"); + var pathListSeparator = FileInfo.pathListSeparator(); var args = [ "-classpath", classPaths.join(pathListSeparator), "-s", product.buildDirectory, @@ -246,7 +274,7 @@ function helperFullyQualifiedNames(type) { ]; if (type === "java") { return names.filter(function (name) { - return !name.contains("$"); + return !name.includes("$"); }); } else if (type === "class") { return names; @@ -312,7 +340,7 @@ function outputArtifacts(product, inputs) { process.setWorkingDirectory( FileInfo.joinPaths(ModUtils.moduleProperty(product, "internalClassFilesDir"))); - var sep = product.moduleProperty("qbs", "pathListSeparator"); + var sep = FileInfo.pathListSeparator(); var toolsJarPath = ModUtils.moduleProperty(product, "toolsJarPath"); var javaArgs = [ "-classpath", process.workingDirectory() + (toolsJarPath ? (sep + toolsJarPath) : ""), diff --git a/share/qbs/modules/nodejs/NodeJS.qbs b/share/qbs/modules/nodejs/NodeJS.qbs index 7fd992fdb..412f9b8bb 100644 --- a/share/qbs/modules/nodejs/NodeJS.qbs +++ b/share/qbs/modules/nodejs/NodeJS.qbs @@ -31,6 +31,7 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Probes @@ -84,13 +85,13 @@ Module { "tmp", "nodejs.intermediate") setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, product.qbs.hostOS.contains("windows")); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), Host.os().includes("windows")); v.prepend(product.nodejs.toolchainInstallPath); v.set(); } setupRunEnvironment: { - var v = new ModUtils.EnvironmentVariable("NODE_PATH", product.qbs.pathListSeparator, product.qbs.hostOS.contains("windows")); + var v = new ModUtils.EnvironmentVariable("NODE_PATH", FileInfo.pathListSeparator(), Host.os().includes("windows")); v.prepend(FileInfo.path(Environment.getEnv("QBS_RUN_FILE_PATH"))); v.set(); } @@ -118,7 +119,7 @@ Module { outputArtifacts: { var tags = ["nodejs_processed_js"]; - if (input.fileTags.contains("application_js") || + if (input.fileTags.includes("application_js") || product.moduleProperty("nodejs", "applicationFile") === input.filePath) tags.push("application"); diff --git a/share/qbs/modules/nsis/NSISModule.qbs b/share/qbs/modules/nsis/NSISModule.qbs index 23d673880..11a5670b5 100644 --- a/share/qbs/modules/nsis/NSISModule.qbs +++ b/share/qbs/modules/nsis/NSISModule.qbs @@ -30,11 +30,12 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Utilities Module { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") property path toolchainInstallPath: Utilities.getNativeSetting(registryKey) @@ -89,7 +90,7 @@ Module { // Private properties property string registryKey: { - if (!qbs.hostOS.contains("windows")) + if (!Host.os().includes("windows")) return undefined; var keys = [ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS" ]; @@ -104,7 +105,7 @@ Module { // Only *require* the toolchain install path on Windows // On other (Unix-like) operating systems it'll probably be in the PATH - if (qbs.targetOS.contains("windows")) + if (qbs.targetOS.includes("windows")) validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); validator.setRequiredProperty("versionMajor", versionMajor); @@ -158,7 +159,7 @@ Module { var args = []; // Prefix character for makensis options - var opt = product.moduleProperty("qbs", "hostOS").contains("windows") ? "/" : "-"; + var opt = product.moduleProperty("qbs", "hostOS").includes("windows") ? "/" : "-"; if (ModUtils.moduleProperty(product, "disableConfig")) { args.push(opt + "NOCONFIG"); diff --git a/share/qbs/modules/pkgconfig/pkgconfig.qbs b/share/qbs/modules/pkgconfig/pkgconfig.qbs index b436263e9..901e7e02e 100644 --- a/share/qbs/modules/pkgconfig/pkgconfig.qbs +++ b/share/qbs/modules/pkgconfig/pkgconfig.qbs @@ -47,7 +47,7 @@ Module { } property path sysroot: { - if (qbs.targetOS.contains("macos")) + if (qbs.targetOS.includes("macos")) return ""; return qbs.sysroot; } diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs index e1459658d..bd4a08557 100644 --- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs +++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs @@ -6,27 +6,30 @@ import "../protobufbase.qbs" as ProtobufBase import "../protobuf.js" as HelperFunctions ProtobufBase { - property string includePath: includeProbe.found ? includeProbe.path : undefined - property string libraryPath: libraryProbe.found ? libraryProbe.path : undefined - property bool useGrpc: false property bool _linkLibraries: true + readonly property bool _hasModules: protobuflib.present && (!useGrpc || grpcpp.present) - property string grpcIncludePath: grpcIncludeProbe.found ? grpcIncludeProbe.path : undefined - property string grpcLibraryPath: grpcLibraryProbe.found ? grpcLibraryProbe.path : undefined + property string _cxxLanguageVersion: "c++17" - readonly property string _libraryName: { - var libraryName; - if (libraryProbe.found) { - libraryName = FileInfo.baseName(libraryProbe.fileName); - if (libraryName.startsWith("lib")) - libraryName = libraryName.substring(3); - } - return libraryName; - } + _searchPaths: protobuflib.present ? protobuflib.hostBinDirs : undefined + property stringList _grpcSearchPaths: grpcpp.present ? grpcpp.hostBinDirs : undefined + + cpp.includePaths: outputDir Depends { name: "cpp" } + Depends { + name: "protobuflib"; + condition: _linkLibraries; + required: false + } + Depends { + name: "grpc++"; + id: grpcpp + condition: _linkLibraries && useGrpc; + required: false + } property path grpcPluginPath: grpcPluginProbe.filePath @@ -34,43 +37,10 @@ ProtobufBase { condition: useGrpc id: grpcPluginProbe names: "grpc_cpp_plugin" + searchPaths: _grpcSearchPaths } - cpp.libraryPaths: { - if (!_linkLibraries) - return []; - - var result = []; - if (libraryProbe.found) - result.push(libraryProbe.path); - if (useGrpc && grpcLibraryProbe.found) - result.push(grpcLibraryPath); - return result; - } - cpp.dynamicLibraries: { - if (!_linkLibraries) - return []; - - var result = []; - if (_libraryName) - result.push(_libraryName) - if (qbs.targetOS.contains("unix")) - result.push("pthread"); - if (useGrpc) - result.push("grpc++"); - return result; - } - cpp.includePaths: { - if (!_linkLibraries) - return [outputDir]; - - var result = [outputDir]; - if (includeProbe.found) - result.push(includePath); - if (useGrpc && grpcIncludeProbe.found) - result.push(grpcIncludePath); - return result; - } + cpp.cxxLanguageVersion: _cxxLanguageVersion Rule { inputs: ["protobuf.input", "protobuf.grpc"] @@ -82,7 +52,7 @@ ProtobufBase { ".pb.h"), HelperFunctions.cppArtifact(outputDir, input, "cpp", ".pb.cc") ]; - if (input.fileTags.contains("protobuf.grpc")) { + if (input.fileTags.includes("protobuf.grpc")) { result.push( HelperFunctions.cppArtifact(outputDir, input, ["hpp", "protobuf.hpp"], ".grpc.pb.h"), @@ -95,7 +65,7 @@ ProtobufBase { prepare: { var result = HelperFunctions.doPrepare( input.protobuf.cpp, product, input, outputs, "cpp"); - if (input.fileTags.contains("protobuf.grpc")) { + if (input.fileTags.includes("protobuf.grpc")) { result = ModUtils.concatAll(result, HelperFunctions.doPrepare( input.protobuf.cpp, product, input, outputs, "grpc", "protoc-gen-grpc=" + input.protobuf.cpp.grpcPluginPath)); @@ -104,53 +74,16 @@ ProtobufBase { } } - Probes.IncludeProbe { - id: includeProbe - names: "google/protobuf/message.h" - platformSearchPaths: includePath ? [] : base - searchPaths: includePath ? [includePath] : [] - } - - Probes.LibraryProbe { - id: libraryProbe - names: [ - "protobuf", - "protobufd", - ] - platformSearchPaths: libraryPath ? [] : base - searchPaths: libraryPath ? [libraryPath] : [] - } - - Probes.IncludeProbe { - id: grpcIncludeProbe - pathSuffixes: "grpc++" - names: "grpc++.h" - platformSearchPaths: grpcIncludePath ? [] : base - searchPaths: grpcIncludePath ? [grpcIncludePath] : [] - } - - Probes.LibraryProbe { - id: grpcLibraryProbe - names: "grpc++" - platformSearchPaths: grpcLibraryPath ? [] : base - searchPaths: grpcLibraryPath ? [grpcLibraryPath] : [] - } - validate: { HelperFunctions.validateCompiler(compilerName, compilerPath); - if (_linkLibraries && !includeProbe.found) - throw "Can't find cpp protobuf include files. Please set the includePath property."; - if (_linkLibraries && !libraryProbe.found) - throw "Can't find cpp protobuf library. Please set the libraryPath property."; + if (_linkLibraries && ! _hasModules) { + throw "Can't find cpp protobuf runtime. Make sure .pc files are present"; + } if (useGrpc) { if (!File.exists(grpcPluginPath)) throw "Can't find grpc_cpp_plugin plugin. Please set the grpcPluginPath property."; - if (_linkLibraries && !grpcIncludeProbe.found) - throw "Can't find grpc++ include files. Please set the grpcIncludePath property."; - if (_linkLibraries && !grpcLibraryProbe.found) - throw "Can't find grpc++ library. Please set the grpcLibraryPath property."; } } } diff --git a/share/qbs/modules/protobuf/nanopb/nanopb.qbs b/share/qbs/modules/protobuf/nanopb/nanopb.qbs index 898e230b6..ada7c20bb 100644 --- a/share/qbs/modules/protobuf/nanopb/nanopb.qbs +++ b/share/qbs/modules/protobuf/nanopb/nanopb.qbs @@ -41,6 +41,7 @@ ProtobufBase { result.push(includePath); return result; } + cpp.cxxLanguageVersion: qbs.targetOS.contains("darwin") ? ["c++17"] : ["c++11"] Rule { inputs: ["protobuf.input"] diff --git a/share/qbs/modules/protobuf/protobuf.js b/share/qbs/modules/protobuf/protobuf.js index abc2c2c4d..60d6f48e4 100644 --- a/share/qbs/modules/protobuf/protobuf.js +++ b/share/qbs/modules/protobuf/protobuf.js @@ -75,6 +75,7 @@ function cppArtifact(outputDir, input, tags, suffix) { filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix), cpp: { includePaths: [].concat(input.cpp.includePaths, outputDir), + defines: ["NDEBUG"], warningLevel: "none", } }; diff --git a/share/qbs/modules/protobuf/protobufbase.qbs b/share/qbs/modules/protobuf/protobufbase.qbs index e302d3758..e32ee30b9 100644 --- a/share/qbs/modules/protobuf/protobufbase.qbs +++ b/share/qbs/modules/protobuf/protobufbase.qbs @@ -6,6 +6,7 @@ import "protobuf.js" as HelperFunctions Module { property string compilerName: "protoc" property string compilerPath: compilerProbe.filePath + property var _searchPaths property pathList importPaths: [] @@ -19,5 +20,6 @@ Module { Probes.BinaryProbe { id: compilerProbe names: [compilerName] + searchPaths: _searchPaths } } diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs index 8ddfc582d..8b31092a9 100644 --- a/share/qbs/modules/qbs/common.qbs +++ b/share/qbs/modules/qbs/common.qbs @@ -53,10 +53,10 @@ Module { readonly property string hostPlatform: undefined // set internally readonly property stringList hostOS: Utilities.canonicalPlatform(hostPlatform) property string hostOSVersion: { - if (hostOS && hostOS.contains("macos")) { + if (hostOS && hostOS.includes("macos")) { return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") || Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion"); - } else if (hostOS && hostOS.contains("windows")) { + } else if (hostOS && hostOS.includes("windows")) { var version = Utilities.getNativeSetting(windowsRegistryKey, "CurrentVersion"); return version + "." + hostOSBuildVersion; } @@ -64,10 +64,10 @@ Module { readonly property string hostArchitecture: undefined // set internally property string hostOSBuildVersion: { - if (hostOS.contains("macos")) { + if (hostOS.includes("macos")) { return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductBuildVersion") || Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductBuildVersion"); - } else if (hostOS.contains("windows")) { + } else if (hostOS.includes("windows")) { return Utilities.getNativeSetting(windowsRegistryKey, "CurrentBuildNumber"); } } @@ -79,19 +79,19 @@ Module { property string targetPlatform: hostPlatform readonly property stringList targetOS: Utilities.canonicalPlatform(targetPlatform) - property string pathListSeparator: hostOS.contains("windows") ? ";" : ":" - property string pathSeparator: hostOS.contains("windows") ? "\\" : "/" - property string nullDevice: hostOS.contains("windows") ? "NUL" : "/dev/null" - property path shellPath: hostOS.contains("windows") ? windowsShellPath : "/bin/sh" + property string pathListSeparator: hostOS.includes("windows") ? ";" : ":" + property string pathSeparator: hostOS.includes("windows") ? "\\" : "/" + property string nullDevice: hostOS.includes("windows") ? "NUL" : "/dev/null" + property path shellPath: hostOS.includes("windows") ? windowsShellPath : "/bin/sh" property string profile: project.profile property string toolchainType: { - if (targetOS.contains("windows")) - return hostOS.contains("windows") ? "msvc" : "mingw"; - if (targetOS.contains("darwin")) - return hostOS.contains("macos") ? "xcode" : "clang"; - if (targetOS.contains("freebsd")) + if (targetOS.includes("windows")) + return hostOS.includes("windows") ? "msvc" : "mingw"; + if (targetOS.includes("darwin")) + return hostOS.includes("macos") ? "xcode" : "clang"; + if (targetOS.includes("freebsd")) return "clang"; - if (targetOS.contains("qnx")) + if (targetOS.includes("qnx")) return "qcc"; if (targetOS.containsAny(["haiku", "vxworks", "unix"])) return "gcc"; @@ -102,7 +102,7 @@ Module { property path installSourceBase property string installRoot: project.buildDirectory + "/install-root" property string installDir - property string installPrefix: qbs.targetOS.contains("unix") ? "/usr/local" : "" + property string installPrefix: targetOS.includes("unix") ? "/usr/local" : "" property path sysroot PropertyOptions { @@ -122,12 +122,12 @@ Module { validator.setRequiredProperty("hostOS", hostOS); validator.setRequiredProperty("targetOS", targetOS); validator.addCustomValidator("targetOS", targetOS, function (value) { - if (!value || (value.contains("osx") && !value.contains("macos"))) + if (!value || (value.includes("osx") && !value.includes("macos"))) return false; return true; }, "the value 'osx' has been replaced by 'macos'; use that instead and update " + "hostOS and targetOS condition checks in your project accordingly"); - if (hostOS && (hostOS.contains("windows") || hostOS.contains("macos"))) { + if (hostOS && (hostOS.includes("windows") || hostOS.includes("macos"))) { validator.setRequiredProperty("hostOSVersion", hostOSVersion, "could not detect host operating system version; " + "verify that system files and registry keys have not " + @@ -143,8 +143,9 @@ Module { validator.addCustomValidator("architecture", architecture, function (value) { return !architecture || architecture === Utilities.canonicalArchitecture(architecture); - }, "'" + architecture + "' is invalid. You must use the canonical name '" + - Utilities.canonicalArchitecture(architecture) + "'"); + }, "'" + architecture + "' is invalid." + (architecture + ? " You must use the canonical name '" + Utilities.canonicalArchitecture(architecture) + : "") + "'"); validator.addCustomValidator("toolchain", toolchain, function (value) { if (toolchain === undefined) @@ -169,7 +170,7 @@ Module { ]; var canonical = Utilities.canonicalToolchain.apply(Utilities, value); for (var i = 0; i < pairs.length; ++i) { - if (canonical.contains(pairs[i][0]) && canonical.contains(pairs[i][1])) + if (canonical.includes(pairs[i][0]) && canonical.includes(pairs[i][1])) return false; } return true; @@ -182,14 +183,14 @@ Module { property string windowsRegistryKey: "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion" property path windowsSystemRoot: FileInfo.fromWindowsSeparators(Utilities.getNativeSetting(windowsRegistryKey, "SystemRoot")) property path windowsShellPath: FileInfo.fromWindowsSeparators(Environment.getEnv("COMSPEC")) || FileInfo.joinPaths(windowsSystemRoot, "System32", "cmd.exe") - property string windowsPathVariable: hostOS.contains("windows") ? "PATH" : "WINEPATH" + property string windowsPathVariable: hostOS.includes("windows") ? "PATH" : "WINEPATH" property var commonRunEnvironment: ({}) setupRunEnvironment: { var env = product.qbs.commonRunEnvironment; for (var i in env) { var v = new ModUtils.EnvironmentVariable(i, product.qbs.pathListSeparator, - product.qbs.hostOS.contains("windows")); + product.qbs.hostOS.includes("windows")); v.value = env[i]; v.set(); } diff --git a/share/qbs/modules/qnx/qnx.qbs b/share/qbs/modules/qnx/qnx.qbs index 9cab5abb6..899b72441 100644 --- a/share/qbs/modules/qnx/qnx.qbs +++ b/share/qbs/modules/qnx/qnx.qbs @@ -31,6 +31,7 @@ import qbs.Environment import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Probes import qbs.Utilities @@ -39,7 +40,7 @@ Module { Probes.PathProbe { id: qnxSdkProbe names: ["qnx700", "qnx660", "qnx650"] - searchPaths: qbs.hostOS.contains("windows") + searchPaths: Host.os().includes("windows") ? [Environment.getEnv("USERPROFILE"), Environment.getEnv("SystemDrive")] : [Environment.getEnv("HOME"), "/opt"] } @@ -77,11 +78,11 @@ Module { property string hostArch: qnx7 ? "x86_64" : "x86" property string hostOs: { - if (qbs.hostOS.contains("linux")) + if (Host.os().includes("linux")) return "linux"; - if (qbs.hostOS.contains("macos")) + if (Host.os().includes("macos")) return "darwin"; - if (qbs.hostOS.contains("windows")) + if (Host.os().includes("windows")) return qnx7 ? "win64" : "win32"; } @@ -111,7 +112,7 @@ Module { } if (!hostOs) { - throw ModUtils.ModuleError("Host operating system '" + qbs.hostOS + throw ModUtils.ModuleError("Host operating system '" + Host.os() + "' is not supported by the QNX SDK."); } else if (!File.exists(hostDir)) { throw ModUtils.ModuleError("Detected host tools operating system '" + hostOs diff --git a/share/qbs/modules/typescript/TypeScriptModule.qbs b/share/qbs/modules/typescript/TypeScriptModule.qbs index d8e61b17f..76df529c2 100644 --- a/share/qbs/modules/typescript/TypeScriptModule.qbs +++ b/share/qbs/modules/typescript/TypeScriptModule.qbs @@ -129,7 +129,7 @@ Module { var preValidator = new ModUtils.PropertyValidator("nodejs"); preValidator.addCustomValidator("interpreterFileName", nodejs.interpreterFileName, function (value) { - return value === "node" + (qbs.hostOS.contains("windows") ? ".exe" : ""); + return value === "node" + FileInfo.executableSuffix(); }, interpreterMessage); preValidator.addCustomValidator("interpreterFilePath", nodejs.interpreterFilePath, function (value) { return value.endsWith(nodejs.interpreterFileName); diff --git a/share/qbs/modules/typescript/typescript.js b/share/qbs/modules/typescript/typescript.js index 858041b3d..199f486cf 100644 --- a/share/qbs/modules/typescript/typescript.js +++ b/share/qbs/modules/typescript/typescript.js @@ -132,7 +132,7 @@ function outputArtifacts(product, inputs) { process.setEnv("NODE_PATH", [ ModUtils.moduleProperty(product, "toolchainInstallPath"), product.moduleProperty("nodejs", "packageManagerRootPath") - ].join(product.moduleProperty("qbs", "pathListSeparator"))); + ].join(FileInfo.pathListSeparator())); process.exec(product.moduleProperty("nodejs", "interpreterFilePath"), [FileInfo.joinPaths(product.buildDirectory, ".io.qt.qbs.internal.typescript", @@ -146,7 +146,7 @@ function outputArtifacts(product, inputs) { var i, appIndex = -1; if (product.moduleProperty("typescript", "singleFile")) { for (i = 0; i < artifacts.length; ++i) { - if (artifacts[i].fileTags.contains("compiled_typescript")) { + if (artifacts[i].fileTags.includes("compiled_typescript")) { appIndex = i; break; } @@ -169,7 +169,7 @@ function outputArtifacts(product, inputs) { } } - if (appIndex === -1 || !artifacts[appIndex].fileTags.contains("compiled_typescript")) + if (appIndex === -1 || !artifacts[appIndex].fileTags.includes("compiled_typescript")) throw "nodejs.applicationFile was set, but Qbs couldn't find the compiled " + "JavaScript file corresponding to '" + applicationFile + "'"; diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs index ad8586da1..077f259f4 100644 --- a/share/qbs/modules/wix/WiXModule.qbs +++ b/share/qbs/modules/wix/WiXModule.qbs @@ -30,12 +30,13 @@ import qbs.File import qbs.FileInfo +import qbs.Host import qbs.ModUtils import qbs.Probes import qbs.Utilities Module { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") Probes.WiXProbe { id: wixProbe @@ -112,19 +113,19 @@ Module { description: "the list of localizations to build the MSI for; leave undefined to build all localizations" } - property stringList extensions: product.type.contains("wixsetup") ? ["WixBalExtension"] : [] // default to WiX Standard Bootstrapper extension + property stringList extensions: product.type.includes("wixsetup") ? ["WixBalExtension"] : [] // default to WiX Standard Bootstrapper extension // private properties property string targetSuffix: { - if (product.type.contains("msi")) { + if (product.type.includes("msi")) { return windowsInstallerSuffix; - } else if (product.type.contains("wixsetup")) { + } else if (product.type.includes("wixsetup")) { return executableSuffix; } } // MSI/MSM package validation only works natively on Windows - property bool enablePackageValidation: qbs.hostOS.contains("windows") + property bool enablePackageValidation: Host.os().includes("windows") property string executableSuffix: ".exe" property string windowsInstallerSuffix: ".msi" @@ -151,7 +152,7 @@ Module { } setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true); v.prepend(product.wix.toolchainInstallPath); v.prepend(product.wix.toolchainInstallRoot); v.set(); @@ -226,7 +227,7 @@ Module { if (ModUtils.moduleProperty(input, "visualStudioCompatibility")) { var toolchain = product.moduleProperty("qbs", "toolchain"); var toolchainInstallPath = product.moduleProperty("cpp", "toolchainInstallPath"); - if (toolchain && toolchain.contains("msvc") && toolchainInstallPath) { + if (toolchain && toolchain.includes("msvc") && toolchainInstallPath) { var vcDir = toolchainInstallPath.replace(/[\\/]bin$/i, ""); var vcRootDir = vcDir.replace(/[\\/]VC$/i, ""); args.push("-dDevEnvDir=" + FileInfo.toWindowsSeparators(FileInfo.joinPaths(vcRootDir, 'Common7', 'IDE'))); @@ -334,12 +335,12 @@ Module { multiplex: true inputs: ["wixobj", "wxl"] auxiliaryInputs: ["installable"] - inputsFromDependencies: product.type.contains("wixsetup") ? ["msi"] : [] + inputsFromDependencies: product.type.includes("wixsetup") ? ["msi"] : [] outputArtifacts: { var artifacts = []; - if (product.type.contains("wixsetup")) { + if (product.type.includes("wixsetup")) { artifacts.push({ fileTags: ["wixsetup", "application"], filePath: FileInfo.joinPaths(product.destinationDirectory, @@ -349,7 +350,7 @@ Module { }); } - if (product.type.contains("msi")) { + if (product.type.includes("msi")) { artifacts.push({ fileTags: ["msi"], filePath: FileInfo.joinPaths(product.destinationDirectory, @@ -375,9 +376,9 @@ Module { prepare: { var i; var primaryOutput; - if (product.type.contains("wixsetup")) { + if (product.type.includes("wixsetup")) { primaryOutput = outputs.wixsetup[0]; - } else if (product.type.contains("msi")) { + } else if (product.type.includes("msi")) { primaryOutput = outputs.msi[0]; } else { throw("WiX: Unsupported product type '" + product.type + "'"); @@ -426,7 +427,7 @@ Module { args.push(FileInfo.toWindowsSeparators(inputs.wxl[i].filePath)); } - if (product.type.contains("msi")) { + if (product.type.includes("msi")) { var cultures = ModUtils.moduleProperty(product, "cultures"); args.push("-cultures:" + (cultures && cultures.length > 0 ? cultures.join(";") : "null")); diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js index 1060894d4..726974579 100644 --- a/share/qbs/modules/xcode/xcode.js +++ b/share/qbs/modules/xcode/xcode.js @@ -112,7 +112,7 @@ function sdkInfoList(sdksPath) { if (!sdks[i].match(/[0-9]+/)) continue; - if (sdks[i].startsWith("DriverKit")) + if (sdks[i].startsWith("DriverKit") || sdks[i].startsWith("AssetRuntime")) continue; var settingsPlist = FileInfo.joinPaths(sdksPath, sdks[i], "SDKSettings.plist"); @@ -202,18 +202,41 @@ function boolFromSdkOrPlatform(varName, sdkProps, platformProps, defaultValue) { return defaultValue; } -function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath) { +function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath, + developerPath) { + if (Utilities.versionCompare(version, "13.3") >= 0) { + var pluginsDir; + if (Utilities.versionCompare(version, "15.3") >= 0) { + pluginsDir = FileInfo.joinPaths(developerPath, "..", + "SharedFrameworks", "XCBuild.framework", "PlugIns", "XCBBuildService.bundle", + "Contents", "PlugIns"); + } else if (Utilities.versionCompare(version, "14.3") >= 0) { + pluginsDir = FileInfo.joinPaths(developerPath, "Library", "Xcode", "Plug-ins"); + } else { + pluginsDir = FileInfo.joinPaths(developerPath, "..", "PlugIns"); + } + var baseDir = FileInfo.joinPaths(pluginsDir, + "XCBSpecifications.ideplugin", "Contents", "Resources"); + + var baseName = targetOS.includes("macos") ? "MacOSX Architectures" + : targetOS.includes("ios-simulator") ? "iOS Simulator" + : targetOS.includes("ios") ? "iOS Device" + : targetOS.includes("tvos-simulator") ? "tvOS Simulator" + : targetOS.includes("tvos") ? "tvOS Device" + : targetOS.includes("watchos-simulator") ? "watchOS Simulator" : "watchOS Device"; + return FileInfo.joinPaths(baseDir, baseName + ".xcspec"); + } var _specsPluginBaseName; if (Utilities.versionCompare(version, "12") >= 0) { - if (targetOS.contains("macos")) + if (targetOS.includes("macos")) _specsPluginBaseName = "OSX"; } if (Utilities.versionCompare(version, "7") >= 0) { - if (targetOS.contains("ios")) + if (targetOS.includes("ios")) _specsPluginBaseName = "iOSPlatform"; - if (targetOS.contains("tvos")) + if (targetOS.includes("tvos")) _specsPluginBaseName = "AppleTV"; - if (targetOS.contains("watchos")) + if (targetOS.includes("watchos")) _specsPluginBaseName = "Watch"; } @@ -224,8 +247,8 @@ function archsSpecsPath(version, targetOS, platformType, platformPath, devicePla "Resources") : FileInfo.joinPaths(platformPath, "Developer", "Library", "Xcode", "Specifications"); - var _archSpecsFileBaseName = targetOS.contains("ios") - ? (targetOS.contains("ios-simulator") ? "iPhone Simulator " : "iPhoneOS") + var _archSpecsFileBaseName = targetOS.includes("ios") + ? (targetOS.includes("ios-simulator") ? "iPhone Simulator " : "iPhoneOS") : DarwinTools.applePlatformDirectoryName(targetOS, platformType) + " "; if (_specsPluginBaseName) { diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs index 6c0584c81..2c0cd001a 100644 --- a/share/qbs/modules/xcode/xcode.qbs +++ b/share/qbs/modules/xcode/xcode.qbs @@ -9,25 +9,23 @@ import qbs.PropertyList import qbs.Utilities Module { - id: xcodeModule - Probes.XcodeLocationProbe { id: xcodeLocationProbe - condition: !xcodeModule.developerPath + condition: !developerPath } Probes.XcodeProbe { id: xcodeProbe - developerPath: xcodeModule.developerPath - platformType: xcodeModule.platformType - platformPath: xcodeModule.platformPath - devicePlatformPath: xcodeModule.devicePlatformPath - xcodebuildPath: xcodeModule.xcodebuildPath - sdksPath: xcodeModule.sdksPath + developerPath: parent.developerPath + platformType: parent.platformType + platformPath: parent.platformPath + devicePlatformPath: parent.devicePlatformPath + xcodebuildPath: parent.xcodebuildPath + sdksPath: parent.sdksPath } - condition: qbs.targetOS.contains("darwin") && - qbs.toolchain && qbs.toolchain.contains("xcode") + condition: qbs.targetOS.includes("darwin") && + qbs.toolchain && qbs.toolchain.includes("xcode") version: xcodeProbe.xcodeVersion @@ -134,6 +132,7 @@ Module { readonly property var _sdkSettings: { if (_availableSdks) { + // see 'sdk' property doc to understand why this loop is needed for (var i in _availableSdks) { if (_availableSdks[i]["Version"] === sdk) return _availableSdks[i]; @@ -141,7 +140,7 @@ Module { return _availableSdks[i]; } - // Latest SDK available for the platform + // Latest SDK available for the platform (default case) if (DarwinTools.applePlatformName(qbs.targetOS, platformType) === sdk) return _latestSdk; } @@ -193,7 +192,7 @@ Module { }) setupBuildEnvironment: { - var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, false); + var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), false); v.prepend(product.xcode.platformPath + "/Developer/usr/bin"); v.prepend(product.xcode.developerPath + "/usr/bin"); v.set(); diff --git a/share/share.qbs b/share/share.qbs index c5cf6893e..75e1e83ba 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -18,22 +18,13 @@ Product { } Group { - name: "Python executables" - files: ["../src/3rdparty/python/bin/dmgbuild"] - fileTags: ["qbs resources"] - qbs.install: true - qbs.installDir: qbsbuildconfig.libexecInstallDir - qbs.installSourceBase: "../src/3rdparty/python/bin" - } - - Group { name: "Python packages" prefix: "../src/3rdparty/python/**/" files: ["*.py"] fileTags: ["qbs resources"] qbs.install: true qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share/qbs/python" - qbs.installSourceBase: "../src/3rdparty/python/lib/python2.7/site-packages" + qbs.installSourceBase: "../src/3rdparty/python/lib/python3.9/site-packages" } Group { @@ -81,7 +72,7 @@ Product { } prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Generating " + output.fileName; + cmd.description = "generating " + output.fileName; cmd.highlight = "codegen"; cmd.sourceCode = function() { var tf; @@ -106,7 +97,7 @@ Product { } prepare: { var cmd = new JavaScriptCommand(); - cmd.description = "Generating " + output.fileName; + cmd.description = "generating " + output.fileName; cmd.highlight = "codegen"; cmd.sourceCode = function() { var tf; |