diff options
Diffstat (limited to 'share/qbs')
55 files changed, 1940 insertions, 908 deletions
diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 9b81310f0..0a944802d 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -260,19 +260,3 @@ function cleanPropertyList(plist) { cleanPropertyList(plist[key]); } } - -function _codeSignTimestampFlags(product) { - // If signingTimestamp is undefined, do not specify the flag at all - - // this uses the system-specific default behavior - var signingTimestamp = product.moduleProperty("xcode", "signingTimestamp"); - if (signingTimestamp !== undefined) { - // If signingTimestamp is an empty string, specify the flag but do - // not specify a value - this uses a default Apple-provided server - var flag = "--timestamp"; - if (signingTimestamp) - flag += "=" + signingTimestamp; - return [flag]; - } - - return []; -} diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index 586564d73..0433fa46e 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -48,7 +48,7 @@ function mergeCFiles(inputs, outputFilePath) } function sanitizedList(list, product, fullPropertyName) { - if (!Array.isArray(list)) + if (!(list instanceof Array)) return list; var filterFunc = function(elem) { if (typeof elem === "string" && elem.length === 0) { @@ -588,6 +588,8 @@ function guessArchitecture(m) { architecture = "sh"; } else if (hasAnyOf(m, ["__CR16__"])) { architecture = "cr16"; + } else if (hasAnyOf(m, ["__mc68hc1x__", "__mc68hc1x"])) { + architecture = "hcs12"; } } diff --git a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs index 8205e92fa..658da8a9f 100644 --- a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs @@ -42,6 +42,7 @@ PathProbe { property string preferredArchitecture property string _nullDevice: qbs.nullDevice property string _pathListSeparator: qbs.pathListSeparator + property string winSdkVersion // Outputs property int versionMajor @@ -58,9 +59,21 @@ PathProbe { languages = ["c"]; var info = languages.contains("c") - ? Utilities.clangClCompilerInfo(compilerFilePath, preferredArchitecture, vcvarsallFilePath, "c") : {}; + ? Utilities.clangClCompilerInfo( + compilerFilePath, + preferredArchitecture, + vcvarsallFilePath, + "c", + winSdkVersion) + : {}; var infoCpp = languages.contains("cpp") - ? Utilities.clangClCompilerInfo(compilerFilePath, preferredArchitecture, vcvarsallFilePath, "cpp") : {}; + ? Utilities.clangClCompilerInfo( + compilerFilePath, + preferredArchitecture, + vcvarsallFilePath, + "cpp", + winSdkVersion) + : {}; found = (!languages.contains("c") || (!!info && !!info.macros && !!info.buildEnvironment)) && (!languages.contains("cpp") || diff --git a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs index 2d5faecdd..d3624e010 100644 --- a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs +++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs @@ -38,6 +38,7 @@ PathProbe { property string compilerFilePath property stringList enableDefinesByLanguage property string preferredArchitecture + property string winSdkVersion // Outputs property string architecture @@ -54,9 +55,9 @@ PathProbe { languages = ["c"]; var info = languages.contains("c") - ? Utilities.msvcCompilerInfo(compilerFilePath, "c") : {}; + ? Utilities.msvcCompilerInfo(compilerFilePath, "c", winSdkVersion) : {}; var infoCpp = languages.contains("cpp") - ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp") : {}; + ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp", winSdkVersion) : {}; found = (!languages.contains("c") || (!!info && !!info.macros && !!info.buildEnvironment)) && (!languages.contains("cpp") || diff --git a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs index e0ed99346..edd67433b 100644 --- a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs +++ b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs @@ -48,6 +48,7 @@ Probe { // Outputs property var architectureSettings property var availableSdks + property var platformSettings property string xcodeVersion configure: { @@ -97,6 +98,8 @@ Probe { }); availableSdks = Xcode.sdkInfoList(sdksPath); + var platformInfoPlist = FileInfo.joinPaths(platformPath, "Info.plist"); + platformSettings = Xcode.platformInfo(platformInfoPlist); found = !!xcodeVersion; } } diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js index b1bdf9930..1f55dcf32 100644 --- a/share/qbs/imports/qbs/Probes/path-probe.js +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -36,7 +36,7 @@ var ModUtils = require("qbs.ModUtils"); function asStringList(key, value) { if (typeof(value) === "string") return [value]; - if (Array.isArray(value)) + if (value instanceof Array) return value; throw key + " must be a string or a stringList"; } @@ -45,7 +45,7 @@ function canonicalSelectors(selectors, nameSuffixes) { var mapper = function(selector) { if (typeof(selector) === "string") return {names : [selector]}; - if (Array.isArray(selector)) + if (selector instanceof Array) return {names : selector}; // dict if (!selector.names) diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs index 80ee7b51e..76536c68e 100644 --- a/share/qbs/imports/qbs/base/Application.qbs +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -60,7 +60,7 @@ NativeBinary { installDir: isBundle ? "Applications" : "bin" Group { - condition: install + condition: install && _installable fileTagsFilter: isBundle ? "bundle.content" : "application" qbs.install: true qbs.installDir: installDir @@ -68,7 +68,7 @@ NativeBinary { } Group { - condition: installDebugInformation + condition: installDebugInformation && _installable fileTagsFilter: ["debuginfo_app"] qbs.install: true qbs.installDir: debugInformationInstallDir diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs index c8a114624..914d79bcb 100644 --- a/share/qbs/imports/qbs/base/Library.qbs +++ b/share/qbs/imports/qbs/base/Library.qbs @@ -52,7 +52,7 @@ NativeBinary { property string importLibInstallDir: "lib" Group { - condition: install + condition: install && _installable fileTagsFilter: { if (isBundle) return ["bundle.content"]; @@ -70,14 +70,16 @@ NativeBinary { } Group { - condition: installImportLib && type.contains("dynamiclibrary") + condition: installImportLib + && type.contains("dynamiclibrary") + && _installable fileTagsFilter: "dynamiclibrary_import" qbs.install: true qbs.installDir: importLibInstallDir } Group { - condition: installDebugInformation + condition: installDebugInformation && _installable fileTagsFilter: { if (isDynamicLibrary) return ["debuginfo_dll"]; diff --git a/share/qbs/imports/qbs/base/NativeBinary.qbs b/share/qbs/imports/qbs/base/NativeBinary.qbs index 0928e96bb..c51f132ee 100644 --- a/share/qbs/imports/qbs/base/NativeBinary.qbs +++ b/share/qbs/imports/qbs/base/NativeBinary.qbs @@ -35,6 +35,9 @@ Product { property bool install: false property string installDir + // Product artifacts should be installed if it's not multiplexed or aggregated, + // or if it is multiplexed and it's the aggregate product + readonly property bool _installable: !multiplexed || !aggregate || !multiplexConfigurationId property bool installDebugInformation: false property string debugInformationInstallDir: installDir diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index 70acc753c..5cf1bcb44 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -267,6 +267,10 @@ function getQtProperties(qmakeFilePath, qbs) { qtProps.mkspecBasePath = FileInfo.joinPaths (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs"); } + + if (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) + qtProps.libExecPath = pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); + if (!File.exists(qtProps.mkspecBasePath)) throw "Cannot extract the mkspecs directory."; @@ -514,8 +518,13 @@ function isFramework(modInfo, qtProps) { if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) return false; var modulesNeverBuiltAsFrameworks = [ - "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "uitools", "harfbuzzng" + "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" ]; + + if (qtProps.qtMajorVersion <= 5) { + modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 + } + return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); } @@ -1334,6 +1343,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { binPath: ModUtils.toJSLiteral(qtProps.binaryPath), installPath: ModUtils.toJSLiteral(qtProps.installPath), libPath: ModUtils.toJSLiteral(qtProps.libraryPath), + libExecPath: ModUtils.toJSLiteral(qtProps.libExecPath), pluginPath: ModUtils.toJSLiteral(qtProps.pluginPath), incPath: ModUtils.toJSLiteral(qtProps.includePath), docPath: ModUtils.toJSLiteral(qtProps.documentationPath), @@ -1536,6 +1546,8 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { allFiles); copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, allFiles); + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + allFiles); } else if (module.qbsName === "gui") { moduleTemplateFileName = "gui.qbs"; } else if (module.qbsName === "scxml") { @@ -1559,6 +1571,8 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { moduleTemplateFileName = "quick.qbs"; copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbis[a], location, allFiles); + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + allFiles); } else if (module.isPlugin) { moduleTemplateFileName = "plugin.qbs"; } else { diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 3790037f3..68a29bb95 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -4,6 +4,7 @@ import qbs.ModUtils import qbs.TextFile import qbs.Utilities import qbs.Process +import qbs.Xml Module { version: @version@ @@ -35,10 +36,14 @@ Module { property bool _multiAbi: Utilities.versionCompare(version, "5.14") >= 0 + // QTBUG-87288: correct QtNetwork jar dependencies for 5.15.0 < Qt < 5.15.3 + property bool _correctQtNetworkDependencies: Utilities.versionCompare(version, "5.15.0") > 0 && + Utilities.versionCompare(version, "5.15.3") < 0 + Depends { name: "Android.sdk"; condition: _enableSdkSupport } Depends { name: "Android.ndk"; condition: _enableNdkSupport } Depends { name: "java"; condition: _enableSdkSupport } - Depends { name: "cpp" } + Depends { name: "cpp"; condition: _enableNdkSupport } Properties { condition: _enableNdkSupport && qbs.toolchain.contains("clang") @@ -62,8 +67,10 @@ Module { condition: _enableSdkSupport && Utilities.versionCompare(version, "6.0") >= 0 java.additionalClassPaths: [FileInfo.joinPaths(_qtInstallDir, "jar", "Qt6Android.jar")] } + // "ANDROID_HAS_WSTRING" was removed from qtcore qstring.h in Qt 5.14.0 Properties { - condition: _enableNdkSupport && (Android.ndk.abi === "armeabi-v7a" || Android.ndk.abi === "x86") + condition: _enableNdkSupport && Utilities.versionCompare(version, "5.14.0") < 0 && + (Android.ndk.abi === "armeabi-v7a" || Android.ndk.abi === "x86") cpp.defines: "ANDROID_HAS_WSTRING" } Properties { @@ -78,7 +85,11 @@ Module { condition: _enableSdkSupport && Utilities.versionCompare(version, "6.0") >= 0 Android.sdk.minimumVersion: "23" } - cpp.archSuffix: _multiAbi ? "_" + Android.ndk.abi : "" + + Properties { + condition: _enableNdkSupport + cpp.archSuffix: _multiAbi ? "_" + Android.ndk.abi : "" + } Rule { condition: _enableSdkSupport @@ -191,7 +202,7 @@ Module { var prefixDirs = product.Qt.android_support.extraPrefixDirs; if (prefixDirs && prefixDirs.length > 0) f.writeLine('"extraPrefixDirs": ' + JSON.stringify(prefixDirs) + ','); - if (Array.isArray(product.qmlImportPaths) && product.qmlImportPaths.length > 0) + 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) { @@ -349,8 +360,6 @@ Module { var moveCmd = new JavaScriptCommand(); moveCmd.description = "processing androiddeployqt outout"; moveCmd.sourceCode = function() { - File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml", - outputs["android.manifest_final"][0].filePath); var libsDir = product.Qt.android_support._deployQtOutDir + "/libs"; var libDir = product.Android.sdk.packageContentsDir + "/lib"; var listFilePath = outputs["android.deployqt_list"][0].filePath; @@ -394,7 +403,78 @@ Module { File.remove(oldLibs[i]); } }; - return [copyCmd, androidDeployQtCmd, moveCmd]; + + var correctingCmd = new JavaScriptCommand(); + if (product.Qt.android_support._correctQtNetworkDependencies) { + correctingCmd.description = "correcting network jar dependency"; + correctingCmd.sourceCode = function() { + var findNetworkLib = function() { + var libsDir = product.Android.sdk.packageContentsDir + "/lib"; + var dirList = File.directoryEntries(libsDir, File.Dirs | + File.NoDotAndDotDot); + for (var i = 0; i < dirList.length; ++i) { + var archDir = FileInfo.joinPaths(libsDir, dirList[i]); + var fileList = File.directoryEntries(archDir, File.Files); + if (fileList) { + for (var j = 0; j < fileList.length; ++j) { + if (fileList[j].contains("libQt5Network")) { + return true; + } + } + } + } + return false; + } + + if (findNetworkLib()) { + var manifestData = new Xml.DomDocument(); + var manifestFilePath = product.Qt.android_support._deployQtOutDir + + "/AndroidManifest.xml" + manifestData.load(manifestFilePath); + + var rootElem = manifestData.documentElement(); + if (!rootElem || !rootElem.isElement() || rootElem.tagName() != "manifest") + throw "No manifest tag found in '" + manifestFilePath + "'."; + var appElem = rootElem.firstChild("application"); + if (!appElem || !appElem.isElement() || appElem.tagName() != "application") + throw "No application tag found in '" + manifestFilePath + "'."; + var activityElem = appElem.firstChild("activity"); + if (!activityElem || !activityElem.isElement() || + activityElem.tagName() != "activity") + throw "No activity tag found in '" + manifestFilePath + "'."; + var metaDataElem = activityElem.firstChild("meta-data"); + while (metaDataElem && metaDataElem.isElement()) { + if (metaDataElem.attribute("android:name") == + "android.app.load_local_jars" ) { + var value = metaDataElem.attribute("android:value"); + var fileName = "QtAndroidNetwork.jar"; + metaDataElem.setAttribute("android:value", value + ":jar/" + + fileName); + var jarFilePath = FileInfo.joinPaths( + product.Qt.android_support._qtInstallDir, "jar", + fileName); + var targetFilePath = FileInfo.joinPaths(product.java.classFilesDir, + fileName); + File.copy(jarFilePath, targetFilePath); + break; + } + metaDataElem = metaDataElem.nextSibling("meta-data"); + } + manifestData.save(outputs["android.manifest_final"][0].filePath, 4); + } else { + File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml", + outputs["android.manifest_final"][0].filePath); + } + }; + } else { + correctingCmd.description = "copying manifest"; + correctingCmd.sourceCode = function() { + File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml", + outputs["android.manifest_final"][0].filePath); + } + } + + return [copyCmd, androidDeployQtCmd, moveCmd, correctingCmd]; } } diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index 8f0b0e2df..2e810eeed 100644 --- a/share/qbs/module-providers/Qt/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs @@ -5,6 +5,7 @@ import qbs.Utilities import qbs.Xml import "moc.js" as Moc import "qdoc.js" as Qdoc +import "rcc.js" as Rcc Module { condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild) @@ -41,12 +42,14 @@ Module { property path installPath: @installPath@ property path incPath: @incPath@ property path libPath: @libPath@ + property path libExecPath: @libExecPath@ property path pluginPath: @pluginPath@ property string mkspecName: @mkspecName@ property path mkspecPath: @mkspecPath@ property string mocName: "moc" property stringList mocFlags: [] property string lreleaseName: "lrelease" + property string rccName: "rcc" property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3" property stringList qdocEnvironment property path docPath: @docPath@ @@ -433,7 +436,7 @@ Module { "-o", output.filePath]; if (input.Qt.core.enableBigResources) args.push("-pass", "1"); - var cmd = new Command(product.Qt.core.binPath + '/rcc', args); + var cmd = new Command(Rcc.fullPath(product), args); cmd.description = "rcc " + (input.Qt.core.enableBigResources ? "(pass 1) " : "") + input.fileName; @@ -445,7 +448,7 @@ Module { Rule { inputs: ["intermediate_obj"] Artifact { - filePath: input.completeBaseName + ".2.o" + filePath: input.completeBaseName + ".2" + input.cpp.objectSuffix fileTags: ["obj"] } prepare: { @@ -464,7 +467,7 @@ Module { } var qrcArtifact = findChild(input, function(c) { return c.fileTags.contains("qrc"); }); var cppArtifact = findChild(input, function(c) { return c.fileTags.contains("cpp"); }); - var cmd = new Command(product.Qt.core.binPath + '/rcc', + var cmd = new Command(Rcc.fullPath(product), [qrcArtifact.filePath, "-temp", input.filePath, "-name", FileInfo.completeBaseName(input.filePath), diff --git a/share/qbs/module-providers/Qt/templates/dbus.js b/share/qbs/module-providers/Qt/templates/dbus.js index 0674bf684..3cdd6fb90 100644 --- a/share/qbs/module-providers/Qt/templates/dbus.js +++ b/share/qbs/module-providers/Qt/templates/dbus.js @@ -29,6 +29,7 @@ ****************************************************************************/ var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); function outputFileName(input, suffix) { diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs index a3c427175..1d45c6f1e 100644 --- a/share/qbs/module-providers/Qt/templates/gui.qbs +++ b/share/qbs/module-providers/Qt/templates/gui.qbs @@ -23,9 +23,11 @@ QtModule { } prepare: { - var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/' - + ModUtils.moduleProperty(product, "uicName"), - [input.filePath, '-o', output.filePath]) + var uicPath = Utilities.versionCompare(product.Qt.gui.version, "6.1") < 0 + ? product.Qt.core.binPath + '/' + product.Qt.gui.uicName + : product.Qt.core.libExecPath + '/' + product.Qt.gui.uicName; + + var cmd = new Command(uicPath, [input.filePath, '-o', output.filePath]); cmd.description = 'uic ' + input.fileName; cmd.highlight = 'codegen'; return cmd; diff --git a/share/qbs/module-providers/Qt/templates/moc.js b/share/qbs/module-providers/Qt/templates/moc.js index 92983e4f7..7d230267c 100644 --- a/share/qbs/module-providers/Qt/templates/moc.js +++ b/share/qbs/module-providers/Qt/templates/moc.js @@ -29,6 +29,7 @@ ****************************************************************************/ var ModUtils = require("qbs.ModUtils"); +var Utilities = require("qbs.Utilities"); function args(product, input, outputs) { @@ -78,7 +79,9 @@ function args(product, input, outputs) function fullPath(product) { - return product.Qt.core.binPath + '/' + product.Qt.core.mocName; + if (Utilities.versionCompare(product.Qt.core.version, "6.1") < 0) + return product.Qt.core.binPath + '/' + product.Qt.core.mocName; + return product.Qt.core.libExecPath + '/' + product.Qt.core.mocName; } function outputArtifacts(project, product, inputs, input) diff --git a/share/qbs/module-providers/Qt/templates/plugin_support.qbs b/share/qbs/module-providers/Qt/templates/plugin_support.qbs index 1de923f17..19a739589 100644 --- a/share/qbs/module-providers/Qt/templates/plugin_support.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin_support.qbs @@ -32,7 +32,7 @@ Module { newValue = []; else if (typeof newValue == "string") newValue = [newValue]; - if (!Array.isArray(newValue)) + if (!newValue instanceof Array) throw "Invalid value '" + newValue + "' in Qt.plugin_support.pluginsByType"; eppt[pluginType] = (eppt[pluginType] || []).uniqueConcat(newValue); } diff --git a/share/qbs/module-providers/Qt/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js index df69034fe..e48c9230e 100644 --- a/share/qbs/module-providers/Qt/templates/qml.js +++ b/share/qbs/module-providers/Qt/templates/qml.js @@ -3,14 +3,31 @@ var FileInfo = require("qbs.FileInfo"); var Process = require("qbs.Process"); var TextFile = require("qbs.TextFile"); -function scannerData(scannerFilePath, qmlFiles, qmlPath) +function scannerData(scannerFilePath, qmlFiles, qmlPath, targetOS) { var p; try { p = new Process(); - p.exec(scannerFilePath, ["-qmlFiles"].concat(qmlFiles).concat(["-importPath", qmlPath]), - true); - return JSON.parse(p.readStdOut()); + if (!targetOS.contains("windows")) { + p.exec(scannerFilePath, ["-qmlFiles"].concat(qmlFiles).concat(["-importPath", qmlPath]), + true); + return JSON.parse(p.readStdOut()); + } + var data = []; + var nextFileIndex = 0; + while (nextFileIndex < qmlFiles.length) { + var currentFileList = []; + var currentFileListStringLength = 0; + while (nextFileIndex < qmlFiles.length && currentFileListStringLength < 30000) { + var currentFile = qmlFiles[nextFileIndex++]; + currentFileList.push(currentFile); + currentFileListStringLength += currentFile.length; + } + p.exec(scannerFilePath, ["-qmlFiles"].concat(currentFileList) + .concat(["-importPath", qmlPath]), true); + data = data.concat(JSON.parse(p.readStdOut())); + } + return data; } finally { if (p) p.close(); diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs index f608ba4dd..af7b0fb5f 100644 --- a/share/qbs/module-providers/Qt/templates/qml.qbs +++ b/share/qbs/module-providers/Qt/templates/qml.qbs @@ -144,7 +144,7 @@ QtModule { qmlInputs = []; var scannerData = Qml.scannerData(product.Qt.qml.qmlImportScannerFilePath, qmlInputs.map(function(inp) { return inp.filePath; }), - product.Qt.qml.qmlPath); + product.Qt.qml.qmlPath, product.qbs.targetOS); var cppFile; var listFile; try { diff --git a/share/qbs/module-providers/Qt/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js index 4f3da2fb0..ad433736c 100644 --- a/share/qbs/module-providers/Qt/templates/quick.js +++ b/share/qbs/module-providers/Qt/templates/quick.js @@ -30,13 +30,14 @@ var FileInfo = require("qbs.FileInfo"); var Process = require("qbs.Process"); +var Rcc = require("rcc.js"); -function scanQrc(qrcFilePath) { +function scanQrc(product, qrcFilePath) { var absInputDir = FileInfo.path(qrcFilePath); var result = []; var process = new Process(); try { - var rcc = FileInfo.joinPaths(product.Qt.core.binPath, 'rcc' + product.cpp.executableSuffix); + var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + product.cpp.executableSuffix); var exitCode = process.exec(rcc, ["--list", qrcFilePath], true); for (;;) { var line = process.readLine(); @@ -65,8 +66,8 @@ function qtQuickResourceFileOutputName(fileName) { return fileName.replace(/\.qrc$/, "_qtquickcompiler.qrc"); } -function contentFromQrc(qrcFilePath) { - var filesInQrc = scanQrc(qrcFilePath); +function contentFromQrc(product, qrcFilePath) { + var filesInQrc = scanQrc(product, qrcFilePath); var qmlJsFiles = filesInQrc.filter(function (filePath) { return (/\.(js|qml)$/).test(filePath); } ); diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs index bf04fe869..ac3ab5b26 100644 --- a/share/qbs/module-providers/Qt/templates/quick.qbs +++ b/share/qbs/module-providers/Qt/templates/quick.qbs @@ -79,7 +79,7 @@ QtModule { condition: useCompiler inputs: 'qt.quick.qrc' searchPaths: [FileInfo.path(input.filePath)] - scan: QC.scanQrc(input.filePath) + scan: QC.scanQrc(product, input.filePath) } FileTagger { @@ -100,7 +100,7 @@ QtModule { var cmd = new JavaScriptCommand(); cmd.silent = true; cmd.sourceCode = function() { - var content = QC.contentFromQrc(input.filePath); + var content = QC.contentFromQrc(product, input.filePath); content.qrcFilePath = input.filePath; var tf = new TextFile(output.filePath, TextFile.WriteOnly); diff --git a/share/qbs/module-providers/Qt/templates/rcc.js b/share/qbs/module-providers/Qt/templates/rcc.js new file mode 100644 index 000000000..89c57d99b --- /dev/null +++ b/share/qbs/module-providers/Qt/templates/rcc.js @@ -0,0 +1,8 @@ +var Utilities = require("qbs.Utilities"); + +function fullPath(product) +{ + if (Utilities.versionCompare(product.Qt.core.version, "6.1") < 0) + return product.Qt.core.binPath + '/' + product.Qt.core.rccName; + return product.Qt.core.libExecPath + '/' + product.Qt.core.rccName; +} diff --git a/share/qbs/modules/Android/ndk/utils.js b/share/qbs/modules/Android/ndk/utils.js index 3605df314..e763ed9b6 100644 --- a/share/qbs/modules/Android/ndk/utils.js +++ b/share/qbs/modules/Android/ndk/utils.js @@ -100,6 +100,6 @@ function commonLinkerFlags(abi) { return ["-z", "noexecstack", "-z", "relro", "-z", "now", "--build-id=sha1", "--gc-sections" ]; } -function stlFilePath(path, ndk, suffix) { - return path + ndk.appStl.slice(0, ndk.appStl.indexOf('_')) + suffix + "." + ndk.platformVersion; +function stlFileName(prefix, ndk, suffix) { + return prefix + ndk.appStl.slice(0, ndk.appStl.indexOf('_')) + suffix; } diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index f0e727caf..cae5fc2e5 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -178,20 +178,18 @@ Module { property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory, "compiled_resources") property string packageContentsDir: FileInfo.joinPaths(product.buildDirectory, packageType) - property string debugKeyStorePath: FileInfo.joinPaths( - Environment.getEnv(qbs.hostOS.contains("windows") - ? "USERPROFILE" : "HOME"), - ".android", "debug.keystore") - property bool useApksigner: buildToolsVersion && Utilities.versionCompare( - buildToolsVersion, "24.0.3") >= 0 property stringList aidlSearchPaths Depends { name: "java"; condition: _enableRules } + Depends { name: "codesign"; condition: _enableRules } Properties { condition: _enableRules java.languageVersion: platformJavaVersion java.runtimeVersion: platformJavaVersion java.bootClassPaths: androidJarFilePath + codesign.apksignerFilePath: Android.sdk.apksignerFilePath + codesign._packageName: Android.sdk.apkBaseName + (_generateAab ? ".aab" : ".apk") + codesign.useApksigner: !_generateAab } validate: { @@ -211,6 +209,11 @@ Module { + "Android bundletool can be downloaded from " + "https://github.com/google/bundletool"); } + if (Utilities.versionCompare(buildToolsVersion, "24.0.3") < 0) { + throw ModUtils.ModuleError("Version of Android SDK build tools too old. This version " + + "is " + buildToolsVersion + " and minimum version is " + + "24.0.3. Please update the Android SDK.") + } } FileTagger { @@ -223,36 +226,6 @@ Module { fileTags: ["android.aidl"] } - FileTagger { - patterns: ["*.keystore"] - fileTags: ["android.keystore"] - } - - // Typically there is a debug keystore in ~/.android/debug.keystore which gets created - // by the native build tools the first time a build is done. However, we don't want to create it - // ourselves, because writing to a location outside the qbs build directory is both polluting - // and has the potential for race conditions. So we'll instruct the user what to do. - Group { - name: "Android debug keystore" - files: { - if (!File.exists(Android.sdk.debugKeyStorePath)) { - throw ModUtils.ModuleError("Could not find an Android debug keystore at " + - Android.sdk.debugKeyStorePath + ". " + - "If you are developing for Android on this machine for the first time and " + - "have never built an application using the native Gradle / Android Studio " + - "tooling, this is normal. You must create the debug keystore now using the " + - "following command, in order to continue:\n\n" + - SdkUtils.createDebugKeyStoreCommandString(java.keytoolFilePath, - Android.sdk.debugKeyStorePath) + - "\n\n" + - "See the following URL for more information: " + - "https://developer.android.com/studio/publish/app-signing.html#debug-mode"); - } - return [Android.sdk.debugKeyStorePath]; - } - fileTags: ["android.keystore"] - } - Parameter { property bool embedJar: true } @@ -273,7 +246,7 @@ Module { for (var i = 0; i < (aidlSearchPaths ? aidlSearchPaths.length : 0); ++i) args.push("-I" + aidlSearchPaths[i]); args.push(input.filePath, output.filePath); - cmd = new Command(aidl, args); + var cmd = new Command(aidl, args); cmd.description = "Processing " + input.fileName; return [cmd]; } @@ -498,7 +471,7 @@ Module { outputArtifacts: { var deploymentData = SdkUtils.stlDeploymentData(product, inputs, "stl"); var outputs = []; - for (i = 0; i < deploymentData.outputFilePaths.length; ++i) { + for (var i = 0; i < deploymentData.outputFilePaths.length; ++i) { outputs.push({filePath: deploymentData.outputFilePaths[i], fileTags: "android.stl_deployed"}); } @@ -525,11 +498,11 @@ Module { inputs: [ "android.resources", "android.assets", "android.manifest_final", "android.dex", "android.stl_deployed", - "android.nativelibrary_deployed", "android.keystore" + "android.nativelibrary_deployed" ] Artifact { - filePath: product.Android.sdk.apkBaseName + ".apk" - fileTags: "android.package" + filePath: product.Android.sdk.apkBaseName + ".apk_unsigned" + fileTags: "android.package_unsigned" } prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments) } @@ -540,11 +513,11 @@ Module { inputs: [ "android.apk_resources", "android.manifest_final", "android.dex", "android.stl_deployed", - "android.nativelibrary_deployed", "android.keystore" + "android.nativelibrary_deployed" ] Artifact { - filePath: product.Android.sdk.apkBaseName + ".apk" - fileTags: "android.package" + filePath: product.Android.sdk.apkBaseName + ".apk_unsigned" + fileTags: "android.package_unsigned" } prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments) } @@ -558,8 +531,8 @@ Module { "android.nativelibrary_deployed" ] Artifact { - filePath: product.Android.sdk.apkBaseName + ".aab" - fileTags: "android.package" + filePath: product.Android.sdk.apkBaseName + ".aab_unsigned" + fileTags: "android.package_unsigned" } prepare: SdkUtils.prepareBundletoolPackage.apply(SdkUtils, arguments) } diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index 63bf1f5c4..264ad2da7 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -30,9 +30,11 @@ var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); var Process = require("qbs.Process"); var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); +var Xml = require("qbs.Xml"); function availableBuildToolsVersions(sdkDir) { var re = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/; @@ -172,7 +174,8 @@ function prepareAapt2CompileResource(project, product, inputs, outputs, input, o throw "Cannot create directory '" + FileInfo.toNativeSeparators(compilesResourcesDir) + "'."; } - var args = ["compile", input.filePath, "-o", compilesResourcesDir]; + var args = ["compile", FileInfo.toNativeSeparators(input.filePath), + "-o", FileInfo.toNativeSeparators(compilesResourcesDir)]; var cmd = new Command(product.Android.sdk.aaptFilePath, args); var outputFileName = generateAapt2ResourceFileName(input.filePath); cmd.description = "compiling resource " + input.fileName + " into " + outputFileName; @@ -240,7 +243,7 @@ function prepareAaptGenerate(project, product, inputs, outputs, input, output, function prepareAaptPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; - var apkOutput = outputs["android.package"][0]; + var apkOutput = outputs["android.package_unsigned"][0]; var args = commonAaptPackageArgs.apply(this, arguments); args.push("-F", apkOutput.filePath + ".unaligned"); args.push(product.Android.sdk.packageContentsDir); @@ -248,17 +251,6 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex cmd.description = "generating " + apkOutput.fileName; cmds.push(cmd); - if (!product.Android.sdk.useApksigner) { - args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1", - "-keystore", inputs["android.keystore"][0].filePath, - "-storepass", "android", - apkOutput.filePath + ".unaligned", - "androiddebugkey"]; - cmd = new Command(product.java.jarsignerFilePath, args); - cmd.description = "signing " + apkOutput.fileName; - cmds.push(cmd); - } - cmd = new Command(product.Android.sdk.zipalignFilePath, ["-f", "4", apkOutput.filePath + ".unaligned", apkOutput.filePath]); cmd.silent = true; @@ -269,26 +261,14 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex cmd.unalignedApk = apkOutput.filePath + ".unaligned"; cmd.sourceCode = function() { File.remove(unalignedApk); }; cmds.push(cmd); - - if (product.Android.sdk.useApksigner) { - // TODO: Implement full signing support, not just using the debug keystore - args = ["sign", - "--ks", inputs["android.keystore"][0].filePath, - "--ks-pass", "pass:android", - apkOutput.filePath]; - cmd = new Command(product.Android.sdk.apksignerFilePath, args); - cmd.description = "signing " + apkOutput.fileName; - cmds.push(cmd); - } - return cmds; } function prepareApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; var apkInputFilePath = inputs["android.apk_resources"][0].filePath; - var apkOutput = outputs["android.package"][0]; - var apkOutputFilePathUnaligned = outputs["android.package"][0].filePath + ".unaligned"; + var apkOutput = outputs["android.package_unsigned"][0]; + var apkOutputFilePathUnaligned = apkOutput.filePath + ".unaligned"; var dexFilePath = inputs["android.dex"][0].filePath; var copyCmd = new JavaScriptCommand(); @@ -307,17 +287,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp jarCmd.workingDirectory = libPath; cmds.push(jarCmd); - if (!product.Android.sdk.useApksigner) { - args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1", - "-keystore", inputs["android.keystore"][0].filePath, - "-storepass", "android", - apkOutputFilePathUnaligned, - "androiddebugkey"]; - cmd = new Command(product.java.jarsignerFilePath, args); - cmd.description = "signing " + apkOutput.fileName; - cmds.push(cmd); - } - cmd = new Command(product.Android.sdk.zipalignFilePath, ["-f", "4", apkOutputFilePathUnaligned, apkOutput.filePath]); cmd.silent = true; @@ -328,18 +297,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp cmd.unalignedApk = apkOutputFilePathUnaligned; cmd.sourceCode = function() { File.remove(unalignedApk); }; cmds.push(cmd); - - if (product.Android.sdk.useApksigner) { - // TODO: Implement full signing support, not just using the debug keystore - args = ["sign", - "--ks", inputs["android.keystore"][0].filePath, - "--ks-pass", "pass:android", - apkOutput.filePath]; - cmd = new Command(product.Android.sdk.apksignerFilePath, args); - cmd.description = "signing " + apkOutput.fileName; - cmds.push(cmd); - } - return cmds; } @@ -375,7 +332,7 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp jarBaseCmd.workingDirectory = baseModuleDir; cmds.push(jarBaseCmd); - var aabFilePath = outputs["android.package"][0].filePath; + var aabFilePath = outputs["android.package_unsigned"][0].filePath; var removeCmd = new JavaScriptCommand(); removeCmd.description = "removing previous aab"; removeCmd.filePath = aabFilePath; @@ -389,20 +346,12 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp args.push("--modules=" + baseFilePath); args.push("--output=" + aabFilePath); var cmd = new Command(product.java.interpreterFilePath, args); - cmd.description = "generating " + outputs["android.package"][0].fileName; + cmd.description = "generating " + aabFilePath.fileName; cmds.push(cmd); return cmds; } -function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) { - var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey", - "-storepass", "android", "-keypass", "android", "-keyalg", "RSA", - "-keysize", "2048", "-validity", "10000", "-dname", - "CN=Android Debug,O=Android,C=US"]; - return Process.shellQuote(keytoolFilePath, args); -} - function stlDeploymentData(product, inputs, type) { var data = { uniqueInputs: [], outputFilePaths: []}; diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js index a3109d61d..52b4dffe3 100644 --- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js +++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js @@ -44,9 +44,9 @@ function writeEntry(product, file, key, propertyName, required, additionalValues var value = product.Exporter.pkgconfig[propertyName]; if (additionalValues && additionalValues.length > 0) value = (value || []).concat(additionalValues); - var valueIsNotEmpty = value && (!Array.isArray(value) || value.length > 0); + var valueIsNotEmpty = value && (!(value instanceof Array) || value.length > 0); if (valueIsNotEmpty) { - if (Array.isArray(value)) + if (value instanceof Array) value = value.join(' '); file.writeLine(key + ": " + value); } else if (required) { @@ -83,7 +83,7 @@ function collectAutodetectedData(topLevelProduct) || (value.length > installPrefix.length && value[installPrefix.length] !== '/')) { return quotedValue; } - return quotedValue.replace(product.qbs.installPrefix, "${prefix}"); + return quotedValue.replace(topLevelProduct.qbs.installPrefix, "${prefix}"); } function transformedValue(product, moduleName, propertyName) @@ -92,7 +92,7 @@ function collectAutodetectedData(topLevelProduct) var value = transformFunc ? eval("(" + transformFunc + ")(product, moduleName, propertyName, originalValue)") : originalValue; - if (Array.isArray(value)) + if (value instanceof Array) value.forEach(function(v, i, a) { a[i] = quoteAndPrefixify(v); }); else if (value) value = quoteAndPrefixify(value); diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.js b/share/qbs/modules/Exporter/qbs/qbsexporter.js index 015ed4418..be46372c3 100644 --- a/share/qbs/modules/Exporter/qbs/qbsexporter.js +++ b/share/qbs/modules/Exporter/qbs/qbsexporter.js @@ -113,7 +113,7 @@ function checkValuePrefix(name, value, forbiddenPrefix, prefixDescription) function stringifyValue(project, product, moduleInstallDir, prop, value) { - if (Array.isArray(value)) { + if (value instanceof Array) { var repr = "["; for (var i = 0; i < value.length; ++i) { repr += stringifyValue(project, product, moduleInstallDir, prop, value[i]) + ", "; @@ -267,6 +267,6 @@ function writeImportStatements(product, moduleFile) if (!imports.contains("import qbs.FileInfo")) imports.push("import qbs.FileInfo"); - for (var i = 0; i < product.exports.imports.length; ++i) - moduleFile.writeLine(product.exports.imports[i]); + for (var i = 0; i < imports.length; ++i) + moduleFile.writeLine(imports[i]); } diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index 05e77c81b..63f12db07 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -38,9 +38,11 @@ import qbs.PropertyList import qbs.TextFile import qbs.Utilities import "bundle.js" as Bundle +import "../codesign/codesign.js" as Codesign Module { Depends { name: "xcode"; required: false; } + Depends { name: "codesign"; required: false; } Probe { id: bundleSettingsProbe @@ -510,9 +512,9 @@ Module { multiplex: true inputs: ["bundle.input", "aggregate_infoplist", "pkginfo", "hpp", - "icns", "xcent", + "icns", "codesign.xcent", "compiled_ibdoc", "compiled_assetcatalog", - "xcode.provisioningprofile.main"] + "codesign.embedded_provisioningprofile"] // Make sure the inputs of this rule are only those rules which produce outputs compatible // with the type of the bundle being produced. @@ -540,13 +542,13 @@ Module { }); } - for (i in inputs["xcode.provisioningprofile.main"]) { - var ext = inputs["xcode.provisioningprofile.main"][i].fileName.split('.')[1]; + var provprofiles = inputs["codesign.embedded_provisioningprofile"]; + for (i in provprofiles) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "contentsFolderPath"), - "embedded." + ext), + provprofiles[i].fileName), fileTags: ["bundle.provisioningprofile", "bundle.content"] }); } @@ -613,8 +615,8 @@ Module { for (var i = 0; i < artifacts.length; ++i) artifacts[i].bundle = { wrapperPath: wrapperPath }; - if (product.qbs.hostOS.contains("darwin") && product.xcode - && product.xcode.signingIdentity) { + if (product.qbs.hostOS.contains("darwin") && product.codesign + && product.codesign.enableCodeSigning) { artifacts.push({ filePath: FileInfo.joinPaths(product.bundle.contentsFolderPath, "_CodeSignature/CodeResources"), fileTags: ["bundle.code-signature", "bundle.content"] @@ -706,18 +708,21 @@ Module { commands.push(cmd); } - var provisioningProfiles = outputs["bundle.provisioningprofile"]; - for (i in provisioningProfiles) { - cmd = new JavaScriptCommand(); - cmd.description = "copying provisioning profile"; - cmd.highlight = "filegen"; - cmd.source = inputs["xcode.provisioningprofile.main"][i].filePath; - cmd.destination = provisioningProfiles[i].filePath; - cmd.sourceCode = function() { - File.copy(source, destination); - }; + cmd = new JavaScriptCommand(); + cmd.description = "copying provisioning profile"; + cmd.highlight = "filegen"; + cmd.sources = (inputs["codesign.embedded_provisioningprofile"] || []) + .map(function(artifact) { return artifact.filePath; }); + cmd.destination = (outputs["bundle.provisioningprofile"] || []) + .map(function(artifact) { return artifact.filePath; }); + cmd.sourceCode = function() { + var i; + for (var i in sources) { + File.copy(sources[i], destination[i]); + } + }; + if (cmd.sources && cmd.sources.length) commands.push(cmd); - } cmd = new JavaScriptCommand(); cmd.description = "copying public headers"; @@ -762,34 +767,8 @@ Module { commands.push(cmd); if (product.moduleProperty("qbs", "hostOS").contains("darwin")) { - var actualSigningIdentity = product.moduleProperty("xcode", "actualSigningIdentity"); - var codesignDisplayName = product.moduleProperty("xcode", "actualSigningIdentityDisplayName"); - if (actualSigningIdentity) { - var args = product.moduleProperty("xcode", "codesignFlags") || []; - args.push("--force"); - args.push("--sign", actualSigningIdentity); - args = args.concat(DarwinTools._codeSignTimestampFlags(product)); - - for (var j in inputs.xcent) { - args.push("--entitlements", inputs.xcent[j].filePath); - break; // there should only be one - } - - // If this is a framework, we need to sign its versioned directory - if (bundleType === "framework") { - args.push(product.bundle.contentsFolderPath); - } else { - args.push(product.bundle.bundleName); - } - - cmd = new Command(product.moduleProperty("xcode", "codesignPath"), args); - cmd.workingDirectory = product.destinationDirectory; - cmd.description = "codesign " - + ModUtils.moduleProperty(product, "bundleName") - + " using " + codesignDisplayName - + " (" + actualSigningIdentity + ")"; - commands.push(cmd); - } + Array.prototype.push.apply(commands, Codesign.prepareSign( + project, product, inputs, outputs, input, output)); if (bundleType === "application" && product.moduleProperty("qbs", "targetOS").contains("macos")) { diff --git a/share/qbs/modules/bundle/bundle.js b/share/qbs/modules/bundle/bundle.js index 6d9305702..6bb43ecdc 100644 --- a/share/qbs/modules/bundle/bundle.js +++ b/share/qbs/modules/bundle/bundle.js @@ -89,7 +89,7 @@ function productTypeIdentifier(productType) { } function excludedAuxiliaryInputs(project, product) { - var chain = product.moduleProperty("bundle", "_productTypeIdentifierChain"); + var chain = product.bundle._productTypeIdentifierChain; var bestPossibleType; for (var i = chain.length - 1; i >= 0; --i) { switch (chain[i]) { diff --git a/share/qbs/modules/codesign/CodeSignModule.qbs b/share/qbs/modules/codesign/CodeSignModule.qbs new file mode 100644 index 000000000..1951ec374 --- /dev/null +++ b/share/qbs/modules/codesign/CodeSignModule.qbs @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** 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 +import qbs.File +import qbs.FileInfo +import "codesign.js" as CodeSign + +Module { + condition: false + + property bool enableCodeSigning: false + + property string codesignName + property string codesignPath: codesignName + property stringList codesignFlags + + property bool _canSignArtifacts: false // whether can sign individual actifacts +} diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs new file mode 100644 index 000000000..be96d42de --- /dev/null +++ b/share/qbs/modules/codesign/android.qbs @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Raphaël Cotty <raphael.cotty@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. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import "codesign.js" as CodeSign + +CodeSignModule { + condition: qbs.targetOS.contains("android") + priority: 1 + enableCodeSigning: true + + property bool useApksigner: true + property path apksignerFilePath + + Probes.JdkProbe { + id: jdk + environmentPaths: (jdkPath ? [jdkPath] : []).concat(base) + } + property string jdkPath: jdk.path + property string jarsignerFilePath: FileInfo.joinPaths(jdkPath, "bin", jarsignerName) + property string jarsignerName: "jarsigner" + property string keytoolFilePath: FileInfo.joinPaths(jdkPath, "bin", keytoolName) + property string keytoolName: "keytool" + + property string debugKeystorePath: FileInfo.joinPaths( + Environment.getEnv(qbs.hostOS.contains("windows") + ? "USERPROFILE" : "HOME"), + ".android", "debug.keystore") + readonly property string debugKeystorePassword: "android" + readonly property string debugPassword: "android" + readonly property string debugKeyAlias: "androiddebugkey" + + property string keystorePath: debugKeystorePath + property string keystorePassword: debugKeystorePassword + property string keyPassword: debugPassword + property string keyAlias: debugKeyAlias + + // Private property set by the Android.sdk module + property string _packageName + + Rule { + condition: useApksigner + inputs: ["android.package_unsigned"] + Artifact { + filePath: product.codesign._packageName + fileTags: "android.package" + } + prepare: CodeSign.signApkPackage.apply(this, arguments) + } + + Rule { + condition: !useApksigner + inputs: ["android.package_unsigned"] + Artifact { + filePath: product.codesign._packageName + fileTags: "android.package" + } + prepare: CodeSign.signAabPackage.apply(this, arguments) + } + + validate: { + // Typically there is a debug keystore in ~/.android/debug.keystore which gets created + // by the native build tools the first time a build is done. However, we don't want to + // create it ourselves, because writing to a location outside the qbs build directory is + // both polluting and has the potential for race conditions. So we'll instruct the user what + // to do. + if (keystorePath === debugKeystorePath && !File.exists(debugKeystorePath)) { + throw ModUtils.ModuleError("Could not find an Android debug keystore at " + + codesign.debugKeystorePath + ". " + + "If you are developing for Android on this machine for the first time and " + + "have never built an application using the native Gradle / Android Studio " + + "tooling, this is normal. You must create the debug keystore now using the " + + "following command, in order to continue:\n\n" + + CodeSign.createDebugKeyStoreCommandString(codesign.keytoolFilePath, + codesign.debugKeystorePath, + codesign.debugKeystorePassword, + codesign.debugPassword, + codesign.debugKeyAlias) + + "\n\n" + + "See the following URL for more information: " + + "https://developer.android.com/studio/publish/app-signing.html#debug-mode"); + } + } +} diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs new file mode 100644 index 000000000..31e2c366d --- /dev/null +++ b/share/qbs/modules/codesign/apple.qbs @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** 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 +import qbs.BundleTools +import qbs.DarwinTools +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PropertyList +import qbs.Probes +import qbs.Utilities +import "codesign.js" as CodeSign +import "../xcode/xcode.js" as XcodeUtils + +CodeSignModule { + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + + Probes.BinaryProbe { + id: codesignProbe + names: [codesignName] + } + + condition: qbs.hostOS.contains("macos") && qbs.targetOS.contains("darwin") + priority: 0 + + enableCodeSigning: _codeSigningRequired + + codesignName: "codesign" + codesignPath: codesignProbe.filePath + + _canSignArtifacts: true + + property string signingType: { + if (_adHocCodeSigningAllowed) + return "ad-hoc"; + if (_codeSigningAllowed) + return "app-store"; + } + + PropertyOptions { + name: "signingType" + allowedValues: ["app-store", "apple-id", "ad-hoc"] + } + + property string signingIdentity: { + if (signingType === "ad-hoc") // only useful on macOS + return "-"; + + var isDebug = qbs.buildVariant !== "release"; + + if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("tvos") + || qbs.targetOS.contains("watchos")) { + switch (signingType) { + case "app-store": + return isDebug ? "iPhone Developer" : "iPhone Distribution"; + } + } + + if (qbs.targetOS.contains("macos")) { + switch (signingType) { + case "app-store": + return isDebug ? "Mac Developer" : "3rd Party Mac Developer Application"; + case "apple-id": + return "Developer ID Application"; + } + } + } + + property string signingTimestamp: "none" + + property string provisioningProfile + PropertyOptions { + name: "provisioningProfile" + description: "Name or UUID of the provisioning profile to embed in the application; " + + "typically left blank to allow automatic provisioning" + } + + property string teamIdentifier + PropertyOptions { + name: "teamIdentifier" + description: "Name or identifier of the development team whose identities will be used; " + + "typically left blank unless signed into multiple development teams" + } + + property path provisioningProfilesPath: "~/Library/MobileDevice/Provisioning Profiles" + + readonly property var _actualSigningIdentity: { + if (signingIdentity === "-") { + return { + SHA1: signingIdentity, + subjectInfo: { CN: "ad hoc" } + } + } + + var identities = CodeSign.findSigningIdentities(signingIdentity, teamIdentifier); + if (identities && Object.keys(identities).length > 1) { + throw "Multiple codesigning identities (i.e. certificate and private key pairs) " + + "matching '" + signingIdentity + "' were found." + + CodeSign.humanReadableIdentitySummary(identities); + } + + for (var i in identities) + return identities[i]; + } + + // Allowed for macOS + readonly property bool _adHocCodeSigningAllowed: + XcodeUtils.boolFromSdkOrPlatform("AD_HOC_CODE_SIGNING_ALLOWED", + xcode._sdkProps, xcode._platformProps, true) + + // Allowed for all device platforms (not simulators) + readonly property bool _codeSigningAllowed: + XcodeUtils.boolFromSdkOrPlatform("CODE_SIGNING_ALLOWED", + xcode._sdkProps, xcode._platformProps, true) + + // Required for tvOS, iOS, and watchOS (not simulators) + property bool _codeSigningRequired: { + // allow to override value from Xcode so tests do not require signing + var envRequired = Environment.getEnv("QBS_AUTOTEST_CODE_SIGNING_REQUIRED"); + if (envRequired) + return envRequired === "1"; + return XcodeUtils.boolFromSdkOrPlatform("CODE_SIGNING_REQUIRED", + xcode._sdkProps, xcode._platformProps, false) + } + + // Required for tvOS, iOS, and watchOS (not simulators) + readonly property bool _entitlementsRequired: + XcodeUtils.boolFromSdkOrPlatform("ENTITLEMENTS_REQUIRED", + xcode._sdkProps, xcode._platformProps, false) + + readonly property bool _provisioningProfileAllowed: + product.bundle + && product.bundle.isBundle + && product.type.contains("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") + + // Not used on simulator platforms either but provisioning profiles aren't used there anyways + readonly property string _provisioningProfilePlatform: { + if (qbs.targetOS.contains("macos")) + return "OSX"; + if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("watchos")) + return "iOS"; + if (qbs.targetOS.contains("tvos")) + return "tvOS"; + } + + readonly property string _embeddedProfileName: + (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] + + setupBuildEnvironment: { + var prefixes = product.xcode ? [ + product.xcode.platformPath + "/Developer", + product.xcode.toolchainPath, + product.xcode.developerPath + ] : []; + for (var i = 0; i < prefixes.length; ++i) { + var codesign_allocate = prefixes[i] + "/usr/bin/codesign_allocate"; + if (File.exists(codesign_allocate)) { + var v = new ModUtils.EnvironmentVariable("CODESIGN_ALLOCATE"); + v.value = codesign_allocate; + v.set(); + break; + } + } + } + + Group { + name: "Provisioning Profiles" + prefix: codesign.provisioningProfilesPath + "/" + files: ["*.mobileprovision", "*.provisionprofile"] + } + + FileTagger { + fileTags: ["codesign.entitlements"] + patterns: ["*.entitlements"] + } + + FileTagger { + fileTags: ["codesign.provisioningprofile"] + patterns: ["*.mobileprovision", "*.provisionprofile"] + } + + Rule { + multiplex: true + condition: product.codesign.enableCodeSigning && + product.codesign._provisioningProfileAllowed + inputs: ["codesign.provisioningprofile"] + + outputFileTags: ["codesign.embedded_provisioningprofile"] + outputArtifacts: { + var artifacts = []; + var provisioningProfiles = (inputs["codesign.provisioningprofile"] || []) + .map(function (a) { return a.filePath; }); + var bestProfile = CodeSign.findBestProvisioningProfile(product, provisioningProfiles); + var uuid = product.provisioningProfile; + if (bestProfile) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.codesign._embeddedProfileName), + fileTags: ["codesign.embedded_provisioningprofile"], + codesign: { + _provisioningProfileFilePath: bestProfile.filePath, + _provisioningProfileData: JSON.stringify(bestProfile.data), + } + }); + } else if (uuid) { + throw "Your build settings specify a provisioning profile with the UUID '" + + uuid + "', however, no such provisioning profile was found."; + } else if (product._provisioningProfileRequired) { + var hasProfiles = !!((inputs["codesign.provisioningprofile"] || []).length); + var teamIdentifier = product.teamIdentifier; + var codeSignIdentity = product.signingIdentity; + if (hasProfiles) { + if (codeSignIdentity) { + console.warn("No provisioning profiles matching the bundle identifier '" + + product.bundle.identifier + + "' were found."); + } else { + console.warn("No provisioning profiles matching an applicable signing " + + "identity were found."); + } + } else { + if (codeSignIdentity) { + if (teamIdentifier) { + console.warn("No provisioning profiles with a valid signing identity " + + "(i.e. certificate and private key pair) matching the " + + "team ID '" + teamIdentifier + "' were found.") + } else { + console.warn("No provisioning profiles with a valid signing identity " + + "(i.e. certificate and private key pair) were found."); + } + } else { + console.warn("No non-expired provisioning profiles were found."); + } + } + } + return artifacts; + } + + prepare: { + var cmd = new JavaScriptCommand(); + var data = JSON.parse(output.codesign._provisioningProfileData); + cmd.source = output.codesign._provisioningProfileFilePath; + cmd.destination = output.filePath; + cmd.description = "using provisioning profile " + data.Name + " (" + data.UUID + ")"; + cmd.highlight = "filegen"; + cmd.sourceCode = function() { + File.copy(source, destination); + }; + return [cmd]; + } + } + + Rule { + multiplex: true + condition: product.codesign.enableCodeSigning + inputs: ["codesign.entitlements", "codesign.embedded_provisioningprofile"] + + Artifact { + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + ".xcent") + fileTags: ["codesign.xcent"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating entitlements"; + cmd.highlight = "codegen"; + cmd.bundleIdentifier = product.bundle.identifier; + cmd.signingEntitlements = (inputs["codesign.entitlements"] || []) + .map(function (a) { return a.filePath; }); + cmd.provisioningProfiles = (inputs["codesign.embedded_provisioningprofile"] || []) + .map(function (a) { return a.filePath; }); + cmd.platformPath = product.xcode ? product.xcode.platformPath : undefined; + cmd.sdkPath = product.xcode ? product.xcode.sdkPath : undefined; + cmd.sourceCode = function() { + var i; + var provData = {}; + var provisionProfiles = inputs["codesign.embedded_provisioningprofile"]; + for (i in provisionProfiles) { + var plist = new PropertyList(); + try { + plist.readFromData(Utilities.smimeMessageContent( + provisionProfiles[i].filePath)); + provData = plist.toObject(); + } finally { + plist.clear(); + } + } + + var aggregateEntitlements = {}; + + // Start building up an aggregate entitlements plist from the files in the SDKs, + // which contain placeholders in the same manner as Info.plist + function entitlementsFileContents(path) { + return File.exists(path) ? BundleTools.infoPlistContents(path) : undefined; + } + var entitlementsSources = []; + if (platformPath) { + entitlementsSources.push( + entitlementsFileContents( + FileInfo.joinPaths(platformPath, "Entitlements.plist"))); + } + if (sdkPath) { + entitlementsSources.push( + entitlementsFileContents( + FileInfo.joinPaths(sdkPath, "Entitlements.plist"))); + } + + for (i = 0; i < signingEntitlements.length; ++i) { + entitlementsSources.push(entitlementsFileContents(signingEntitlements[i])); + } + + for (i = 0; i < entitlementsSources.length; ++i) { + var contents = entitlementsSources[i]; + for (var key in contents) { + if (contents.hasOwnProperty(key)) + aggregateEntitlements[key] = contents[key]; + } + } + + contents = provData["Entitlements"]; + for (key in contents) { + if (contents.hasOwnProperty(key) && !aggregateEntitlements.hasOwnProperty(key)) + aggregateEntitlements[key] = contents[key]; + } + + // Expand entitlements variables with data from the provisioning profile + var env = { + "AppIdentifierPrefix": (provData["ApplicationIdentifierPrefix"] || "") + ".", + "CFBundleIdentifier": bundleIdentifier + }; + DarwinTools.expandPlistEnvironmentVariables(aggregateEntitlements, env, true); + + // Anything with an undefined or otherwise empty value should be removed + // Only JSON-formatted plists can have null values, other formats error out + // This also follows Xcode behavior + DarwinTools.cleanPropertyList(aggregateEntitlements); + + var plist = new PropertyList(); + try { + plist.readFromObject(aggregateEntitlements); + plist.writeToFile(outputs["codesign.xcent"][0].filePath, "xml1"); + } finally { + plist.clear(); + } + }; + return [cmd]; + } + } +} diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js new file mode 100644 index 000000000..bf7e95224 --- /dev/null +++ b/share/qbs/modules/codesign/codesign.js @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** 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. +** +****************************************************************************/ + +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var PathTools = require("qbs.PathTools"); +var Process = require("qbs.Process"); +var PropertyList = require("qbs.PropertyList"); +var Utilities = require("qbs.Utilities"); + +function findSigningIdentities(searchString, team) { + if (!searchString) + return {}; + var identities = Utilities.signingIdentities(); + var matchedIdentities = {}; + for (var key in identities) { + var identity = identities[key]; + if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].contains(team)) + continue; + if (searchString === key || identity.subjectInfo.CN.startsWith(searchString)) + matchedIdentities[key] = identity; + } + return matchedIdentities; +} + +function humanReadableIdentitySummary(identities) { + return "\n\t" + Object.keys(identities).map(function (key) { + return identities[key].subjectInfo.CN + + " in team " + + identities[key].subjectInfo.O + + " (" + identities[key].subjectInfo.OU + ")"; + }).join("\n\t"); +} + +/** + * Returns the best provisioning profile for code signing a binary with the given parameters. + * Ideally, this should behave identically as Xcode but the algorithm is not documented + * \l{https://developer.apple.com/library/ios/qa/qa1814/_index.html}{Automatic Provisioning} + */ +function findBestProvisioningProfile(product, files) { + var actualSigningIdentity = product.codesign._actualSigningIdentity || {}; + var teamIdentifier = product.codesign.teamIdentifier; + var bundleIdentifier = product.bundle.identifier; + var targetOS = product.qbs.targetOS; + var buildVariant = product.qbs.buildVariant; + var query = product.codesign.provisioningProfile; + var profilePlatform = product.codesign._provisioningProfilePlatform; + + // Read all provisioning profiles on disk into plist objects in memory + var profiles = files.map(function(filePath) { + var plist = new PropertyList(); + try { + plist.readFromData(Utilities.smimeMessageContent(filePath)); + return { + data: plist.toObject(), + filePath: filePath + }; + } finally { + plist.clear(); + } + }); + + // Do a simple search by matching UUID or Name + if (query) { + for (var i = 0; i < profiles.length; ++i) { + var obj = profiles[i]; + if (obj.data && (obj.data.UUID === query || obj.data.Name === query)) + return obj; + } + + // If we asked for a specific provisioning profile, don't select one automatically + return undefined; + } + + // 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")) + return undefined; + + // Filter out any provisioning profiles we know to be unsuitable from the start + profiles = profiles.filter(function (profile) { + var data = profile.data; + + if (actualSigningIdentity.subjectInfo) { + var certCommonNames = (data["DeveloperCertificates"] || []).map(function (cert) { + return Utilities.certificateInfo(cert).subjectInfo.CN; + }); + if (!certCommonNames.contains(actualSigningIdentity.subjectInfo.CN)) { + console.log("Skipping provisioning profile with no matching certificate names for '" + + actualSigningIdentity.subjectInfo.CN + + "' (found " + certCommonNames.join(", ") + "): " + + profile.filePath); + return false; + } + } + + var platforms = data["Platform"] || []; + if (platforms.length > 0 && profilePlatform && !platforms.contains(profilePlatform)) { + console.log("Skipping provisioning profile for platform " + platforms.join(", ") + + " (current platform " + profilePlatform + ")" + + ": " + profile.filePath); + return false; + } + + if (teamIdentifier + && !data["TeamIdentifier"].contains(teamIdentifier) + && data["TeamName"] !== teamIdentifier) { + console.log("Skipping provisioning profile for team " + data["TeamIdentifier"] + + " (" + data["TeamName"] + ") (current team " + teamIdentifier + ")" + + ": " + profile.filePath); + return false; + } + + if (Date.parse(data["ExpirationDate"]) <= Date.now()) { + console.log("Skipping expired provisioning profile: " + profile.filePath); + return false; + } + + // Filter development vs distribution profiles; + // though the certificate common names check should have been sufficient + var isDebug = buildVariant === "debug"; + if (data["Entitlements"]["get-task-allow"] !== isDebug) { + console.log("Skipping provisioning profile for wrong debug mode: " + profile.filePath); + return false; + } + + var prefix = data["ApplicationIdentifierPrefix"]; + var fullAppId = data["Entitlements"]["application-identifier"]; + if ([prefix, bundleIdentifier].join(".") !== fullAppId + && [prefix, "*"].join(".") !== fullAppId) { + console.log("Skipping provisioning profile not matching full (" + + [prefix, bundleIdentifier].join(".") + ") or wildcard (" + + [prefix, "*"].join(".") + ") app ID (found " + fullAppId + "): " + + profile.filePath); + return false; + } + + return true; + }); + + // Sort by expiration date - sooner expiration dates come last + profiles.sort(function(profileA, profileB) { + var expA = Date.parse(profileA.data["ExpirationDate"]); + var expB = Date.parse(profileB.data["ExpirationDate"]); + if (expA < expB) + return -1; + if (expA > expB) + return 1; + return 0; + }); + + // Sort by application identifier - wildcard profiles come last + profiles.sort(function(profileA, profileB) { + var idA = profileA.data["Entitlements"]["application-identifier"]; + var idB = profileB.data["Entitlements"]["application-identifier"]; + if (!idA.endsWith(".*") && idB.endsWith(".*")) + return -1; + if (idA.endsWith(".*") && !idB.endsWith(".*")) + return 1; + return 0; + }); + + if (profiles.length) { + console.log("Automatic provisioning using profile " + + profiles[0].data.UUID + + " (" + + profiles[0].data.TeamName + + " - " + + profiles[0].data.Name + + ") in product " + + product.name); + return profiles[0]; + } +} + +function prepareSign(project, product, inputs, outputs, input, output) { + var cmd, cmds = []; + + if (!product.codesign.enableCodeSigning) + 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 shouldSignArtifact = !isProductBundle || isBundle; + + var enableCodeSigning = product.codesign.enableCodeSigning; + if (enableCodeSigning && shouldSignArtifact) { + var actualSigningIdentity = product.codesign._actualSigningIdentity; + if (!actualSigningIdentity) { + throw "No codesigning identities (i.e. certificate and private key pairs) matching “" + + product.codesign.signingIdentity + "” were found."; + } + + // If this is a framework, we need to sign its versioned directory + var subpath = ""; + if (isBundle) { + var frameworkVersion = product.bundle.frameworkVersion; + if (frameworkVersion) { + subpath = product.bundle.contentsFolderPath; + subpath = subpath.substring(product.bundle.bundleName.length); + } + } + + var args = product.codesign.codesignFlags || []; + args.push("--force"); + args.push("--sign", actualSigningIdentity.SHA1); + + // If signingTimestamp is undefined, do not specify the flag at all - + // this uses the system-specific default behavior + var signingTimestamp = product.codesign.signingTimestamp; + if (signingTimestamp !== undefined) { + // If signingTimestamp is an empty string, specify the flag but do + // not specify a value - this uses a default Apple-provided server + var flag = "--timestamp"; + if (signingTimestamp) + flag += "=" + signingTimestamp; + args.push(flag); + } + + for (var j in inputs["codesign.xcent"]) { + args.push("--entitlements", inputs["codesign.xcent"][j].filePath); + break; // there should only be one + } + 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) { + cmd = new Command("touch", ["-c", outputFilePath]); + cmd.silent = true; + cmds.push(cmd); + } + + return cmds; +} + +function signApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var apkInput = inputs["android.package_unsigned"][0]; + var apkOutput = outputs["android.package"][0]; + var cmd; + if (product.codesign.enableCodeSigning) { + var args = ["sign", + "--ks", product.codesign.keystorePath, + "--ks-pass", "pass:" + product.codesign.keystorePassword, + "--ks-key-alias", product.codesign.keyAlias, + "--key-pass", "pass:" + product.codesign.keyPassword, + "--out", apkOutput.filePath, + apkInput.filePath]; + cmd = new Command(product.codesign.apksignerFilePath, args); + cmd.description = "signing " + apkOutput.fileName; + } else { + cmd = new JavaScriptCommand(); + cmd.description = "copying without signing " + apkOutput.fileName; + cmd.source = apkInput.filePath; + cmd.target = apkOutput.filePath; + cmd.silent = true; + cmd.sourceCode = function() { + // If enableCodeSigning is changed to false without any change to unsigned package then + // the copy won't happen because of timestamps. So the target file needs file needs to + // be removed to avoid it. + File.remove(target); + File.copy(source, target); + } + } + return cmd; +} + +function signAabPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var aabInput = inputs["android.package_unsigned"][0]; + var aabOutput = outputs["android.package"][0]; + var cmd; + if (product.codesign.enableCodeSigning) { + args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1", + "-keystore", product.codesign.keystorePath, + "-storepass", product.codesign.keystorePassword, + "-keypass", product.codesign.keyPassword, + "-signedjar", aabOutput.filePath, + aabInput.filePath, + product.codesign.keyAlias]; + cmd = new Command(product.codesign.jarsignerFilePath, args); + cmd.description = "signing " + aabOutput.fileName; + } else { + cmd = new JavaScriptCommand(); + cmd.description = "copying without signing " + aabOutput.fileName; + cmd.source = aabInput.filePath; + cmd.target = aabOutput.filePath; + cmd.silent = true; + cmd.sourceCode = function() { + // If enableCodeSigning is changed to false without any change to unsigned package then + // the copy won't happen because of timestamps. So the target file needs file needs to + // be removed to avoid it. + File.remove(target); + File.copy(source, target); + } + } + return cmd; +} + +function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath, keystorePassword, + keyPassword, keyAlias) { + var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", keyAlias, + "-storepass", keystorePassword, "-keypass", keyPassword, "-keyalg", "RSA", + "-keysize", "2048", "-validity", "10000", "-dname", + "CN=Android Debug,O=Android,C=US"]; + return Process.shellQuote(keytoolFilePath, args); +} diff --git a/share/qbs/modules/codesign/noop.qbs b/share/qbs/modules/codesign/noop.qbs new file mode 100644 index 000000000..3234d7476 --- /dev/null +++ b/share/qbs/modules/codesign/noop.qbs @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** 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 + +CodeSignModule { + condition: true + priority: -100 +} diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 9f4e6be99..39077bec8 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -145,6 +145,12 @@ Module { be set." } + property string toolchainInstallPath + PropertyOptions { + name: "toolchainInstallPath" + description: "a path to the directory where the toolchain executable files are located." + } + property pathList includePaths property pathList systemIncludePaths property pathList distributionIncludePaths @@ -180,6 +186,10 @@ Module { property string debugInfoBundleSuffix: "" property string variantSuffix: "" property string dynamicLibraryImportSuffix: ".lib" + property string objectSuffix: ".o" + property string linkerMapSuffix: ".map" + property string compilerListingSuffix: ".lst" + property string assemblerListingSuffix: ".lst" 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 9332603ec..158c3c061 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -98,7 +98,7 @@ UnixGCC { setupBuildEnvironment: { for (var key in product.cpp.buildEnv) { - v = new ModUtils.EnvironmentVariable(key); + var v = new ModUtils.EnvironmentVariable(key); v.value = product.cpp.buildEnv[key]; v.set(); } @@ -216,7 +216,8 @@ UnixGCC { multiplex: true outputFileTags: ["bundle.input", "application", "primary", "debuginfo_app", - "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist"] + "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist", + "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "application", "app") prepare: Darwin.prepareLipo.apply(Darwin, arguments) @@ -227,7 +228,8 @@ UnixGCC { inputsFromDependencies: ["loadablemodule"] multiplex: true - outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule"] + outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule", + "debuginfo_bundle", "debuginfo_plist", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "loadablemodule", "loadablemodule") @@ -241,7 +243,7 @@ UnixGCC { outputFileTags: ["bundle.input", "dynamiclibrary", "dynamiclibrary_symbols", "primary", "debuginfo_dll","debuginfo_bundle","bundle.variant_symlink", - "debuginfo_plist"] + "debuginfo_plist", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "dynamiclibrary", "dll") prepare: Darwin.prepareLipo.apply(Darwin, arguments) @@ -252,7 +254,7 @@ UnixGCC { inputsFromDependencies: ["staticlibrary"] multiplex: true - outputFileTags: ["bundle.input", "staticlibrary", "primary"] + outputFileTags: ["bundle.input", "staticlibrary", "primary", "codesign.signed_artifact"] outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "staticlibrary") prepare: Darwin.prepareLipo.apply(Darwin, arguments) diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index b69f26739..934636849 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -44,6 +44,8 @@ CppModule { condition: qbs.toolchain && qbs.toolchain.contains("gcc") priority: -100 + Depends { name: "codesign" } + Probes.GccBinaryProbe { id: compilerPathProbe condition: !toolchainInstallPath && !_skipAllChecks @@ -134,8 +136,7 @@ CppModule { property string toolchainPrefix: compilerPathProbe.found ? compilerPathProbe.tcPrefix : undefined - property string toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path - : undefined + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined property string binutilsPath: binutilsProbe.found ? binutilsProbe.path : toolchainInstallPath assemblerName: 'as' + compilerExtension @@ -211,6 +212,11 @@ CppModule { return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat); } + readonly property bool shouldSignArtifacts: codesign._canSignArtifacts + && codesign.enableCodeSigning + // codesigning is done during the lipo step + && !product.multiplexed + property string internalVersion: { if (product.version === undefined) return undefined; @@ -401,12 +407,15 @@ CppModule { "bundle.input", "dynamiclibrary", "dynamiclibrary_symlink", "dynamiclibrary_symbols", "debuginfo_dll", "debuginfo_bundle","dynamiclibrary_import", "debuginfo_plist", + "codesign.signed_artifact", ] outputArtifacts: { var artifacts = [{ filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product), - fileTags: ["bundle.input", "dynamiclibrary"], + fileTags: ["bundle.input", "dynamiclibrary"] + .concat(product.cpp.shouldSignArtifacts + ? ["codesign.signed_artifact"] : []), bundle: { _bundleFilePath: product.destinationDirectory + "/" + PathTools.bundleExecutableFilePath(product) @@ -511,12 +520,14 @@ CppModule { inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"] outputFileTags: ["bundle.input", "loadablemodule", "debuginfo_loadablemodule", - "debuginfo_bundle","debuginfo_plist"] + "debuginfo_bundle", "debuginfo_plist", "codesign.signed_artifact"] outputArtifacts: { var app = { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.loadableModuleFilePath(product)), - fileTags: ["bundle.input", "loadablemodule"], + fileTags: ["bundle.input", "loadablemodule"] + .concat(product.cpp.shouldSignArtifacts + ? ["codesign.signed_artifact"] : []), bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.bundleExecutableFilePath(product)) @@ -548,13 +559,14 @@ CppModule { } inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"] - outputFileTags: ["bundle.input", "application", "debuginfo_app","debuginfo_bundle", - "debuginfo_plist", "mem_map"] + outputFileTags: ["bundle.input", "application", "debuginfo_app", "debuginfo_bundle", + "debuginfo_plist", "mem_map", "codesign.signed_artifact"] outputArtifacts: { var app = { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.applicationFilePath(product)), - fileTags: ["bundle.input", "application"], + fileTags: ["bundle.input", "application"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.bundleExecutableFilePath(product)) @@ -566,7 +578,7 @@ CppModule { if (product.cpp.generateLinkerMapFile) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, - product.targetName + ".map"), + product.targetName + product.cpp.linkerMapSuffix), fileTags: ["mem_map"] }); } @@ -598,7 +610,7 @@ CppModule { return [{ fileTags: tags, filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), - input.fileName + ".o") + input.fileName + input.cpp.objectSuffix) }]; } @@ -613,7 +625,7 @@ CppModule { Artifact { fileTags: ["obj"] - filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), input.fileName + ".o") + filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), input.fileName + input.cpp.objectSuffix) } prepare: { diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs index 5759606aa..e42601a23 100644 --- a/share/qbs/modules/cpp/android-gcc.qbs +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -43,21 +43,32 @@ LinuxGCC { priority: 2 rpaths: [] + // toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android cxxLanguageVersion: "c++14" - property string cxxStlBaseDir: FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "cxx-stl") - property string stlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++") - property string stlLibsDir: { - if (stlBaseDir) - return FileInfo.joinPaths(stlBaseDir, "libs", Android.ndk.abi); - return undefined; + property string archLibsDir: { + switch (qbs.architecture) { + case "arm64": + return "aarch64"; + case "armv7a": + return "arm"; + case "x86_64": + return qbs.architecture; + case "x86": + return "i686"; + } } + property string targetDir: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + property string triple: [archLibsDir, targetSystem, targetDir].join("-") + property string libsDir: FileInfo.joinPaths(sysroot, "usr", "lib", triple); - property string sharedStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_shared")) - ? FileInfo.joinPaths(stlLibsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) + property string sharedStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_shared")) + ? FileInfo.joinPaths(libsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) : undefined - property string staticStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_static")) - ? FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(staticLibraryPrefix, Android.ndk, staticLibrarySuffix)) + property string staticStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_static")) + ? FileInfo.joinPaths(libsDir, Android.ndk.platformVersion, + NdkUtils.stlFileName(staticLibraryPrefix, Android.ndk, + staticLibrarySuffix)) : undefined Group { @@ -91,52 +102,35 @@ LinuxGCC { linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk.abi); driverLinkerFlags: { - var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"]; - if (Android.ndk.appStl.startsWith("c++") && Android.ndk.abi === "armeabi-v7a") - flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]); + var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-nostdlib++"]; + // See https://android.googlesource.com/platform/ndk/+/ndk-release-r21/docs/BuildSystemMaintainers.md#Unwinding + if (Android.ndk.abi === "armeabi-v7a") { + flags = flags.concat(["-Wl,--exclude-libs,libgcc_real.a"]); + if (Android.ndk.appStl.startsWith("c++")) + flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]); + } return flags; } platformDriverFlags: ["-fdata-sections", "-ffunction-sections", "-funwind-tables", "-fstack-protector-strong", "-no-canonical-prefixes"] - libraryPaths: { - var prefix = FileInfo.joinPaths(sysroot, "usr"); - var paths = []; - if (Android.ndk.abi === "x86_64") // no lib64 for arm64-v8a - paths.push(FileInfo.joinPaths(prefix, "lib64")); - paths.push(FileInfo.joinPaths(prefix, "lib")); - paths.push(stlLibsDir); - return paths; - } - dynamicLibraries: { var libs = ["c", "m"]; if (sharedStlFilePath) - libs.push(FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(dynamicLibraryPrefix, Android.ndk, dynamicLibrarySuffix))); + libs.push(FileInfo.joinPaths(libsDir, Android.ndk.platformVersion, + NdkUtils.stlFileName(dynamicLibraryPrefix, Android.ndk, + dynamicLibrarySuffix))); return libs; } - staticLibraries: staticStlFilePath - systemIncludePaths: { - var includes = [FileInfo.joinPaths(sysroot, "usr", "include", toolchainTriple)]; - if (Android.ndk.abi === "armeabi-v7a") { - includes.push(FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "android", - "support", "include")); - } - includes.push(FileInfo.joinPaths(stlBaseDir, "include")); - includes.push(FileInfo.joinPaths(stlBaseDir + "abi", "include")); - return includes; - } defines: ["ANDROID", "__ANDROID__"] binutilsPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt", Android.ndk.hostArch, "bin"); binutilsPathPrefix: FileInfo.joinPaths(binutilsPath, "llvm-") - syslibroot: FileInfo.joinPaths(Android.ndk.ndkDir, "platforms", - Android.ndk.platform, "arch-" - + NdkUtils.abiNameToDirName(Android.ndk.abi)) - sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "sysroot") + sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt", + Android.ndk.hostArch, "sysroot") targetArch: { switch (qbs.architecture) { diff --git a/share/qbs/modules/cpp/darwin.js b/share/qbs/modules/cpp/darwin.js index 6373b57c4..7f7e9a05d 100644 --- a/share/qbs/modules/cpp/darwin.js +++ b/share/qbs/modules/cpp/darwin.js @@ -28,6 +28,7 @@ ** ****************************************************************************/ +var Codesign = require("../codesign/codesign.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); var Gcc = require("./gcc.js"); @@ -99,6 +100,9 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) { else tags.push(fileTag, "primary"); + if (product.codesign.enableCodeSigning) + tags.push("codesign.signed_artifact"); + return { filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.linkerOutputFilePath(fileTag, product, @@ -188,6 +192,12 @@ function prepareLipo(project, product, inputs, outputs, input, output) { commands.push(cmd); if (outputs.dynamiclibrary_symbols) Array.prototype.push.apply(commands, Gcc.createSymbolCheckingCommands(product, outputs)); + + if (product.codesign.enableCodeSigning) { + Array.prototype.push.apply( + commands, Codesign.prepareSign(project, product, inputs, outputs, input, output)); + } + return commands; } diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index 9b6a074d2..775c06a31 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -28,6 +28,7 @@ ** ****************************************************************************/ +var Codesign = require("../codesign/codesign.js"); var Cpp = require("cpp.js"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); @@ -132,7 +133,7 @@ function collectLibraryDependencies(product, isDarwin) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); @@ -355,11 +356,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat return rpath; } + function isNotSystemRunPath(p) { + return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.contains(p) + && !canonicalSystemRunPaths.contains(File.canonicalFilePath(p))); + }; + if (!product.qbs.targetOS.contains("windows")) { - function isNotSystemRunPath(p) { - return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.contains(p) - && !canonicalSystemRunPaths.contains(File.canonicalFilePath(p))); - }; for (i in rpaths) { if (isNotSystemRunPath(rpaths[i])) escapableLinkerFlags.push("-rpath", fixupRPath(rpaths[i])); @@ -1383,27 +1385,9 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } } - if (product.xcode && product.bundle) { - var actualSigningIdentity = product.xcode.actualSigningIdentity; - var codesignDisplayName = product.xcode.actualSigningIdentityDisplayName; - if (actualSigningIdentity && !product.bundle.isBundle) { - args = product.xcode.codesignFlags || []; - args.push("--force"); - args.push("--sign", actualSigningIdentity); - args = args.concat(DarwinTools._codeSignTimestampFlags(product)); - - for (var j in inputs.xcent) { - args.push("--entitlements", inputs.xcent[j].filePath); - break; // there should only be one - } - args.push(primaryOutput.filePath); - cmd = new Command(product.xcode.codesignPath, args); - cmd.description = "codesign " - + primaryOutput.fileName - + " using " + codesignDisplayName - + " (" + actualSigningIdentity + ")"; - commands.push(cmd); - } + if (product.cpp.shouldSignArtifacts) { + Array.prototype.push.apply( + commands, Codesign.prepareSign(project, product, inputs, outputs, input, output)); } return commands; diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js index f1c41fe64..416de7ee2 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -38,35 +38,138 @@ var TemporaryDir = require("qbs.TemporaryDir"); var TextFile = require("qbs.TextFile"); function supportXLinker(architecture) { - return architecture === "78k" || architecture === "avr" - || architecture === "avr32" || architecture === "mcs51" - || architecture === "msp430" || architecture === "v850" - || architecture === "m68k" || architecture === "m32c" - || architecture === "r32c" || architecture === "m16c" - || architecture === "cr16"; + return architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "v850"; } function supportILinker(architecture) { return architecture.startsWith("arm") - || architecture === "rh850" || architecture === "rl78" - || architecture === "rx" || architecture === "stm8" - || architecture === "sh" || architecture === "riscv"; + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8"; } function supportXArchiver(architecture) { - return architecture === "mcs51" || architecture === "avr" - || architecture === "msp430" || architecture === "v850" - || architecture === "78k" || architecture === "avr32" - || architecture === "m68k" || architecture === "m32c" - || architecture === "r32c" || architecture === "m16c" - || architecture === "cr16"; + return architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "v850"; } function supportIArchiver(architecture) { return architecture.startsWith("arm") - || architecture === "stm8" || architecture === "rl78" - || architecture === "rx" || architecture === "rh850" - || architecture === "sh" || architecture === "riscv"; + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8"; +} + +function supportXAssembler(architecture) { + return architecture.startsWith("arm") + || architecture === "78k" + || architecture === "avr" + || architecture === "hcs12" + || architecture === "m16c" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "m32c" + || architecture === "v850"; +} + +function supportIAssembler(architecture) { + return architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs8" + || architecture === "r32c" + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8" + || architecture === "m68k"; +} + +function supportEndianness(architecture) { + return architecture.startsWith("arm") + || architecture === "rx"; +} + +function supportCppExceptions(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx"; +} + +function supportCppRtti(architecture) { + return architecture.startsWith("arm") + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx"; +} + +function supportCppWarningAboutCStyleCast(architecture) { + return architecture.startsWith("arm") + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "rh850" + || architecture === "rl78" + || architecture === "rx" + || architecture === "stm8" + || architecture === "v850"; +} + +function supportDeprecatedFeatureWarnings(architecture) { + return architecture.startsWith("arm") + || architecture === "avr" + || architecture === "cr16" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "rh850" + || architecture === "rl78" + || architecture === "rx" + || architecture === "stm8" + || architecture === "v850"; +} + +function supportCLanguageVersion(architecture) { + return architecture !== "78k"; +} + +function supportCppLanguage(compilerFilePath) { + var baseName = FileInfo.baseName(compilerFilePath); + return baseName !== "iccs08"; } // It is a 'magic' IAR-specific target architecture code. @@ -94,7 +197,11 @@ function architectureCode(architecture) { return "34"; case "cr16": return "45"; - case "rh850": case "rl78": case "rx": case "stm8": case "sh": case "riscv": + case "hcs12": + return "12"; + case "hcs8": + return "78"; + case "rh850": case "riscv": case "rl78": case "rx": case "sh": case "stm8": return ""; default: if (architecture.startsWith("arm")) @@ -107,40 +214,44 @@ function compilerName(qbs) { var architecture = qbs.architecture; if (architecture.startsWith("arm")) return "iccarm"; - else if (architecture === "mcs51") - return "icc8051"; + else if (architecture === "78k") + return "icc78k"; else if (architecture === "avr") return "iccavr"; - else if (architecture === "stm8") - return "iccstm8"; + else if (architecture === "avr32") + return "iccavr32"; + else if (architecture === "cr16") + return "icccr16c"; + else if (architecture === "hcs12") + return "icchcs12"; + else if (architecture === "hcs8") + return "iccs08"; + else if (architecture === "m16c") + return "iccm16c"; + else if (architecture === "m32c") + return "iccm32c"; + else if (architecture === "m68k") + return "icccf"; + else if (architecture === "mcs51") + return "icc8051"; else if (architecture === "msp430") return "icc430"; - else if (architecture === "v850") - return "iccv850"; - else if (architecture === "78k") - return "icc78k"; + else if (architecture === "r32c") + return "iccr32c"; + else if (architecture === "rh850") + return "iccrh850"; + else if (architecture === "riscv") + return "iccriscv"; else if (architecture === "rl78") return "iccrl78"; else if (architecture === "rx") return "iccrx"; - else if (architecture === "rh850") - return "iccrh850"; - else if (architecture === "avr32") - return "iccavr32"; else if (architecture === "sh") return "iccsh"; - else if (architecture === "riscv") - return "iccriscv"; - else if (architecture === "m68k") - return "icccf"; - else if (architecture === "m32c") - return "iccm32c"; - else if (architecture === "r32c") - return "iccr32c"; - else if (architecture === "m16c") - return "iccm16c"; - else if (architecture === "cr16") - return "icccr16c"; + else if (architecture === "stm8") + return "iccstm8"; + else if (architecture === "v850") + return "iccv850"; throw "Unable to deduce compiler name for unsupported architecture: '" + architecture + "'"; } @@ -149,40 +260,44 @@ function assemblerName(qbs) { var architecture = qbs.architecture; if (architecture.startsWith("arm")) return "iasmarm"; - else if (architecture === "rl78") - return "iasmrl78"; - else if (architecture === "rx") - return "iasmrx"; - else if (architecture === "rh850") - return "iasmrh850"; - else if (architecture === "mcs51") - return "a8051"; - else if (architecture === "avr") - return "aavr"; - else if (architecture === "stm8") - return "iasmstm8"; - else if (architecture === "msp430") - return "a430"; - else if (architecture === "v850") - return "av850"; else if (architecture === "78k") return "a78k"; + else if (architecture === "avr") + return "aavr"; else if (architecture === "avr32") return "aavr32"; - else if (architecture === "sh") - return "iasmsh"; - else if (architecture === "riscv") - return "iasmriscv"; - else if (architecture === "m68k") - return "acf"; + else if (architecture === "cr16") + return "acr16c"; + else if (architecture === "hcs12") + return "ahcs12"; + else if (architecture === "hcs8") + return "as08"; + else if (architecture === "m16c") + return "am16c"; else if (architecture === "m32c") return "am32c"; + else if (architecture === "m68k") + return "acf"; + else if (architecture === "mcs51") + return "a8051"; + else if (architecture === "msp430") + return "a430"; else if (architecture === "r32c") return "ar32c"; - else if (architecture === "m16c") - return "am16c"; - else if (architecture === "cr16") - return "acr16c"; + else if (architecture === "rh850") + return "iasmrh850"; + else if (architecture === "riscv") + return "iasmriscv"; + else if (architecture === "rl78") + return "iasmrl78"; + else if (architecture === "rx") + return "iasmrx"; + else if (architecture === "sh") + return "iasmsh"; + else if (architecture === "stm8") + return "iasmstm8"; + else if (architecture === "v850") + return "av850"; throw "Unable to deduce assembler name for unsupported architecture: '" + architecture + "'"; } @@ -253,16 +368,16 @@ function guessArmArchitecture(core) { arch += "v4m"; else if (core === "__ARM4TM__") arch += "v4tm"; - else if (core === "__ARM5__") - arch += "v5"; else if (core === "__ARM5E__") arch += "v5e"; - else if (core === "__ARM6__") - arch += "v6"; + else if (core === "__ARM5__") + arch += "v5"; else if (core === "__ARM6M__") arch += "v6m"; else if (core === "__ARM6SM__") arch += "v6sm"; + else if (core === "__ARM6__") + arch += "v6"; else if (core === "__ARM7M__") arch += "v7m"; else if (core === "__ARM7R__") @@ -271,42 +386,46 @@ function guessArmArchitecture(core) { } function guessArchitecture(macros) { - if (macros["__ICCARM__"] === "1") - return guessArmArchitecture(macros["__CORE__"]); - else if (macros["__ICC8051__"] === "1") - return "mcs51"; - else if (macros["__ICCAVR__"] === "1") - return "avr"; - else if (macros["__ICCSTM8__"] === "1") - return "stm8"; - else if (macros["__ICC430__"] === "1") + if (macros["__ICC430__"] === "1") return "msp430"; - else if (macros["__ICCRL78__"] === "1") - return "rl78"; - else if (macros["__ICCRX__"] === "1") - return "rx"; - else if (macros["__ICCRH850__"] === "1") - return "rh850"; - else if (macros["__ICCV850__"] === "1") - return "v850"; else if (macros["__ICC78K__"] === "1") return "78k"; + else if (macros["__ICC8051__"] === "1") + return "mcs51"; + else if (macros["__ICCARM__"] === "1") + return guessArmArchitecture(macros["__CORE__"]); else if (macros["__ICCAVR32__"] === "1") return "avr32"; - else if (macros["__ICCSH__"] === "1") - return "sh"; - else if (macros["__ICCRISCV__"] === "1") - return "riscv"; + else if (macros["__ICCAVR__"] === "1") + return "avr"; else if (macros["__ICCCF__"] === "1") return "m68k"; + else if (macros["__ICCCR16C__"] === "1") + return "cr16"; + else if (macros["__ICCHCS12__"] === "1") + return "hcs12"; + else if (macros["__ICCM16C__"] === "1") + return "m16c"; else if (macros["__ICCM32C__"] === "1") return "m32c"; else if (macros["__ICCR32C__"] === "1") return "r32c"; - else if (macros["__ICCM16C__"] === "1") - return "m16c"; - else if (macros["__ICCCR16C__"] === "1") - return "cr16"; + else if (macros["__ICCRH850__"] === "1") + return "rh850"; + else if (macros["__ICCRISCV__"] === "1") + return "riscv"; + else if (macros["__ICCRL78__"] === "1") + return "rl78"; + else if (macros["__ICCRX__"] === "1") + return "rx"; + else if (macros["__ICCS08__"] === "1") + return "hcs8"; + else if (macros["__ICCSH__"] === "1") + return "sh"; + else if (macros["__ICCSTM8__"] === "1") + return "stm8"; + else if (macros["__ICCV850__"] === "1") + return "v850"; } function guessEndianness(macros) { @@ -315,19 +434,31 @@ function guessEndianness(macros) { return "big" } -function guessVersion(macros, architecture) -{ +function guessVersion(macros, architecture) { var version = parseInt(macros["__VER__"], 10); if (architecture.startsWith("arm")) { return { major: parseInt(version / 1000000), minor: parseInt(version / 1000) % 1000, patch: parseInt(version) % 1000 } - } else if (architecture === "mcs51" || architecture === "avr" || architecture === "stm8" - || architecture === "msp430" || architecture === "rl78" || architecture === "rx" - || architecture === "rh850" || architecture === "v850" || architecture === "78k" - || architecture === "avr32" || architecture === "sh" || architecture === "riscv" - || architecture === "m68k" || architecture === "m32c" || architecture === "r32c" - || architecture === "m16c" || architecture === "cr16") { + } else if (architecture === "78k" + || architecture === "avr" + || architecture === "avr32" + || architecture === "cr16" + || architecture === "hcs12" + || architecture === "hcs8" + || architecture === "m16c" + || architecture === "m32c" + || architecture === "m68k" + || architecture === "mcs51" + || architecture === "msp430" + || architecture === "r32c" + || architecture === "rh850" + || architecture === "riscv" + || architecture === "rl78" + || architecture === "rx" + || architecture === "sh" + || architecture === "stm8" + || architecture === "v850") { return { major: parseInt(version / 100), minor: parseInt(version % 100), patch: 0 } @@ -338,24 +469,25 @@ function cppLanguageOption(compilerFilePath) { var baseName = FileInfo.baseName(compilerFilePath); switch (baseName) { case "iccarm": - case "iccrl78": - case "iccrx": case "iccrh850": case "iccriscv": + case "iccrl78": + case "iccrx": return "--c++"; - case "icc8051": - case "iccavr": - case "iccstm8": case "icc430": - case "iccv850": case "icc78k": + case "icc8051": + case "iccavr": case "iccavr32": - case "iccsh": case "icccf": + case "icccr16c": + case "icchcs12": + case "iccm16c": case "iccm32c": case "iccr32c": - case "iccm16c": - case "icccr16c": + case "iccsh": + case "iccstm8": + case "iccv850": return "--ec++"; } throw "Unable to deduce C++ language option for unsupported compiler: '" @@ -369,7 +501,7 @@ function dumpMacros(compilerFilePath, tag) { var outFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/iar-macros.predef"); var args = [ inFilePath, "--predef_macros", outFilePath ]; - if (tag && tag === "cpp") + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) args.push(cppLanguageOption(compilerFilePath)); var p = new Process(); @@ -378,45 +510,48 @@ function dumpMacros(compilerFilePath, tag) { return ModUtils.extractMacros(outFile.readAll()); } -function dumpDefaultPaths(compilerFilePath, tag) { +function dumpCompilerIncludePaths(compilerFilePath, tag) { + // We can dump the compiler include paths using the undocumented `--IDE3` flag, + // e.g. which also is used in the IAR extension for the VSCode. In this case the + // compiler procuces the console output in the following format: + // `$$TOOL_BEGIN $$VERSION "3" $$INC_BEGIN $$FILEPATH "<path\\to\\directory>" $$TOOL_END` + var tempDir = new TemporaryDir(); var inFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/empty-source.c"); var inFile = new TextFile(inFilePath, TextFile.WriteOnly); - var args = [ inFilePath, "--preinclude", "." ]; - if (tag === "cpp") + var args = ["--IDE3", inFilePath]; + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) args.push(cppLanguageOption(compilerFilePath)); + var includePaths = []; var p = new Process(); - // This process should return an error, don't throw - // an error in this case. + // 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); - var output = p.readStdErr(); - - var includePaths = []; - var pass = 0; - for (var pos = 0; pos < output.length; ++pos) { - var searchIndex = output.indexOf("searched:", pos); - if (searchIndex === -1) - break; - var startQuoteIndex = output.indexOf('"', searchIndex + 1); - if (startQuoteIndex === -1) - break; - var endQuoteIndex = output.indexOf('"', startQuoteIndex + 1); - if (endQuoteIndex === -1) - break; - pos = endQuoteIndex + 1; - - // Ignore the first path as it is not a compiler include path. - ++pass; - if (pass === 1) - continue; + p.readStdOut().trim().split(/\r?\n/g).map(function(line) { + var m = line.match(/\$\$INC_BEGIN\s\$\$FILEPATH\s\"([^"]*)/); + if (m) { + var includePath = m[1].replace(/\\\\/g, '/'); + if (includePath && File.exists(includePath)) + includePaths.push(includePath); + } + }); - var path = output.substring(startQuoteIndex + 1, endQuoteIndex) - .replace(/[\s]{2,}/g, ' '); - includePaths.push(path); + if (includePaths.length === 0) { + // This can happen if the compiler does not support the `--IDE3` flag, + // e.g. IAR for S08 architecture. In this case we use fallback to the + // detection of the `inc` directory. + var includePath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "../inc/"); + if (File.exists(includePath)) + includePaths.push(includePath); } + return includePaths; +} + +function dumpDefaultPaths(compilerFilePath, tag) { + var includePaths = dumpCompilerIncludePaths(compilerFilePath, tag); return { "includePaths": includePaths }; @@ -441,7 +576,7 @@ function collectLibraryDependencies(product) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); @@ -477,18 +612,24 @@ function collectLibraryDependencies(product) { return result; } -function compilerOutputArtifacts(input, useListing) { +function compilerOutputArtifacts(input, isCompilerArtifacts) { var artifacts = []; artifacts.push({ fileTags: ["obj"], filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + input.cpp.objectSuffix }); - if (useListing) { + if (isCompilerArtifacts && input.cpp.generateCompilerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: Utilities.getHash(input.baseDir) + "/" + + input.fileName + input.cpp.compilerListingSuffix + }); + } else if (!isCompilerArtifacts && input.cpp.generateAssemblerListingFiles) { artifacts.push({ fileTags: ["lst"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".lst" + + input.fileName + input.cpp.assemblerListingSuffix }); } return artifacts; @@ -505,7 +646,7 @@ function applicationLinkerOutputArtifacts(product) { fileTags: ["mem_map"], filePath: FileInfo.joinPaths( product.destinationDirectory, - product.targetName + ".map") + product.targetName + product.cpp.linkerMapSuffix) }; return [app, mem_map] } @@ -521,9 +662,6 @@ function staticLibraryLinkerOutputArtifacts(product) { } function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { - // Determine which C-language we're compiling. - var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); - var args = []; // Input. @@ -580,6 +718,7 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { } var architecture = input.qbs.architecture; + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags)); // Warning level flags. switch (input.cpp.warningLevel) { @@ -587,24 +726,21 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("--no_warnings"); break; case "all": - if (architecture !== "78k") { - if (architecture !== "avr32" && architecture !== "r32c" - && architecture !== "sh" && architecture !== "m16c") { + if (supportDeprecatedFeatureWarnings(architecture)) { args.push("--deprecated_feature_warnings=" +"+attribute_syntax," +"+preprocessor_extensions," +"+segment_pragmas"); } - if (tag === "cpp") + if (tag === "cpp" && supportCppWarningAboutCStyleCast(architecture)) args.push("--warn_about_c_style_casts"); - } break; } if (input.cpp.treatWarningsAsErrors) args.push("--warnings_are_errors"); // C language version flags. - if (tag === "c" && (architecture !== "78k")) { + if (tag === "c" && supportCLanguageVersion(architecture)) { var knownValues = ["c89"]; var cLanguageVersion = Cpp.languageVersion( input.cpp.cLanguageVersion, knownValues, "C"); @@ -620,35 +756,27 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { } // C++ language version flags. - if (tag === "cpp") { - if (architecture.startsWith("arm") - || architecture === "rl78" || architecture === "rx" - || architecture === "rh850" || architecture === "riscv") { - // Enable C++ language flags. - args.push("--c++"); - // Exceptions flags. - if (!input.cpp.enableExceptions) - args.push("--no_exceptions"); - // RTTI flags. - if (!input.cpp.enableRtti) - args.push("--no_rtti"); - } else if (architecture === "stm8" || architecture === "mcs51" - || architecture === "avr" || architecture === "msp430" - || architecture === "v850" || architecture === "78k" - || architecture === "avr32" || architecture === "sh" - || architecture === "m68k" || architecture === "m32c" - || architecture === "r32c" || architecture === "m16c" - || architecture === "cr16") { - args.push("--ec++"); - } + var compilerFilePath = input.cpp.compilerPath; + if (tag === "cpp" && supportCppLanguage(compilerFilePath)) { + // C++ language flag. + var cppOption = cppLanguageOption(compilerFilePath); + args.push(cppOption); + + // Exceptions flag. + var enableExceptions = input.cpp.enableExceptions; + if (!enableExceptions && supportCppExceptions(architecture)) + args.push("--no_exceptions"); + + // RTTI flag. + var enableRtti = input.cpp.enableRtti; + if (!enableRtti && supportCppRtti(architecture)) + args.push("--no_rtti"); } // Byte order flags. - if (architecture.startsWith("arm") || architecture === "rx") { - var endianness = input.cpp.endianness; - if (endianness) - args.push("--endian=" + endianness); - } + var endianness = input.cpp.endianness; + if (endianness && supportEndianness(architecture)) + args.push("--endian=" + endianness); // Listing files generation flag. if (input.cpp.generateCompilerListingFiles) @@ -675,6 +803,16 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { // Output. args.push("-o", outputs.obj[0].filePath); + var architecture = input.qbs.architecture; + + // The `--preinclude` flag is only supported for a certain + // set of assemblers, not for all. + if (supportIAssembler(architecture)) { + var prefixHeaders = input.cpp.prefixHeaders; + for (var i in prefixHeaders) + args.push("--preinclude", prefixHeaders[i]); + } + // Includes. var allIncludePaths = []; var systemIncludePaths = input.cpp.systemIncludePaths; @@ -690,12 +828,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("-r"); // Architecture specific flags. - var architecture = input.qbs.architecture; - if (architecture === "stm8" || architecture === "rl78" - || architecture === "rx" || architecture === "rh850" - || architecture === "avr32" || architecture === "sh" - || architecture === "riscv" || architecture === "m68k" - || architecture === "r32c" || architecture === "cr16") { + if (supportIAssembler(architecture)) { // Silent output generation flag. args.push("--silent"); // Warning level flags. @@ -703,13 +836,18 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { args.push("--no_warnings"); if (input.cpp.treatWarningsAsErrors) args.push("--warnings_are_errors"); - } else { + } else if (supportXAssembler(architecture)){ // Silent output generation flag. args.push("-S"); // Warning level flags. args.push("-w" + (input.cpp.warningLevel === "none" ? "-" : "+")); } + // Byte order flags. + var endianness = input.cpp.endianness; + if (endianness && supportEndianness(architecture)) + args.push("--endian", endianness); + // Listing files generation flag. if (input.cpp.generateAssemblerListingFiles) args.push("-l", outputs.lst[0].filePath); diff --git a/share/qbs/modules/cpp/iar.qbs b/share/qbs/modules/cpp/iar.qbs index 519d30f2a..9709695c1 100644 --- a/share/qbs/modules/cpp/iar.qbs +++ b/share/qbs/modules/cpp/iar.qbs @@ -64,8 +64,7 @@ CppModule { compilerDefinesByLanguage: iarProbe.compilerDefinesByLanguage compilerIncludePaths: iarProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" @@ -89,8 +88,7 @@ CppModule { staticLibrarySuffix: IAR.staticLibrarySuffix(qbs) executableSuffix: IAR.executableSuffix(qbs) - - property string objectSuffix: IAR.objectSuffix(qbs) + objectSuffix: IAR.objectSuffix(qbs) imageFormat: IAR.imageFormat(qbs) @@ -101,8 +99,7 @@ CppModule { id: assembler inputs: ["asm"] outputFileTags: ["obj", "lst"] - outputArtifacts: IAR.compilerOutputArtifacts( - input, input.cpp.generateAssemblerListingFiles) + outputArtifacts: IAR.compilerOutputArtifacts(input, false) prepare: IAR.prepareAssembler.apply(IAR, arguments) } @@ -116,8 +113,7 @@ CppModule { inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] outputFileTags: ["obj", "lst"] - outputArtifacts: IAR.compilerOutputArtifacts( - input, input.cpp.generateCompilerListingFiles) + outputArtifacts: IAR.compilerOutputArtifacts(input, true) prepare: IAR.prepareCompiler.apply(IAR, arguments) } diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 1e8169853..27e4e12d7 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -168,7 +168,7 @@ function objectSuffix(qbs) { + architecture + "'"; } -function mapFileSuffix(qbs) { +function linkerMapSuffix(qbs) { var architecture = qbs.architecture; if (isMcs51Architecture(architecture)) return ".m51"; @@ -507,14 +507,6 @@ function dumpDefaultPaths(compilerFilePath, nullDevice) { return { "includePaths": includePaths }; } -function adjustPathsToWindowsSeparators(sourcePaths) { - var resulingPaths = []; - sourcePaths.forEach(function(path) { - resulingPaths.push(FileInfo.toWindowsSeparators(path)); - }); - return resulingPaths; -} - function collectLibraryDependencies(product) { var seen = {}; var result = []; @@ -534,7 +526,7 @@ function collectLibraryDependencies(product) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); @@ -590,19 +582,24 @@ function filterC166Output(output) { return filteredLines.join('\n'); }; -function compilerOutputArtifacts(input, useListing) { +function compilerOutputArtifacts(input, isCompilerArtifacts) { var artifacts = []; artifacts.push({ fileTags: ["obj"], filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + input.cpp.objectSuffix }); - if (useListing) { + if (isCompilerArtifacts && input.cpp.generateCompilerListingFiles) { artifacts.push({ fileTags: ["lst"], filePath: Utilities.getHash(input.baseDir) + "/" - + (isArmCCCompiler(input.cpp.compilerPath) ? input.baseName : input.fileName) - + ".lst" + + input.fileName + input.cpp.compilerListingSuffix + }); + } else if (!isCompilerArtifacts && input.cpp.generateAssemblerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: Utilities.getHash(input.baseDir) + "/" + + input.fileName + input.cpp.assemblerListingSuffix }); } return artifacts; @@ -619,7 +616,7 @@ function applicationLinkerOutputArtifacts(product) { fileTags: ["mem_map"], filePath: FileInfo.joinPaths( product.destinationDirectory, - product.targetName + product.cpp.mapFileSuffix) + product.targetName + product.cpp.linkerMapSuffix) }; return [app, mem_map]; } @@ -661,20 +658,18 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { var architecture = input.qbs.architecture; if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { // Input. - args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push(input.filePath); // Output. - args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); + args.push("OBJECT (" + outputs.obj[0].filePath + ")"); // Defines. if (allDefines.length > 0) args = args.concat("DEFINE (" + allDefines.join(",") + ")"); // Includes. - if (allIncludePaths.length > 0) { - var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); - args = args.concat("INCDIR (" + adjusted.join(";") + ")"); - } + if (allIncludePaths.length > 0) + args = args.concat("INCDIR (" + allIncludePaths.join(";") + ")"); // Debug information flags. if (input.cpp.debugInformation) @@ -709,7 +704,7 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) { if (!input.cpp.generateCompilerListingFiles) args.push("NOPRINT"); else - args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")"); + args.push("PRINT(" + outputs.lst[0].filePath + ")"); } else if (isArmArchitecture(architecture)) { // Input. args.push("-c", input.filePath); @@ -897,20 +892,18 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { var architecture = input.qbs.architecture; if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { // Input. - args.push(FileInfo.toWindowsSeparators(input.filePath)); + args.push(input.filePath); // Output. - args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")"); + args.push("OBJECT (" + outputs.obj[0].filePath + ")"); // Defines. if (allDefines.length > 0) args = args.concat("DEFINE (" + allDefines.join(",") + ")"); // Includes. - if (allIncludePaths.length > 0) { - var adjusted = adjustPathsToWindowsSeparators(allIncludePaths); + if (allIncludePaths.length > 0) args = args.concat("INCDIR (" + adjusted.join(";") + ")"); - } // Debug information flags. if (input.cpp.debugInformation) @@ -923,7 +916,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { if (!input.cpp.generateAssemblerListingFiles) args.push("NOPRINT"); else - args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")"); + args.push("PRINT(" + outputs.lst[0].filePath + ")"); } else if (isArmArchitecture(architecture)) { // Input. args.push(input.filePath); @@ -982,39 +975,50 @@ function disassemblerFlags(project, product, input, outputs, explicitlyDependsOn function linkerFlags(project, product, inputs, outputs) { var args = []; + // Library paths. + var libraryPaths = product.cpp.libraryPaths; + var architecture = product.qbs.architecture; if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) { - // Note: The C51/256/166 linker does not distinguish an object files and + // Note: The C51, C251, or C166 linker does not distinguish an object files and // a libraries, it interpret all this stuff as an input objects, // so, we need to pass it together in one string. - var allObjectPaths = []; - function addObjectPath(obj) { - allObjectPaths.push(obj.filePath); - } // Inputs. if (inputs.obj) - inputs.obj.map(function(obj) { addObjectPath(obj) }); + inputs.obj.map(function(obj) { allObjectPaths.push(obj.filePath) }); // Library dependencies. var libraryObjects = collectLibraryDependencies(product); - libraryObjects.forEach(function(dep) { addObjectPath(dep); }) + allObjectPaths = allObjectPaths.concat(libraryObjects.map(function(lib) { + // Semi-intelligent handling the library paths. + // We need to add the full path prefix to the library file if this + // file is not absolute or not relative. Reason is that the C51, C251, + // and C166 linkers does not support the library paths. + var filePath = lib.filePath; + if (FileInfo.isAbsolutePath(filePath)) + return filePath; + for (var i = 0; i < libraryPaths.length; ++i) { + var fullPath = FileInfo.joinPaths(libraryPaths[i], filePath); + if (File.exists(fullPath)) + return fullPath; + } + return filePath; + })); // Add all input objects as arguments (application and library object files). - var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); - args = args.concat(adjusted.join(",")); + if (allObjectPaths.length > 0) + args = args.concat(allObjectPaths.join(",")); // Output. - // Note: We need to wrap an output file name with quotes. Otherwise - // the linker will ignore a specified file name. - args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.application[0].filePath) + '"'); + args.push("TO", outputs.application[0].filePath); // Map file generation flag. if (!product.cpp.generateLinkerMapFile) args.push("NOPRINT"); else - args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.mem_map[0].filePath) + ")"); + args.push("PRINT(" + outputs.mem_map[0].filePath + ")"); } else if (isArmArchitecture(architecture)) { // Inputs. if (inputs.obj) @@ -1023,8 +1027,6 @@ function linkerFlags(project, product, inputs, outputs) { // Output. args.push("--output", outputs.application[0].filePath); - // Library paths. - var libraryPaths = product.cpp.libraryPaths; if (libraryPaths) args.push("--userlibpath=" + libraryPaths.join(",")); @@ -1074,13 +1076,11 @@ function archiverFlags(project, product, inputs, outputs) { inputs.obj.map(function(obj) { addObjectPath(obj) }); // Add all input objects as arguments. - var adjusted = adjustPathsToWindowsSeparators(allObjectPaths); - args = args.concat(adjusted.join(",")); + if (allObjectPaths.length > 0) + args = args.concat(allObjectPaths.join(",")); // Output. - // Note: We need to wrap a output file name with quotes. Otherwise - // the linker will ignore a specified file name. - args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath) + '"'); + args.push("TO", outputs.staticlibrary[0].filePath); } else if (isArmArchitecture(architecture)) { // Note: The ARM archiver command line expect the output file // first, and then a set of input objects. @@ -1100,6 +1100,38 @@ function archiverFlags(project, product, inputs, outputs) { return args; } +// The ARMCLANG compiler does not support generation +// for the listing files: +// * https://www.keil.com/support/docs/4152.htm +// So, we generate the listing files from the object files +// using the disassembler. +function generateClangCompilerListing(project, product, inputs, outputs, input, output) { + if (isArmClangCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) { + var args = disassemblerFlags(project, product, input, outputs, explicitlyDependsOn); + var disassemblerPath = input.cpp.disassemblerPath; + var cmd = new Command(disassemblerPath, args); + cmd.silent = true; + return cmd; + } +} + +// The ARMCC compiler generates the listing files only in a short form, +// e.g. to 'module.lst' instead of 'module.{c|cpp}.lst', that complicates +// the auto-tests. Therefore we need to rename generated listing files +// with correct unified names. +function generateArmccCompilerListing(project, product, inputs, outputs, input, output) { + if (isArmCCCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) { + var listingPath = FileInfo.path(outputs.lst[0].filePath); + var cmd = new JavaScriptCommand(); + cmd.oldListing = FileInfo.joinPaths(listingPath, input.baseName + ".lst"); + cmd.newListing = FileInfo.joinPaths( + listingPath, input.fileName + input.cpp.compilerListingSuffix); + cmd.silent = true; + cmd.sourceCode = function() { File.move(oldListing, newListing); }; + return cmd; + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); @@ -1117,18 +1149,14 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli } cmds.push(cmd); - // The ARMCLANG compiler does not support generation - // for the listing files: - // * https://www.keil.com/support/docs/4152.htm - // So, we generate the listing files from the object files - // using the disassembler. - if (isArmClangCompiler(compilerPath) && input.cpp.generateCompilerListingFiles) { - args = disassemblerFlags(project, product, input, outputs, explicitlyDependsOn); - var disassemblerPath = input.cpp.disassemblerPath; - cmd = new Command(disassemblerPath, args); - cmd.silent = true; + cmd = generateClangCompilerListing(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } + + cmd = generateArmccCompilerListing(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + return cmds; } diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs index eba52adff..ea99b589c 100644 --- a/share/qbs/modules/cpp/keil.qbs +++ b/share/qbs/modules/cpp/keil.qbs @@ -62,8 +62,7 @@ CppModule { compilerDefinesByLanguage: keilProbe.compilerDefinesByLanguage compilerIncludePaths: keilProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" @@ -90,9 +89,8 @@ CppModule { staticLibrarySuffix: KEIL.staticLibrarySuffix(qbs) executableSuffix: KEIL.executableSuffix(qbs) - - property string objectSuffix: KEIL.objectSuffix(qbs) - property string mapFileSuffix: KEIL.mapFileSuffix(qbs) + objectSuffix: KEIL.objectSuffix(qbs) + linkerMapSuffix: KEIL.linkerMapSuffix(qbs) imageFormat: KEIL.imageFormat(qbs) @@ -105,8 +103,7 @@ CppModule { id: assembler inputs: ["asm"] outputFileTags: ["obj", "lst"] - outputArtifacts: KEIL.compilerOutputArtifacts( - input, input.cpp.generateAssemblerListingFiles) + outputArtifacts: KEIL.compilerOutputArtifacts(input, false) prepare: KEIL.prepareAssembler.apply(KEIL, arguments) } @@ -120,8 +117,7 @@ CppModule { inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] outputFileTags: ["obj", "lst"] - outputArtifacts: KEIL.compilerOutputArtifacts( - input, input.cpp.generateCompilerListingFiles) + outputArtifacts: KEIL.compilerOutputArtifacts(input, true) prepare: KEIL.prepareCompiler.apply(KEIL, arguments) } diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index df1f5eb5b..566059610 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -354,7 +354,7 @@ function collectLibraryDependencies(product) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index 36454031e..1904f59fc 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -28,6 +28,7 @@ ** ****************************************************************************/ +var BinaryFile = require("qbs.BinaryFile"); var Cpp = require("cpp.js"); var Environment = require("qbs.Environment"); var File = require("qbs.File"); @@ -50,6 +51,8 @@ function assemblerName(qbs) { return "sdas8051"; case "stm8": return "sdasstm8"; + case "hcs8": + return "sdas6808"; } throw "Unable to deduce assembler name for unsupported architecture: '" + qbs.architecture + "'"; @@ -61,6 +64,8 @@ function linkerName(qbs) { return "sdld"; case "stm8": return "sdldstm8"; + case "hcs8": + return "sdld6808"; } throw "Unable to deduce linker name for unsupported architecture: '" + qbs.architecture + "'"; @@ -75,6 +80,8 @@ function targetArchitectureFlag(architecture) { return "-mmcs51"; if (architecture === "stm8") return "-mstm8"; + if (architecture === "hcs8") + return "-mhc08"; } function guessArchitecture(macros) { @@ -82,6 +89,8 @@ function guessArchitecture(macros) { return "mcs51"; if (macros["__SDCC_stm8"] === "1") return "stm8"; + if (macros["__SDCC_hc08"] === "1") + return "hcs8"; } function guessEndianness(macros) { @@ -142,7 +151,8 @@ function dumpDefaultPaths(compilerFilePath, architecture) { || line.startsWith("libpath:")) { addIncludePaths = false; } else if (addIncludePaths) { - includePaths.push(line); + if (File.exists(line)) + includePaths.push(line); } } @@ -193,7 +203,7 @@ function collectLibraryDependencies(product) { if (!obj.cpp) return; function ensureArray(a) { - return Array.isArray(a) ? a : []; + return (a instanceof Array) ? a : []; } function sanitizedModuleListProperty(obj, moduleName, propertyName) { return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName)); @@ -229,7 +239,7 @@ function collectLibraryDependencies(product) { return result; } -function compilerOutputArtifacts(input, useListing) { +function compilerOutputArtifacts(input, isCompilerArtifacts) { var obj = { fileTags: ["obj"], filePath: Utilities.getHash(input.baseDir) + "/" @@ -260,11 +270,17 @@ function compilerOutputArtifacts(input, useListing) { + input.fileName + ".rst" }; var artifacts = [obj, asm_adb, asm_src, asm_sym, rst_data]; - if (useListing) { + if (isCompilerArtifacts && input.cpp.generateCompilerListingFiles) { artifacts.push({ fileTags: ["lst"], filePath: Utilities.getHash(input.baseDir) + "/" - + input.fileName + ".lst" + + input.fileName + input.cpp.compilerListingSuffix + }); + } else if (!isCompilerArtifacts && input.cpp.generateAssemblerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: Utilities.getHash(input.baseDir) + "/" + + input.fileName + input.cpp.assemblerListingSuffix }); } return artifacts; @@ -293,7 +309,7 @@ function applicationLinkerOutputArtifacts(product) { fileTags: ["mem_map"], filePath: FileInfo.joinPaths( product.destinationDirectory, - product.targetName + ".map") + product.targetName + product.cpp.linkerMapSuffix) }; return [app, lk_cmd, mem_summary, mem_map] } @@ -537,6 +553,119 @@ function archiverFlags(project, product, inputs, outputs) { return args; } +function buildLinkerMapFilePath(target, suffix) { + return FileInfo.joinPaths(FileInfo.path(target.filePath), + FileInfo.completeBaseName(target.fileName) + suffix); +} + +// This is the workaround for the SDCC bug on a Windows host: +// * https://sourceforge.net/p/sdcc/bugs/2970/ +// 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"); + if (isWindows && input.cpp.debugInformation) { + var cmd = new JavaScriptCommand(); + cmd.objectPath = outputs.obj[0].filePath; + cmd.silent = true; + cmd.sourceCode = function() { + var file = new BinaryFile(objectPath, BinaryFile.ReadWrite); + var data = file.read(file.size()); + file.resize(0); + for (var pos = 0; pos < data.length; ++pos) { + // Find the next index of CR (\r) symbol. + var index = data.indexOf(0x0d, pos); + if (index < 0) + index = data.length; + // Write next data chunk between the previous position and the CR + // symbol, exclude the CR symbol. + file.write(data.slice(pos, index)); + pos = index; + } + }; + return cmd; + } +} + +// It is a workaround which removes the generated linker map file +// if it is disabled by cpp.generateLinkerMapFile property. +// Reason is that the SDCC compiler always generates this file, +// and does not have an option to disable generation for a linker +// map file. So, we can to remove a listing files only after the +// linking completes. +function removeLinkerMapFile(project, product, inputs, outputs, input, output) { + if (!product.cpp.generateLinkerMapFile) { + var target = outputs.application[0]; + var cmd = new JavaScriptCommand(); + cmd.mapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix) + cmd.silent = true; + cmd.sourceCode = function() { File.remove(mapFilePath); }; + return cmd; + } +} + +// It is a workaround to rename the extension of the output linker +// map file to the specified one, since the linker generates only +// files with the '.map' extension. +function renameLinkerMapFile(project, product, inputs, outputs, input, output) { + if (product.cpp.generateLinkerMapFile && (product.cpp.linkerMapSuffix !== ".map")) { + var target = outputs.application[0]; + var cmd = new JavaScriptCommand(); + 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 which removes the generated listing files +// if it is disabled by cpp.generateCompilerListingFiles property +// or when the cpp.compilerListingSuffix differs with '.lst'. +// Reason is that the SDCC compiler does not have an option to +// disable generation for a listing files. Besides, the SDCC +// compiler use this files and for the linking. So, we can to +// 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) { + if (!object.filePath.endsWith(".c" + object.cpp.objectSuffix)) + return; // Skip the assembler generated objects. + if (!object.cpp.generateCompilerListingFiles + || (object.cpp.compilerListingSuffix !== ".lst")) { + var listingPath = FileInfo.joinPaths(FileInfo.path(object.filePath), + object.completeBaseName + ".lst"); + File.remove(listingPath); + } + }) + }; + return cmd; +} + +// It is a workaround that duplicates the generated listing files +// but with desired names. The problem is that the SDCC compiler does +// not support an options to specify names for the generated listing +// files. At the same time, the compiler always generates the listing +// files in the form of 'module.c.lst', which makes it impossible to +// change the file suffix to a user-specified one. In addition, these +// files are also somehow used for linking. Thus, we can not rename them +// on the compiling stage. +function duplicateCompilerListingFile(project, product, inputs, outputs, input, output) { + if (input.cpp.generateCompilerListingFiles + && (input.cpp.compilerListingSuffix !== ".lst")) { + var cmd = new JavaScriptCommand(); + cmd.newListing = outputs.lst[0].filePath; + cmd.oldListing = FileInfo.joinPaths(FileInfo.path(outputs.lst[0].filePath), + outputs.lst[0].completeBaseName + ".lst"); + cmd.silent = true; + cmd.sourceCode = function() { File.copy(oldListing, newListing); }; + return cmd; + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); @@ -546,87 +675,53 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli cmd.highlight = "compiler"; cmds.push(cmd); - // This is the workaround for the SDCC bug on a Windows host: - // * https://sourceforge.net/p/sdcc/bugs/2970/ - // We need to replace the '\r\n\' line endings with the'\n' line - // endings for each generated object file. - var isWindows = input.qbs.hostOS.contains("windows"); - if (isWindows) { - cmd = new JavaScriptCommand(); - cmd.objectPath = outputs.obj[0].filePath; - cmd.silent = true; - cmd.sourceCode = function() { - var lines = []; - var file = new TextFile(objectPath, TextFile.ReadWrite); - while (!file.atEof()) - lines.push(file.readLine() + "\n"); - file.truncate(); - for (var l in lines) - file.write(lines[l]); - }; + cmd = patchObjectFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + cmd = duplicateCompilerListingFile(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } return cmds; } function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { + var cmds = []; var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn); var assemblerPath = input.cpp.assemblerPath; var cmd = new Command(assemblerPath, args); cmd.description = "assembling " + input.fileName; cmd.highlight = "compiler"; - return [cmd]; + cmds.push(cmd); + + cmd = patchObjectFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + return cmds; } function prepareLinker(project, product, inputs, outputs, input, output) { var cmds = []; - var target = outputs.application[0]; var args = linkerFlags(project, product, inputs, outputs); var linkerPath = effectiveLinkerPath(product); var cmd = new Command(linkerPath, args); - cmd.description = "linking " + target.fileName; + cmd.description = "linking " + outputs.application[0].fileName; cmd.highlight = "linker"; cmds.push(cmd); - // It is a workaround which removes the generated listing files - // if it is disabled by cpp.generateCompilerListingFiles property. - // Reason is that the SDCC compiler does not have an option to - // disable generation for a listing files. Besides, the SDCC - // compiler use this files and for the linking. So, we can to - // remove a listing files only after the linking completes. - if (!product.cpp.generateCompilerListingFiles) { - cmd = new JavaScriptCommand(); - cmd.objectPaths = inputs.obj.map(function(a) { return a.filePath; }); - cmd.objectSuffix = product.cpp.objectSuffix; - cmd.silent = true; - cmd.sourceCode = function() { - objectPaths.forEach(function(objectPath) { - if (!objectPath.endsWith(".c" + objectSuffix)) - return; // Skip the assembler objects. - var listingPath = FileInfo.joinPaths( - FileInfo.path(objectPath), - FileInfo.completeBaseName(objectPath) + ".lst"); - File.remove(listingPath); - }); - }; + cmd = removeCompilerListingFiles(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } - // It is a workaround which removes the generated linker map file - // if it is disabled by cpp.generateLinkerMapFile property. - // Reason is that the SDCC compiler always generates this file, - // and does not have an option to disable generation for a linker - // map file. So, we can to remove a listing files only after the - // linking completes. - if (!product.cpp.generateLinkerMapFile) { - cmd = new JavaScriptCommand(); - cmd.mapFilePath = FileInfo.joinPaths( - FileInfo.path(target.filePath), - FileInfo.completeBaseName(target.fileName) + ".map"); - cmd.silent = true; - cmd.sourceCode = function() { File.remove(mapFilePath); }; + + cmd = renameLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) + cmds.push(cmd); + + cmd = removeLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } return cmds; } diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs index da1b89402..c5a0893d4 100644 --- a/share/qbs/modules/cpp/sdcc.qbs +++ b/share/qbs/modules/cpp/sdcc.qbs @@ -63,8 +63,7 @@ CppModule { compilerDefinesByLanguage: sdccProbe.compilerDefinesByLanguage compilerIncludePaths: sdccProbe.includePaths - property string toolchainInstallPath: compilerPathProbe.found - ? compilerPathProbe.path : undefined + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" @@ -90,8 +89,7 @@ CppModule { staticLibrarySuffix: ".lib" executableSuffix: ".ihx" - - property string objectSuffix: ".rel" + objectSuffix: ".rel" imageFormat: "ihx" @@ -102,7 +100,7 @@ CppModule { id: assembler inputs: ["asm"] outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"] - outputArtifacts: SDCC.compilerOutputArtifacts(input, true) + outputArtifacts: SDCC.compilerOutputArtifacts(input, false) prepare: SDCC.prepareAssembler.apply(SDCC, arguments) } @@ -116,8 +114,7 @@ CppModule { inputs: ["cpp", "c"] auxiliaryInputs: ["hpp"] outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"] - outputArtifacts: SDCC.compilerOutputArtifacts( - input, input.cpp.generateCompilerListingFiles) + outputArtifacts: SDCC.compilerOutputArtifacts(input, true) prepare: SDCC.prepareCompiler.apply(SDCC, arguments) } diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs index a34a67ad2..556efb042 100644 --- a/share/qbs/modules/cpp/windows-clang-cl.qbs +++ b/share/qbs/modules/cpp/windows-clang-cl.qbs @@ -53,6 +53,7 @@ MsvcBaseModule { vcvarsallFilePath: vcvarsallPath enableDefinesByLanguage: enableCompilerDefinesByLanguage preferredArchitecture: qbs.architecture + winSdkVersion: windowsSdkVersion } qbs.architecture: clangClProbe.found ? clangClProbe.architecture : original diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs index ffed76cdd..ef2ef4946 100644 --- a/share/qbs/modules/cpp/windows-mingw.qbs +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -68,7 +68,7 @@ MingwBaseModule { auxiliaryInputs: ["hpp"] Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o" + filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res" + input.cpp.objectSuffix fileTags: ["obj"] } diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index e88c3f15f..81fe48385 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -81,7 +81,6 @@ CppModule { separateDebugInformation: true property bool generateManifestFile: true - property string toolchainInstallPath architecture: qbs.architecture endianness: "little" @@ -89,6 +88,7 @@ CppModule { dynamicLibrarySuffix: ".dll" executableSuffix: ".exe" debugInfoSuffix: ".pdb" + objectSuffix: ".obj" imageFormat: "pe" Properties { condition: product.multiplexByQbsProperties.contains("buildVariants") @@ -108,13 +108,15 @@ CppModule { } } + property string windowsSdkVersion + Rule { condition: useCPrecompiledHeader inputs: ["c_pch_src"] auxiliaryInputs: ["hpp"] Artifact { fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_c.obj' + filePath: Utilities.getHash(input.completeBaseName) + '_c' + input.cpp.objectSuffix } Artifact { fileTags: ['c_pch'] @@ -132,7 +134,7 @@ CppModule { auxiliaryInputs: ["hpp"] Artifact { fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_cpp.obj' + filePath: Utilities.getHash(input.completeBaseName) + '_cpp' + input.cpp.objectSuffix } Artifact { fileTags: ['cpp_pch'] @@ -157,12 +159,13 @@ CppModule { var artifacts = []; artifacts.push({ fileTags: tags, - filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" + filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + input.cpp.objectSuffix }); if (input.cpp.generateCompilerListingFiles) { artifacts.push({ fileTags: ["lst"], - filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".lst" + filePath: Utilities.getHash(input.baseDir) + + "/" + input.fileName + input.cpp.compilerListingSuffix }); } return artifacts; @@ -210,7 +213,7 @@ CppModule { fileTags: ["mem_map"], filePath: FileInfo.joinPaths( product.destinationDirectory, - product.targetName + ".map") + product.targetName + product.cpp.linkerMapSuffix) }); } return artifacts; @@ -327,7 +330,7 @@ CppModule { Rule { inputs: ["asm"] Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" + filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + input.cpp.objectSuffix fileTags: ["obj"] } prepare: { diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index d5b5baf92..33c5e74c8 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -51,6 +51,7 @@ MsvcBaseModule { compilerFilePath: compilerPath enableDefinesByLanguage: enableCompilerDefinesByLanguage preferredArchitecture: qbs.architecture + winSdkVersion: windowsSdkVersion } qbs.architecture: msvcProbe.found ? msvcProbe.architecture : original diff --git a/share/qbs/modules/ico/ico.js b/share/qbs/modules/ico/ico.js index 997a6dc23..a61d585ab 100644 --- a/share/qbs/modules/ico/ico.js +++ b/share/qbs/modules/ico/ico.js @@ -29,6 +29,7 @@ ****************************************************************************/ var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); var Process = require("qbs.Process"); function prepareIconset(project, product, inputs, outputs, input, output) { diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs index ad055fe34..ceb29f36f 100644 --- a/share/qbs/modules/java/JavaModule.qbs +++ b/share/qbs/modules/java/JavaModule.qbs @@ -240,7 +240,7 @@ Module { if (!product.java._tagJniHeaders) { for (var i = 0; i < artifacts.length; ++i) { var a = artifacts[i]; - if (Array.isArray(a.fileTags)) + if (a.fileTags instanceof Array) a.fileTags = a.fileTags.map(function(tag) { if (tag === "hpp") return "java.jni-hpp"; diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs index b443b4dbf..36b92dd30 100644 --- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs +++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs @@ -12,6 +12,8 @@ ProtobufBase { property bool useGrpc: false + property bool _linkLibraries: true + property string grpcIncludePath: grpcIncludeProbe.found ? grpcIncludeProbe.path : undefined property string grpcLibraryPath: grpcLibraryProbe.found ? grpcLibraryProbe.path : undefined @@ -36,6 +38,9 @@ ProtobufBase { } cpp.libraryPaths: { + if (!_linkLibraries) + return []; + var result = []; if (libraryProbe.found) result.push(libraryProbe.path); @@ -44,6 +49,9 @@ ProtobufBase { return result; } cpp.dynamicLibraries: { + if (!_linkLibraries) + return []; + var result = []; if (_libraryName) result.push(_libraryName) @@ -54,6 +62,9 @@ ProtobufBase { return result; } cpp.includePaths: { + if (!_linkLibraries) + return [outputDir]; + var result = [outputDir]; if (includeProbe.found) result.push(includePath); @@ -129,17 +140,17 @@ ProtobufBase { validate: { HelperFunctions.validateCompiler(compilerName, compilerPath); - if (!includeProbe.found) + if (_linkLibraries && !includeProbe.found) throw "Can't find cpp protobuf include files. Please set the includePath property."; - if (!libraryProbe.found) + if (_linkLibraries && !libraryProbe.found) throw "Can't find cpp protobuf library. Please set the libraryPath property."; if (useGrpc) { if (!File.exists(grpcPluginPath)) throw "Can't find grpc_cpp_plugin plugin. Please set the grpcPluginPath property."; - if (!grpcIncludeProbe.found) + if (_linkLibraries && !grpcIncludeProbe.found) throw "Can't find grpc++ include files. Please set the grpcIncludePath property."; - if (!grpcLibraryProbe.found) + if (_linkLibraries && !grpcLibraryProbe.found) throw "Can't find grpc++ library. Please set the grpcLibraryPath property."; } } diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js index 9c87e09dc..1060894d4 100644 --- a/share/qbs/modules/xcode/xcode.js +++ b/share/qbs/modules/xcode/xcode.js @@ -93,6 +93,16 @@ var XcodeArchSpecsReader = (function () { return XcodeArchSpecsReader; }()); +function platformInfo(platformInfoPlist) { + var propertyList = new PropertyList(); + try { + propertyList.readFromFile(platformInfoPlist); + return propertyList.toObject(); + } finally { + propertyList.clear(); + } +} + function sdkInfoList(sdksPath) { var sdkInfo = []; var sdks = File.directoryEntries(sdksPath, File.Dirs | File.NoDotAndDotDot); @@ -181,6 +191,17 @@ function provisioningProfilePlistContents(filePath) { } } +function boolFromSdkOrPlatform(varName, sdkProps, platformProps, defaultValue) { + var values = [(sdkProps || {})[varName], (platformProps || {})[varName]]; + for (var i = 0; i < values.length; ++i) { + if (values[i] === "YES") + return true; + if (values[i] === "NO") + return false; + } + return defaultValue; +} + function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath) { var _specsPluginBaseName; if (Utilities.versionCompare(version, "12") >= 0) { diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs index aeb0760ac..6c0584c81 100644 --- a/share/qbs/modules/xcode/xcode.qbs +++ b/share/qbs/modules/xcode/xcode.qbs @@ -7,7 +7,6 @@ import qbs.ModUtils import qbs.Probes import qbs.PropertyList import qbs.Utilities -import 'xcode.js' as Xcode Module { id: xcodeModule @@ -87,31 +86,6 @@ Module { } } - property string signingIdentity - readonly property string actualSigningIdentity: { - if (_actualSigningIdentity && _actualSigningIdentity.length === 2) - return _actualSigningIdentity[0]; - } - - readonly property string actualSigningIdentityDisplayName: { - if (_actualSigningIdentity && _actualSigningIdentity.length === 2) - return _actualSigningIdentity[1]; - } - - property string signingTimestamp: "none" - - property string provisioningProfile - - property string xcodebuildName: "xcodebuild" - property string xcodebuildPath: FileInfo.joinPaths(developerPath, "usr", "bin", xcodebuildName) - - property string securityName: "security" - property string securityPath: securityName - - property string codesignName: "codesign" - property string codesignPath: codesignName - property stringList codesignFlags - readonly property path toolchainPath: FileInfo.joinPaths(toolchainsPath, "XcodeDefault" + ".xctoolchain") readonly property path platformPath: FileInfo.joinPaths(platformsPath, @@ -143,35 +117,11 @@ Module { readonly property path toolchainInfoPlist: FileInfo.joinPaths(toolchainPath, "ToolchainInfo.plist") - readonly property stringList _actualSigningIdentity: { - if (/^[A-Fa-f0-9]{40}$/.test(signingIdentity)) { - return [signingIdentity, signingIdentity]; - } - - var result = []; + readonly property var _platformSettings: xcodeProbe.platformSettings - if (signingIdentity) { - var identities = Utilities.signingIdentities(); - for (var key in identities) { - if (identities[key].subjectInfo.CN === signingIdentity) { - result.push([key, signingIdentity]); - } - } - - if (result.length == 0) { - throw "Unable to find signingIdentity '" + signingIdentity + "'"; - } - - if (result.length > 1) { - throw "Signing identity '" + signingIdentity + "' is ambiguous"; - } - } - - return result[0]; - } - - property path provisioningProfilesPath: { - return FileInfo.joinPaths(Environment.getEnv("HOME"), "Library/MobileDevice/Provisioning Profiles"); + readonly property var _platformProps: { + if (_platformSettings) + return _platformSettings["DefaultProperties"]; } readonly property stringList standardArchitectures: _architectureSettings["ARCHS_STANDARD"] @@ -197,6 +147,12 @@ Module { } } + readonly property var _sdkProps: { + if (_sdkSettings) { + return _sdkSettings["DefaultProperties"]; + } + } + qbs.sysroot: sdkPath validate: { @@ -231,23 +187,10 @@ Module { validator.validate(); } - property var buildEnv: { - var env = { - "DEVELOPER_DIR": developerPath, - "SDKROOT": sdkPath - }; - - var prefixes = [platformPath + "/Developer", toolchainPath, developerPath]; - for (var i = 0; i < prefixes.length; ++i) { - var codesign_allocate = prefixes[i] + "/usr/bin/codesign_allocate"; - if (File.exists(codesign_allocate)) { - env["CODESIGN_ALLOCATE"] = codesign_allocate; - break; - } - } - - return env; - } + property var buildEnv: ({ + "DEVELOPER_DIR": developerPath, + "SDKROOT": sdkPath + }) setupBuildEnvironment: { var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, false); @@ -261,191 +204,4 @@ Module { v.set(); } } - - Group { - name: "Provisioning Profiles" - prefix: xcode.provisioningProfilesPath + "/" - files: ["*.mobileprovision", "*.provisionprofile"] - fileTags: ["xcode.provisioningprofile"] - } - - FileTagger { - fileTags: ["xcode.entitlements"] - patterns: ["*.entitlements"] - } - - FileTagger { - fileTags: ["xcode.provisioningprofile"] - patterns: ["*.mobileprovision", "*.provisionprofile"] - } - - Rule { - inputs: ["xcode.provisioningprofile"] - - Artifact { - filePath: FileInfo.joinPaths("provisioning-profiles", input.fileName + ".xml") - fileTags: ["xcode.provisioningprofile.data"] - } - - prepare: { - var cmds = []; - - var cmd = new Command("openssl", ["smime", "-verify", "-noverify", "-inform", "DER", - "-in", input.filePath, "-out", output.filePath]); - cmd.silent = true; - cmd.stderrFilterFunction = function (output) { - return output.replace("Verification successful\n", ""); - }; - cmds.push(cmd); - - cmd = new JavaScriptCommand(); - cmd.silent = true; - cmd.inputFilePath = input.filePath; - cmd.outputFilePath = output.filePath; - cmd.sourceCode = function() { - var propertyList = new PropertyList(); - try { - propertyList.readFromFile(outputFilePath); - propertyList.readFromObject({ - data: propertyList.toObject(), - fileName: FileInfo.fileName(inputFilePath), - filePath: inputFilePath - }); - propertyList.writeToFile(outputFilePath, "xml1"); - } finally { - propertyList.clear(); - } - }; - cmds.push(cmd); - - return cmds; - } - } - - Rule { - multiplex: true - inputs: ["xcode.provisioningprofile.data"] - outputFileTags: ["xcode.provisioningprofile.main", "xcode.provisioningprofile.data.main"] - - outputArtifacts: { - var artifacts = []; - for (var i = 0; i < inputs["xcode.provisioningprofile.data"].length; ++i) { - var dataFile = inputs["xcode.provisioningprofile.data"][i].filePath; - var query = product.moduleProperty("xcode", "provisioningProfile"); - var obj = Xcode.provisioningProfilePlistContents(dataFile); - if (obj && obj.data && (obj.data.UUID === query || obj.data.Name === query)) { - console.log("Using provisioning profile: " + obj.filePath); - artifacts.push({ - filePath: obj.fileName, - fileTags: ["xcode.provisioningprofile.main"], - qbs: { _inputFilePath: obj.filePath } - }); - - artifacts.push({ - filePath: obj.fileName + ".xml", - fileTags: ["xcode.provisioningprofile.data.main"], - qbs: { _inputFilePath: dataFile } - }); - } - } - return artifacts; - } - - prepare: { - var cmds = []; - for (var tag in outputs) { - for (var i = 0; i < outputs[tag].length; ++i) { - var output = outputs[tag][i]; - var cmd = new JavaScriptCommand(); - cmd.silent = true; - cmd.inputFilePath = output.qbs._inputFilePath; // there's no such prop in qbs, see QBS-754 - cmd.outputFilePath = output.filePath; - cmd.sourceCode = function() { - File.copy(inputFilePath, outputFilePath); - }; - cmds.push(cmd); - } - } - return cmds; - } - } - - Rule { - inputs: ["xcode.entitlements", "xcode.provisioningprofile.data.main"] - - Artifact { - filePath: FileInfo.joinPaths(product.destinationDirectory, - product.targetName + ".xcent") - fileTags: ["xcent"] - } - - prepare: { - var cmd = new JavaScriptCommand(); - cmd.description = "generating entitlements"; - cmd.highlight = "codegen"; - cmd.bundleIdentifier = product.moduleProperty("bundle", "identifier"); - cmd.signingEntitlements = inputs["xcode.entitlements"] - ? inputs["xcode.entitlements"].map(function (a) { return a.filePath; }) - : []; - cmd.platformPath = ModUtils.moduleProperty(product, "platformPath"); - cmd.sdkPath = ModUtils.moduleProperty(product, "sdkPath"); - cmd.sourceCode = function() { - var i; - var provData = Xcode.provisioningProfilePlistContents(input.filePath); - if (provData) - provData = provData.data; - - var aggregateEntitlements = {}; - - // Start building up an aggregate entitlements plist from the files in the SDKs, - // which contain placeholders in the same manner as Info.plist - function entitlementsFileContents(path) { - return File.exists(path) ? BundleTools.infoPlistContents(path) : undefined; - } - var entitlementsSources = [ - entitlementsFileContents(FileInfo.joinPaths(platformPath, "Entitlements.plist")), - entitlementsFileContents(FileInfo.joinPaths(sdkPath, "Entitlements.plist")) - ]; - - for (i = 0; i < signingEntitlements.length; ++i) { - entitlementsSources.push(entitlementsFileContents(signingEntitlements[i])); - } - - for (i = 0; i < entitlementsSources.length; ++i) { - var contents = entitlementsSources[i]; - for (var key in contents) { - if (contents.hasOwnProperty(key)) - aggregateEntitlements[key] = contents[key]; - } - } - - contents = provData["Entitlements"]; - for (key in contents) { - if (contents.hasOwnProperty(key) && !aggregateEntitlements.hasOwnProperty(key)) - aggregateEntitlements[key] = contents[key]; - } - - // Expand entitlements variables with data from the provisioning profile - var env = { - "AppIdentifierPrefix": provData["ApplicationIdentifierPrefix"] + ".", - "CFBundleIdentifier": bundleIdentifier - }; - DarwinTools.expandPlistEnvironmentVariables(aggregateEntitlements, env, true); - - // Anything with an undefined or otherwise empty value should be removed - // Only JSON-formatted plists can have null values, other formats error out - // This also follows Xcode behavior - DarwinTools.cleanPropertyList(aggregateEntitlements); - - var plist = new PropertyList(); - try { - plist.readFromObject(aggregateEntitlements); - plist.writeToFile(outputs.xcent[0].filePath, "xml1"); - } finally { - plist.clear(); - } - }; - return [cmd]; - } - } } |