From 532656a552b8b4a263fd58003dbd7063564d1448 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Mon, 6 Mar 2017 21:46:17 -0800 Subject: Implement basic support for multi-config products on Darwin and Android Task-number: QBS-13 Task-number: QBS-292 Change-Id: I63c2e0a4de5949c73ca33af7381c32606190a43a Reviewed-by: Jake Petroules Reviewed-by: Christian Kandeler --- share/qbs/imports/qbs/PathTools/path-tools.js | 69 ++++++---- share/qbs/imports/qbs/base/Application.qbs | 13 +- share/qbs/imports/qbs/base/Library.qbs | 13 +- share/qbs/imports/qbs/base/LoadableModule.qbs | 5 +- share/qbs/imports/qbs/base/NativeBinary.qbs | 54 ++++++++ share/qbs/modules/bundle/BundleModule.qbs | 5 +- share/qbs/modules/cpp/CppModule.qbs | 1 + share/qbs/modules/cpp/DarwinGCC.qbs | 57 ++++++++ share/qbs/modules/cpp/GenericGCC.qbs | 31 ++++- share/qbs/modules/cpp/darwin.js | 186 ++++++++++++++++++++++++++ share/qbs/modules/cpp/gcc.js | 172 ++++++++++++++---------- share/qbs/modules/qbs/common.qbs | 27 +++- 12 files changed, 508 insertions(+), 125 deletions(-) create mode 100644 share/qbs/imports/qbs/base/NativeBinary.qbs create mode 100644 share/qbs/modules/cpp/darwin.js (limited to 'share') diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js index 2bbec5255..68c37f568 100644 --- a/share/qbs/imports/qbs/PathTools/path-tools.js +++ b/share/qbs/imports/qbs/PathTools/path-tools.js @@ -30,44 +30,44 @@ var FileInfo = require("qbs.FileInfo"); -function _bundleExecutableTemporaryFilePath(product) { - return ".tmp/" + FileInfo.fileName(bundleExecutableFilePath(product)); +function _bundleExecutableTemporaryFilePath(product, variantSuffix) { + return ".tmp/" + FileInfo.fileName(bundleExecutableFilePath(product, variantSuffix)); } -function bundleExecutableFilePath(product) { - return product.moduleProperty("bundle", "executablePath"); +function bundleExecutableFilePath(product, variantSuffix) { + return product.moduleProperty("bundle", "executablePath") + (variantSuffix || ""); } -function applicationFilePath(product) { +function applicationFilePath(product, variantSuffix) { if (product.moduleProperty("bundle", "isBundle")) - return _bundleExecutableTemporaryFilePath(product); + return _bundleExecutableTemporaryFilePath(product, variantSuffix); return product.moduleProperty("cpp", "executablePrefix") - + product.targetName + + product.targetName + (variantSuffix || "") + product.moduleProperty("cpp", "executableSuffix"); } -function loadableModuleFilePath(product) { +function loadableModuleFilePath(product, variantSuffix) { if (product.moduleProperty("bundle", "isBundle")) - return _bundleExecutableTemporaryFilePath(product); + return _bundleExecutableTemporaryFilePath(product, variantSuffix); return product.moduleProperty("cpp", "loadableModulePrefix") - + product.targetName + + product.targetName + (variantSuffix || "") + product.moduleProperty("cpp", "loadableModuleSuffix"); } -function staticLibraryFilePath(product) { +function staticLibraryFilePath(product, variantSuffix) { if (product.moduleProperty("bundle", "isBundle")) - return _bundleExecutableTemporaryFilePath(product); + return _bundleExecutableTemporaryFilePath(product, variantSuffix); return product.moduleProperty("cpp", "staticLibraryPrefix") - + product.targetName + + product.targetName + (variantSuffix || "") + product.moduleProperty("cpp", "staticLibrarySuffix"); } -function dynamicLibraryFilePath(product, version, maxParts) { +function dynamicLibraryFilePath(product, variantSuffix, version, maxParts) { if (product.moduleProperty("bundle", "isBundle")) - return _bundleExecutableTemporaryFilePath(product); + return _bundleExecutableTemporaryFilePath(product, variantSuffix); // If no override version was given, use the product's version // We specifically want to differentiate between undefined and i.e. @@ -84,7 +84,9 @@ function dynamicLibraryFilePath(product, version, maxParts) { version = version.split('.').slice(0, maxParts).join('.'); // Start with prefix + name (i.e. libqbs, qbs) - var fileName = product.moduleProperty("cpp", "dynamicLibraryPrefix") + product.targetName; + var fileName = product.moduleProperty("cpp", "dynamicLibraryPrefix") + + product.targetName + + (variantSuffix || ""); // For Mach-O images, append the version number if there is one (i.e. libqbs.1.0.0) var imageFormat = product.moduleProperty("cpp", "imageFormat"); @@ -103,6 +105,21 @@ function dynamicLibraryFilePath(product, version, maxParts) { return fileName; } +function linkerOutputFilePath(fileTag, product, variantSuffix, version, maxParts) { + switch (fileTag) { + case "application": + return applicationFilePath(product, variantSuffix); + case "loadablemodule": + return loadableModuleFilePath(product, variantSuffix); + case "staticlibrary": + return staticLibraryFilePath(product, variantSuffix); + case "dynamiclibrary": + return dynamicLibraryFilePath(product, variantSuffix, version, maxParts); + default: + throw new Error("Unknown linker output file tag: " + fileTag); + } +} + function importLibraryFilePath(product) { return product.moduleProperty("cpp", "dynamicLibraryPrefix") + product.targetName @@ -116,7 +133,7 @@ function debugInfoIsBundle(product) { return !flags.contains("-f") && !flags.contains("--flat"); } -function debugInfoFileName(product, fileTag) { +function debugInfoFileName(product, variantSuffix, fileTag) { var suffix = ""; // For dSYM bundles, the DWARF debug info file has no suffix @@ -125,19 +142,19 @@ function debugInfoFileName(product, fileTag) { suffix = product.moduleProperty("cpp", "debugInfoSuffix"); if (product.moduleProperty("bundle", "isBundle")) { - return FileInfo.fileName(bundleExecutableFilePath(product)) + suffix; + return FileInfo.fileName(bundleExecutableFilePath(product, variantSuffix)) + suffix; } else { switch (fileTag) { case "application": - return applicationFilePath(product) + suffix; + return applicationFilePath(product, variantSuffix) + suffix; case "dynamiclibrary": - return dynamicLibraryFilePath(product) + suffix; + return dynamicLibraryFilePath(product, variantSuffix) + suffix; case "loadablemodule": - return loadableModuleFilePath(product) + suffix; + return loadableModuleFilePath(product, variantSuffix) + suffix; case "staticlibrary": - return staticLibraryFilePath(product) + suffix; + return staticLibraryFilePath(product, variantSuffix) + suffix; default: - return product.targetName + suffix; + return product.targetName + (variantSuffix || "") + suffix; } } } @@ -149,11 +166,11 @@ function debugInfoBundlePath(product, fileTag) { if (product.moduleProperty("qbs", "targetOS").contains("darwin") && product.moduleProperty("bundle", "isBundle")) return product.moduleProperty("bundle", "bundleName") + suffix; - return debugInfoFileName(product, fileTag) + suffix; + return debugInfoFileName(product, undefined, fileTag) + suffix; } -function debugInfoFilePath(product, fileTag) { - var name = debugInfoFileName(product, fileTag); +function debugInfoFilePath(product, variantSuffix, fileTag) { + var name = debugInfoFileName(product, variantSuffix, fileTag); if (product.moduleProperty("qbs", "targetOS").contains("darwin") && debugInfoIsBundle(product)) { return FileInfo.joinPaths(debugInfoBundlePath(product, fileTag), "Contents", "Resources", "DWARF", name); diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs index 781b440da..e16a93761 100644 --- a/share/qbs/imports/qbs/base/Application.qbs +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -28,19 +28,12 @@ ** ****************************************************************************/ -Product { +import qbs + +NativeBinary { type: { if (isForAndroid && !consoleApplication) return ["dynamiclibrary", "android.nativelibrary"]; return ["application"]; } - - property bool isForAndroid: qbs.targetOS.contains("android") - property stringList architectures: isForAndroid && !qbs.architecture ? ["armv5te"] : undefined - - Depends { name: "bundle" } - - profiles: architectures - ? architectures.map(function(arch) { return project.profile + '-' + arch; }) - : [project.profile] } diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs index 65e677830..4f0b45789 100644 --- a/share/qbs/imports/qbs/base/Library.qbs +++ b/share/qbs/imports/qbs/base/Library.qbs @@ -28,19 +28,12 @@ ** ****************************************************************************/ -Product { +import qbs + +NativeBinary { type: { if (qbs.targetOS.contains("ios") && parseInt(cpp.minimumIosVersion, 10) < 8) return ["staticlibrary"]; return ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []); } - - property bool isForAndroid: qbs.targetOS.contains("android") - property stringList architectures: isForAndroid && !qbs.architecture ? ["armv5te"] : undefined - - Depends { name: "bundle" } - - profiles: architectures - ? architectures.map(function(arch) { return project.profile + '-' + arch; }) - : [project.profile] } diff --git a/share/qbs/imports/qbs/base/LoadableModule.qbs b/share/qbs/imports/qbs/base/LoadableModule.qbs index 05234de60..607a7d8d2 100644 --- a/share/qbs/imports/qbs/base/LoadableModule.qbs +++ b/share/qbs/imports/qbs/base/LoadableModule.qbs @@ -30,7 +30,6 @@ import qbs -Product { - Depends { name: "bundle" } - type: qbs.targetOS.contains("darwin") ? ["loadablemodule"] : ["dynamiclibrary"] +DynamicLibrary { + type: isForDarwin ? ["loadablemodule"] : base } diff --git a/share/qbs/imports/qbs/base/NativeBinary.qbs b/share/qbs/imports/qbs/base/NativeBinary.qbs new file mode 100644 index 000000000..2651a4e31 --- /dev/null +++ b/share/qbs/imports/qbs/base/NativeBinary.qbs @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +Product { + property bool isForAndroid: qbs.targetOS.contains("android") + property bool isForDarwin: qbs.targetOS.contains("darwin") + property stringList architectures: isForAndroid && !qbs.architecture ? ["armv5te"] : undefined + + Depends { name: "bundle" } + + profiles: architectures + ? architectures.map(function(arch) { return project.profile + '-' + arch; }) + : [project.profile] + + aggregate: ((qbs.architectures && qbs.architectures.length > 1) + || (qbs.buildVariants && qbs.buildVariants.length > 1)) && isForDarwin + + multiplexByQbsProperties: { + if (isForDarwin) + return ["profiles", "architectures", "buildVariants"]; + if (isForAndroid) + return ["profiles"]; + return base; + } +} diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index a7332a4ea..c651d8f61 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -98,7 +98,8 @@ Module { } } - additionalProductTypes: ["bundle.content"] + additionalProductTypes: !(product.multiplexed || product.aggregate) + || !product.multiplexConfigurationId ? ["bundle.content"] : [] property bool isBundle: !product.consoleApplication && qbs.targetOS.contains("darwin") @@ -641,7 +642,7 @@ Module { var executables = outputs["bundle.symlink.executable"]; for (i in executables) { - cmd = new Command("ln", ["-sf", FileInfo.joinPaths("Versions", "Current", product.targetName), + cmd = new Command("ln", ["-sfn", FileInfo.joinPaths("Versions", "Current", product.targetName), executables[i].filePath]); cmd.silent = true; commands.push(cmd); diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index c4bd35462..bbd34ee68 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -164,6 +164,7 @@ Module { property string executableSuffix property string debugInfoSuffix property string debugInfoBundleSuffix + property string variantSuffix property bool createSymlinks: true property stringList dynamicLibraries // list of names, will be linked with -lname property stringList staticLibraries // list of static library files diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 598fe6c99..b5aa04d8f 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -30,10 +30,14 @@ import qbs import qbs.DarwinTools +import qbs.File import qbs.FileInfo import qbs.ModUtils +import qbs.PathTools import qbs.PropertyList import qbs.TextFile +import "darwin.js" as Darwin +import "gcc.js" as Gcc UnixGCC { condition: false @@ -51,6 +55,13 @@ UnixGCC { loadableModulePrefix: "" loadableModuleSuffix: ".bundle" dynamicLibrarySuffix: ".dylib" + variantSuffix: { + // "release" corresponds to the "normal" (non-suffixed) variant + if (qbs.buildVariant !== "release") + return "_" + qbs.buildVariant; + return ""; + } + separateDebugInformation: true debugInfoBundleSuffix: ".dSYM" debugInfoSuffix: ".dwarf" @@ -167,6 +178,52 @@ UnixGCC { property string minimumDarwinVersionCompilerFlag property string minimumDarwinVersionLinkerFlag + Rule { + condition: product.aggregate + inputsFromDependencies: ["application"] + multiplex: true + + outputFileTags: ["bundle.input", "application", "primary", "debuginfo_app"] + outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "application", "app") + + prepare: Darwin.prepareLipo.apply(Darwin, arguments) + } + + Rule { + condition: product.aggregate + inputsFromDependencies: ["loadablemodule"] + multiplex: true + + outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule"] + outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "loadablemodule", + "loadablemodule") + + prepare: Darwin.prepareLipo.apply(Darwin, arguments) + } + + Rule { + condition: product.aggregate + inputsFromDependencies: ["dynamiclibrary"] + multiplex: true + + outputFileTags: ["bundle.input", "dynamiclibrary", "dynamiclibrary_copy", "primary", + "debuginfo_dll"] + outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "dynamiclibrary", "dll") + + prepare: Darwin.prepareLipo.apply(Darwin, arguments) + } + + Rule { + condition: product.aggregate + inputsFromDependencies: ["staticlibrary"] + multiplex: true + + outputFileTags: ["bundle.input", "staticlibrary", "primary"] + outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "staticlibrary") + + prepare: Darwin.prepareLipo.apply(Darwin, arguments) + } + Rule { condition: qbs.targetOS.contains("darwin") multiplex: true diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 9afb93064..9266f1ed0 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -110,6 +110,7 @@ CppModule { property string objcopyName: "objcopy" property string stripName: "strip" property string dsymutilName: "dsymutil" + property string lipoName: "lipo" property path sysroot: qbs.sysroot property string linkerMode: "automatic" @@ -173,8 +174,11 @@ CppModule { property string objcopyPath: toolchainPathPrefix + objcopyName property string stripPath: toolchainPathPrefix + stripName property string dsymutilPath: toolchainPathPrefix + dsymutilName + property string lipoPath: toolchainPathPrefix + lipoName property stringList dsymutilFlags + property bool alwaysUseLipo: false + readonly property bool shouldCreateSymlinks: { return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat); } @@ -296,8 +300,14 @@ CppModule { validator.validate(); } + // Product should be linked if it's not multiplexed or aggregated at all, + // or if it is multiplexed, if it's not the aggregate product + readonly property bool shouldLink: !(product.multiplexed || product.aggregate) + || product.multiplexConfigurationId + Rule { id: dynamicLibraryLinker + condition: product.cpp.shouldLink multiplex: true inputs: { var tags = ["obj", "linkerscript", "versionscript"]; @@ -338,7 +348,8 @@ CppModule { for (var i = 0; i < maxVersionParts; ++i) { var symlink = { filePath: product.destinationDirectory + "/" - + PathTools.dynamicLibraryFilePath(product, undefined, i), + + PathTools.dynamicLibraryFilePath(product, undefined, undefined, + i), fileTags: ["dynamiclibrary_symlink"] }; if (i > 0 && artifacts[i-1].filePath == symlink.filePath) @@ -346,7 +357,9 @@ CppModule { artifacts.push(symlink); } } - return artifacts.concat(Gcc.debugInfoArtifacts(product, "dll")); + if (!product.aggregate) + artifacts = artifacts.concat(Gcc.debugInfoArtifacts(product, undefined, "dll")); + return artifacts; } prepare: { @@ -356,6 +369,7 @@ CppModule { Rule { id: staticLibraryLinker + condition: product.cpp.shouldLink multiplex: true inputs: ["obj", "linkerscript"] inputsFromDependencies: ["dynamiclibrary", "staticlibrary"] @@ -395,6 +409,7 @@ CppModule { Rule { id: loadableModuleLinker + condition: product.cpp.shouldLink multiplex: true inputs: { var tags = ["obj", "linkerscript"]; @@ -417,7 +432,11 @@ CppModule { PathTools.bundleExecutableFilePath(product)) } } - return [app].concat(Gcc.debugInfoArtifacts(product, "loadablemodule")); + var artifacts = [app]; + if (!product.aggregate) + artifacts = artifacts.concat(Gcc.debugInfoArtifacts(product, undefined, + "loadablemodule")); + return artifacts; } prepare: { @@ -427,6 +446,7 @@ CppModule { Rule { id: applicationLinker + condition: product.cpp.shouldLink multiplex: true inputs: { var tags = ["obj", "linkerscript"]; @@ -449,7 +469,10 @@ CppModule { PathTools.bundleExecutableFilePath(product)) } } - return [app].concat(Gcc.debugInfoArtifacts(product, "app")); + var artifacts = [app]; + if (!product.aggregate) + artifacts = artifacts.concat(Gcc.debugInfoArtifacts(product, undefined, "app")); + return artifacts; } prepare: { diff --git a/share/qbs/modules/cpp/darwin.js b/share/qbs/modules/cpp/darwin.js new file mode 100644 index 000000000..87f638831 --- /dev/null +++ b/share/qbs/modules/cpp/darwin.js @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var Gcc = require("./gcc.js"); +var ModUtils = loadExtension("qbs.ModUtils"); +var PathTools = loadExtension("qbs.PathTools"); + +function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) { + var buildVariants = []; + for (var i = 0; i < inputs[fileTag].length; ++i) { + var variant = inputs[fileTag][i].qbs.buildVariant; + var suffix = inputs[fileTag][i].cpp.variantSuffix; + if (!buildVariants.some(function (x) { return x.name === variant; })) + buildVariants.push({ name: variant, suffix: suffix }); + } + + var list = []; + + if (fileTag === "dynamiclibrary") { + Array.prototype.push.apply(list, buildVariants.map(function (variant) { + return { + filePath: product.destinationDirectory + "/.sosymbols/" + + PathTools.dynamicLibraryFilePath(product, variant.suffix), + fileTags: ["dynamiclibrary_copy"], + qbs: { buildVariant: variant.name, variantSuffix: variant.suffix }, + alwaysUpdated: false + }; + })); + } + + // Bundles should have a "normal" variant. In the case of frameworks, they cannot normally be + // linked to without a default variant unless a variant is specifically chosen at link time + // by passing the full path to the shared library executable instead of the -framework switch. + // Technically this doesn't affect qbs since qbs always uses full paths for internal + // dependencies but the "normal" variant is always the one that is linked to, since the + // alternative variants should only be chosen at runtime using the DYLD_IMAGE_SUFFIX variable. + // So for frameworks we'll create a symlink to the "default" variant as chosen by the user + // (we cannot do this automatically since the user must tell us which variant should be + // preferred, if there are multiple alternative variants). Applications are fine without a + // symlink but still need an explicitly chosen variant to set as the CFBundleExecutable so that + // Finder/LaunchServices can launch it normally but for simplicity we'll just use the symlink + // approach for all bundle types. + var defaultVariant; + if (!buildVariants.some(function (x) { return x.name === "release"; })) { + var defaultBuildVariant = product.qbs.defaultBuildVariant; + buildVariants.map(function (variant) { + if (variant.name === defaultBuildVariant) + defaultVariant = variant; + }); + if (!defaultVariant) { + throw new Error("qbs.defaultBuildVariant is '" + defaultBuildVariant + "', but this " + + "variant is not in the qbs.buildVariants list (" + + product.qbs.buildVariants.join(", ") + ")"); + } + + buildVariants.push({ + name: "release", + suffix: "", + isSymLink: true + }); + } + + Array.prototype.push.apply(list, buildVariants.map(function (variant) { + var tags = ["bundle.input"]; + if (variant.isSymLink) + tags.push("bundle.variant_symlink"); + else + tags.push(fileTag, "primary"); + + return { + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.linkerOutputFilePath(fileTag, product, + variant.suffix)), + fileTags: tags, + qbs: { + buildVariant: variant.name, + variantSuffix: variant.suffix, + _buildVariantFileName: variant.isSymLink && defaultVariant + ? FileInfo.fileName(PathTools.linkerOutputFilePath( + fileTag, product, + defaultVariant.suffix)) + : undefined + }, + bundle: { + _bundleFilePath: product.destinationDirectory + "/" + + PathTools.bundleExecutableFilePath(product, variant.suffix) + } + }; + })); + if (debugSuffix) + Array.prototype.push.apply(list, Gcc.debugInfoArtifacts(product, buildVariants, + debugSuffix)); + return list; +} + +function prepareLipo(project, product, inputs, outputs, input, output) { + var cmd; + var commands = []; + var allInputs = [].concat.apply([], Object.keys(inputs).map(function (tag) { + return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].contains(tag) + ? inputs[tag] : []; + })); + + (outputs["bundle.variant_symlink"] || []).map(function (symlink) { + cmd = new Command("ln", ["-sfn", symlink.qbs._buildVariantFileName, symlink.filePath]); + cmd.silent = true; + commands.push(cmd); + }); + + for (var i = 0; i < outputs.primary.length; ++i) { + var vInputs = allInputs.filter(function (f) { + return f.qbs.buildVariant === outputs.primary[i].qbs.buildVariant + }).map(function (f) { + return f.filePath + }); + + if (vInputs.length > 1 || product.cpp.alwaysUseLipo) { + cmd = new Command(ModUtils.moduleProperty(product, "lipoPath"), + ["-create", "-output", outputs.primary[i].filePath].concat(vInputs)); + cmd.description = "lipo " + outputs.primary[i].fileName; + cmd.highlight = "linker"; + } else { + cmd = new JavaScriptCommand(); + cmd.src = vInputs[0]; + cmd.dst = outputs.primary[i].filePath; + cmd.sourceCode = function () { + File.copy(src, dst); + }; + cmd.silent = true; + } + + commands.push(cmd); + } + + var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll + || outputs.debuginfo_loadablemodule; + if (debugInfo) { + var dsymPath = debugInfo[0].filePath; + if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) + dsymPath = outputs.debuginfo_bundle[0].filePath; + var flags = ModUtils.moduleProperty(product, "dsymutilFlags") || []; + cmd = new Command(ModUtils.moduleProperty(product, "dsymutilPath"), flags.concat([ + "-o", dsymPath + ]).concat(outputs.primary.map(function (f) { return f.filePath; }))); + cmd.description = "generating dSYM for " + product.name; + commands.push(cmd); + } + + cmd = new Command(ModUtils.moduleProperty(product, "stripPath"), + ["-S", outputs.primary[0].filePath]); + cmd.silent = true; + commands.push(cmd); + if (outputs.dynamiclibrary_copy) + Array.prototype.push.apply(commands, Gcc.createSymbolCheckingCommands(product, outputs)); + return commands; +} + diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index 916935ac6..b94f93312 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -748,6 +748,17 @@ function prepareAssembler(project, product, inputs, outputs, input, output) { return cmd; } +function nativeConfigString(product) { + var props = []; + if ((product.multiplexed || product.aggregate) && product.multiplexConfigurationId) { + if (product.qbs.targetOS.containsAny(["android", "darwin"])) + props.push(product.qbs.architecture); + if (product.qbs.targetOS.contains("darwin")) + props.push(product.qbs.buildVariant); + } + return props.length > 0 ? (" (" + props.join(", ") + ")") : ""; +} + function prepareCompiler(project, product, inputs, outputs, input, output) { var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain, input, output); @@ -768,6 +779,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output) { cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + input.fileName; if (pchOutput) cmd.description += ' (' + compilerInfo.tag + ')'; + cmd.description += nativeConfigString(product); cmd.highlight = "compiler"; cmd.responseFileArgumentIndex = wrapperArgsLength; cmd.responseFileUsagePrefix = '@'; @@ -872,61 +884,79 @@ function readSymbolFile(filePath) return result; } -function createSymbolCheckingCommand(product, outputs) -{ - // Update the symbols file if the list of relevant symbols has changed. - cmd = new JavaScriptCommand(); - cmd.silent = true; - cmd.sourceCode = function() { - if (!outputs.dynamiclibrary_copy) - return; - - var libFilePath = outputs.dynamiclibrary[0].filePath; - var symbolFilePath = outputs.dynamiclibrary_copy[0].filePath; - - var newNmResult = getSymbolInfo(product, libFilePath); - if (!newNmResult.success) - return; +function createSymbolCheckingCommands(product, outputs) { + var commands = []; + if (!outputs.dynamiclibrary || !outputs.dynamiclibrary_copy) + return commands; + + if (outputs.dynamiclibrary.length !== outputs.dynamiclibrary_copy.length) + throw new Error("The number of outputs tagged dynamiclibrary (" + + outputs.dynamiclibrary.length + ") must be equal to the number of " + + "outputs tagged dynamiclibrary_copy (" + + outputs.dynamiclibrary_copy.length + ")"); + + for (var d = 0; d < outputs.dynamiclibrary_copy.length; ++d) { + // Update the symbols file if the list of relevant symbols has changed. + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.d = d; + cmd.sourceCode = function() { + if (outputs.dynamiclibrary[d].qbs.buildVariant + !== outputs.dynamiclibrary_copy[d].qbs.buildVariant) + throw new Error("Build variant of output tagged dynamiclibrary (" + + outputs.dynamiclibrary[d].qbs.buildVariant + ") is not equal to " + + "build variant of output tagged dynamiclibrary_copy (" + + outputs.dynamiclibrary_copy[d].qbs.buildVariant + ") at index " + + d); + + var libFilePath = outputs.dynamiclibrary[d].filePath; + var symbolFilePath = outputs.dynamiclibrary_copy[d].filePath; + + var newNmResult = getSymbolInfo(product, libFilePath); + if (!newNmResult.success) + return; - if (!File.exists(symbolFilePath)) { - console.debug("Symbol file '" + symbolFilePath + "' does not yet exist."); - createSymbolFile(symbolFilePath, newNmResult.allGlobalSymbols, - newNmResult.definedGlobalSymbols); - return; - } + if (!File.exists(symbolFilePath)) { + console.debug("Symbol file '" + symbolFilePath + "' does not yet exist."); + createSymbolFile(symbolFilePath, newNmResult.allGlobalSymbols, + newNmResult.definedGlobalSymbols); + return; + } - var oldNmResult = readSymbolFile(symbolFilePath); - var checkMode = product.cpp.exportedSymbolsCheckMode; - var oldSymbols; - var newSymbols; - if (checkMode === "strict") { - oldSymbols = oldNmResult.allGlobalSymbols; - newSymbols = newNmResult.allGlobalSymbols; - } else { - oldSymbols = oldNmResult.definedGlobalSymbols; - newSymbols = newNmResult.definedGlobalSymbols; - } - if (oldSymbols.length !== newSymbols.length) { - console.debug("List of relevant symbols differs for '" + libFilePath + "'."); - createSymbolFile(symbolFilePath, newNmResult.allGlobalSymbols, - newNmResult.definedGlobalSymbols); - return; - } - for (var i = 0; i < oldSymbols.length; ++i) { - var oldLine = oldSymbols[i]; - var newLine = newSymbols[i]; - var oldLineElems = oldLine.split(/\s+/); - var newLineElems = newLine.split(/\s+/); - if (oldLineElems[0] !== newLineElems[0] // Object name. - || oldLineElems[1] !== newLineElems[1]) { // Object type + var oldNmResult = readSymbolFile(symbolFilePath); + var checkMode = product.cpp.exportedSymbolsCheckMode; + var oldSymbols; + var newSymbols; + if (checkMode === "strict") { + oldSymbols = oldNmResult.allGlobalSymbols; + newSymbols = newNmResult.allGlobalSymbols; + } else { + oldSymbols = oldNmResult.definedGlobalSymbols; + newSymbols = newNmResult.definedGlobalSymbols; + } + if (oldSymbols.length !== newSymbols.length) { console.debug("List of relevant symbols differs for '" + libFilePath + "'."); createSymbolFile(symbolFilePath, newNmResult.allGlobalSymbols, newNmResult.definedGlobalSymbols); return; } + for (var i = 0; i < oldSymbols.length; ++i) { + var oldLine = oldSymbols[i]; + var newLine = newSymbols[i]; + var oldLineElems = oldLine.split(/\s+/); + var newLineElems = newLine.split(/\s+/); + if (oldLineElems[0] !== newLineElems[0] // Object name. + || oldLineElems[1] !== newLineElems[1]) { // Object type + console.debug("List of relevant symbols differs for '" + libFilePath + "'."); + createSymbolFile(symbolFilePath, newNmResult.allGlobalSymbols, + newNmResult.definedGlobalSymbols); + return; + } + } } + commands.push(cmd); } - return cmd; + return commands; } function prepareLinker(project, product, inputs, outputs, input, output) { @@ -953,7 +983,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } cmd = new Command(linkerPath, args); - cmd.description = 'linking ' + primaryOutput.fileName; + cmd.description = 'linking ' + primaryOutput.fileName + nativeConfigString(product); cmd.highlight = 'linker'; cmd.responseFileArgumentIndex = wrapperArgsLength; cmd.responseFileUsagePrefix = '@'; @@ -963,20 +993,22 @@ function prepareLinker(project, product, inputs, outputs, input, output) { || outputs.debuginfo_loadablemodule; if (debugInfo) { if (product.qbs.targetOS.contains("darwin")) { - var dsymPath = debugInfo[0].filePath; - if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) - dsymPath = outputs.debuginfo_bundle[0].filePath; - var flags = product.cpp.dsymutilFlags || []; - cmd = new Command(product.cpp.dsymutilPath, flags.concat([ - "-o", dsymPath, primaryOutput.filePath - ])); - cmd.description = "generating dSYM for " + product.name; - commands.push(cmd); - - cmd = new Command(product.cpp.stripPath, - ["-S", primaryOutput.filePath]); - cmd.silent = true; - commands.push(cmd); + if (!product.aggregate) { + var dsymPath = debugInfo[0].filePath; + if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) + dsymPath = outputs.debuginfo_bundle[0].filePath; + var flags = product.cpp.dsymutilFlags || []; + cmd = new Command(product.cpp.dsymutilPath, flags.concat([ + "-o", dsymPath, primaryOutput.filePath + ])); + cmd.description = "generating dSYM for " + product.name; + commands.push(cmd); + + cmd = new Command(product.cpp.stripPath, + ["-S", primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + } } else { var objcopy = product.cpp.objcopyPath; @@ -997,7 +1029,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } if (outputs.dynamiclibrary) { - commands.push(createSymbolCheckingCommand(product, outputs)); + Array.prototype.push.apply(commands, createSymbolCheckingCommands(product, outputs)); // Create symlinks from {libfoo, libfoo.1, libfoo.1.0} to libfoo.1.0.0 var links = outputs["dynamiclibrary_symlink"]; @@ -1050,7 +1082,7 @@ function concatLibsFromArtifacts(libs, artifacts, filePathGetter) return concatLibs(deps, libs); } -function debugInfoArtifacts(product, debugInfoTagSuffix) { +function debugInfoArtifacts(product, variants, debugInfoTagSuffix) { var fileTag; switch (debugInfoTagSuffix) { case "app": @@ -1064,12 +1096,18 @@ function debugInfoArtifacts(product, debugInfoTagSuffix) { break; } + variants = variants || [{}]; + var artifacts = []; if (product.cpp.separateDebugInformation) { - artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, - PathTools.debugInfoFilePath(product, fileTag)), - fileTags: ["debuginfo_" + debugInfoTagSuffix] + variants.map(function (variant) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.debugInfoFilePath(product, + variant.suffix, + fileTag)), + fileTags: ["debuginfo_" + debugInfoTagSuffix] + }); }); if (PathTools.debugInfoIsBundle(product)) { artifacts.push({ diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs index f81b78c29..d71b0201b 100644 --- a/share/qbs/modules/qbs/common.qbs +++ b/share/qbs/modules/qbs/common.qbs @@ -37,7 +37,7 @@ import qbs.Utilities Module { readonly property string configurationName: "default" - property string buildVariant: { + property string defaultBuildVariant: { switch (configurationName.toLowerCase()) { case "release": return "release"; @@ -46,6 +46,8 @@ Module { } } + property string buildVariant: defaultBuildVariant + property bool enableDebugCode: buildVariant == "debug" property bool debugInformation: (buildVariant == "debug") property string optimization: (buildVariant == "debug" ? "none" : "fast") @@ -211,8 +213,27 @@ Module { // Properties that can be set for multiplexing products. property stringList profiles - property stringList architectures - property stringList buildVariants + property stringList architectures: { + if (targetOS.contains("android")) + return ["armv5te"]; + if (targetOS.contains("ios-simulator")) + return ["x86", "x86_64"]; + if (targetOS.contains("ios")) + return ["armv7a", "arm64"]; + if (targetOS.contains("macos")) + return ["x86_64"]; + if (targetOS.contains("tvos-simulator")) + return ["x86_64"]; + if (targetOS.contains("tvos")) + return ["arm64"]; + if (targetOS.contains("watchos-simulator")) + return ["x86"]; + if (targetOS.contains("watchos")) + return ["armv7k"]; + return architecture ? [architecture] : undefined; + } + + property stringList buildVariants: [defaultBuildVariant] // internal properties readonly property string version: [versionMajor, versionMinor, versionPatch].join(".") -- cgit v1.2.3