diff options
Diffstat (limited to 'share/qbs/modules')
-rw-r--r-- | share/qbs/modules/codesign/CodeSignModule.qbs | 6 | ||||
-rw-r--r-- | share/qbs/modules/codesign/apple.qbs | 2 | ||||
-rw-r--r-- | share/qbs/modules/codesign/codesign.js | 119 | ||||
-rw-r--r-- | share/qbs/modules/codesign/signtool.qbs | 94 | ||||
-rw-r--r-- | share/qbs/modules/cpp/msvc.js | 7 | ||||
-rw-r--r-- | share/qbs/modules/cpp/windows-msvc-base.qbs | 24 |
6 files changed, 245 insertions, 7 deletions
diff --git a/share/qbs/modules/codesign/CodeSignModule.qbs b/share/qbs/modules/codesign/CodeSignModule.qbs index 1951ec374..2115baebf 100644 --- a/share/qbs/modules/codesign/CodeSignModule.qbs +++ b/share/qbs/modules/codesign/CodeSignModule.qbs @@ -43,5 +43,11 @@ Module { property string codesignPath: codesignName property stringList codesignFlags + property string signingTimestamp + PropertyOptions { + name: "signingTimestamp" + description: "URL of the RFC 3161 time stamp server." + } + property bool _canSignArtifacts: false // whether can sign individual actifacts } diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs index 31e2c366d..565d29080 100644 --- a/share/qbs/modules/codesign/apple.qbs +++ b/share/qbs/modules/codesign/apple.qbs @@ -96,7 +96,7 @@ CodeSignModule { } } - property string signingTimestamp: "none" + signingTimestamp: "none" property string provisioningProfile PropertyOptions { diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js index bf7e95224..5aa303c9c 100644 --- a/share/qbs/modules/codesign/codesign.js +++ b/share/qbs/modules/codesign/codesign.js @@ -202,6 +202,75 @@ function findBestProvisioningProfile(product, files) { } } +/** + * Finds out the search paths for the `signtool.exe` utility supplied with + * the Windows SDK's. + */ +function findBestSignToolSearchPaths(arch) { + var searchPaths = []; + var keys = [ + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows" + ]; + for (var keyIndex = 0; keyIndex < keys.length; ++keyIndex) { + var re = /^v([0-9]+)\.([0-9]+)$/; + var groups = Utilities.nativeSettingGroups(keys[keyIndex]).filter(function(version) { + return version.match(re); + }); + + groups.sort(function(a, b) { + return Utilities.versionCompare(b.substring(1), a.substring(1)); + }); + + function addSearchPath(searchPath) { + if (File.exists(searchPath) && !searchPaths.contains(searchPath)) { + searchPaths.push(searchPath); + return true; + } + return false; + } + + for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) { + var fullKey = keys[keyIndex] + "\\" + groups[groupIndex]; + var fullVersion = Utilities.getNativeSetting(fullKey, "ProductVersion"); + if (fullVersion) { + var installRoot = FileInfo.cleanPath( + Utilities.getNativeSetting(fullKey, "InstallationFolder")); + if (installRoot) { + // Try to add the architecture-independent path at first. + var searchPath = FileInfo.joinPaths(installRoot, "App Certification Kit"); + if (!addSearchPath(searchPath)) { + // Try to add the architecture-dependent paths at second. + var binSearchPath = FileInfo.joinPaths(installRoot, "bin/" + fullVersion); + if (!File.exists(binSearchPath)) { + binSearchPath += ".0"; + if (!File.exists(binSearchPath)) + continue; + } + + function kitsArchitectureSubDirectory(arch) { + if (arch === "x86") + return "x86"; + else if (arch === "x86_64") + return "x64"; + else if (arch.startsWith("arm64")) + return "arm64"; + else if (arch.startsWith("arm")) + return "arm"; + } + + var archDir = kitsArchitectureSubDirectory(arch); + searchPath = FileInfo.joinPaths(binSearchPath, archDir); + addSearchPath(searchPath); + } + } + } + } + } + + return searchPaths; +} + function prepareSign(project, product, inputs, outputs, input, output) { var cmd, cmds = []; @@ -243,10 +312,10 @@ function prepareSign(project, product, inputs, outputs, input, output) { args.push("--force"); args.push("--sign", actualSigningIdentity.SHA1); - // If signingTimestamp is undefined, do not specify the flag at all - + // If signingTimestamp is undefined or empty, do not specify the flag at all - // this uses the system-specific default behavior var signingTimestamp = product.codesign.signingTimestamp; - if (signingTimestamp !== undefined) { + if (signingTimestamp) { // If signingTimestamp is an empty string, specify the flag but do // not specify a value - this uses a default Apple-provided server var flag = "--timestamp"; @@ -349,3 +418,49 @@ function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath, key "CN=Android Debug,O=Android,C=US"]; return Process.shellQuote(keytoolFilePath, args); } + +function prepareSigntool(project, product, inputs, outputs, input, output) { + var cmd, cmds = []; + + if (!product.codesign.enableCodeSigning) + return cmds; + + var args = ["sign"].concat(product.codesign.codesignFlags || []); + + var subjectName = product.codesign.subjectName; + if (subjectName) + args.push("/n", subjectName); + + var rootSubjectName = product.codesign.rootSubjectName; + if (rootSubjectName) + args.push("/r", rootSubjectName); + + var hashAlgorithm = product.codesign.hashAlgorithm; + if (hashAlgorithm) + args.push("/fd", hashAlgorithm); + + var signingTimestamp = product.codesign.signingTimestamp; + if (signingTimestamp) + args.push("/tr", signingTimestamp); + + var certificatePath = product.codesign.certificatePath; + if (certificatePath) + args.push("/f", certificatePath); + + var certificatePassword = product.codesign.certificatePassword; + if (certificatePassword) + args.push("/p", certificatePassword); + + var crossCertificatePath = product.codesign.crossCertificatePath; + if (crossCertificatePath) + args.push("/ac", crossCertificatePath); + + var outputArtifact = outputs["codesign.signed_artifact"][0]; + args.push(outputArtifact.filePath); + + cmd = new Command(product.codesign.codesignPath, args); + cmd.description = "signing " + outputArtifact.fileName; + cmd.highlight = "linker"; + cmds.push(cmd); + return cmds; +} diff --git a/share/qbs/modules/codesign/signtool.qbs b/share/qbs/modules/codesign/signtool.qbs new file mode 100644 index 000000000..13933c6f6 --- /dev/null +++ b/share/qbs/modules/codesign/signtool.qbs @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.ModUtils +import qbs.Probes +import "codesign.js" as CODESIGN + +CodeSignModule { + condition: qbs.targetOS.contains("windows") && qbs.hostOS.contains("windows") + + _canSignArtifacts: true + + Probes.BinaryProbe { + id: signtoolProbe + names: [codesignName] + searchPaths: CODESIGN.findBestSignToolSearchPaths(qbs.hostArchitecture) + } + + codesignName: "signtool" + codesignPath: signtoolProbe.filePath + + property string subjectName + PropertyOptions { + name: "subjectName" + description: "Name of the subject of the signing certificate." + } + + property string rootSubjectName + PropertyOptions { + name: "rootSubjectName" + description: "Name of the subject of the root certificate that the signing " + + "certificate must chain to." + } + + property string hashAlgorithm + PropertyOptions { + name: "hashAlgorithm" + description: "Name of the hash algorithm used on the signing certificate." + allowedValues: ["sha1", "sha256", "sha384", "sha512"] + } + + property path certificatePath + PropertyOptions { + name: "certificatePath" + description: "Path to the signing certificate PFX file." + } + + property path certificatePassword + PropertyOptions { + name: "certificatePassword" + description: "Password to use when opening a certificate PFX file." + } + + property path crossCertificatePath + PropertyOptions { + name: "crossCertificatePath" + description: "Path to the additional certificate CER file." + } + + validate: { + if (enableCodeSigning && !File.exists(codesignPath)) { + throw ModUtils.ModuleError("Could not find 'signtool' utility"); + } + } +} diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index 566059610..9f3d20282 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.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"); @@ -655,6 +656,12 @@ function prepareLinker(project, product, inputs, outputs, input, output) { commands.push(cmd); } + if (product.cpp.shouldSignArtifacts) { + Array.prototype.push.apply( + commands, Codesign.prepareSigntool( + project, product, inputs, outputs, input, output)); + } + return commands; } diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index 81fe48385..c45ec5ec3 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -40,6 +40,8 @@ import 'msvc.js' as MSVC CppModule { condition: false + Depends { name: "codesign" } + windowsApiCharacterSet: "unicode" platformDefines: { var defines = base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) @@ -100,6 +102,8 @@ CppModule { property var buildEnv + readonly property bool shouldSignArtifacts: codesign.enableCodeSigning + setupBuildEnvironment: { for (var key in product.cpp.buildEnv) { var v = new ModUtils.EnvironmentVariable(key, ';'); @@ -192,10 +196,16 @@ CppModule { inputs: ['obj', 'native.pe.manifest', 'def'] inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_app"] - outputFileTags: ["application", "debuginfo_app", "mem_map"] + outputFileTags: { + var tags = ["application", "debuginfo_app", "mem_map"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var app = { - fileTags: ["application"], + fileTags: ["application"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), filePath: FileInfo.joinPaths( product.destinationDirectory, PathTools.applicationFilePath(product)) @@ -230,11 +240,17 @@ CppModule { inputs: ['obj', 'native.pe.manifest', 'def'] inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_dll"] - outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"] + outputFileTags: { + var tags = ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"]; + if (shouldSignArtifacts) + tags.push("codesign.signed_artifact"); + return tags; + } outputArtifacts: { var artifacts = [ { - fileTags: ["dynamiclibrary"], + fileTags: ["dynamiclibrary"].concat( + product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []), filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product) }, { |