From af7d67396edadebd3a1f38623453f1af5cae440c Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 31 Mar 2021 16:33:32 +0300 Subject: baremetal: Pass externalStaticLibraries() test for KEIL C51/C251/C166 Toolchains C51, C251, C166 do not support configuring library search paths to link. They support linking with libraries specified with full absolute paths to them. To work around this we supplement the absolute library paths using the cpp.libraryPaths property, if the library is specified without an absolute or relative path. Change-Id: Ic11fd8b87356b3a07ba5fd5c9763c8df39d0d4ac Reviewed-by: Ivan Komissarov --- share/qbs/modules/cpp/keil.js | 29 +++++++++++++++------- .../external-static-libraries.qbs | 14 ----------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 567b0ddd3..372b08e4b 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -970,24 +970,37 @@ 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). if (allObjectPaths.length > 0) @@ -1009,8 +1022,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(",")); diff --git a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs index 6fbbb8647..fffb6a03d 100644 --- a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs +++ b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs @@ -2,20 +2,6 @@ import "../BareMetalApplication.qbs" as BareMetalApplication import "../BareMetalStaticLibrary.qbs" as BareMetalStaticLibrary Project { - condition: { - // The KEIL C51/C251/C166 toolchains support only a - // full paths to the external libraries. - if (qbs.toolchainType === "keil") { - if (qbs.architecture === "mcs51" - || qbs.architecture === "mcs251" - || qbs.architecture === "c166") { - console.info("unsupported toolset: %%" - + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); - return false; - } - } - return true; - } property string outputLibrariesDirectory: sourceDirectory + "/libs" BareMetalStaticLibrary { name: "lib-a" -- cgit v1.2.3 From ef7b698b74b4f77b2dd8135ff8f4451aca727763 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Sat, 3 Apr 2021 17:37:03 +0300 Subject: baremetal: Improve linkerMapFile() test Right now we can use the cpp.linkerMapSuffix property to find out the currently used linker map suffix. Also we can set a custom cpp.linkerMapSuffix value to make sure that this applies to the module. In addition, the generation of a custom linker map file for SDCC toolchain has been fixed. Change-Id: I8798cd6bea0ab6b5ea9728400827b8c98b11ba7b Reviewed-by: Ivan Komissarov Reviewed-by: Denis Shienkov --- share/qbs/modules/cpp/sdcc.js | 21 +++++++-- .../testdata-baremetal/linker-map/linker-map.qbs | 6 +-- tests/auto/blackbox/tst_blackboxbaremetal.cpp | 54 ++++++++++++---------- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index a47063401..4010e38db 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -641,6 +641,12 @@ function prepareLinker(project, product, inputs, outputs, input, output) { }; cmds.push(cmd); } + + function buildLinkerMapFilePath(target, suffix) { + return FileInfo.joinPaths(FileInfo.path(target.filePath), + FileInfo.completeBaseName(target.fileName) + suffix); + } + // 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, @@ -649,13 +655,22 @@ function prepareLinker(project, product, inputs, outputs, input, output) { // linking completes. if (!product.cpp.generateLinkerMapFile) { cmd = new JavaScriptCommand(); - cmd.mapFilePath = FileInfo.joinPaths( - FileInfo.path(target.filePath), - FileInfo.completeBaseName(target.fileName) + product.cpp.linkerMapSuffix); + cmd.mapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix); cmd.silent = true; cmd.sourceCode = function() { File.remove(mapFilePath); }; cmds.push(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. + if (product.cpp.generateLinkerMapFile && (product.cpp.linkerMapSuffix !== ".map")) { + 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); }; + cmds.push(cmd); + } return cmds; } diff --git a/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs b/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs index 676221e11..fe93ac144 100644 --- a/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs +++ b/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs @@ -1,10 +1,8 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { - condition: { - console.info("current toolset: %%" - + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); - return true; + property bool dummy: { + console.info("linker map suffix: %%" + cpp.linkerMapSuffix + "%%"); } files: ["main.c"] } diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp index a5feedd48..1e300e246 100644 --- a/tests/auto/blackbox/tst_blackboxbaremetal.cpp +++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp @@ -59,6 +59,16 @@ static bool extractCompilerIncludePaths(const QByteArray &output, QStringList &c return true; } +static bool extractQuitedValue(const QByteArray &output, QString &pattern) +{ + const QRegularExpression re("%%(.+)%%"); + const QRegularExpressionMatch match = re.match(output); + if (!match.hasMatch()) + return false; + pattern = match.captured(1); + return true; +} + static QByteArray unsupportedToolsetMessage(const QByteArray &output) { QByteArray toolchain; @@ -68,17 +78,6 @@ static QByteArray unsupportedToolsetMessage(const QByteArray &output) + "' for architecture '" + architecture + "'"; } -static QString linkerMapFileExtension(const QByteArray &toolchain, const QByteArray &architecture) -{ - if (toolchain == "keil") { - if (architecture == "mcs51") - return QStringLiteral(".m51"); - if (architecture == "c166") - return QStringLiteral(".m66"); - } - return QStringLiteral(".map"); -} - TestBlackboxBareMetal::TestBlackboxBareMetal() : TestBlackboxBase (SRCDIR "/testdata-baremetal", "blackbox-baremetal") { @@ -215,33 +214,38 @@ void TestBlackboxBareMetal::compilerListingFiles() void TestBlackboxBareMetal::linkerMapFile_data() { QTest::addColumn("generateMap"); - QTest::newRow("do-not-generate-linker-map") << false; - QTest::newRow("generate-linker-map") << true; + QTest::addColumn("customMapSuffix"); + QTest::newRow("do-not-generate-linker-map") << false << ""; + QTest::newRow("generate-default-linker-map") << true << ""; + QTest::newRow("generate-custom-linker-map") << true << ".mmm"; } void TestBlackboxBareMetal::linkerMapFile() { QFETCH(bool, generateMap); + QFETCH(QString, customMapSuffix); QDir::setCurrent(testDataDir + "/linker-map"); rmDirR(relativeBuildDir()); - const QStringList args = {QStringLiteral("modules.cpp.generateLinkerMapFile:%1") - .arg(generateMap ? "true" : "false")}; + QStringList args = {QStringLiteral("modules.cpp.generateLinkerMapFile:%1") + .arg(generateMap ? "true" : "false")}; + if (!customMapSuffix.isEmpty()) + args << QStringLiteral("modules.cpp.linkerMapSuffix:%1").arg(customMapSuffix); + QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0); - if (m_qbsStdout.contains("unsupported toolset:")) - QSKIP(unsupportedToolsetMessage(m_qbsStdout)); - if (!m_qbsStdout.contains("current toolset:")) - QFAIL("No current toolset pattern exists"); + if (!m_qbsStdout.contains("linker map suffix:")) + QFAIL("No current linker map suffix pattern exists"); - QByteArray toolchain; - QByteArray architecture; - if (!extractToolset(m_qbsStdout, toolchain, architecture)) - QFAIL("Unable to extract current toolset"); + QString linkerMapSuffix; + if (!extractQuitedValue(m_qbsStdout, linkerMapSuffix)) + QFAIL("Unable to extract current linker map suffix"); + + if (!customMapSuffix.isEmpty()) + QCOMPARE(linkerMapSuffix, customMapSuffix); QCOMPARE(runQbs(QbsRunParameters(args)), 0); const QString productBuildDir = relativeProductBuildDir("linker-map"); - const auto extension = linkerMapFileExtension(toolchain, architecture); - const QString linkerMap = productBuildDir + "/linker-map" + extension; + const QString linkerMap = productBuildDir + "/linker-map" + linkerMapSuffix; QCOMPARE(regularFileExists(linkerMap), generateMap); } -- cgit v1.2.3 From 44ef034472337abdb894f76f593da6648f9782d5 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 5 Apr 2021 12:14:34 +0300 Subject: Share cpp::{assembler|compiler}ListingSuffix properties It makes sense to add the cpp.assemblerListingSuffix and the cpp.compilerListingSuffix properties to the base CppModule due the following reasons: 1. It is possible that the user wants to change the extension for the generated listing files, which makes working with Qbs more flexible. 2. It will be easier to write an autotests that check the generation of the listing files for a bare metal platforms, where listing files can have various extensions such as ".lst", ".ls", and so forth. Change-Id: I9989288bff0659dd3e8b7a443d0354bb78475270 Reviewed-by: Ivan Komissarov --- doc/reference/modules/cpp-module.qdoc | 16 ++++++++++++++++ share/qbs/modules/cpp/CppModule.qbs | 2 ++ share/qbs/modules/cpp/iar.js | 12 +++++++++--- share/qbs/modules/cpp/iar.qbs | 6 ++---- share/qbs/modules/cpp/keil.js | 13 ++++++++++--- share/qbs/modules/cpp/keil.qbs | 6 ++---- share/qbs/modules/cpp/sdcc.js | 15 +++++++++++---- share/qbs/modules/cpp/sdcc.qbs | 5 ++--- share/qbs/modules/cpp/windows-msvc-base.qbs | 3 ++- 9 files changed, 56 insertions(+), 22 deletions(-) diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc index 1b09320f0..56f41af5d 100644 --- a/doc/reference/modules/cpp-module.qdoc +++ b/doc/reference/modules/cpp-module.qdoc @@ -633,6 +633,22 @@ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".map" */ +/*! + \qmlproperty string cpp::compilerListingSuffix + + A string to append to the generated compiler listing files. + + \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".lst" +*/ + +/*! + \qmlproperty string cpp::assemblerListingSuffix + + A string to append to the generated assembler listing files. + + \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".lst" +*/ + /*! \qmlproperty pathList cpp::prefixHeaders \since Qbs 1.0.1 diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 761e8be85..39077bec8 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -188,6 +188,8 @@ Module { 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/iar.js b/share/qbs/modules/cpp/iar.js index a1f1a9a88..416de7ee2 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -612,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 + ".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; diff --git a/share/qbs/modules/cpp/iar.qbs b/share/qbs/modules/cpp/iar.qbs index 6c00f8f36..9709695c1 100644 --- a/share/qbs/modules/cpp/iar.qbs +++ b/share/qbs/modules/cpp/iar.qbs @@ -99,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) } @@ -114,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 372b08e4b..2b35007b3 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -582,19 +582,26 @@ 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.cpp.compilerListingSuffix + }); + } else if (!isCompilerArtifacts && input.cpp.generateAssemblerListingFiles) { + artifacts.push({ + fileTags: ["lst"], + filePath: Utilities.getHash(input.baseDir) + "/" + + (isArmCCCompiler(input.cpp.compilerPath) ? input.baseName : input.fileName) + + input.cpp.assemblerListingSuffix }); } return artifacts; diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs index f1f3b50e8..ea99b589c 100644 --- a/share/qbs/modules/cpp/keil.qbs +++ b/share/qbs/modules/cpp/keil.qbs @@ -103,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) } @@ -118,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/sdcc.js b/share/qbs/modules/cpp/sdcc.js index 4010e38db..ca085a421 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -239,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) + "/" @@ -270,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; @@ -628,6 +634,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { cmd = new JavaScriptCommand(); cmd.objectPaths = inputs.obj.map(function(a) { return a.filePath; }); cmd.objectSuffix = product.cpp.objectSuffix; + cmd.listingSuffix = product.cpp.compilerListingSuffix cmd.silent = true; cmd.sourceCode = function() { objectPaths.forEach(function(objectPath) { @@ -635,7 +642,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { return; // Skip the assembler objects. var listingPath = FileInfo.joinPaths( FileInfo.path(objectPath), - FileInfo.completeBaseName(objectPath) + ".lst"); + FileInfo.completeBaseName(objectPath) + listingSuffix); File.remove(listingPath); }); }; diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs index 8b631d4e5..c5a0893d4 100644 --- a/share/qbs/modules/cpp/sdcc.qbs +++ b/share/qbs/modules/cpp/sdcc.qbs @@ -100,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) } @@ -114,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-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index b25fdf159..f5fde9556 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -162,7 +162,8 @@ CppModule { 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; -- cgit v1.2.3 From 4fc55903fac11719641517c55454c9c0f0a86951 Mon Sep 17 00:00:00 2001 From: Richard Weickelt Date: Sat, 27 Mar 2021 17:36:54 +0100 Subject: Add changelog for the 1.18.2 release Change-Id: I950ec79e270e09407312fdad958f067f1db6b32c Reviewed-by: Christian Kandeler --- changelogs/changes-1.18.2.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 changelogs/changes-1.18.2.md diff --git a/changelogs/changes-1.18.2.md b/changelogs/changes-1.18.2.md new file mode 100644 index 000000000..5019e491f --- /dev/null +++ b/changelogs/changes-1.18.2.md @@ -0,0 +1,41 @@ +# C/C++ Support + +* qbs-setup-toolchains is now able to detect clang-cl properly +* The Library and Include probes take more paths into account on Linux to better + support containerization systems such as Flatpak. +* Xcode autodetection now uses xcode-select to find Xcode on the system. + + +# Protocol Buffers Support (Protobuf Module) + +* A missing nanopb generator file extension on windows has been added. +* The problem that property _libraryName was incorrect when protobuf was not + found has been fixed. + + +# Android Support + +* An assertion when building Android applications using additional java classes + with native methods has been fixed (QBS-1628). + + +# Qt Support + +* A problem related to handling Qt6EntryPoint in the Qt module provider has been + fixed. + + +# Infrastructure + +* A Qt4 docker image for basic testing has been added. + + +# Contributors + +* Christian Kandeler +* Eike Ziller +* Ivan Komissarov +* Jan Blackquill +* Kai Dohmen +* Raphaël Cotty +* Richard Weickelt -- cgit v1.2.3 From b11188646b2af369ad09f4e97b81f9fa4d50466c Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Mon, 5 Apr 2021 16:39:28 +0200 Subject: Avoid using unicode characters in apple.qbs Some editors struggle with them Change-Id: I61672b6ca83facd7a2b2bf2fa763245aee94bce5 Reviewed-by: Denis Shienkov Reviewed-by: Christian Kandeler --- share/qbs/modules/codesign/apple.qbs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs index 06f07d72d..31e2c366d 100644 --- a/share/qbs/modules/codesign/apple.qbs +++ b/share/qbs/modules/codesign/apple.qbs @@ -125,7 +125,7 @@ CodeSignModule { 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." + + "matching '" + signingIdentity + "' were found." + CodeSign.humanReadableIdentitySummary(identities); } @@ -240,17 +240,17 @@ CodeSignModule { } }); } else if (uuid) { - throw "Your build settings specify a provisioning profile with the UUID “" - + uuid + "”, however, no such provisioning profile was found."; + 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 “" + console.warn("No provisioning profiles matching the bundle identifier '" + product.bundle.identifier - + "” were found."); + + "' were found."); } else { console.warn("No provisioning profiles matching an applicable signing " + "identity were found."); @@ -260,13 +260,13 @@ CodeSignModule { 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.") + + "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."); + console.warn("No non-expired provisioning profiles were found."); } } } -- cgit v1.2.3 From 475cf5064e04009ffdc730d7915b52af58c3dd69 Mon Sep 17 00:00:00 2001 From: Max Bespalov Date: Tue, 6 Apr 2021 12:42:19 +0300 Subject: Android: Fix aapt compiling command on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building appt2 package on windows aapt expects to see paths with windows separators. If path isn't converted you get "bad resource path" error. https://android.googlesource.com/platform/frameworks/base/+/9ba47d8/tools/aapt2/compile/Compile.cpp#59 Change-Id: Iabacb020920310533b034138e47d1839b845573a Reviewed-by: Raphaël Cotty Reviewed-by: Ivan Komissarov --- share/qbs/modules/Android/sdk/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index 9511ae9de..264ad2da7 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -174,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; -- cgit v1.2.3 From 5720a12ffb1ede3b9bac039f737fe868387221b3 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 6 Apr 2021 10:04:00 +0300 Subject: baremetal: Improve 'compiler-listing' test We do not need multiple test data instances (*.qbs) for each property, because we can pass the required properties from the C++ code directly. Also now we can know about the compiler listing file suffix directly, through the cpp.compilerListingSuffix property. Change-Id: I644277458e3ae460cbfb6bba4a24583d9e6ba3e1 Reviewed-by: Ivan Komissarov --- .../compiler-listing/compiler-listing.qbs | 16 ++++++++++ .../testdata-baremetal/compiler-listing/fun.c | 4 +++ .../testdata-baremetal/compiler-listing/main.c | 6 ++++ .../do-not-generate-compiler-listing.qbs | 16 ---------- .../do-not-generate-compiler-listing/fun.c | 4 --- .../do-not-generate-compiler-listing/main.c | 6 ---- .../generate-compiler-listing/fun.c | 4 --- .../generate-compiler-listing.qbs | 16 ---------- .../generate-compiler-listing/main.c | 6 ---- tests/auto/blackbox/tst_blackboxbaremetal.cpp | 36 +++++++++++++++------- 10 files changed, 51 insertions(+), 63 deletions(-) create mode 100644 tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs create mode 100644 tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c create mode 100644 tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c delete mode 100644 tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/do-not-generate-compiler-listing.qbs delete mode 100644 tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/fun.c delete mode 100644 tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/main.c delete mode 100644 tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/fun.c delete mode 100644 tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/generate-compiler-listing.qbs delete mode 100644 tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/main.c diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs new file mode 100644 index 000000000..0adfbe37b --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs @@ -0,0 +1,16 @@ +import "../BareMetalApplication.qbs" as BareMetalApplication + +BareMetalApplication { + condition: { + if (!qbs.toolchain.contains("gcc")) { + if (cpp.compilerName.startsWith("armcc")) + console.info("using short listing file names"); + console.info("compiler listing suffix: %%" + cpp.compilerListingSuffix + "%%"); + return true; + } + console.info("unsupported toolset: %%" + + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); + return false; + } + files: ["main.c", "fun.c"] +} diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c b/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c new file mode 100644 index 000000000..3b8c8f2f4 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c @@ -0,0 +1,4 @@ +int f(void) +{ + return 0; +} diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c b/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c new file mode 100644 index 000000000..2c3d7726c --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c @@ -0,0 +1,6 @@ +extern int f(void); + +int main(void) +{ + return f(); +} diff --git a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/do-not-generate-compiler-listing.qbs b/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/do-not-generate-compiler-listing.qbs deleted file mode 100644 index 1bc4ba208..000000000 --- a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/do-not-generate-compiler-listing.qbs +++ /dev/null @@ -1,16 +0,0 @@ -import "../BareMetalApplication.qbs" as BareMetalApplication - -BareMetalApplication { - condition: { - if (!qbs.toolchain.contains("gcc")) { - if (cpp.compilerName.startsWith("armcc")) - console.info("using short listing file names"); - return true; - } - console.info("unsupported toolset: %%" - + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); - return false; - } - cpp.generateCompilerListingFiles: false - files: ["main.c", "fun.c"] -} diff --git a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/fun.c b/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/fun.c deleted file mode 100644 index 3b8c8f2f4..000000000 --- a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/fun.c +++ /dev/null @@ -1,4 +0,0 @@ -int f(void) -{ - return 0; -} diff --git a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/main.c b/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/main.c deleted file mode 100644 index 2c3d7726c..000000000 --- a/tests/auto/blackbox/testdata-baremetal/do-not-generate-compiler-listing/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int f(void); - -int main(void) -{ - return f(); -} diff --git a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/fun.c b/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/fun.c deleted file mode 100644 index 3b8c8f2f4..000000000 --- a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/fun.c +++ /dev/null @@ -1,4 +0,0 @@ -int f(void) -{ - return 0; -} diff --git a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/generate-compiler-listing.qbs b/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/generate-compiler-listing.qbs deleted file mode 100644 index a6731d224..000000000 --- a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/generate-compiler-listing.qbs +++ /dev/null @@ -1,16 +0,0 @@ -import "../BareMetalApplication.qbs" as BareMetalApplication - -BareMetalApplication { - condition: { - if (!qbs.toolchain.contains("gcc")) { - if (cpp.compilerName.startsWith("armcc")) - console.info("using short listing file names"); - return true; - } - console.info("unsupported toolset: %%" - + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); - return false; - } - cpp.generateCompilerListingFiles: true - files: ["main.c", "fun.c"] -} diff --git a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/main.c b/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/main.c deleted file mode 100644 index 2c3d7726c..000000000 --- a/tests/auto/blackbox/testdata-baremetal/generate-compiler-listing/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int f(void); - -int main(void) -{ - return f(); -} diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp index 1e300e246..b027a4e70 100644 --- a/tests/auto/blackbox/tst_blackboxbaremetal.cpp +++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp @@ -186,28 +186,42 @@ void TestBlackboxBareMetal::defines() void TestBlackboxBareMetal::compilerListingFiles_data() { - QTest::addColumn("testPath"); QTest::addColumn("generateListing"); - QTest::newRow("do-not-generate-compiler-listing") << "/do-not-generate-compiler-listing" << false; - QTest::newRow("generate-compiler-listing") << "/generate-compiler-listing" << true; + QTest::newRow("do-not-generate-compiler-listing") << false; + QTest::newRow("generate-compiler-listing") << true; } void TestBlackboxBareMetal::compilerListingFiles() { - QFETCH(QString, testPath); QFETCH(bool, generateListing); - QDir::setCurrent(testDataDir + testPath); - QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0); + QDir::setCurrent(testDataDir + "/compiler-listing"); + + rmDirR(relativeBuildDir()); + QStringList args = {QStringLiteral("modules.cpp.generateCompilerListingFiles:%1") + .arg(generateListing ? "true" : "false")}; + + QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0); if (m_qbsStdout.contains("unsupported toolset:")) QSKIP(unsupportedToolsetMessage(m_qbsStdout)); - QCOMPARE(runQbs(), 0); + if (!m_qbsStdout.contains("compiler listing suffix:")) + QFAIL("No current compiler listing suffix pattern exists"); + + QString compilerListingSuffix; + if (!extractQuitedValue(m_qbsStdout, compilerListingSuffix)) + QFAIL("Unable to extract current compiler listing suffix"); + const bool isShortListingNames = m_qbsStdout.contains("using short listing file names"); - const QString productName = testPath.mid(1); - const QString productBuildDir = relativeProductBuildDir(productName); + + QCOMPARE(runQbs(QbsRunParameters(args)), 0); + const QString productBuildDir = relativeProductBuildDir("compiler-listing"); const QString hash = inputDirHash("."); - const QString mainListing = productBuildDir + "/" + hash + (isShortListingNames ? "/main.lst" : "/main.c.lst"); + const QString mainListing = productBuildDir + "/" + hash + + (isShortListingNames ? "/main" : "/main.c") + + compilerListingSuffix; QCOMPARE(regularFileExists(mainListing), generateListing); - const QString funListing = productBuildDir + "/" + hash + (isShortListingNames ? "/fun.lst" : "/fun.c.lst"); + const QString funListing = productBuildDir + "/" + hash + + (isShortListingNames ? "/fun" : "/fun.c") + + compilerListingSuffix; QCOMPARE(regularFileExists(funListing), generateListing); } -- cgit v1.2.3 From d8cd1d151528da700ef0789c77e2f3dfdd5e17bb Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 7 Apr 2021 15:18:19 +0300 Subject: baremetal: Move post linker commands to separate functions for SDCC It makes sense to move an additional post-build linker commands (that delete or change the linker map files, delete the listing files) into separate functions. Change-Id: I636347a9417f76f3f3dcfa000518437721357431 Reviewed-by: Christian Kandeler --- share/qbs/modules/cpp/sdcc.js | 125 ++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 53 deletions(-) diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index ca085a421..8635d468b 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -553,6 +553,11 @@ 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 @@ -560,7 +565,7 @@ function archiverFlags(project, product, inputs, outputs) { function patchObjectFiles(project, product, inputs, outputs, input, output) { var isWindows = input.qbs.hostOS.contains("windows"); if (isWindows && input.cpp.debugInformation) { - cmd = new JavaScriptCommand(); + var cmd = new JavaScriptCommand(); cmd.objectPath = outputs.obj[0].filePath; cmd.silent = true; cmd.sourceCode = function() { @@ -582,6 +587,64 @@ function patchObjectFiles(project, product, inputs, outputs, input, output) { } } +// 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. +// 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) { + if (!product.cpp.generateCompilerListingFiles) { + var 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); + }); + }; + return cmd; + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var cmds = []; var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn); @@ -616,68 +679,24 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl 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.listingSuffix = product.cpp.compilerListingSuffix - 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) + listingSuffix); - File.remove(listingPath); - }); - }; + cmd = removeCompilerListingFiles(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } - - function buildLinkerMapFilePath(target, suffix) { - return FileInfo.joinPaths(FileInfo.path(target.filePath), - FileInfo.completeBaseName(target.fileName) + suffix); - } - // 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 = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix); - cmd.silent = true; - cmd.sourceCode = function() { File.remove(mapFilePath); }; + cmd = renameLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(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. - if (product.cpp.generateLinkerMapFile && (product.cpp.linkerMapSuffix !== ".map")) { - 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); }; + + cmd = removeLinkerMapFile(project, product, inputs, outputs, input, output); + if (cmd) cmds.push(cmd); - } return cmds; } -- cgit v1.2.3 From 4b5680cc16f5a190b98b7c0ed42bc43f8e9438b7 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 7 Apr 2021 20:11:29 +0300 Subject: baremetal: Fix generation compiler listing using ARMCC compiler The ARMCC compiler has no options for specifying the name of the output listing file; it only has an options for specifying an output directory. In addition, the generated listing files are in truncated format, e.g. instead of the 'module.{c|cpp}.lst' file will be generated the 'module.lst' file. This behavior complicates the writing of unit tests, and also complicates the implementation if the user wants to change the cpp.compilerListingSuffix property. A workaround is to post-process the compiler listing files after they are generated. In this case, we only need to rename the generated compiler listing file to the desired one. Change-Id: I89c81896711b90b146a94c35d2ec75e296824752 Reviewed-by: Ivan Komissarov --- share/qbs/modules/cpp/keil.js | 56 ++++++++++++++++------ .../compiler-listing/compiler-listing.qbs | 2 - tests/auto/blackbox/tst_blackboxbaremetal.cpp | 8 +--- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 2b35007b3..27e4e12d7 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -593,15 +593,13 @@ function compilerOutputArtifacts(input, isCompilerArtifacts) { artifacts.push({ fileTags: ["lst"], filePath: Utilities.getHash(input.baseDir) + "/" - + (isArmCCCompiler(input.cpp.compilerPath) ? input.baseName : input.fileName) - + input.cpp.compilerListingSuffix + + input.fileName + input.cpp.compilerListingSuffix }); } else if (!isCompilerArtifacts && input.cpp.generateAssemblerListingFiles) { artifacts.push({ fileTags: ["lst"], filePath: Utilities.getHash(input.baseDir) + "/" - + (isArmCCCompiler(input.cpp.compilerPath) ? input.baseName : input.fileName) - + input.cpp.assemblerListingSuffix + + input.fileName + input.cpp.assemblerListingSuffix }); } return artifacts; @@ -1102,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); @@ -1119,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/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs index 0adfbe37b..bcf983c88 100644 --- a/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs +++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs @@ -3,8 +3,6 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { condition: { if (!qbs.toolchain.contains("gcc")) { - if (cpp.compilerName.startsWith("armcc")) - console.info("using short listing file names"); console.info("compiler listing suffix: %%" + cpp.compilerListingSuffix + "%%"); return true; } diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp index b027a4e70..467c95122 100644 --- a/tests/auto/blackbox/tst_blackboxbaremetal.cpp +++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp @@ -210,18 +210,14 @@ void TestBlackboxBareMetal::compilerListingFiles() if (!extractQuitedValue(m_qbsStdout, compilerListingSuffix)) QFAIL("Unable to extract current compiler listing suffix"); - const bool isShortListingNames = m_qbsStdout.contains("using short listing file names"); - QCOMPARE(runQbs(QbsRunParameters(args)), 0); const QString productBuildDir = relativeProductBuildDir("compiler-listing"); const QString hash = inputDirHash("."); const QString mainListing = productBuildDir + "/" + hash - + (isShortListingNames ? "/main" : "/main.c") - + compilerListingSuffix; + + "/main.c" + compilerListingSuffix; QCOMPARE(regularFileExists(mainListing), generateListing); const QString funListing = productBuildDir + "/" + hash - + (isShortListingNames ? "/fun" : "/fun.c") - + compilerListingSuffix; + + "/fun.c" + compilerListingSuffix; QCOMPARE(regularFileExists(funListing), generateListing); } -- cgit v1.2.3 From c3e10d620124f18452f0f059d3f3d2a4a7c632ae Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 8 Apr 2021 17:17:03 +0300 Subject: baremetal: Fix generation of compiler listing files ... with custom extension for SDCC compiler. The SDCC compiler always generates the listing files in the format of 'module.c.lst', and there is no way to disable a generation, or to specify a different name for the listing file. In addition, we cannot change or delete the generated listing file until the linking is complete (this is such a feature of the SDCC compiler). So, to turn off the listing file generation, or to specify a custom listing file extension, we need to do the following extra steps: 1. If the custom cpp.compilerListingSuffix property is set, then we need to make a copy of the generated listing file after the compilation completes. And then to delete the all listing files with the '*.lst' extension after the linking completes. 2. If the cpp.generateCompilerListingFiles property is disabled, then we need to remove the all generated listing files with the '*.lst' extension after the linking completes. Change-Id: Ia235f7e2ebf88695e4648fb894624c7420968079 Reviewed-by: Ivan Komissarov --- share/qbs/modules/cpp/sdcc.js | 59 +++++++++++++++++++-------- tests/auto/blackbox/tst_blackboxbaremetal.cpp | 12 +++++- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index 8635d468b..1904f59fc 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -562,7 +562,7 @@ function buildLinkerMapFilePath(target, suffix) { // * 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 patchObjectFiles(project, product, inputs, outputs, input, output) { +function patchObjectFile(project, product, inputs, outputs, input, output) { var isWindows = input.qbs.hostOS.contains("windows"); if (isWindows && input.cpp.debugInformation) { var cmd = new JavaScriptCommand(); @@ -620,27 +620,48 @@ function renameLinkerMapFile(project, product, inputs, outputs, input, output) { } // It is a workaround which removes the generated listing files -// if it is disabled by cpp.generateCompilerListingFiles property. +// 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) { - if (!product.cpp.generateCompilerListingFiles) { + 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.objectPaths = inputs.obj.map(function(a) { return a.filePath; }); - cmd.objectSuffix = product.cpp.objectSuffix; + 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() { - 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.sourceCode = function() { File.copy(oldListing, newListing); }; return cmd; } } @@ -654,7 +675,11 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli cmd.highlight = "compiler"; cmds.push(cmd); - cmd = patchObjectFiles(project, product, inputs, outputs, input, output); + 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); @@ -670,7 +695,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl cmd.highlight = "compiler"; cmds.push(cmd); - cmd = patchObjectFiles(project, product, inputs, outputs, input, output); + cmd = patchObjectFile(project, product, inputs, outputs, input, output); if (cmd) cmds.push(cmd); diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp index 467c95122..e0a068bc6 100644 --- a/tests/auto/blackbox/tst_blackboxbaremetal.cpp +++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp @@ -187,18 +187,23 @@ void TestBlackboxBareMetal::defines() void TestBlackboxBareMetal::compilerListingFiles_data() { QTest::addColumn("generateListing"); - QTest::newRow("do-not-generate-compiler-listing") << false; - QTest::newRow("generate-compiler-listing") << true; + QTest::addColumn("customListingSuffix"); + QTest::newRow("do-not-generate-compiler-listing") << false << ""; + QTest::newRow("generate-default-compiler-listing") << true << ""; + QTest::newRow("generate-custom-compiler-listing") << true << ".lll"; } void TestBlackboxBareMetal::compilerListingFiles() { QFETCH(bool, generateListing); + QFETCH(QString, customListingSuffix); QDir::setCurrent(testDataDir + "/compiler-listing"); rmDirR(relativeBuildDir()); QStringList args = {QStringLiteral("modules.cpp.generateCompilerListingFiles:%1") .arg(generateListing ? "true" : "false")}; + if (!customListingSuffix.isEmpty()) + args << QStringLiteral("modules.cpp.compilerListingSuffix:%1").arg(customListingSuffix); QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0); if (m_qbsStdout.contains("unsupported toolset:")) @@ -210,6 +215,9 @@ void TestBlackboxBareMetal::compilerListingFiles() if (!extractQuitedValue(m_qbsStdout, compilerListingSuffix)) QFAIL("Unable to extract current compiler listing suffix"); + if (!customListingSuffix.isEmpty()) + QCOMPARE(compilerListingSuffix, customListingSuffix); + QCOMPARE(runQbs(QbsRunParameters(args)), 0); const QString productBuildDir = relativeProductBuildDir("compiler-listing"); const QString hash = inputDirHash("."); -- cgit v1.2.3 From bf0a3750e0845eeba3814a4f16c20d112181e280 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 9 Apr 2021 16:08:14 +0200 Subject: Qt support: Fix possible command line length issue with qmlimportscanner When there's a lot of QML files, we can hit the dreaded Windows limit for maximum command line length. Detect this condition and call the tool repeatedly if necessary. Fixes: QBS-1633 Change-Id: I20d123d6184eab08c5fa280a7c4811a753275f1f Reviewed-by: Ivan Komissarov --- share/qbs/module-providers/Qt/templates/qml.js | 25 +++++++++++++++++++++---- share/qbs/module-providers/Qt/templates/qml.qbs | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) 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 { -- cgit v1.2.3 From 5eb3f70640edb9f1291e97c2ece7d46614a2bf30 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sun, 11 Apr 2021 16:06:54 +0300 Subject: Do not try to generate FwdHeaders when not using bundled Qt.Script Apparently, if the submodule is not present, the Rule is not invoked at all, but it fails on Windows when submodule is there but Perl is not found in PATH Change-Id: I3edb4d3679f4528f05996c630c55c052c8202369 Reviewed-by: Christian Kandeler --- src/lib/scriptengine/scriptengine.qbs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/scriptengine/scriptengine.qbs b/src/lib/scriptengine/scriptengine.qbs index bb9984999..3225ceaac 100644 --- a/src/lib/scriptengine/scriptengine.qbs +++ b/src/lib/scriptengine/scriptengine.qbs @@ -383,6 +383,13 @@ Project { Product { type: ["hpp"] name: "QtScriptFwdHeaders" + condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present + Depends { name: "qbsbuildconfig" } + Depends { + name: "Qt.script" + condition: !qbsbuildconfig.useBundledQtScript + required: false + } Depends { name: "Qt.core" } Group { files: [ -- cgit v1.2.3 From 8e71e73540eeba20bf8866b3b3909784c83d6c3c Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sun, 11 Apr 2021 17:38:46 +0300 Subject: Fix setting PATH on Windows when invoking Qbs The original approach did not work when using NMake or MinGW Makefiles (but worked with Ninja) Change-Id: I1bfdb213b3007b100bb4cdcdbe4deb3935b6e266 Reviewed-by: Denis Shienkov Reviewed-by: Christian Kandeler --- cmake/QbsBuildConfig.cmake | 3 ++- doc/CMakeLists.txt | 5 ++--- share/CMakeLists.txt | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmake/QbsBuildConfig.cmake b/cmake/QbsBuildConfig.cmake index 111778dc7..b6614ce3f 100644 --- a/cmake/QbsBuildConfig.cmake +++ b/cmake/QbsBuildConfig.cmake @@ -60,7 +60,8 @@ function(get_update_path_command var) endif() get_filename_component(_QT_LIBRARY_PATH "${_QTCORE_LIBRARY}" DIRECTORY) get_target_property(_QBS_LIBRARY_PATH qbscore LIBRARY_OUTPUT_DIRECTORY) - set(${var} "PATH=${_QT_LIBRARY_PATH}\;${_QBS_LIBRARY_PATH}\;%PATH%" PARENT_SCOPE) + file(TO_NATIVE_PATH "${_QT_LIBRARY_PATH}\;${_QBS_LIBRARY_PATH}\;$ENV{PATH}" _NEW_PATH) + set(${var} "PATH=${_NEW_PATH}" PARENT_SCOPE) else() set(${var} "") endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 74d71f4dc..0b5922a1a 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -46,8 +46,7 @@ if (QBS_INSTALL_HTML_DOCS OR QBS_INSTALL_QCH_DOCS) get_update_path_command(UPDATE_PATH_COMMAND) add_custom_target( BuildQbsDocumentation ALL - COMMAND ${UPDATE_PATH_COMMAND} - COMMAND ${_QBS_OUTPUT_DIR}/qbs + COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs resolve --settings-dir ${PROJECT_BINARY_DIR}/settings -f ${PROJECT_SOURCE_DIR}/qbs.qbs @@ -62,7 +61,7 @@ if (QBS_INSTALL_HTML_DOCS OR QBS_INSTALL_QCH_DOCS) modules.qbsbuildconfig.installHtml:${_INSTALL_HTML_DOCS} modules.qbsbuildconfig.installQch:${_INSTALL_QCH_DOCS} moduleProviders.Qt.qmakeFilePaths:${_QT_QMAKE_EXECUTABLE} - COMMAND ${_QBS_OUTPUT_DIR}/qbs + COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs build --settings-dir ${PROJECT_BINARY_DIR}/settings -f ${PROJECT_SOURCE_DIR}/qbs.qbs diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index f607e0a85..bde65d450 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -36,8 +36,7 @@ get_update_path_command(UPDATE_PATH_COMMAND) get_target_property(_QBS_OUTPUT_DIR qbs RUNTIME_OUTPUT_DIRECTORY) add_custom_target( BuildQbsResources ALL - COMMAND ${UPDATE_PATH_COMMAND} - COMMAND ${_QBS_OUTPUT_DIR}/qbs + COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs resolve --settings-dir ${PROJECT_BINARY_DIR}/settings -f ${PROJECT_SOURCE_DIR}/qbs.qbs @@ -47,7 +46,7 @@ add_custom_target( project.withCode:false project.withDocumentation:false profile:none - COMMAND ${_QBS_OUTPUT_DIR}/qbs + COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs build --settings-dir ${PROJECT_BINARY_DIR}/settings -f ${PROJECT_SOURCE_DIR}/qbs.qbs -- cgit v1.2.3 From 0976a94724854501a12581201b2c6d860a0f6dd4 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sun, 18 Apr 2021 13:46:19 +0200 Subject: update dmgbuild to master branch This contains fixes for Python 3.9 Change-Id: I087e21a4208c9091aab4cd9fcb9b3c2d1bf9945a Fixes: QBS-1642 Reviewed-by: Max Bespalov Reviewed-by: Denis Shienkov Reviewed-by: Christian Kandeler --- .../lib/python2.7/site-packages/dmgbuild/core.py | 79 ++++++++++++++++++---- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py index efccd0028..d94a06c1f 100644 --- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py +++ b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import os import pkg_resources +import platform import re import shutil import stat @@ -25,7 +26,19 @@ try: except NameError: unicode = str -import biplist +if sys.version_info < (3, 4): + import biplist + def plist_from_bytes(data): + return biplist.readPlistFromString(data) + def plist_bytes(data): + return biplist.Data(data) +else: + import plistlib + def plist_from_bytes(data): + return plistlib.loads(data) + def plist_bytes(data): + return data + from mac_alias import * from ds_store import * @@ -39,6 +52,10 @@ except ImportError: _hexcolor_re = re.compile(r'#[0-9a-f]{3}(?:[0-9a-f]{3})?') +# The first element in the platform.mac_ver() tuple is a string containing the +# macOS version (e.g., '10.15.6'). Parse into an integer tuple. +MACOS_VERSION = tuple(int(v) for v in platform.mac_ver()[0].split('.')) + class DMGError(Exception): pass @@ -51,7 +68,7 @@ def hdiutil(cmd, *args, **kwargs): p = subprocess.Popen(all_args, stdout=subprocess.PIPE, close_fds=True) output, errors = p.communicate() if plist: - results = biplist.readPlistFromString(output) + results = plist_from_bytes(output) else: results = output retcode = p.wait() @@ -103,6 +120,8 @@ def load_json(filename, settings): settings['compression_level'] = json_data.get('compression-level', None) settings['license'] = json_data.get('license', None) files = [] + hide = [] + hide_extensions = [] symlinks = {} icon_locations = {} for fileinfo in json_data.get('contents', []): @@ -123,8 +142,16 @@ def load_json(filename, settings): elif kind == 'position': pass icon_locations[name] = (fileinfo['x'], fileinfo['y']) + hide_ext = fileinfo.get('hide_extension', False) + if hide_ext: + hide_extensions.append(name) + hidden = fileinfo.get('hidden', False) + if hidden: + hide.append(name) settings['files'] = files + settings['hide_extensions'] = hide_extensions + settings['hide'] = hide settings['symlinks'] = symlinks settings['icon_locations'] = icon_locations @@ -139,6 +166,8 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, 'size': None, 'files': [], 'symlinks': {}, + 'hide': [], + 'hide_extensions': [], 'icon': None, 'badge_icon': None, 'background': None, @@ -258,7 +287,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, } background = options['background'] - + columns = { 'name': 'name', 'date-modified': 'dateModified', @@ -297,7 +326,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, 'version': 'ascending', 'comments': 'ascending', } - + lsvp = { 'viewOptionsVersion': 1, 'sortColumn': columns.get(options['list_sort_by'], 'name'), @@ -319,7 +348,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, default_widths[column]) asc = 'ascending' == options['list_column_sort_directions'].get(column, default_sort_directions[column]) - + lsvp['columns'][columns[column]] = { 'index': n, 'width': width, @@ -334,7 +363,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, cndx[k] = n width = default_widths[k] asc = 'ascending' == default_sort_directions[k] - + lsvp['columns'][columns[column]] = { 'index': n, 'width': width, @@ -344,7 +373,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, } n += 1 - + default_view = options['default_view'] views = { 'icon-view': b'icnv', @@ -411,11 +440,19 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, if ret: raise DMGError('Unable to create disk image') - ret, output = hdiutil('attach', - '-nobrowse', - '-owners', 'off', - '-noidme', - writableFile.name) + # IDME was deprecated in macOS 10.15/Catalina; as a result, use of -noidme + # started raising a warning. + if MACOS_VERSION >= (10, 15): + ret, output = hdiutil('attach', + '-nobrowse', + '-owners', 'off', + writableFile.name) + else: + ret, output = hdiutil('attach', + '-nobrowse', + '-owners', 'off', + '-noidme', + writableFile.name) if ret: raise DMGError('Unable to attach disk image') @@ -506,7 +543,7 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, background_bmk = Bookmark.for_file(path_in_image) icvp['backgroundType'] = 2 - icvp['backgroundImageAlias'] = biplist.Data(alias.to_bytes()) + icvp['backgroundImageAlias'] = plist_bytes(alias.to_bytes()) for f in options['files']: if isinstance(f, tuple): @@ -523,6 +560,22 @@ def build_dmg(filename, volume_name, settings_file=None, settings={}, name_in_image = os.path.join(mount_point, name) os.symlink(target, name_in_image) + to_hide = [] + for name in options['hide_extensions']: + name_in_image = os.path.join(mount_point, name) + to_hide.append(name_in_image) + + if to_hide: + subprocess.call(['/usr/bin/SetFile', '-a', 'E'] + to_hide) + + to_hide = [] + for name in options['hide']: + name_in_image = os.path.join(mount_point, name) + to_hide.append(name_in_image) + + if to_hide: + subprocess.call(['/usr/bin/SetFile', '-a', 'V'] + to_hide) + userfn = options.get('create_hook', None) if callable(userfn): userfn(mount_point, options) -- cgit v1.2.3 From d38fd9fa901cd7f30821367b831676837eb82fe3 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sun, 18 Apr 2021 22:24:23 +0300 Subject: Fix detecting MSVC via Probe when multiple versions are present Previously, Qbs iterated over different versions in the ascending order. During the setup-toolchains this means that the newest one will be written in the settings as only the last one is actually written. When running ClBinaryProbe, the first one (i.e. the oldest was picked up) which did not work well with vcvarsall (without -vcvars_ver parameter, it uses the newest one). So, pick up the newest compiler both when running setup-toolchains and when detecting via Probe. Task-number: QBS-1498 Change-Id: Ib1b433ca7e17747dee986ba383a3c01ee91851fb Reviewed-by: Christian Kandeler --- src/app/qbs-setup-toolchains/msvcprobe.cpp | 6 ++++++ src/lib/corelib/tools/msvcinfo.cpp | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index bb54add9f..19da1e25a 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -58,6 +58,7 @@ #include #include +#include #include using namespace qbs; @@ -182,9 +183,14 @@ void msvcProbe(Settings *settings, std::vector &profiles) } } + std::unordered_set seenNames; for (MSVC &msvc : msvcs) { const QString name = QLatin1String("MSVC") + msvc.version + QLatin1Char('-') + msvc.architecture; + // TODO: Qbs currently does not support multiple versions of installed MSVCs + // so we stop at the first (the newest) one + if (!seenNames.insert(name).second) + continue; try { msvc.init(); addMSVCPlatform(settings, profiles, name, &msvc); diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index 42cfefe7b..83c53ba5c 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -471,12 +472,20 @@ static std::vector installedCompilersHelper(Logger &logger) QDir vcInstallDir = vsInstallDir; vcInstallDir.cd(QStringLiteral("Tools/MSVC")); const auto vcVersionStrs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + std::vector vcVersions; + vcVersions.reserve(vcVersionStrs.size()); for (const QString &vcVersionStr : vcVersionStrs) { const Version vcVersion = Version::fromString(vcVersionStr); if (!vcVersion.isValid()) continue; + vcVersions.push_back(vcVersion); + } + // sort the versions so the new one comes first + std::sort(vcVersions.begin(), vcVersions.end(), std::greater()); + + for (const Version &vcVersion : vcVersions) { QDir specificVcInstallDir = vcInstallDir; - if (!specificVcInstallDir.cd(vcVersionStr) + if (!specificVcInstallDir.cd(vcVersion.toString()) || !specificVcInstallDir.cd(QStringLiteral("bin"))) { continue; } -- cgit v1.2.3 From f9ad1f5844af1e42c8de3cf10f6a9597924d9f9e Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sat, 17 Apr 2021 21:23:44 +0300 Subject: Allow to specify windowsSdkVersion Previously, it was not possible to use older windows SDK version. Fix that by introducing the new property that is passed to vcvarsall.bat Task-number: QBS-1498 Change-Id: Ic526eabb8ff82ddd85c93e90fd20421595a121f4 Reviewed-by: Christian Kandeler --- doc/reference/modules/cpp-module.qdoc | 11 ++++++++ share/qbs/imports/qbs/Probes/ClangClProbe.qbs | 17 ++++++++++-- share/qbs/imports/qbs/Probes/MsvcProbe.qbs | 5 ++-- share/qbs/modules/cpp/windows-clang-cl.qbs | 1 + share/qbs/modules/cpp/windows-msvc-base.qbs | 2 ++ share/qbs/modules/cpp/windows-msvc.qbs | 1 + .../corelib/jsextensions/utilitiesextension.cpp | 32 ++++++++++++++++------ src/lib/corelib/tools/msvcinfo.h | 6 ++-- src/lib/corelib/tools/vsenvironmentdetector.cpp | 6 ++-- 9 files changed, 64 insertions(+), 17 deletions(-) diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc index 56f41af5d..1628bc80e 100644 --- a/doc/reference/modules/cpp-module.qdoc +++ b/doc/reference/modules/cpp-module.qdoc @@ -1914,3 +1914,14 @@ \defaultvalue \c{true} */ + +/*! + \qmlproperty string cpp::windowsSdkVersion + \since Qbs 1.19 + + Which Windows SDK version should be used with MSVC toolchain. By default, + the latest SDK is used. + + \windowsproperty + \nodefaultvalue +*/ 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/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-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs index f5fde9556..81fe48385 100644 --- a/share/qbs/modules/cpp/windows-msvc-base.qbs +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -108,6 +108,8 @@ CppModule { } } + property string windowsSdkVersion + Rule { condition: useCPrecompiledHeader inputs: ["c_pch_src"] 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/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index 6c693cb61..282362382 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -468,9 +468,10 @@ static std::pair msvcCompilerInfoHelp const QString &compilerFilePath, MSVC::CompilerLanguage language, const QString &vcvarsallPath, - const QString &arch) + const QString &arch, + const QString &sdkVersion) { - MSVC msvc(compilerFilePath, arch); + MSVC msvc(compilerFilePath, arch, sdkVersion); VsEnvironmentDetector envdetector(vcvarsallPath); if (!envdetector.start(&msvc)) return { {}, QStringLiteral("Detecting the MSVC build environment failed: ") @@ -501,12 +502,16 @@ QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QS return context->throwError(QScriptContext::UnknownError, QStringLiteral("msvcCompilerInfo is not available on this platform")); #else - if (Q_UNLIKELY(context->argumentCount() < 2)) + if (Q_UNLIKELY(context->argumentCount() < 3)) return context->throwError(QScriptContext::SyntaxError, - QStringLiteral("msvcCompilerInfo expects 2 arguments")); + QStringLiteral("msvcCompilerInfo expects 3 arguments")); const QString compilerFilePath = context->argument(0).toString(); const QString compilerLanguage = context->argument(1).toString(); + const QString sdkVersion = + !context->argument(2).isNull() && !context->argument(2).isUndefined() + ? context->argument(2).toString() + : QString(); MSVC::CompilerLanguage language; if (compilerLanguage == QStringLiteral("c")) language = MSVC::CLanguage; @@ -517,7 +522,11 @@ QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QS QStringLiteral("msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument")); const auto result = msvcCompilerInfoHelper( - compilerFilePath, language, {}, MSVC::architectureFromClPath(compilerFilePath)); + compilerFilePath, + language, + {}, + MSVC::architectureFromClPath(compilerFilePath), + sdkVersion); if (result.first.isEmpty()) return context->throwError(QScriptContext::UnknownError, result.second); return engine->toScriptValue(result.first); @@ -531,9 +540,9 @@ QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, return context->throwError(QScriptContext::UnknownError, QStringLiteral("clangClCompilerInfo is not available on this platform")); #else - if (Q_UNLIKELY(context->argumentCount() < 4)) + if (Q_UNLIKELY(context->argumentCount() < 5)) return context->throwError(QScriptContext::SyntaxError, - QStringLiteral("clangClCompilerInfo expects 4 arguments")); + QStringLiteral("clangClCompilerInfo expects 5 arguments")); const QString compilerFilePath = context->argument(0).toString(); // architecture cannot be empty as vcvarsall.bat requires at least 1 arg, so fallback @@ -542,9 +551,14 @@ QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, ? context->argument(1).toString() : QString::fromStdString(HostOsInfo::hostOSArchitecture()); QString vcvarsallPath = context->argument(2).toString(); - const QString compilerLanguage = context->argumentCount() > 3 + const QString compilerLanguage = + !context->argument(3).isNull() && !context->argument(3).isUndefined() ? context->argument(3).toString() : QString(); + const QString sdkVersion = + !context->argument(4).isNull() && !context->argument(4).isUndefined() + ? context->argument(4).toString() + : QString(); MSVC::CompilerLanguage language; if (compilerLanguage == QStringLiteral("c")) language = MSVC::CLanguage; @@ -555,7 +569,7 @@ QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, QStringLiteral("clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument")); const auto result = msvcCompilerInfoHelper( - compilerFilePath, language, vcvarsallPath, arch); + compilerFilePath, language, vcvarsallPath, arch, sdkVersion); if (result.first.isEmpty()) return context->throwError(QScriptContext::UnknownError, result.second); return engine->toScriptValue(result.first); diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index de4470bf0..d081e5c15 100644 --- a/src/lib/corelib/tools/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -81,12 +81,14 @@ public: QString binPath; QString pathPrefix; QString architecture; + QString sdkVersion; QProcessEnvironment environment; MSVC() = default; - MSVC(const QString &clPath, QString arch): - architecture(std::move(arch)) + MSVC(const QString &clPath, QString arch, QString sdkVersion = {}): + architecture(std::move(arch)), + sdkVersion(std::move(sdkVersion)) { QDir parentDir = QFileInfo(clPath).dir(); binPath = parentDir.absolutePath(); diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index b0788823f..a11934d52 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -241,8 +241,10 @@ void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcv << "setlocal" << endl; batClearVars(s, varnames); s << "set PATH=" << m_windowsSystemDirPath << endl; // vcvarsall.bat needs tools from here - s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc) - << " || exit /b 1" << endl; + s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc); + if (!msvc->sdkVersion.isEmpty()) + s << " " << msvc->sdkVersion; + s << " || exit /b 1" << endl; batPrintVars(s, varnames); s << "endlocal" << endl; } -- cgit v1.2.3 From 33634338b925490d1ee713d30670e5c3f96e9139 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Mon, 19 Apr 2021 17:37:14 +0200 Subject: Fix build with older compilers This amends d38fd9fa9. Change-Id: I55661497de04749dfd27134ef7c8c9cc436053e2 Reviewed-by: Christian Kandeler Reviewed-by: Eike Ziller --- src/lib/corelib/tools/msvcinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index 83c53ba5c..ed94613d9 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -481,7 +481,7 @@ static std::vector installedCompilersHelper(Logger &logger) vcVersions.push_back(vcVersion); } // sort the versions so the new one comes first - std::sort(vcVersions.begin(), vcVersions.end(), std::greater()); + std::sort(vcVersions.begin(), vcVersions.end(), std::greater<>()); for (const Version &vcVersion : vcVersions) { QDir specificVcInstallDir = vcInstallDir; -- cgit v1.2.3 From 18c97249f38af5492739f9215d702b918ad9132a Mon Sep 17 00:00:00 2001 From: Raphael Cotty Date: Mon, 19 Apr 2021 00:10:14 +0200 Subject: Android: Compensate Qt issue in network dependencies for 5.15.0 < Qt < 5.15.3 Because of QTBUG-87288 the Qt5Network_ARCH-android-dependencies.xml is not properly generated. The bug is only corrected in Qt 5.13. This patch automatically updates the android manifest for version prior to 5.15.3. Change-Id: I0e7ea7f316f8af2d6c7b05340d9db3a72ba0110c Reviewed-by: Ivan Komissarov --- .../Qt/templates/android_support.qbs | 80 +++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 87c980448..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,6 +36,10 @@ 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 } @@ -355,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; @@ -400,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]; } } -- cgit v1.2.3 From d54d84a213041f9b84bc3061019f1d089ab74019 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Wed, 21 Apr 2021 14:03:58 +0200 Subject: Use binary search in Set::find() Change-Id: I5ed154633233dfeedf6b69b52fc5339fef3a956a Reviewed-by: Christian Kandeler --- src/lib/corelib/tools/set.h | 4 ++-- src/lib/corelib/tools/stlutils.h | 15 +++++++++++++++ tests/auto/tools/tst_tools.cpp | 24 ++++++++++++++++++++++++ tests/auto/tools/tst_tools.h | 1 + 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/lib/corelib/tools/set.h b/src/lib/corelib/tools/set.h index 75db0528b..463d3beb6 100644 --- a/src/lib/corelib/tools/set.h +++ b/src/lib/corelib/tools/set.h @@ -112,8 +112,8 @@ public: Set &operator&=(const Set &other) { return intersect(other); } Set &operator&=(const T &v) { return intersect(Set{ v }); } - iterator find(const T &v) { return std::find(m_data.begin(), m_data.end(), v); } - const_iterator find(const T &v) const { return std::find(m_data.cbegin(), m_data.cend(), v); } + iterator find(const T &v) { return binaryFind(m_data.begin(), m_data.end(), v); } + const_iterator find(const T &v) const { return binaryFind(m_data.cbegin(), m_data.cend(), v); } std::pair insert(const T &v); Set &operator+=(const T &v) { insert(v); return *this; } Set &operator|=(const T &v) { return operator+=(v); } diff --git a/src/lib/corelib/tools/stlutils.h b/src/lib/corelib/tools/stlutils.h index 1b6d7278f..d4c569a95 100644 --- a/src/lib/corelib/tools/stlutils.h +++ b/src/lib/corelib/tools/stlutils.h @@ -123,6 +123,21 @@ bool none_of(const Container &container, const UnaryPredicate &predicate) return std::none_of(std::begin(container), std::end(container), predicate); } +template +It binaryFind(It begin, It end, const T &value, Compare comp) +{ + const auto it = std::lower_bound(begin, end, value, comp); + if (it == end || comp(value, *it)) + return end; + return it; +} + +template +It binaryFind(It begin, It end, const T &value) +{ + return binaryFind(begin, end, value, std::less()); +} + template C &operator<<(C &container, const typename C::value_type &v) { diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp index edf5a1308..92e0978b5 100644 --- a/tests/auto/tools/tst_tools.cpp +++ b/tests/auto/tools/tst_tools.cpp @@ -673,6 +673,30 @@ void TestTools::set_containsSet() QVERIFY(set3.contains(set4)); } +void TestTools::set_find() +{ + Set set1; + + for (int i = 0; i < 500; ++i) { + QVERIFY(set1.find(QString::number(i)) == set1.end()); + set1.insert(QString::number(i)); + const auto it = set1.find(QString::number(i)); + QVERIFY(it != set1.end()); + QVERIFY(*it == QString::number(i)); + } + + QCOMPARE(set1.size(), size_t { 500 }); + + for (int j = 0; j < 500; ++j) { + int i = (j * 17) % 500; + const auto it = set1.find(QString::number(i)); + QVERIFY(it != set1.end()); + QVERIFY(*it == QString::number(i)); + set1.remove(QString::number(i)); + QVERIFY(set1.find(QString::number(i)) == set1.end()); + } +} + void TestTools::set_begin() { Set set1; diff --git a/tests/auto/tools/tst_tools.h b/tests/auto/tools/tst_tools.h index bd8538be2..d1ba0a57b 100644 --- a/tests/auto/tools/tst_tools.h +++ b/tests/auto/tools/tst_tools.h @@ -79,6 +79,7 @@ private slots: void set_remove(); void set_contains(); void set_containsSet(); + void set_find(); void set_begin(); void set_end(); void set_insert(); -- cgit v1.2.3 From 2f34e637828f7e519a25a498bd5aa4e8f955217d Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Tue, 20 Apr 2021 22:25:49 +0300 Subject: msvc: Pass -vcvars_ver to vcvarsall.bat when detecting environment This is required to properly support multiple MSVC toolchains (also now supported). Fixes: QBS-1498 Change-Id: I1274379496d47ebf7842eaed05f498b7af111b5b Reviewed-by: Christian Kandeler --- src/app/qbs-setup-toolchains/msvcprobe.cpp | 21 ++++++++++++++------- src/lib/corelib/tools/msvcinfo.cpp | 21 +++++++++++++++++++++ src/lib/corelib/tools/msvcinfo.h | 1 + src/lib/corelib/tools/vsenvironmentdetector.cpp | 3 +++ src/lib/corelib/tools/vsenvironmentdetector.h | 3 ++- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index 19da1e25a..00aedaaab 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -58,7 +58,7 @@ #include #include -#include +#include #include using namespace qbs; @@ -183,14 +183,21 @@ void msvcProbe(Settings *settings, std::vector &profiles) } } - std::unordered_set seenNames; + // we want the same MSVC version share the same suffix in profiles, thus use + // a set to know the number of versions processed so far + std::map> msvcCounters; for (MSVC &msvc : msvcs) { - const QString name = QLatin1String("MSVC") + msvc.version + QLatin1Char('-') + // each VS needs its own counter + auto &msvcVersions = msvcCounters[msvc.version]; + // vcInstallPath is "Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.16.27023/bin" + // Since msvcs are sorted by version, when the new vcInstallPath is inserted, we start + // a new group of compilers of the same version incrementing the set size + msvcVersions.insert(msvc.vcInstallPath); + // index is the number of specific vcInstallPaths (e.g. compiler versions) seen so far + const qsizetype index = msvcVersions.size() - 1; + const QString suffix = index == 0 ? QString() : QStringLiteral("-%1").arg(index); + const QString name = QLatin1String("MSVC") + msvc.version + suffix + QLatin1Char('-') + msvc.architecture; - // TODO: Qbs currently does not support multiple versions of installed MSVCs - // so we stop at the first (the newest) one - if (!seenNames.insert(name).second) - continue; try { msvc.init(); addMSVCPlatform(settings, profiles, name, &msvc); diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index ed94613d9..58cd458c3 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -510,11 +510,32 @@ QString MSVC::architectureFromClPath(const QString &clPath) { const auto parentDir = QFileInfo(clPath).absolutePath(); const auto parentDirName = QFileInfo(parentDir).fileName().toLower(); + // can be the case when cl.exe is present within the Windows SDK installation... but can it? if (parentDirName == QLatin1String("bin")) return QStringLiteral("x86"); return parentDirName; } +QString MSVC::vcVariablesVersionFromBinPath(const QString &binPath) +{ + const auto binDirName = QFileInfo(binPath).fileName().toLower(); + // the case when cl.exe is present within the Windows SDK installation + if (binDirName == QLatin1String("bin")) + return {}; + // binPath is something like + // Microsoft Visual Studio 14.0/VC/bin/amd64_x86 + // or + // Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64 + QDir dir(binPath); + dir.cdUp(); + // older Visual Studios do not support multiple compiler versions + if (dir.dirName().toLower() == QLatin1String("bin")) + return {}; + dir.cdUp(); + dir.cdUp(); + return dir.dirName(); +} + QString MSVC::canonicalArchitecture(const QString &arch) { if (arch == QLatin1String("x64") || arch == QLatin1String("amd64")) diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index d081e5c15..efaf0b6c2 100644 --- a/src/lib/corelib/tools/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -100,6 +100,7 @@ public: QBS_EXPORT void init(); QBS_EXPORT static QString architectureFromClPath(const QString &clPath); + QBS_EXPORT static QString vcVariablesVersionFromBinPath(const QString &binPath); QBS_EXPORT static QString canonicalArchitecture(const QString &arch); QBS_EXPORT static std::pair getHostTargetArchPair(const QString &arch); QBS_EXPORT QString binPathForArchitecture(const QString &arch) const; diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index a11934d52..82dff578f 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -244,6 +244,9 @@ void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcv s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc); if (!msvc->sdkVersion.isEmpty()) s << " " << msvc->sdkVersion; + const auto vcVarsVer = MSVC::vcVariablesVersionFromBinPath(msvc->binPath); + if (!vcVarsVer.isEmpty()) + s << " -vcvars_ver=" << vcVarsVer; s << " || exit /b 1" << endl; batPrintVars(s, varnames); s << "endlocal" << endl; diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h index 7fa152cb6..39bea07d6 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.h +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -57,7 +57,7 @@ class MSVC; class QBS_EXPORT VsEnvironmentDetector { public: - explicit VsEnvironmentDetector(QString vcvarsallPath = QString()); + explicit VsEnvironmentDetector(QString vcvarsallPath = {}); bool start(MSVC *msvc); bool start(std::vector msvcs); @@ -71,6 +71,7 @@ private: const QString m_windowsSystemDirPath; const QString m_vcvarsallPath; + const QString m_vcVariablesVersion; QString m_errorString; }; -- cgit v1.2.3 From 2f6eecdc96fcd693cecef8011d8f9500c7872fc7 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Mon, 29 Mar 2021 18:15:17 +0300 Subject: codesign: Long live `signtool` signing on Windows Change-Id: I320cd1a1f3d8a1eed11d1c70007214f19a109b6e Reviewed-by: Christian Kandeler Reviewed-by: Ivan Komissarov --- .github/workflows/main.yml | 6 + doc/reference/modules/codesign-module.qdoc | 118 +++++++++++--- share/qbs/modules/codesign/CodeSignModule.qbs | 6 + share/qbs/modules/codesign/apple.qbs | 2 +- share/qbs/modules/codesign/codesign.js | 119 +++++++++++++- share/qbs/modules/codesign/signtool.qbs | 94 +++++++++++ share/qbs/modules/cpp/msvc.js | 7 + share/qbs/modules/cpp/windows-msvc-base.qbs | 24 ++- tests/auto/auto.qbs | 3 +- tests/auto/blackbox/CMakeLists.txt | 9 ++ tests/auto/blackbox/blackbox-windows.pro | 18 +++ tests/auto/blackbox/blackbox-windows.qbs | 21 +++ .../blackbox/testdata-windows/codesign/app.cpp | 1 + .../testdata-windows/codesign/codesign.qbs | 37 +++++ tests/auto/blackbox/tst_blackboxwindows.cpp | 174 +++++++++++++++++++++ tests/auto/blackbox/tst_blackboxwindows.h | 51 ++++++ 16 files changed, 658 insertions(+), 32 deletions(-) create mode 100644 share/qbs/modules/codesign/signtool.qbs create mode 100644 tests/auto/blackbox/blackbox-windows.pro create mode 100644 tests/auto/blackbox/blackbox-windows.qbs create mode 100644 tests/auto/blackbox/testdata-windows/codesign/app.cpp create mode 100644 tests/auto/blackbox/testdata-windows/codesign/codesign.qbs create mode 100644 tests/auto/blackbox/tst_blackboxwindows.cpp create mode 100644 tests/auto/blackbox/tst_blackboxwindows.h diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 29f737a1e..953f19949 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -625,6 +625,12 @@ jobs: qbs config defaultProfile qt qbs config --list shell: bash + - name: Setup self-signed certificate + run: | + New-SelfSignedCertificate -DnsName qbs@community.test -Type CodeSigning -CertStoreLocation cert:\CurrentUser\My + Export-Certificate -Cert (Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert)[0] -FilePath qbs-code-signing.crt + Import-Certificate -FilePath .\qbs-code-signing.crt -CertStoreLocation Cert:\CurrentUser\TrustedPublisher + shell: powershell - name: Run Tests run: ${{ matrix.config.script }} ./release/install-root/bin shell: bash diff --git a/doc/reference/modules/codesign-module.qdoc b/doc/reference/modules/codesign-module.qdoc index 70a37477b..d0aba4688 100644 --- a/doc/reference/modules/codesign-module.qdoc +++ b/doc/reference/modules/codesign-module.qdoc @@ -63,15 +63,25 @@ */ /*! - \qmlproperty string codesign::codesignFlags + \qmlproperty string codesign::signingTimestamp - Additional flags passed to the \c{codesign} tool. + URL of the timestamp authority RFC 3161 server to be contacted to authenticate code signing. + \c undefined or \c empty indicates that a system-specific default should be used; + \c{"none"} explicitly disables the use of timestamp services on Apple platforms. \since Qbs 1.19 - \defaultvalue \c{undefined} + \defaultvalue \c "none" on Apple, \c undefined otherwise +*/ + +/*! + \qmlproperty stringList codesign::codesignFlags - \appleproperty + Additional flags passed to the \c{codesign} tool. + + \since Qbs 1.19 + + \nodefaultvalue */ /*! @@ -81,9 +91,7 @@ \since Qbs 1.19 - \defaultvalue \c{"codesign"} - - \appleproperty + \defaultvalue Determined automatically */ /*! @@ -94,8 +102,6 @@ \since Qbs 1.19 \defaultvalue Determined automatically - - \appleproperty */ /*! @@ -104,6 +110,7 @@ Whether to actually perform code signing. \since Qbs 1.19 + \defaultvalue \c false */ @@ -163,21 +170,6 @@ \appleproperty */ -/*! - \qmlproperty string codesign::signingTimestamp - - URL of the timestamp authority server to be contacted to authenticate code signing. - \c{undefined} indicates that a system-specific default should be used, and the empty - string indicates the default server provided by Apple. \c{"none"} explicitly disables - the use of timestamp services and this should not usually need to be changed. - - \since Qbs 1.19 - - \defaultvalue \c{"none"} - - \appleproperty -*/ - /*! \qmlproperty string codesign::signingType @@ -307,3 +299,81 @@ \androidproperty */ + +/*! + \qmlproperty string codesign::subjectName + + Specifies the name of the subject of the signing certificate. + This value can be a substring of the entire subject name. + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ + +/*! + \qmlproperty string codesign::rootSubjectName + + Specifies the name of the subject of the root certificate that + the signing certificate must chain to. This value may be a substring + of the entire subject name of the root certificate. + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ + +/*! + \qmlproperty string codesign::hashAlgorithm + + Specifies the default hash algorithm used on the signing certificate. + The possible values are \c sha1, \c sha256, \c sha384, and \c sha512. + + \note Only available in Windows 10 kit builds 20236 and later. + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ + +/*! + \qmlproperty string codesign::certificatePath + + Specifies the full path to the signing certificate file (*.pfx). + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ + +/*! + \qmlproperty string codesign::certificatePassword + + Specifies the password to use when opening a signing certificate file (*.pfx). + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ + +/*! + \qmlproperty string codesign::crossCertificatePath + + Specifies the full path to the additional certificate file (*.cer). + + \since Qbs 1.19 + + \defaultvalue \c undefined + + \windowsproperty +*/ 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 +** 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) }, { diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index 8dd301f68..0d87af9fe 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -4,7 +4,6 @@ Project { name: "Autotests" references: [ "api/api.qbs", - "blackbox/blackbox.qbs", "blackbox/blackbox-android.qbs", "blackbox/blackbox-apple.qbs", "blackbox/blackbox-baremetal.qbs", @@ -13,6 +12,8 @@ Project { "blackbox/blackbox-java.qbs", "blackbox/blackbox-joblimits.qbs", "blackbox/blackbox-qt.qbs", + "blackbox/blackbox-windows.qbs", + "blackbox/blackbox.qbs", "buildgraph/buildgraph.qbs", "cmdlineparser/cmdlineparser.qbs", "language/language.qbs", diff --git a/tests/auto/blackbox/CMakeLists.txt b/tests/auto/blackbox/CMakeLists.txt index 88eed4b42..0bf79a433 100644 --- a/tests/auto/blackbox/CMakeLists.txt +++ b/tests/auto/blackbox/CMakeLists.txt @@ -70,3 +70,12 @@ add_qbs_test(blackbox-qt tst_blackboxqt.cpp tst_blackboxqt.h ) + +add_qbs_test(blackbox-windows + SOURCES + ../shared.h + tst_blackboxbase.cpp + tst_blackboxbase.h + tst_blackboxwindows.cpp + tst_blackboxwindows.h + ) diff --git a/tests/auto/blackbox/blackbox-windows.pro b/tests/auto/blackbox/blackbox-windows.pro new file mode 100644 index 000000000..a9e8fdbd2 --- /dev/null +++ b/tests/auto/blackbox/blackbox-windows.pro @@ -0,0 +1,18 @@ +TARGET = tst_blackbox-windows + +HEADERS = tst_blackboxwindows.h tst_blackboxbase.h +SOURCES = tst_blackboxwindows.cpp tst_blackboxbase.cpp +OBJECTS_DIR = windows +MOC_DIR = $${OBJECTS_DIR}-moc + +include(../auto.pri) + +DATA_DIRS = testdata-windows ../find + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + win32:files ~= s|\\\\|/|g + for(file, files):!exists($$file/*):FILES += $$file +} + +OTHER_FILES += $$FILES diff --git a/tests/auto/blackbox/blackbox-windows.qbs b/tests/auto/blackbox/blackbox-windows.qbs new file mode 100644 index 000000000..e32421e3b --- /dev/null +++ b/tests/auto/blackbox/blackbox-windows.qbs @@ -0,0 +1,21 @@ +import qbs.Utilities + +QbsAutotest { + testName: "blackbox-windows" + Depends { name: "qbs_app" } + Depends { name: "qbs-setup-toolchains" } + Group { + name: "testdata" + prefix: "testdata-windows/" + files: ["**/*"] + fileTags: [] + } + files: [ + "../shared.h", + "tst_blackboxbase.cpp", + "tst_blackboxbase.h", + "tst_blackboxwindows.cpp", + "tst_blackboxwindows.h", + ] + cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)]) +} diff --git a/tests/auto/blackbox/testdata-windows/codesign/app.cpp b/tests/auto/blackbox/testdata-windows/codesign/app.cpp new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata-windows/codesign/app.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs b/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs new file mode 100644 index 000000000..2b48c67ff --- /dev/null +++ b/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs @@ -0,0 +1,37 @@ +Project { + name: "p" + + property bool enableSigning: true + property string hashAlgorithm + property string subjectName + property string signingTimestamp + + CppApplication { + name: "A" + files: "app.cpp" + codesign.enableCodeSigning: project.enableSigning + codesign.hashAlgorithm: project.hashAlgorithm + codesign.subjectName: project.subjectName + codesign.signingTimestamp: project.signingTimestamp + install: true + installDir: "" + property bool dummy: { + console.info("signtool path: %%" + codesign.codesignPath + "%%"); + } + } + + DynamicLibrary { + Depends { name: "cpp" } + name: "B" + files: "app.cpp" + codesign.enableCodeSigning: project.enableSigning + codesign.hashAlgorithm: project.hashAlgorithm + codesign.subjectName: project.subjectName + codesign.signingTimestamp: project.signingTimestamp + install: true + installDir: "" + property bool dummy: { + console.info("signtool path: %%" + codesign.codesignPath + "%%"); + } + } +} diff --git a/tests/auto/blackbox/tst_blackboxwindows.cpp b/tests/auto/blackbox/tst_blackboxwindows.cpp new file mode 100644 index 000000000..0c82754fb --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxwindows.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Denis Shienkov +** Contact: https://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. +** +****************************************************************************/ + +#include "tst_blackboxwindows.h" + +#include "../shared.h" + +#include + +#include +#include + +using qbs::Internal::HostOsInfo; + +struct SigntoolInfo +{ + enum class CodeSignResult { Failed = 0, Signed, Unsigned }; + CodeSignResult result = CodeSignResult::Failed; + bool timestamped = false; + QString hashAlgorithm; + QString subjectName; +}; + +Q_DECLARE_METATYPE(SigntoolInfo::CodeSignResult) + +static SigntoolInfo extractSigntoolInfo(const QString &signtoolPath, const QString &appPath) +{ + QProcess signtool; + signtool.start(signtoolPath, { QStringLiteral("verify"), QStringLiteral("/v"), appPath }); + if (!signtool.waitForStarted() || !signtool.waitForFinished()) + return {}; + const auto output = signtool.readAllStandardError(); + SigntoolInfo signtoolInfo; + if (output.contains("No signature found")) { + signtoolInfo.result = SigntoolInfo::CodeSignResult::Unsigned; + } else { + signtoolInfo.result = SigntoolInfo::CodeSignResult::Signed; + const auto output = signtool.readAllStandardOutput(); + const auto lines = output.split('\n'); + for (const auto &line: lines) { + { + const QRegularExpression re("^Hash of file \\((.+)\\):.+$"); + const QRegularExpressionMatch match = re.match(line); + if (match.hasMatch()) { + signtoolInfo.hashAlgorithm = match.captured(1).toLocal8Bit(); + continue; + } + } + { + const QRegularExpression re("Issued to: (.+)"); + const QRegularExpressionMatch match = re.match(line); + if (match.hasMatch()) { + signtoolInfo.subjectName = match.captured(1).toLocal8Bit().trimmed(); + continue; + } + } + if (line.startsWith("The signature is timestamped:")) { + signtoolInfo.timestamped = true; + break; + } else if (line.startsWith("File is not timestamped.")) { + break; + } + } + } + return signtoolInfo; +} + +static QString extractSigntoolPath(const QByteArray &output) +{ + const QRegularExpression re("%%(.+)%%"); + QRegularExpressionMatchIterator it = re.globalMatch(output); + if (!it.hasNext()) + return {}; + const QRegularExpressionMatch match = it.next(); + return match.captured(1).toUtf8(); +} + +TestBlackboxWindows::TestBlackboxWindows() + : TestBlackboxBase (SRCDIR "/testdata-windows", "blackbox-windows") +{ +} + +void TestBlackboxWindows::initTestCase() +{ + if (!HostOsInfo::isWindowsHost()) { + QSKIP("only applies on Windows"); + return; + } + + TestBlackboxBase::initTestCase(); +} + +void TestBlackboxWindows::standaloneCodesign() +{ + QFETCH(SigntoolInfo::CodeSignResult, result); + QFETCH(QString, hashAlgorithm); + QFETCH(QString, subjectName); + QFETCH(QString, signingTimestamp); + + QDir::setCurrent(testDataDir + "/codesign"); + QbsRunParameters params(QStringList{"qbs.installPrefix:''"}); + params.arguments << QStringLiteral("project.enableSigning:%1").arg( + (result == SigntoolInfo::CodeSignResult::Signed) ? "true" : "false") + << QStringLiteral("project.hashAlgorithm:%1").arg(hashAlgorithm) + << QStringLiteral("project.subjectName:%1").arg(subjectName) + << QStringLiteral("project.signingTimestamp:%1").arg(signingTimestamp); + + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + + if (!m_qbsStdout.contains("signtool path:")) + QFAIL("No current signtool path pattern exists"); + + const auto signtoolPath = extractSigntoolPath(m_qbsStdout); + QVERIFY(QFileInfo(signtoolPath).exists()); + + const QStringList outputBinaries = {"A.exe", "B.dll"}; + for (const auto &outputBinary : outputBinaries) { + const auto outputBinaryPath = defaultInstallRoot + "/" + outputBinary; + QVERIFY(QFileInfo(outputBinaryPath).exists()); + + const SigntoolInfo signtoolInfo = extractSigntoolInfo(signtoolPath, outputBinaryPath); + QVERIFY(signtoolInfo.result != SigntoolInfo::CodeSignResult::Failed); + QCOMPARE(signtoolInfo.result, result); + QCOMPARE(signtoolInfo.hashAlgorithm, hashAlgorithm); + QCOMPARE(signtoolInfo.subjectName, subjectName); + QCOMPARE(signtoolInfo.timestamped, !signingTimestamp.isEmpty()); + } +} + +void TestBlackboxWindows::standaloneCodesign_data() +{ + QTest::addColumn("result"); + QTest::addColumn("hashAlgorithm"); + QTest::addColumn("subjectName"); + QTest::addColumn("signingTimestamp"); + + QTest::newRow("standalone, unsigned") + << SigntoolInfo::CodeSignResult::Unsigned << "" << "" << ""; + QTest::newRow("standalone, signed, sha1, qbs@community.test, no timestamp") + << SigntoolInfo::CodeSignResult::Signed << "sha1" << "qbs@community.test" << ""; + QTest::newRow("standalone, signed, sha256, qbs@community.test, RFC3061 timestamp") + << SigntoolInfo::CodeSignResult::Signed << "sha256" << "qbs@community.test" + << "http://timestamp.digicert.com"; +} + +QTEST_MAIN(TestBlackboxWindows) diff --git a/tests/auto/blackbox/tst_blackboxwindows.h b/tests/auto/blackbox/tst_blackboxwindows.h new file mode 100644 index 000000000..fbc597313 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxwindows.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Denis Shienkov +** Contact: https://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. +** +****************************************************************************/ + +#ifndef TST_BLACKBOXWINDOWS_H +#define TST_BLACKBOXWINDOWS_H + +#include "tst_blackboxbase.h" + +class TestBlackboxWindows : public TestBlackboxBase +{ + Q_OBJECT + +public: + TestBlackboxWindows(); + +public slots: + void initTestCase() override; + +private slots: + void standaloneCodesign(); + void standaloneCodesign_data(); +}; + +#endif // TST_BLACKBOXWINDOWS_H -- cgit v1.2.3