From 288248d961bb2a2b8f48fb0b0e0f02d2980839b0 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 7 Jul 2020 16:17:40 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for SDCC MCS51 Change-Id: I2b26c60bd5e9c84c917b58cddf45f25461313c66 Reviewed-by: Ivan Komissarov Reviewed-by: Denis Shienkov --- share/qbs/modules/cpp/sdcc.qbs | 9 +-------- .../testdata-baremetal/one-object-asm-application/mcs51-sdcc.s | 7 +++++++ .../one-object-asm-application/one-object-asm-application.qbs | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs index 1dec2555d..a8d0df3c9 100644 --- a/share/qbs/modules/cpp/sdcc.qbs +++ b/share/qbs/modules/cpp/sdcc.qbs @@ -105,14 +105,7 @@ CppModule { } FileTagger { - condition: qbs.architecture === "stm8"; - patterns: "*.s" - fileTags: ["asm"] - } - - FileTagger { - condition: qbs.architecture === "mcs51"; - patterns: ["*.s51", "*.asm"] + patterns: ["*.s", "*.a51", "*.asm"] fileTags: ["asm"] } diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s new file mode 100644 index 000000000..eaa6467e3 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s @@ -0,0 +1,7 @@ + .globl main + .area PSEG (PAG,XDATA) + .area XSEG (XDATA) + .area HOME (CODE) +main: + mov dptr, #0x0000 + ret diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index e98279d6c..84bb45765 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -8,6 +8,9 @@ BareMetalApplication { } else if (qbs.toolchainType === "iar") { if (qbs.architecture === "mcs51") return true; + } else if (qbs.toolchainType === "sdcc") { + if (qbs.architecture === "mcs51") + return true; } console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); -- cgit v1.2.3 From 6fdfb84bce618e6458423211063bd3ead3de19e5 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 7 Jul 2020 22:13:57 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for KEIL ARM Change-Id: Id4e1c081fa8d6841fbad58532daee7d62ff80942 Reviewed-by: Ivan Komissarov --- tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs | 3 ++- tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs | 3 ++- .../testdata-baremetal/one-object-asm-application/arm-keil.s | 7 +++++++ .../one-object-asm-application/one-object-asm-application.qbs | 5 ++++- 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs index b262290bb..7335ead73 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs @@ -9,7 +9,8 @@ CppApplication { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armclang") - cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] + cpp.assemblerFlags: ["--cpu", "cortex-m0"] + cpp.commonCompilerFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] } Properties { condition: qbs.toolchain.contains("gcc") diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs index b15f2ce9a..3c627074d 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs @@ -9,7 +9,8 @@ StaticLibrary { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armclang") - cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] + cpp.assemblerFlags: ["--cpu", "cortex-m0"] + cpp.commonCompilerFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] } Properties { condition: qbs.toolchain.contains("gcc") diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s new file mode 100644 index 000000000..f3fcd50fe --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s @@ -0,0 +1,7 @@ + THUMB + AREA ||.text||, CODE, READONLY, ALIGN = 1 +main PROC + MOVS r0, #0 + BX lr + ENDP + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 84bb45765..7c297b8b7 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -3,6 +3,8 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { condition: { if (qbs.toolchainType === "keil") { + if (qbs.architecture.startsWith("arm")) + return true; if (qbs.architecture === "mcs51") return true; } else if (qbs.toolchainType === "iar") { @@ -16,5 +18,6 @@ BareMetalApplication { + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); return false; } - files: [qbs.architecture + "-" + qbs.toolchainType + ".s"] + files: [(qbs.architecture.startsWith("arm") ? "arm" : qbs.architecture) + + "-" + qbs.toolchainType + ".s"] } -- cgit v1.2.3 From de0d8be51e4069a698c2564d48909efca53a2350 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Fri, 10 Jul 2020 14:41:36 +0200 Subject: protobuf: use FileInfo.joinPaths instead of string concatenation Change-Id: Id86b94f0ca333942a769d86ebed47b629a43cf03 Reviewed-by: Christian Kandeler --- share/qbs/modules/protobuf/protobuf.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/share/qbs/modules/protobuf/protobuf.js b/share/qbs/modules/protobuf/protobuf.js index 5fd7f0202..3c70dfef0 100644 --- a/share/qbs/modules/protobuf/protobuf.js +++ b/share/qbs/modules/protobuf/protobuf.js @@ -73,7 +73,7 @@ function cppArtifact(module, input, tag, suffix) { var outputDir = getOutputDir(module, input); return { fileTags: [tag], - filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix, + filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix), cpp: { includePaths: [].concat(input.cpp.includePaths, outputDir), warningLevel: "none", @@ -85,7 +85,8 @@ function objcArtifact(module, input, tag, suffix) { var outputDir = getOutputDir(module, input); return { fileTags: [tag], - filePath: outputDir + "/" + toCamelCase(FileInfo.baseName(input.fileName)) + suffix, + filePath: FileInfo.joinPaths( + outputDir, toCamelCase(FileInfo.baseName(input.fileName)) + suffix), cpp: { includePaths: [].concat(input.cpp.includePaths, outputDir), warningLevel: "none", -- cgit v1.2.3 From 157121fca1aeb3d3d5f330fd40cc1239c155e2bb Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Fri, 10 Jul 2020 15:02:28 +0200 Subject: protobuf: Do not call outputDir() that often Change-Id: I1f436eb2d15393fc010a4ab29f879ae0828b0cc3 Reviewed-by: Christian Kandeler --- share/qbs/modules/protobuf/cpp/protobufcpp.qbs | 9 +++++---- share/qbs/modules/protobuf/objc/protobufobjc.qbs | 5 +++-- share/qbs/modules/protobuf/protobuf.js | 6 ++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs index 2697eb227..bd3d94929 100644 --- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs +++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs @@ -54,14 +54,15 @@ ProtobufBase { inputs: ["protobuf.input", "protobuf.grpc"] outputFileTags: ["hpp", "cpp"] outputArtifacts: { + var outputDir = HelperFunctions.getOutputDir(input.protobuf.cpp, input); var result = [ - HelperFunctions.cppArtifact(input.protobuf.cpp, input, "hpp", ".pb.h"), - HelperFunctions.cppArtifact(input.protobuf.cpp, input, "cpp", ".pb.cc") + HelperFunctions.cppArtifact(outputDir, input, "hpp", ".pb.h"), + HelperFunctions.cppArtifact(outputDir, input, "cpp", ".pb.cc") ]; if (input.fileTags.contains("protobuf.grpc")) { result.push( - HelperFunctions.cppArtifact(input.protobuf.cpp, input, "hpp", ".grpc.pb.h"), - HelperFunctions.cppArtifact(input.protobuf.cpp, input, "cpp", ".grpc.pb.cc")); + HelperFunctions.cppArtifact(outputDir, input, "hpp", ".grpc.pb.h"), + HelperFunctions.cppArtifact(outputDir, input, "cpp", ".grpc.pb.cc")); } return result; diff --git a/share/qbs/modules/protobuf/objc/protobufobjc.qbs b/share/qbs/modules/protobuf/objc/protobufobjc.qbs index 67057fb26..9a910bb72 100644 --- a/share/qbs/modules/protobuf/objc/protobufobjc.qbs +++ b/share/qbs/modules/protobuf/objc/protobufobjc.qbs @@ -23,9 +23,10 @@ ProtobufBase { inputs: ["protobuf.input"] outputFileTags: ["hpp", "objc"] outputArtifacts: { + var outputDir = HelperFunctions.getOutputDir(input.protobuf.objc, input); return [ - HelperFunctions.objcArtifact(input.protobuf.objc, input, "hpp", ".pbobjc.h"), - HelperFunctions.objcArtifact(input.protobuf.objc, input, "objc", ".pbobjc.m") + HelperFunctions.objcArtifact(outputDir, input, "hpp", ".pbobjc.h"), + HelperFunctions.objcArtifact(outputDir, input, "objc", ".pbobjc.m") ]; } diff --git a/share/qbs/modules/protobuf/protobuf.js b/share/qbs/modules/protobuf/protobuf.js index 3c70dfef0..8ab535abd 100644 --- a/share/qbs/modules/protobuf/protobuf.js +++ b/share/qbs/modules/protobuf/protobuf.js @@ -69,8 +69,7 @@ function getOutputDir(module, input) { return outputDir; } -function cppArtifact(module, input, tag, suffix) { - var outputDir = getOutputDir(module, input); +function cppArtifact(outputDir, input, tag, suffix) { return { fileTags: [tag], filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix), @@ -81,8 +80,7 @@ function cppArtifact(module, input, tag, suffix) { }; } -function objcArtifact(module, input, tag, suffix) { - var outputDir = getOutputDir(module, input); +function objcArtifact(outputDir, input, tag, suffix) { return { fileTags: [tag], filePath: FileInfo.joinPaths( -- cgit v1.2.3 From 11ff53d236a9bc852b8c980be556b53bf26490fd Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Thu, 9 Jul 2020 14:37:42 +0200 Subject: ci: Fix borked Python environment on macOS It looks like multiple Python3 environments are installed and running pip3 attempts to run a non-existent python3 binary. The recommended workaround is to invoke pip as a python module. Change-Id: I0bf2e47fa11b3c1014706960f5a34a85f64fa166 Reviewed-by: Ivan Komissarov Reviewed-by: Richard Weickelt --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e5a73e9f2..0840ae82c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -94,6 +94,7 @@ jobs: - icoutils - makensis - protobuf + - python3 - p7zip update: true env: @@ -106,7 +107,7 @@ jobs: before_install: - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QT_VERSION} qtbase qtdeclarative qttools qtscript qtscxml - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QTCREATOR_VERSION} qtcreator - - pip3 install --user beautifulsoup4 lxml + - python3 -m pip install --user beautifulsoup4 lxml before_script: - ulimit -c unlimited -S # enable core dumps script: -- cgit v1.2.3 From c68aca29d7a2976e9952c59b91b76dd9f1aa9ca7 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 7 Jul 2020 22:28:43 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for GCC ARM Change-Id: I1cb87180484e17a2eee025fb1f4fada0b399128e Reviewed-by: Christian Kandeler --- .../blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s | 5 +++++ .../one-object-asm-application/one-object-asm-application.qbs | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s new file mode 100644 index 000000000..c7b894230 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s @@ -0,0 +1,5 @@ + .global main + .type main, %function +main: + mov r0, #0 + bx lr diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 7c297b8b7..15ac4a22b 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -13,6 +13,9 @@ BareMetalApplication { } else if (qbs.toolchainType === "sdcc") { if (qbs.architecture === "mcs51") return true; + } else if (qbs.toolchainType === "gcc") { + if (qbs.architecture.startsWith("arm")) + return true; } console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); -- cgit v1.2.3 From 996bc307f34749d31c566da9d2546cc6375f2924 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Sat, 11 Jul 2020 20:32:13 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for GCC AVR Change-Id: I7f355bf7a366e58c68aea449f9afe08e63db9f4e Reviewed-by: Christian Kandeler --- .../testdata-baremetal/one-object-asm-application/avr-gcc.s | 6 ++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s new file mode 100644 index 000000000..4ba005a47 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s @@ -0,0 +1,6 @@ + .global main + .type main, %function +main: + ldi r24, 0 + ldi r25, 0 + ret diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 15ac4a22b..4f2e36ccc 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -16,6 +16,8 @@ BareMetalApplication { } else if (qbs.toolchainType === "gcc") { if (qbs.architecture.startsWith("arm")) return true; + if (qbs.architecture === "avr") + return true; } console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); -- cgit v1.2.3 From 64a7ee68516e4edfb8c1eda382396b311ec412af Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Mon, 13 Apr 2020 17:02:14 +0200 Subject: doc: Add How-To about cpp.rpaths Task-number: QBS-1204 Change-Id: I2d04ccb0cbcd7c1a6b5a5f251e70d34b3a960da7 Reviewed-by: Christian Kandeler --- doc/howtos.qdoc | 37 ++++++++++++++++++++++ doc/reference/modules/cpp-module.qdoc | 4 +++ examples/examples.qbs | 1 + examples/rpaths/main.cpp | 45 +++++++++++++++++++++++++++ examples/rpaths/objecta.cpp | 48 +++++++++++++++++++++++++++++ examples/rpaths/objecta.h | 47 ++++++++++++++++++++++++++++ examples/rpaths/objectb.cpp | 41 +++++++++++++++++++++++++ examples/rpaths/objectb.h | 48 +++++++++++++++++++++++++++++ examples/rpaths/rpaths.qbs | 58 +++++++++++++++++++++++++++++++++++ 9 files changed, 329 insertions(+) create mode 100644 examples/rpaths/main.cpp create mode 100644 examples/rpaths/objecta.cpp create mode 100644 examples/rpaths/objecta.h create mode 100644 examples/rpaths/objectb.cpp create mode 100644 examples/rpaths/objectb.h create mode 100644 examples/rpaths/rpaths.qbs diff --git a/doc/howtos.qdoc b/doc/howtos.qdoc index 696c444cb..7e01624a0 100644 --- a/doc/howtos.qdoc +++ b/doc/howtos.qdoc @@ -40,6 +40,7 @@ \li \l{How do I build release with debug information?} \li \l{How do I separate and install debugging symbols?} \li \l{How do I use precompiled headers?} + \li \l{How do I make use of rpaths?} \li \l{How do I make sure my generated sources are getting compiled?} \li \l{How do I run my autotests?} \li \l{How do I use ccache?} @@ -237,6 +238,42 @@ } \endcode + \section1 How do I make use of rpaths? + + rpath designates the run-time search path used by the dynamic linker when loading + libraries on UNIX platforms. This concept does not apply to Windows. + + Suppose you have a project with two dynamic library products \c LibraryA and \c LibraryB + and one dependent application product. Also, \c LibraryB depends on \c LibraryA. The + application is installed to the \c bin folder and the libraries are installed to the + \c lib folder next to the \c bin folder. You want the application to be able to find the + dependent libraries relative to its own location. This can be achieved by usage of the + \l{cpp::rpaths}{cpp.rpaths} property. + + First, you need to set \l{cpp::rpaths}{cpp.rpaths} in your libraries so they can + find dependent libraries in the same folder where they are located. This can be + done as follows: + + \snippet ../examples/rpaths/rpaths.qbs 0 + + We are setting \l{cpp::rpaths}{cpp.rpaths} to \l{cpp::rpathOrigin}{cpp.rpathOrigin} which + expands to \c "$ORIGIN" on Linux and to \c "@loader_path" on macOS. + + On macOS you also need to set \l{cpp::sonamePrefix}{cpp.sonamePrefix} to \c "@rpath" to + tell the dynamic linker to use RPATHs when loading this library. + + \c LibraryB looks exactly the same: + + \snippet ../examples/rpaths/rpaths.qbs 1 + + In a real project, it might be a good idea to move common properties to some base item + and inherit it in library items. + + The application item is a bit different. It sets \l{cpp::rpaths}{cpp.rpaths} to the + \c "lib" folder which is located one level up from the \c bin folder: + + \snippet ../examples/rpaths/rpaths.qbs 2 + \section1 How do I make sure my generated sources are getting compiled? The rules in a \QBS project do not care whether its inputs are actual source files diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc index 3c1744e09..f5f43f0f9 100644 --- a/doc/reference/modules/cpp-module.qdoc +++ b/doc/reference/modules/cpp-module.qdoc @@ -1370,6 +1370,8 @@ \l{cpp::}{systemRunPaths} are ignored. \nodefaultvalue + + \sa{How do I make use of rpaths?} */ /*! @@ -1406,6 +1408,8 @@ install names. \nodefaultvalue + + \sa{How do I make use of rpaths?} */ /*! diff --git a/examples/examples.qbs b/examples/examples.qbs index 2b1c0933b..18205d67a 100644 --- a/examples/examples.qbs +++ b/examples/examples.qbs @@ -68,5 +68,6 @@ Project { // "protobuf/addressbook_objc/addressbook_objc.qbs", "baremetal/baremetal.qbs", "rule/rule.qbs", + "rpaths/rpaths.qbs", ] } diff --git a/examples/rpaths/main.cpp b/examples/rpaths/main.cpp new file mode 100644 index 000000000..aaaebfbf6 --- /dev/null +++ b/examples/rpaths/main.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objecta.h" +#include "objectb.h" + +#include + +int main(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + + std::cout << ObjectA::className() << std::endl; + std::cout << ObjectB::className() << std::endl; + + ObjectB b; + std::cout << b.getA().name() << std::endl; + + return 0; +} diff --git a/examples/rpaths/objecta.cpp b/examples/rpaths/objecta.cpp new file mode 100644 index 000000000..bf6266653 --- /dev/null +++ b/examples/rpaths/objecta.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objecta.h" + +ObjectA::ObjectA(std::string name) : + m_name(std::move(name)) +{ +} + +std::string ObjectA::className() +{ + return "ObjectA"; +} + +const std::string &ObjectA::name() const +{ + return m_name; +} + +void ObjectA::setName(std::string name) +{ + m_name = std::move(name); +} diff --git a/examples/rpaths/objecta.h b/examples/rpaths/objecta.h new file mode 100644 index 000000000..69b78fbc2 --- /dev/null +++ b/examples/rpaths/objecta.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECTA_H +#define OBJECTA_H + +#include + +class ObjectA +{ +public: + explicit ObjectA(std::string name); + + static std::string className(); + + const std::string &name() const; + void setName(std::string name); + +private: + std::string m_name; +}; + +#endif // OBJECTA_H diff --git a/examples/rpaths/objectb.cpp b/examples/rpaths/objectb.cpp new file mode 100644 index 000000000..758fe09cf --- /dev/null +++ b/examples/rpaths/objectb.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectb.h" + +ObjectB::ObjectB() = default; + +std::string ObjectB::className() +{ + return "ObjectB"; +} + +const ObjectA &ObjectB::getA() const +{ + return objectA; +} + diff --git a/examples/rpaths/objectb.h b/examples/rpaths/objectb.h new file mode 100644 index 000000000..5a63de5eb --- /dev/null +++ b/examples/rpaths/objectb.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECTB_H +#define OBJECTB_H + +#include + +#include "objecta.h" + +class ObjectB +{ +public: + ObjectB(); + + static std::string className(); + + const ObjectA &getA() const; + +private: + ObjectA objectA{"A"}; +}; + +#endif // OBJECTB_H diff --git a/examples/rpaths/rpaths.qbs b/examples/rpaths/rpaths.qbs new file mode 100644 index 000000000..1e5411468 --- /dev/null +++ b/examples/rpaths/rpaths.qbs @@ -0,0 +1,58 @@ +import qbs.FileInfo + +Project { + condition: qbs.targetOS.contains("unix") + + //! [0] + DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "bundle" } + name: "LibraryA" + bundle.isBundle: false + cpp.sonamePrefix: qbs.targetOS.contains("macos") ? "@rpath" : undefined + cpp.rpaths: cpp.rpathOrigin + cpp.cxxLanguageVersion: "c++11" + files: [ + "objecta.cpp", + "objecta.h", + ] + install: true + installDir: "examples/lib" + } + //! [0] + + //! [1] + DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "bundle" } + Depends { name: "LibraryA" } + name: "LibraryB" + bundle.isBundle: false + cpp.cxxLanguageVersion: "c++11" + cpp.sonamePrefix: qbs.targetOS.contains("macos") ? "@rpath" : undefined + cpp.rpaths: cpp.rpathOrigin + files: [ + "objectb.cpp", + "objectb.h", + ] + install: true + installDir: "examples/lib" + } + //! [1] + + //! [2] + CppApplication { + Depends { name: "bundle" } + Depends { name: "LibraryA" } + Depends { name: "LibraryB" } + name: "rpaths-app" + files: "main.cpp" + consoleApplication: true + bundle.isBundle: false + cpp.rpaths: FileInfo.joinPaths(cpp.rpathOrigin, "..", "lib") + cpp.cxxLanguageVersion: "c++11" + install: true + installDir: "examples/bin" + } + //! [2] +} -- cgit v1.2.3 From 5009c517d94be83e5948d33f65a0101db8353330 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Tue, 14 Jul 2020 19:19:12 +0200 Subject: doc: fix the default value for the qbs.targetPlatform property Change-Id: I013c19555af224e2f2ac39af37c63c8869109243 Reviewed-by: Denis Shienkov Reviewed-by: Christian Kandeler --- doc/reference/modules/qbs-module.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc index de7f9ae6e..e72df697c 100644 --- a/doc/reference/modules/qbs-module.qdoc +++ b/doc/reference/modules/qbs-module.qdoc @@ -280,7 +280,7 @@ \sa {Target Platforms} - \nodefaultvalue + \defaultvalue \l{qbs::hostPlatform}{hostPlatform} */ /*! -- cgit v1.2.3 From 3ae5188b20dca13fb2c23ca09457b9eda2147b4c Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Tue, 14 Jul 2020 21:34:41 +0200 Subject: Probes: Remove deprecated properties from PathProbe They were declared deprecated in qbs 1.13, looks like it's safe to remove them. Change-Id: I493f7bb9578da1bb579161338bcb5d391cc15f4c Reviewed-by: Christian Kandeler --- share/qbs/imports/qbs/Probes/PathProbe.qbs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs index 424a621c6..768defd87 100644 --- a/share/qbs/imports/qbs/Probes/PathProbe.qbs +++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs @@ -38,11 +38,9 @@ Probe { property var nameFilter property var candidateFilter property varList selectors - property pathList pathPrefixes property pathList searchPaths property stringList pathSuffixes property pathList platformSearchPaths: hostOS.contains("unix") ? ['/usr', '/usr/local'] : [] - property pathList platformPaths property stringList environmentPaths property stringList platformEnvironmentPaths property stringList hostOS: qbs.hostOS @@ -57,15 +55,9 @@ Probe { property varList allResults configure: { - if (pathPrefixes) - console.warn("PathProbe.pathPrefixes is deprecated, use searchPaths instead"); - if (platformPaths) - console.warn("PathProbe.platformPaths is deprecated, use platformSearchPaths instead"); - var _searchPaths = ModUtils.concatAll(pathPrefixes, searchPaths); - var _platformSearchPaths = ModUtils.concatAll(platformPaths, platformSearchPaths); var results = PathProbeConfigure.configure(selectors, names, nameSuffixes, nameFilter, - candidateFilter, _searchPaths, pathSuffixes, - _platformSearchPaths, environmentPaths, + candidateFilter, searchPaths, pathSuffixes, + platformSearchPaths, environmentPaths, platformEnvironmentPaths, pathListSeparator); found = results.found; allResults = results.files; -- cgit v1.2.3 From 6fa78d83517308a193f4a57818acfb51a4da0652 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Tue, 14 Jul 2020 21:48:53 +0200 Subject: Probes: Remove nameFilter from FrameworkProbe It is not possible to use functions as values for properties with the new JS engine. Remove nameFilter by allowing to have common nameSuffixes for different selectors. Change-Id: I24ae747f4d609c956285e77ee832c6e99304a622 Reviewed-by: Christian Kandeler --- share/qbs/imports/qbs/Probes/FrameworkProbe.qbs | 7 +------ share/qbs/imports/qbs/Probes/path-probe.js | 6 ++++-- .../testdata/path-probe/mult-files-common-suffixes.qbs | 10 ++++++++++ tests/auto/blackbox/tst_blackbox.cpp | 1 + 4 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs diff --git a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs index e0fe73b40..c3d98a49f 100644 --- a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs @@ -35,10 +35,5 @@ PathProbe { "/Library/Frameworks", "/System/Library/Frameworks" ]) - - nameFilter: { - return function(name) { - return name + ".framework"; - } - } + nameSuffixes: ".framework" } diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js index a997f77f2..b4d745428 100644 --- a/share/qbs/imports/qbs/Probes/path-probe.js +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -41,7 +41,7 @@ function asStringList(key, value) { throw key + " must be a string or a stringList"; } -function canonicalSelectors(selectors) { +function canonicalSelectors(selectors, nameSuffixes) { var mapper = function(selector) { if (typeof(selector) === "string") return {names : [selector]}; @@ -53,6 +53,8 @@ function canonicalSelectors(selectors) { selector.names = asStringList("names", selector.names); if (selector.nameSuffixes) selector.nameSuffixes = asStringList("nameSuffixes", selector.nameSuffixes); + else + selector.nameSuffixes = nameSuffixes; return selector; }; return selectors.map(mapper); @@ -70,7 +72,7 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, {names: names, nameSuffixes: nameSuffixes} ]; } else { - selectors = canonicalSelectors(selectors); + selectors = canonicalSelectors(selectors, nameSuffixes); } if (nameFilter) { diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs new file mode 100644 index 000000000..c4d53a715 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs @@ -0,0 +1,10 @@ +BaseApp { + inputSelectors: [ + {names : "tool"}, + {names : "super-tool"}, + ] + inputNameSuffixes: ".1" + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.1", "bin/super-tool.1"] + outputCandidatePaths: [["bin/tool.1"], ["bin/super-tool.1"]] +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index b0d9c2976..786a43725 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -2931,6 +2931,7 @@ void TestBlackbox::pathProbe_data() QTest::newRow("mult-files-mult-variants") << QString("mult-files-mult-variants.qbs") << true; QTest::newRow("single-file-suffixes") << QString("single-file-suffixes.qbs") << true; QTest::newRow("mult-files-suffixes") << QString("mult-files-suffixes.qbs") << true; + QTest::newRow("mult-files-common-suffixes") << QString("mult-files-common-suffixes.qbs") << true; QTest::newRow("mult-files-mult-suffixes") << QString("mult-files-mult-suffixes.qbs") << true; QTest::newRow("name-filter") << QString("name-filter.qbs") << true; QTest::newRow("candidate-filter") << QString("candidate-filter.qbs") << true; -- cgit v1.2.3 From a9d789723053a33649d520913a89839774024f0b Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sat, 13 Jun 2020 16:35:35 +0200 Subject: Fix loading optional transitive dependencies If optional module B depends on an invalid module A (i.e. whos validate script throws an exception), module B should be not present as well. Fix that by remembering the initial value of the "required" property in the Depends item. Change-Id: Ia21587b3f5a8bd49c12b9f31b65e009fb2eeafb9 Reviewed-by: Christian Kandeler --- src/lib/corelib/language/item.h | 1 + src/lib/corelib/language/moduleloader.cpp | 16 +++++++++++++--- .../transitive-invalid-dependencies/modules/a/a.qbs | 5 +++++ .../transitive-invalid-dependencies/modules/b/b.qbs | 3 +++ .../transitive-invalid-dependencies/modules/c/c.qbs | 3 +++ .../transitive-invalid-dependencies/modules/d/d.qbs | 4 ++++ .../transitive-invalid-dependencies.qbs | 11 +++++++++++ tests/auto/blackbox/tst_blackbox.cpp | 10 ++++++++++ tests/auto/blackbox/tst_blackbox.h | 1 + 9 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index c5d8ef980..2d676a7f7 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -78,6 +78,7 @@ public: QualifiedId name; Item *item; bool isProduct; + bool requiredValue = true; // base value of the required prop bool required; QVariantMap parameters; VersionRange versionRange; diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 935f18f9e..09bfb00e8 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -1489,6 +1489,13 @@ void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext) continue; try { m_evaluator->boolValue(module.item, StringConstants::validateProperty()); + for (const auto &dep : module.item->modules()) { + if (dep.requiredValue && !dep.item->isPresentModule()) { + throw ErrorInfo(Tr::tr("Module '%1' depends on module '%2', which was not " + "loaded successfully") + .arg(module.name.toString(), dep.name.toString())); + } + } } catch (const ErrorInfo &error) { handleModuleSetupError(productContext, module, error); if (productContext->info.delayedError.hasError()) @@ -2665,8 +2672,10 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare return; } + const bool isRequiredValue = + m_evaluator->boolValue(dependsItem, StringConstants::requiredProperty()); const bool isRequired = !productTypesIsSet - && m_evaluator->boolValue(dependsItem, StringConstants::requiredProperty()) + && isRequiredValue && !contains(m_requiredChain, false); const Version minVersion = Version::fromString( m_evaluator->stringValue(dependsItem, @@ -2686,8 +2695,8 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare const auto it = std::find_if(moduleResults->begin(), moduleResults->end(), [moduleName](const Item::Module &m) { return m.name == moduleName; }); if (it != moduleResults->end()) { - if (isRequired) - it->required = true; + it->required = it->required || isRequired; + it->requiredValue = it->requiredValue || isRequiredValue; it->versionRange.narrowDown(versionRange); continue; } @@ -2720,6 +2729,7 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare qCDebug(lcModuleLoader) << "module loaded:" << moduleName.toString(); result.name = moduleName; result.item = moduleItem; + result.requiredValue = isRequiredValue; result.required = isRequired; result.parameters = defaultParameters; result.versionRange = versionRange; diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs new file mode 100644 index 000000000..fd52488fb --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs @@ -0,0 +1,5 @@ +Module { + validate: { + throw "Module cannot be loaded"; + } +} diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs new file mode 100644 index 000000000..605a2aaee --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs @@ -0,0 +1,3 @@ +Module { + Depends { name: "a" } +} diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs new file mode 100644 index 000000000..ac7dbbec6 --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs @@ -0,0 +1,3 @@ +Module { + Depends { name: "a"; required: false } +} diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs new file mode 100644 index 000000000..0bdd8c3b7 --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs @@ -0,0 +1,4 @@ +Module { + Depends { name: "b"; } + Depends { name: "c"; } +} diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs new file mode 100644 index 000000000..209b1e47d --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs @@ -0,0 +1,11 @@ +Product { + property bool modulePresent: { + console.info("b.present = " + b.present); + console.info("c.present = " + c.present); + console.info("d.present = " + d.present); + } + + Depends { name: "b"; required: false } + Depends { name: "c"; required: false } + Depends { name: "d"; required: false } +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 786a43725..1a593c73c 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -8106,6 +8106,16 @@ void TestBlackbox::qbsVersion() QVERIFY(runQbs(params) != 0); } +void TestBlackbox::transitiveInvalidDependencies() +{ + QDir::setCurrent(testDataDir + "/transitive-invalid-dependencies"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("b.present = false"), m_qbsStdout); + QVERIFY2(m_qbsStdout.contains("c.present = true"), m_qbsStdout); + QVERIFY2(m_qbsStdout.contains("d.present = false"), m_qbsStdout); +} + void TestBlackbox::transitiveOptionalDependencies() { QDir::setCurrent(testDataDir + "/transitive-optional-dependencies"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index de357169d..8a5d69a02 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -313,6 +313,7 @@ private slots: void trackRemoveFile(); void trackRemoveFileTag(); void trackRemoveProduct(); + void transitiveInvalidDependencies(); void transitiveOptionalDependencies(); void typescript(); void undefinedTargetPlatform(); -- cgit v1.2.3 From b6dc9add8eca59162b4b7c3b2e8c87f7b711350e Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 16 Jul 2020 09:32:25 +0200 Subject: Fix mingw linker issue Change-Id: I4890eb09182446eb4df0e0c1ab8a6ab0cda1e6cd Reviewed-by: Christian Stenger Reviewed-by: Christian Kandeler --- src/plugins/generator/visualstudio/visualstudio.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/generator/visualstudio/visualstudio.pro b/src/plugins/generator/visualstudio/visualstudio.pro index 5231c01dc..49aee0eb4 100644 --- a/src/plugins/generator/visualstudio/visualstudio.pro +++ b/src/plugins/generator/visualstudio/visualstudio.pro @@ -2,6 +2,8 @@ include(visualstudio.pri) include(../../plugins.pri) include(../../../shared/json/json.pri) include(../../../lib/msbuild/use_msbuild.pri) +# Using the indirect usage of corelib via plugins.pri breaks linking on mingw +include(../../../lib/corelib/use_corelib.pri) INCLUDEPATH += ../../../lib/msbuild -- cgit v1.2.3 From 6f91456f190179b5de0ebb1ce79537b8e8844afa Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 20:54:57 +0300 Subject: baremetal: Don't pass cpp.driverFlags to SDCC assembler ... because the cpp.driverFlags intended only for the compiler and the linker; the assembler required the cpp.assemblerFlags instead. Change-Id: Ib2b70b349517dfa7d125e3c15491e2f4a870a95d Reviewed-by: Ivan Komissarov --- share/qbs/modules/cpp/sdcc.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js index 9c7001601..2b544d27c 100644 --- a/share/qbs/modules/cpp/sdcc.js +++ b/share/qbs/modules/cpp/sdcc.js @@ -447,8 +447,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { // Misc flags. args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + ModUtils.moduleProperty(input, "flags", tag)); args.push("-ol"); args.push(outputs.obj[0].filePath); -- cgit v1.2.3 From ba897695f61704a88270f57bfa83419770461cfc Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 21:01:53 +0300 Subject: baremetal: Don't pass cpp.driverFlags to IAR assembler ... because the cpp.driverFlags intended only for the compiler and the linker; the assembler required the cpp.assemblerFlags instead. Change-Id: I0758937d40f02cbc6ff76b36b7c11fe5f4ddcf08 Reviewed-by: Ivan Komissarov --- share/qbs/modules/cpp/iar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js index 3549979be..ffc0773e0 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -723,8 +723,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { // Misc flags. args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + ModUtils.moduleProperty(input, "flags", tag)); return args; } -- cgit v1.2.3 From 059b08a44f163ec6182b50d46e949ceaf4adf8a0 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 20:50:30 +0300 Subject: baremetal: Don't pass cpp.driverFlags to KEIL assembler ... because the cpp.driverFlags intended only for the compiler and the linker; the assembler required the cpp.assemblerFlags instead. Change-Id: Ie1cca820d54095fc1361f5b9b049adc57674b3fd Reviewed-by: Christian Kandeler --- share/qbs/modules/cpp/keil.js | 3 +-- tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs | 3 ++- tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js index 441b49213..4cb2946a6 100644 --- a/share/qbs/modules/cpp/keil.js +++ b/share/qbs/modules/cpp/keil.js @@ -1011,8 +1011,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) { // Misc flags. args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag), - ModUtils.moduleProperty(input, "flags", tag), - ModUtils.moduleProperty(input, "driverFlags", tag)); + ModUtils.moduleProperty(input, "flags", tag)); return args; } diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs index 7335ead73..d802937d1 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs @@ -3,6 +3,7 @@ CppApplication { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armcc") + cpp.assemblerFlags: ["--cpu", "cortex-m0"] cpp.driverFlags: ["--cpu", "cortex-m0"] } Properties { @@ -10,7 +11,7 @@ CppApplication { && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armclang") cpp.assemblerFlags: ["--cpu", "cortex-m0"] - cpp.commonCompilerFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] + cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] } Properties { condition: qbs.toolchain.contains("gcc") diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs index 3c627074d..01493a5ec 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs @@ -3,6 +3,7 @@ StaticLibrary { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armcc") + cpp.assemblerFlags: ["--cpu", "cortex-m0"] cpp.driverFlags: ["--cpu", "cortex-m0"] } Properties { @@ -10,7 +11,7 @@ StaticLibrary { && qbs.architecture.startsWith("arm") && cpp.compilerName.startsWith("armclang") cpp.assemblerFlags: ["--cpu", "cortex-m0"] - cpp.commonCompilerFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] + cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"] } Properties { condition: qbs.toolchain.contains("gcc") -- cgit v1.2.3 From 2ac59c368e4e23b53869abe51cfe7100a58dd9c3 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 21:09:41 +0300 Subject: baremetal: Skip 'preinclude-headers' test for KEIL MCS51 toolchain ... because this toolchain does not support the pre-include headers. Change-Id: I3b34a39da01500540cbaca62244104c8262875fc Reviewed-by: Christian Kandeler --- .../preinclude-headers/preinclude-headers.qbs | 10 ++++++++++ tests/auto/blackbox/tst_blackboxbaremetal.cpp | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs index 8ea1c3652..088af3340 100644 --- a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs +++ b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs @@ -1,6 +1,16 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { + condition: { + if (qbs.toolchainType === "keil") { + if (qbs.architecture === "mcs51") { + console.info("unsupported toolset: %%" + + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); + return false; + } + } + return true; + } cpp.prefixHeaders: ["preinclude.h"] files: ["main.c"] } diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp index a80a1ad8f..2f6169ff9 100644 --- a/tests/auto/blackbox/tst_blackboxbaremetal.cpp +++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp @@ -110,7 +110,16 @@ void TestBlackboxBareMetal::distributionIncludePaths() void TestBlackboxBareMetal::preincludeHeaders() { QDir::setCurrent(testDataDir + "/preinclude-headers"); - QCOMPARE(runQbs(), 0); + QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0); + if (!m_qbsStdout.contains("unsupported toolset:")) { + QCOMPARE(runQbs(), 0); + } else { + QByteArray toolchain; + QByteArray architecture; + extractUnsupportedToolset(m_qbsStdout, toolchain, architecture); + QSKIP("Unsupported toolchain '" + toolchain + + "' for architecture '" + architecture + "'"); + } } QTEST_MAIN(TestBlackboxBareMetal) -- cgit v1.2.3 From eb3203c6d700b873177b330e6058d6cf3b97e890 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 16 Jul 2020 14:38:29 +0200 Subject: Tests: Fix linking of tests when using bundled QtScript Change-Id: Ifd57049c89df83fc5ca9a8cac35af5a16635b312 Reviewed-by: Joerg Bornemann Reviewed-by: Christian Kandeler --- tests/auto/language/language.pro | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/auto/language/language.pro b/tests/auto/language/language.pro index 86248d926..cdb067195 100644 --- a/tests/auto/language/language.pro +++ b/tests/auto/language/language.pro @@ -20,6 +20,5 @@ for(data_dir, DATA_DIRS) { OTHER_FILES += $$FILES qbs_use_bundled_qtscript { - CONFIG += qbs_do_not_link_bundled_qtscript include(../../../src/lib/scriptengine/use_scriptengine.pri) } -- cgit v1.2.3 From 1ba4cceafa1c33207c88d6e897b841680b1f4754 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 20:31:21 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for SDCC STM8 Change-Id: Ifa011d1fe35870b0f5808833fa785259e9ba2cb4 Reviewed-by: Ivan Komissarov --- .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ .../testdata-baremetal/one-object-asm-application/stm8-sdcc.s | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 4f2e36ccc..3c0e91d03 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -13,6 +13,8 @@ BareMetalApplication { } else if (qbs.toolchainType === "sdcc") { if (qbs.architecture === "mcs51") return true; + if (qbs.architecture === "stm8") + return true; } else if (qbs.toolchainType === "gcc") { if (qbs.architecture.startsWith("arm")) return true; diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s new file mode 100644 index 000000000..1a552f4a4 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s @@ -0,0 +1,7 @@ + .globl main + .area DATA + .area SSEG + .area HOME +main: + clrw x + ret -- cgit v1.2.3 From cb958a58d9d5335588cf393c2d1bfe3ad9fe6276 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Sun, 19 Jul 2020 17:57:48 +0200 Subject: xcode: Fix Xcode 12.0 support The *.xcspec files for macOS were moved into the *.ideplugin directory similar to how it is done for iOS/tvOS/etc. Fixes: QBS-1582 Change-Id: I478742c4b52b08f7feee4108272da7bf2b12a744 Reviewed-by: Christian Kandeler --- share/qbs/modules/bundle/BundleModule.qbs | 4 ++-- share/qbs/modules/bundle/bundle.js | 13 +++++++++++++ share/qbs/modules/xcode/xcode.js | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index f285c6e61..bf2555fa3 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -52,6 +52,7 @@ Module { property bool useXcodeBuildSpecs: _useXcodeBuildSpecs property bool isMacOs: qbs.targetOS.contains("macos") property bool xcodePresent: xcode.present + property string xcodeVersion: xcode.version // Note that we include several settings pointing to properties which reference the output // of this probe (WRAPPER_NAME, WRAPPER_EXTENSION, etc.). This is to ensure that derived @@ -83,8 +84,7 @@ Module { var specsPath = path; var specsSeparator = "-"; if (xcodeDeveloperPath && useXcodeBuildSpecs) { - specsPath = xcodeDeveloperPath - + "/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications"; + specsPath = Bundle.macOSSpecsPath(xcodeVersion, xcodeDeveloperPath); specsSeparator = " "; } diff --git a/share/qbs/modules/bundle/bundle.js b/share/qbs/modules/bundle/bundle.js index 2354d88b5..6d9305702 100644 --- a/share/qbs/modules/bundle/bundle.js +++ b/share/qbs/modules/bundle/bundle.js @@ -28,9 +28,11 @@ ** ****************************************************************************/ +var FileInfo = require("qbs.FileInfo"); var DarwinTools = require("qbs.DarwinTools"); var ModUtils = require("qbs.ModUtils"); var Process = require("qbs.Process"); +var Utilities = require("qbs.Utilities"); // HACK: Workaround until the PropertyList extension is supported cross-platform var TextFile = require("qbs.TextFile"); @@ -147,6 +149,17 @@ function _assign(target, source) { } } +function macOSSpecsPath(version, developerPath) { + if (Utilities.versionCompare(version, "12") >= 0) { + return FileInfo.joinPaths( + developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode", + "PrivatePlugIns", "IDEOSXSupportCore.ideplugin", "Contents", "Resources"); + } + return FileInfo.joinPaths( + developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode", + "Specifications"); +} + var XcodeBuildSpecsReader = (function () { function XcodeBuildSpecsReader(specsPath, separator, additionalSettings, useShallowBundles) { this._additionalSettings = additionalSettings; diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js index 48cf3d772..9c87e09dc 100644 --- a/share/qbs/modules/xcode/xcode.js +++ b/share/qbs/modules/xcode/xcode.js @@ -183,6 +183,10 @@ function provisioningProfilePlistContents(filePath) { function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath) { var _specsPluginBaseName; + if (Utilities.versionCompare(version, "12") >= 0) { + if (targetOS.contains("macos")) + _specsPluginBaseName = "OSX"; + } if (Utilities.versionCompare(version, "7") >= 0) { if (targetOS.contains("ios")) _specsPluginBaseName = "iOSPlatform"; -- cgit v1.2.3 From c693cd509e31a946ac5b16662d3d466a20732f3d Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 15 Jul 2020 16:39:41 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for GCC MSP430 Change-Id: Icad69d9f510d09f7e157ef410383f5d39e36f1a6 Reviewed-by: Christian Kandeler --- .../one-object-asm-application/msp430-gcc.s | 5 +++++ .../one-object-asm-application/one-object-asm-application.qbs | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s new file mode 100644 index 000000000..8e8a24980 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s @@ -0,0 +1,5 @@ + .global main + .type main, %function +main: + mov #0, r15 + .LIRD0: diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 3c0e91d03..cd1413d6f 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -20,11 +20,22 @@ BareMetalApplication { return true; if (qbs.architecture === "avr") return true; + if (qbs.architecture === "msp430") + return true; } console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); return false; } + + Properties { + condition: qbs.toolchainType === "gcc" + && qbs.architecture === "msp430" + // We need to use this workaround to enable + // the cpp.driverFlags property. + cpp.linkerPath: cpp.compilerPathByLanguage["c"] + } + files: [(qbs.architecture.startsWith("arm") ? "arm" : qbs.architecture) + "-" + qbs.toolchainType + ".s"] } -- cgit v1.2.3 From f00c3b009f9dc6a50006f4e4811683f19e764763 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Mon, 29 Jun 2020 19:18:10 +0200 Subject: Add support for Cap'n Proto for the c++ language Cap'n Proto is a 'cerialization protocol' similar to protobuf: https://capnproto.org/index.html This patch mainly focuses on the c++ support for the capnp protocol keeping in mind the possibility to add other languages later. Change-Id: Ib19a9df1f45f2787503197791ac597d06cc45e9d Reviewed-by: Christian Kandeler --- .travis.yml | 1 + doc/reference/modules/capnprotocpp-module.qdoc | 116 +++++++ .../capnproto/addressbook_cpp/addressbook.capnp | 55 +++ examples/capnproto/addressbook_cpp/addressbook.cpp | 288 ++++++++++++++++ .../capnproto/addressbook_cpp/addressbook_cpp.qbs | 11 + .../capnproto/calculator_cpp/calculator-client.cpp | 367 +++++++++++++++++++++ .../capnproto/calculator_cpp/calculator-server.cpp | 215 ++++++++++++ examples/capnproto/calculator_cpp/calculator.capnp | 118 +++++++ .../capnproto/calculator_cpp/calculator_cpp.qbs | 26 ++ share/qbs/modules/capnproto/capnproto.js | 97 ++++++ share/qbs/modules/capnproto/capnprotobase.qbs | 65 ++++ share/qbs/modules/capnproto/cpp/capnprotocpp.qbs | 64 ++++ tests/auto/blackbox/testdata/capnproto/bar.capnp | 8 + tests/auto/blackbox/testdata/capnproto/baz.capnp | 8 + .../capnproto/capnproto_absolute_import.cpp | 14 + .../capnproto/capnproto_absolute_import.qbs | 18 + .../blackbox/testdata/capnproto/capnproto_cpp.cpp | 13 + .../blackbox/testdata/capnproto/capnproto_cpp.qbs | 16 + .../capnproto/capnproto_relative_import.cpp | 14 + .../capnproto/capnproto_relative_import.qbs | 17 + tests/auto/blackbox/testdata/capnproto/foo.capnp | 6 + .../blackbox/testdata/capnproto/greeter-client.cpp | 25 ++ .../blackbox/testdata/capnproto/greeter-server.cpp | 27 ++ .../auto/blackbox/testdata/capnproto/greeter.capnp | 13 + .../blackbox/testdata/capnproto/greeter_cpp.qbs | 32 ++ .../blackbox/testdata/capnproto/imports/foo.capnp | 6 + tests/auto/blackbox/tst_blackbox.cpp | 26 ++ tests/auto/blackbox/tst_blackbox.h | 2 + 28 files changed, 1668 insertions(+) create mode 100644 doc/reference/modules/capnprotocpp-module.qdoc create mode 100644 examples/capnproto/addressbook_cpp/addressbook.capnp create mode 100644 examples/capnproto/addressbook_cpp/addressbook.cpp create mode 100644 examples/capnproto/addressbook_cpp/addressbook_cpp.qbs create mode 100644 examples/capnproto/calculator_cpp/calculator-client.cpp create mode 100644 examples/capnproto/calculator_cpp/calculator-server.cpp create mode 100644 examples/capnproto/calculator_cpp/calculator.capnp create mode 100644 examples/capnproto/calculator_cpp/calculator_cpp.qbs create mode 100644 share/qbs/modules/capnproto/capnproto.js create mode 100644 share/qbs/modules/capnproto/capnprotobase.qbs create mode 100644 share/qbs/modules/capnproto/cpp/capnprotocpp.qbs create mode 100644 tests/auto/blackbox/testdata/capnproto/bar.capnp create mode 100644 tests/auto/blackbox/testdata/capnproto/baz.capnp create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp create mode 100644 tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs create mode 100644 tests/auto/blackbox/testdata/capnproto/foo.capnp create mode 100644 tests/auto/blackbox/testdata/capnproto/greeter-client.cpp create mode 100644 tests/auto/blackbox/testdata/capnproto/greeter-server.cpp create mode 100644 tests/auto/blackbox/testdata/capnproto/greeter.capnp create mode 100644 tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs create mode 100644 tests/auto/blackbox/testdata/capnproto/imports/foo.capnp diff --git a/.travis.yml b/.travis.yml index 0840ae82c..ed7c56491 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,6 +89,7 @@ jobs: addons: homebrew: packages: + - capnp - ccache - grpc - icoutils diff --git a/doc/reference/modules/capnprotocpp-module.qdoc b/doc/reference/modules/capnprotocpp-module.qdoc new file mode 100644 index 000000000..b041670ad --- /dev/null +++ b/doc/reference/modules/capnprotocpp-module.qdoc @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \qmltype capnproto.cpp + \inqmlmodule QbsModules + \since Qbs 1.17 + + \brief Provides support for Cap'n Proto for the C++ language. + + The \c capnproto.cpp module provides support for generating C++ headers + and sources from proto definition files using the \c capnpc tool. + + A simple qbs file that uses Cap'n Proto can be written as follows: + \code + CppApplication { + Depends { name: "capnproto.cpp" } + files: ["foo.capnp", "main.cpp"] + } + \endcode + A generated header now can be included in the C++ sources: + \code + #include + + int main(int argc, char* argv[]) { + ::capnp::MallocMessageBuilder message; + + auto foo = message.initRoot(); + foo.setAnswer(42); + return 0; + } + \endcode + + \section2 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"capnproto.input"} + \li \c{*.capnp} + \li 1.17.0 + \li Source files with this tag are considered inputs to the \c capnpc compiler. + \endtable + + \section2 Dependencies + This module depends on the \c capnp module and on the \c capnp-rpc module if + \l{capnproto.cpp::useRpc}{useRpc} property is \c true. These modules are created by the + \l{Module Providers} via the \c pkg-config tool. +*/ + +/*! + \qmlproperty string capnproto.cpp::compilerName + + The name of the capnp binary. + + \defaultvalue \c "capnpc" +*/ + +/*! + \qmlproperty string capnproto.cpp::compilerPath + + The path to the protoc binary. + + Use this property to override the auto-detected location. + + \defaultvalue \c auto-detected +*/ + +/*! + \qmlproperty pathList capnproto.cpp::importPaths + + The list of import paths that are passed to the \c capnpc tool via the \c --import-path option. + + \defaultvalue \c [] +*/ + +/*! + \qmlproperty bool capnproto.cpp::useRpc + + Use this property to enable support for the RPC framework. + + A simple qbs file that uses rpc can be written as follows: + + \quotefile ../examples/capnproto/calculator_cpp/calculator_cpp.qbs + + \defaultvalue \c false +*/ diff --git a/examples/capnproto/addressbook_cpp/addressbook.capnp b/examples/capnproto/addressbook_cpp/addressbook.capnp new file mode 100644 index 000000000..1a6c60937 --- /dev/null +++ b/examples/capnproto/addressbook_cpp/addressbook.capnp @@ -0,0 +1,55 @@ +# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +# Licensed under the MIT License: +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +@0x9eb32e19f86ee174; + +using Cxx = import "/capnp/c++.capnp"; +$Cxx.namespace("addressbook"); + +struct Person { + id @0 :UInt32; + name @1 :Text; + email @2 :Text; + phones @3 :List(PhoneNumber); + + struct PhoneNumber { + number @0 :Text; + type @1 :Type; + + enum Type { + mobile @0; + home @1; + work @2; + } + } + + employment :union { + unemployed @4 :Void; + employer @5 :Text; + school @6 :Text; + selfEmployed @7 :Void; + # We assume that a person is only one of these. + } +} + +struct AddressBook { + people @0 :List(Person); +} diff --git a/examples/capnproto/addressbook_cpp/addressbook.cpp b/examples/capnproto/addressbook_cpp/addressbook.cpp new file mode 100644 index 000000000..b2bece947 --- /dev/null +++ b/examples/capnproto/addressbook_cpp/addressbook.cpp @@ -0,0 +1,288 @@ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// This sample code appears in the documentation for the C++ implementation. +// +// If Cap'n Proto is installed, build the sample like: +// capnp compile -oc++ addressbook.capnp +// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ `pkg-config --cflags --libs capnp` -o addressbook +// +// If Cap'n Proto is not installed, but the source is located at $SRC and has been +// compiled in $BUILD (often both are simply ".." from here), you can do: +// $BUILD/capnp compile -I$SRC/src -o$BUILD/capnpc-c++ addressbook.capnp +// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ -I$SRC/src -L$BUILD/.libs -lcapnp -lkj -o addressbook +// +// Run like: +// ./addressbook write | ./addressbook read +// Use "dwrite" and "dread" to use dynamic code instead. + +// TODO(test): Needs cleanup. + +#include "addressbook.capnp.h" +#include +#include +#include + +using addressbook::Person; +using addressbook::AddressBook; + +void writeAddressBook(int fd) { + ::capnp::MallocMessageBuilder message; + + AddressBook::Builder addressBook = message.initRoot(); + ::capnp::List::Builder people = addressBook.initPeople(2); + + Person::Builder alice = people[0]; + alice.setId(123); + alice.setName("Alice"); + alice.setEmail("alice@example.com"); + // Type shown for explanation purposes; normally you'd use auto. + ::capnp::List::Builder alicePhones = + alice.initPhones(1); + alicePhones[0].setNumber("555-1212"); + alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE); + alice.getEmployment().setSchool("MIT"); + + Person::Builder bob = people[1]; + bob.setId(456); + bob.setName("Bob"); + bob.setEmail("bob@example.com"); + auto bobPhones = bob.initPhones(2); + bobPhones[0].setNumber("555-4567"); + bobPhones[0].setType(Person::PhoneNumber::Type::HOME); + bobPhones[1].setNumber("555-7654"); + bobPhones[1].setType(Person::PhoneNumber::Type::WORK); + bob.getEmployment().setUnemployed(); + + writePackedMessageToFd(fd, message); +} + +void printAddressBook(int fd) { + ::capnp::PackedFdMessageReader message(fd); + + AddressBook::Reader addressBook = message.getRoot(); + + for (Person::Reader person : addressBook.getPeople()) { + std::cout << person.getName().cStr() << ": " + << person.getEmail().cStr() << std::endl; + for (Person::PhoneNumber::Reader phone: person.getPhones()) { + const char* typeName = "UNKNOWN"; + switch (phone.getType()) { + case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break; + case Person::PhoneNumber::Type::HOME: typeName = "home"; break; + case Person::PhoneNumber::Type::WORK: typeName = "work"; break; + } + std::cout << " " << typeName << " phone: " + << phone.getNumber().cStr() << std::endl; + } + Person::Employment::Reader employment = person.getEmployment(); + switch (employment.which()) { + case Person::Employment::UNEMPLOYED: + std::cout << " unemployed" << std::endl; + break; + case Person::Employment::EMPLOYER: + std::cout << " employer: " + << employment.getEmployer().cStr() << std::endl; + break; + case Person::Employment::SCHOOL: + std::cout << " student at: " + << employment.getSchool().cStr() << std::endl; + break; + case Person::Employment::SELF_EMPLOYED: + std::cout << " self-employed" << std::endl; + break; + } + } +} + +#if !CAPNP_LITE + +#include "addressbook.capnp.h" +#include +#include +#include +#include +#include + +using ::capnp::DynamicValue; +using ::capnp::DynamicStruct; +using ::capnp::DynamicEnum; +using ::capnp::DynamicList; +using ::capnp::List; +using ::capnp::Schema; +using ::capnp::StructSchema; +using ::capnp::EnumSchema; + +using ::capnp::Void; +using ::capnp::Text; +using ::capnp::MallocMessageBuilder; +using ::capnp::PackedFdMessageReader; + +void dynamicWriteAddressBook(int fd, StructSchema schema) { + // Write a message using the dynamic API to set each + // field by text name. This isn't something you'd + // normally want to do; it's just for illustration. + + MallocMessageBuilder message; + + // Types shown for explanation purposes; normally you'd + // use auto. + DynamicStruct::Builder addressBook = + message.initRoot(schema); + + DynamicList::Builder people = + addressBook.init("people", 2).as(); + + DynamicStruct::Builder alice = + people[0].as(); + alice.set("id", 123); + alice.set("name", "Alice"); + alice.set("email", "alice@example.com"); + auto alicePhones = alice.init("phones", 1).as(); + auto phone0 = alicePhones[0].as(); + phone0.set("number", "555-1212"); + phone0.set("type", "mobile"); + alice.get("employment").as() + .set("school", "MIT"); + + auto bob = people[1].as(); + bob.set("id", 456); + bob.set("name", "Bob"); + bob.set("email", "bob@example.com"); + + // Some magic: We can convert a dynamic sub-value back to + // the native type with as()! + List::Builder bobPhones = + bob.init("phones", 2).as>(); + bobPhones[0].setNumber("555-4567"); + bobPhones[0].setType(Person::PhoneNumber::Type::HOME); + bobPhones[1].setNumber("555-7654"); + bobPhones[1].setType(Person::PhoneNumber::Type::WORK); + bob.get("employment").as() + .set("unemployed", ::capnp::VOID); + + writePackedMessageToFd(fd, message); +} + +void dynamicPrintValue(DynamicValue::Reader value) { + // Print an arbitrary message via the dynamic API by + // iterating over the schema. Look at the handling + // of STRUCT in particular. + + switch (value.getType()) { + case DynamicValue::VOID: + std::cout << ""; + break; + case DynamicValue::BOOL: + std::cout << (value.as() ? "true" : "false"); + break; + case DynamicValue::INT: + std::cout << value.as(); + break; + case DynamicValue::UINT: + std::cout << value.as(); + break; + case DynamicValue::FLOAT: + std::cout << value.as(); + break; + case DynamicValue::TEXT: + std::cout << '\"' << value.as().cStr() << '\"'; + break; + case DynamicValue::LIST: { + std::cout << "["; + bool first = true; + for (auto element: value.as()) { + if (first) { + first = false; + } else { + std::cout << ", "; + } + dynamicPrintValue(element); + } + std::cout << "]"; + break; + } + case DynamicValue::ENUM: { + auto enumValue = value.as(); + KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) { + std::cout << + enumerant->getProto().getName().cStr(); + } else { + // Unknown enum value; output raw number. + std::cout << enumValue.getRaw(); + } + break; + } + case DynamicValue::STRUCT: { + std::cout << "("; + auto structValue = value.as(); + bool first = true; + for (auto field: structValue.getSchema().getFields()) { + if (!structValue.has(field)) continue; + if (first) { + first = false; + } else { + std::cout << ", "; + } + std::cout << field.getProto().getName().cStr() + << " = "; + dynamicPrintValue(structValue.get(field)); + } + std::cout << ")"; + break; + } + default: + // There are other types, we aren't handling them. + std::cout << "?"; + break; + } +} + +void dynamicPrintMessage(int fd, StructSchema schema) { + PackedFdMessageReader message(fd); + dynamicPrintValue(message.getRoot(schema)); + std::cout << std::endl; +} + +#endif // !CAPNP_LITE + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Missing arg." << std::endl; + return 1; + } else if (strcmp(argv[1], "write") == 0) { + writeAddressBook(1); + } else if (strcmp(argv[1], "read") == 0) { + printAddressBook(0); +#if !CAPNP_LITE + } else if (strcmp(argv[1], "dwrite") == 0) { + StructSchema schema = Schema::from(); + dynamicWriteAddressBook(1, schema); + } else if (strcmp(argv[1], "dread") == 0) { + StructSchema schema = Schema::from(); + dynamicPrintMessage(0, schema); +#endif + } else { + std::cerr << "Invalid arg: " << argv[1] << std::endl; + return 1; + } + return 0; +} diff --git a/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs b/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs new file mode 100644 index 000000000..33f78d5a5 --- /dev/null +++ b/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs @@ -0,0 +1,11 @@ +CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: capnproto.cpp.present && qbs.targetPlatform === qbs.hostPlatform + consoleApplication: true + cpp.minimumMacosVersion: "10.8" + + files: [ + "addressbook.capnp", + "addressbook.cpp" + ] +} diff --git a/examples/capnproto/calculator_cpp/calculator-client.cpp b/examples/capnproto/calculator_cpp/calculator-client.cpp new file mode 100644 index 000000000..5d8452921 --- /dev/null +++ b/examples/capnproto/calculator_cpp/calculator-client.cpp @@ -0,0 +1,367 @@ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "calculator.capnp.h" +#include +#include +#include +#include + +class PowerFunction final: public Calculator::Function::Server { + // An implementation of the Function interface wrapping pow(). Note that + // we're implementing this on the client side and will pass a reference to + // the server. The server will then be able to make calls back to the client. + +public: + kj::Promise call(CallContext context) { + auto params = context.getParams().getParams(); + KJ_REQUIRE(params.size() == 2, "Wrong number of parameters."); + context.getResults().setValue(pow(params[0], params[1])); + return kj::READY_NOW; + } +}; + +int main(int argc, const char* argv[]) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " HOST:PORT\n" + "Connects to the Calculator server at the given address and " + "does some RPCs." << std::endl; + return 1; + } + + capnp::EzRpcClient client(argv[1]); + Calculator::Client calculator = client.getMain(); + + // Keep an eye on `waitScope`. Whenever you see it used is a place where we + // stop and wait for the server to respond. If a line of code does not use + // `waitScope`, then it does not block! + auto& waitScope = client.getWaitScope(); + + { + // Make a request that just evaluates the literal value 123. + // + // What's interesting here is that evaluate() returns a "Value", which is + // another interface and therefore points back to an object living on the + // server. We then have to call read() on that object to read it. + // However, even though we are making two RPC's, this block executes in + // *one* network round trip because of promise pipelining: we do not wait + // for the first call to complete before we send the second call to the + // server. + + std::cout << "Evaluating a literal... "; + std::cout.flush(); + + // Set up the request. + auto request = calculator.evaluateRequest(); + request.getExpression().setLiteral(123); + + // Send it, which returns a promise for the result (without blocking). + auto evalPromise = request.send(); + + // Using the promise, create a pipelined request to call read() on the + // returned object, and then send that. + auto readPromise = evalPromise.getValue().readRequest().send(); + + // Now that we've sent all the requests, wait for the response. Until this + // point, we haven't waited at all! + auto response = readPromise.wait(waitScope); + KJ_ASSERT(response.getValue() == 123); + + std::cout << "PASS" << std::endl; + } + + { + // Make a request to evaluate 123 + 45 - 67. + // + // The Calculator interface requires that we first call getOperator() to + // get the addition and subtraction functions, then call evaluate() to use + // them. But, once again, we can get both functions, call evaluate(), and + // then read() the result -- four RPCs -- in the time of *one* network + // round trip, because of promise pipelining. + + std::cout << "Using add and subtract... "; + std::cout.flush(); + + Calculator::Function::Client add = nullptr; + Calculator::Function::Client subtract = nullptr; + + { + // Get the "add" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::ADD); + add = request.send().getFunc(); + } + + { + // Get the "subtract" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::SUBTRACT); + subtract = request.send().getFunc(); + } + + // Build the request to evaluate 123 + 45 - 67. + auto request = calculator.evaluateRequest(); + + auto subtractCall = request.getExpression().initCall(); + subtractCall.setFunction(subtract); + auto subtractParams = subtractCall.initParams(2); + subtractParams[1].setLiteral(67); + + auto addCall = subtractParams[0].initCall(); + addCall.setFunction(add); + auto addParams = addCall.initParams(2); + addParams[0].setLiteral(123); + addParams[1].setLiteral(45); + + // Send the evaluate() request, read() the result, and wait for read() to + // finish. + auto evalPromise = request.send(); + auto readPromise = evalPromise.getValue().readRequest().send(); + + auto response = readPromise.wait(waitScope); + KJ_ASSERT(response.getValue() == 101); + + std::cout << "PASS" << std::endl; + } + + { + // Make a request to evaluate 4 * 6, then use the result in two more + // requests that add 3 and 5. + // + // Since evaluate() returns its result wrapped in a `Value`, we can pass + // that `Value` back to the server in subsequent requests before the first + // `evaluate()` has actually returned. Thus, this example again does only + // one network round trip. + + std::cout << "Pipelining eval() calls... "; + std::cout.flush(); + + Calculator::Function::Client add = nullptr; + Calculator::Function::Client multiply = nullptr; + + { + // Get the "add" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::ADD); + add = request.send().getFunc(); + } + + { + // Get the "multiply" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::MULTIPLY); + multiply = request.send().getFunc(); + } + + // Build the request to evaluate 4 * 6 + auto request = calculator.evaluateRequest(); + + auto multiplyCall = request.getExpression().initCall(); + multiplyCall.setFunction(multiply); + auto multiplyParams = multiplyCall.initParams(2); + multiplyParams[0].setLiteral(4); + multiplyParams[1].setLiteral(6); + + auto multiplyResult = request.send().getValue(); + + // Use the result in two calls that add 3 and add 5. + + auto add3Request = calculator.evaluateRequest(); + auto add3Call = add3Request.getExpression().initCall(); + add3Call.setFunction(add); + auto add3Params = add3Call.initParams(2); + add3Params[0].setPreviousResult(multiplyResult); + add3Params[1].setLiteral(3); + auto add3Promise = add3Request.send().getValue().readRequest().send(); + + auto add5Request = calculator.evaluateRequest(); + auto add5Call = add5Request.getExpression().initCall(); + add5Call.setFunction(add); + auto add5Params = add5Call.initParams(2); + add5Params[0].setPreviousResult(multiplyResult); + add5Params[1].setLiteral(5); + auto add5Promise = add5Request.send().getValue().readRequest().send(); + + // Now wait for the results. + KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27); + KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29); + + std::cout << "PASS" << std::endl; + } + + { + // Our calculator interface supports defining functions. Here we use it + // to define two functions and then make calls to them as follows: + // + // f(x, y) = x * 100 + y + // g(x) = f(x, x + 1) * 2; + // f(12, 34) + // g(21) + // + // Once again, the whole thing takes only one network round trip. + + std::cout << "Defining functions... "; + std::cout.flush(); + + Calculator::Function::Client add = nullptr; + Calculator::Function::Client multiply = nullptr; + Calculator::Function::Client f = nullptr; + Calculator::Function::Client g = nullptr; + + { + // Get the "add" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::ADD); + add = request.send().getFunc(); + } + + { + // Get the "multiply" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::MULTIPLY); + multiply = request.send().getFunc(); + } + + { + // Define f. + auto request = calculator.defFunctionRequest(); + request.setParamCount(2); + + { + // Build the function body. + auto addCall = request.getBody().initCall(); + addCall.setFunction(add); + auto addParams = addCall.initParams(2); + addParams[1].setParameter(1); // y + + auto multiplyCall = addParams[0].initCall(); + multiplyCall.setFunction(multiply); + auto multiplyParams = multiplyCall.initParams(2); + multiplyParams[0].setParameter(0); // x + multiplyParams[1].setLiteral(100); + } + + f = request.send().getFunc(); + } + + { + // Define g. + auto request = calculator.defFunctionRequest(); + request.setParamCount(1); + + { + // Build the function body. + auto multiplyCall = request.getBody().initCall(); + multiplyCall.setFunction(multiply); + auto multiplyParams = multiplyCall.initParams(2); + multiplyParams[1].setLiteral(2); + + auto fCall = multiplyParams[0].initCall(); + fCall.setFunction(f); + auto fParams = fCall.initParams(2); + fParams[0].setParameter(0); + + auto addCall = fParams[1].initCall(); + addCall.setFunction(add); + auto addParams = addCall.initParams(2); + addParams[0].setParameter(0); + addParams[1].setLiteral(1); + } + + g = request.send().getFunc(); + } + + // OK, we've defined all our functions. Now create our eval requests. + + // f(12, 34) + auto fEvalRequest = calculator.evaluateRequest(); + auto fCall = fEvalRequest.initExpression().initCall(); + fCall.setFunction(f); + auto fParams = fCall.initParams(2); + fParams[0].setLiteral(12); + fParams[1].setLiteral(34); + auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send(); + + // g(21) + auto gEvalRequest = calculator.evaluateRequest(); + auto gCall = gEvalRequest.initExpression().initCall(); + gCall.setFunction(g); + gCall.initParams(1)[0].setLiteral(21); + auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send(); + + // Wait for the results. + KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234); + KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244); + + std::cout << "PASS" << std::endl; + } + + { + // Make a request that will call back to a function defined locally. + // + // Specifically, we will compute 2^(4 + 5). However, exponent is not + // defined by the Calculator server. So, we'll implement the Function + // interface locally and pass it to the server for it to use when + // evaluating the expression. + // + // This example requires two network round trips to complete, because the + // server calls back to the client once before finishing. In this + // particular case, this could potentially be optimized by using a tail + // call on the server side -- see CallContext::tailCall(). However, to + // keep the example simpler, we haven't implemented this optimization in + // the sample server. + + std::cout << "Using a callback... "; + std::cout.flush(); + + Calculator::Function::Client add = nullptr; + + { + // Get the "add" function from the server. + auto request = calculator.getOperatorRequest(); + request.setOp(Calculator::Operator::ADD); + add = request.send().getFunc(); + } + + // Build the eval request for 2^(4+5). + auto request = calculator.evaluateRequest(); + + auto powCall = request.getExpression().initCall(); + powCall.setFunction(kj::heap()); + auto powParams = powCall.initParams(2); + powParams[0].setLiteral(2); + + auto addCall = powParams[1].initCall(); + addCall.setFunction(add); + auto addParams = addCall.initParams(2); + addParams[0].setLiteral(4); + addParams[1].setLiteral(5); + + // Send the request and wait. + auto response = request.send().getValue().readRequest() + .send().wait(waitScope); + KJ_ASSERT(response.getValue() == 512); + + std::cout << "PASS" << std::endl; + } + + return 0; +} diff --git a/examples/capnproto/calculator_cpp/calculator-server.cpp b/examples/capnproto/calculator_cpp/calculator-server.cpp new file mode 100644 index 000000000..c2593be3a --- /dev/null +++ b/examples/capnproto/calculator_cpp/calculator-server.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "calculator.capnp.h" +#include +#include +#include +#include + +typedef unsigned int uint; + +kj::Promise readValue(Calculator::Value::Client value) { + // Helper function to asynchronously call read() on a Calculator::Value and + // return a promise for the result. (In the future, the generated code might + // include something like this automatically.) + + return value.readRequest().send() + .then([](capnp::Response result) { + return result.getValue(); + }); +} + +kj::Promise evaluateImpl( + Calculator::Expression::Reader expression, + capnp::List::Reader params = capnp::List::Reader()) { + // Implementation of CalculatorImpl::evaluate(), also shared by + // FunctionImpl::call(). In the latter case, `params` are the parameter + // values passed to the function; in the former case, `params` is just an + // empty list. + + switch (expression.which()) { + case Calculator::Expression::LITERAL: + return expression.getLiteral(); + + case Calculator::Expression::PREVIOUS_RESULT: + return readValue(expression.getPreviousResult()); + + case Calculator::Expression::PARAMETER: { + KJ_REQUIRE(expression.getParameter() < params.size(), + "Parameter index out-of-range."); + return params[expression.getParameter()]; + } + + case Calculator::Expression::CALL: { + auto call = expression.getCall(); + auto func = call.getFunction(); + + // Evaluate each parameter. + kj::Array> paramPromises = + KJ_MAP(param, call.getParams()) { + return evaluateImpl(param, params); + }; + + // Join the array of promises into a promise for an array. + kj::Promise> joinedParams = + kj::joinPromises(kj::mv(paramPromises)); + + // When the parameters are complete, call the function. + return joinedParams.then([KJ_CPCAP(func)](kj::Array&& paramValues) mutable { + auto request = func.callRequest(); + request.setParams(paramValues); + return request.send().then( + [](capnp::Response&& result) { + return result.getValue(); + }); + }); + } + + default: + // Throw an exception. + KJ_FAIL_REQUIRE("Unknown expression type."); + } +} + +class ValueImpl final: public Calculator::Value::Server { + // Simple implementation of the Calculator.Value Cap'n Proto interface. + +public: + ValueImpl(double value): value(value) {} + + kj::Promise read(ReadContext context) { + context.getResults().setValue(value); + return kj::READY_NOW; + } + +private: + double value; +}; + +class FunctionImpl final: public Calculator::Function::Server { + // Implementation of the Calculator.Function Cap'n Proto interface, where the + // function is defined by a Calculator.Expression. + +public: + FunctionImpl(uint paramCount, Calculator::Expression::Reader body) + : paramCount(paramCount) { + this->body.setRoot(body); + } + + kj::Promise call(CallContext context) { + auto params = context.getParams().getParams(); + KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters."); + + return evaluateImpl(body.getRoot(), params) + .then([KJ_CPCAP(context)](double value) mutable { + context.getResults().setValue(value); + }); + } + +private: + uint paramCount; + // The function's arity. + + capnp::MallocMessageBuilder body; + // Stores a permanent copy of the function body. +}; + +class OperatorImpl final: public Calculator::Function::Server { + // Implementation of the Calculator.Function Cap'n Proto interface, wrapping + // basic binary arithmetic operators. + +public: + OperatorImpl(Calculator::Operator op): op(op) {} + + kj::Promise call(CallContext context) { + auto params = context.getParams().getParams(); + KJ_REQUIRE(params.size() == 2, "Wrong number of parameters."); + + double result; + switch (op) { + case Calculator::Operator::ADD: result = params[0] + params[1]; break; + case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break; + case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break; + case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break; + default: + KJ_FAIL_REQUIRE("Unknown operator."); + } + + context.getResults().setValue(result); + return kj::READY_NOW; + } + +private: + Calculator::Operator op; +}; + +class CalculatorImpl final: public Calculator::Server { + // Implementation of the Calculator Cap'n Proto interface. + +public: + kj::Promise evaluate(EvaluateContext context) override { + return evaluateImpl(context.getParams().getExpression()) + .then([KJ_CPCAP(context)](double value) mutable { + context.getResults().setValue(kj::heap(value)); + }); + } + + kj::Promise defFunction(DefFunctionContext context) override { + auto params = context.getParams(); + context.getResults().setFunc(kj::heap( + params.getParamCount(), params.getBody())); + return kj::READY_NOW; + } + + kj::Promise getOperator(GetOperatorContext context) override { + context.getResults().setFunc(kj::heap( + context.getParams().getOp())); + return kj::READY_NOW; + } +}; + +int main(int argc, const char* argv[]) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n" + "Runs the server bound to the given address/port.\n" + "ADDRESS may be '*' to bind to all local addresses.\n" + ":PORT may be omitted to choose a port automatically." << std::endl; + return 1; + } + + // Set up a server. + capnp::EzRpcServer server(kj::heap(), argv[1]); + + // Write the port number to stdout, in case it was chosen automatically. + auto& waitScope = server.getWaitScope(); + uint port = server.getPort().wait(waitScope); + if (port == 0) { + // The address format "unix:/path/to/socket" opens a unix domain socket, + // in which case the port will be zero. + std::cout << "Listening on Unix socket..." << std::endl; + } else { + std::cout << "Listening on port " << port << "..." << std::endl; + } + + // Run forever, accepting connections and handling requests. + kj::NEVER_DONE.wait(waitScope); +} diff --git a/examples/capnproto/calculator_cpp/calculator.capnp b/examples/capnproto/calculator_cpp/calculator.capnp new file mode 100644 index 000000000..adc8294e5 --- /dev/null +++ b/examples/capnproto/calculator_cpp/calculator.capnp @@ -0,0 +1,118 @@ +# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +# Licensed under the MIT License: +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +@0x85150b117366d14b; + +interface Calculator { + # A "simple" mathematical calculator, callable via RPC. + # + # But, to show off Cap'n Proto, we add some twists: + # + # - You can use the result from one call as the input to the next + # without a network round trip. To accomplish this, evaluate() + # returns a `Value` object wrapping the actual numeric value. + # This object may be used in a subsequent expression. With + # promise pipelining, the Value can actually be used before + # the evaluate() call that creates it returns! + # + # - You can define new functions, and then call them. This again + # shows off pipelining, but it also gives the client the + # opportunity to define a function on the client side and have + # the server call back to it. + # + # - The basic arithmetic operators are exposed as Functions, and + # you have to call getOperator() to obtain them from the server. + # This again demonstrates pipelining -- using getOperator() to + # get each operator and then using them in evaluate() still + # only takes one network round trip. + + evaluate @0 (expression :Expression) -> (value :Value); + # Evaluate the given expression and return the result. The + # result is returned wrapped in a Value interface so that you + # may pass it back to the server in a pipelined request. To + # actually get the numeric value, you must call read() on the + # Value -- but again, this can be pipelined so that it incurs + # no additional latency. + + struct Expression { + # A numeric expression. + + union { + literal @0 :Float64; + # A literal numeric value. + + previousResult @1 :Value; + # A value that was (or, will be) returned by a previous + # evaluate(). + + parameter @2 :UInt32; + # A parameter to the function (only valid in function bodies; + # see defFunction). + + call :group { + # Call a function on a list of parameters. + function @3 :Function; + params @4 :List(Expression); + } + } + } + + interface Value { + # Wraps a numeric value in an RPC object. This allows the value + # to be used in subsequent evaluate() requests without the client + # waiting for the evaluate() that returns the Value to finish. + + read @0 () -> (value :Float64); + # Read back the raw numeric value. + } + + defFunction @1 (paramCount :Int32, body :Expression) + -> (func :Function); + # Define a function that takes `paramCount` parameters and returns the + # evaluation of `body` after substituting these parameters. + + interface Function { + # An algebraic function. Can be called directly, or can be used inside + # an Expression. + # + # A client can create a Function that runs on the server side using + # `defFunction()` or `getOperator()`. Alternatively, a client can + # implement a Function on the client side and the server will call back + # to it. However, a function defined on the client side will require a + # network round trip whenever the server needs to call it, whereas + # functions defined on the server and then passed back to it are called + # locally. + + call @0 (params :List(Float64)) -> (value :Float64); + # Call the function on the given parameters. + } + + getOperator @2 (op :Operator) -> (func :Function); + # Get a Function representing an arithmetic operator, which can then be + # used in Expressions. + + enum Operator { + add @0; + subtract @1; + multiply @2; + divide @3; + } +} diff --git a/examples/capnproto/calculator_cpp/calculator_cpp.qbs b/examples/capnproto/calculator_cpp/calculator_cpp.qbs new file mode 100644 index 000000000..862a237c6 --- /dev/null +++ b/examples/capnproto/calculator_cpp/calculator_cpp.qbs @@ -0,0 +1,26 @@ +Project { + CppApplication { + Depends { name: "capnproto.cpp"; required: false } + name: "server" + condition: capnproto.cpp.present && qbs.targetPlatform === qbs.hostPlatform + consoleApplication: true + capnproto.cpp.useRpc: true + + files: [ + "calculator.capnp", + "calculator-server.cpp" + ] + } + CppApplication { + Depends { name: "capnproto.cpp"; required: false } + name: "client" + condition: capnproto.cpp.present && qbs.targetPlatform === qbs.hostPlatform + consoleApplication: true + capnproto.cpp.useRpc: true + + files: [ + "calculator.capnp", + "calculator-client.cpp" + ] + } +} diff --git a/share/qbs/modules/capnproto/capnproto.js b/share/qbs/modules/capnproto/capnproto.js new file mode 100644 index 000000000..dff379321 --- /dev/null +++ b/share/qbs/modules/capnproto/capnproto.js @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** 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. +** +****************************************************************************/ + +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var Utilities = require("qbs.Utilities"); + +function validateCompiler(name, path) { + if (!File.exists(path)) + throw "Cannot find executable '" + name + "'. Please set the compilerPath " + + "property or make sure the compiler is found in PATH"; +} + +function validatePlugin(name, path) { + if (!name) + throw "pluginName is not set"; + if (!File.exists(path)) + throw "Cannot find plugin '" + name + "'. Please set the pluginPath " + + "property or make sure the plugin is found in PATH"; +} + +function getOutputDir(module, input) { + var outputDir = module._outputDir; + var importPaths = module.importPaths; + if (importPaths.length !== 0) { + var canonicalInput = File.canonicalFilePath(FileInfo.path(input.filePath)); + for (var i = 0; i < importPaths.length; ++i) { + var path = File.canonicalFilePath(importPaths[i]); + + if (canonicalInput.startsWith(path)) { + return outputDir + "/" + FileInfo.relativePath(path, canonicalInput); + } + } + } + return outputDir; +} + +function artifact(outputDir, input, tag, suffix) { + return { + fileTags: [tag], + filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix), + cpp: { + includePaths: [].concat(input.cpp.includePaths, outputDir), + warningLevel: "none", + } + }; +} + +function doPrepare(module, product, input, outputs, lang) +{ + var outputDir = FileInfo.path(outputs["cpp"][0].filePath); + + var args = []; + args.push("--output=" + module.pluginPath + ":" + outputDir); + args.push("--src-prefix=" + FileInfo.path(input.filePath)); + + var importPaths = module.importPaths; + importPaths.forEach(function(path) { + if (!FileInfo.isAbsolutePath(path)) + path = FileInfo.joinPaths(product.sourceDirectory, path); + args.push("--import-path", path); + }); + + args.push(input.filePath); + + var cmd = new Command(module.compilerPath, args); + cmd.highlight = "codegen"; + cmd.description = "generating " + lang + " files for " + input.fileName; + return [cmd]; +} diff --git a/share/qbs/modules/capnproto/capnprotobase.qbs b/share/qbs/modules/capnproto/capnprotobase.qbs new file mode 100644 index 000000000..e557f7b77 --- /dev/null +++ b/share/qbs/modules/capnproto/capnprotobase.qbs @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** 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. +** +****************************************************************************/ + +import qbs +import qbs.Probes +import "capnproto.js" as HelperFunctions + +Module { + property string compilerName: "capnpc" + property string compilerPath: compilerProbe.filePath + + property string pluginName + property string pluginPath: pluginProbe.filePath + + property pathList importPaths: [] + + property string _outputDir: product.buildDirectory + "/capnp" + + Probes.BinaryProbe { + id: compilerProbe + names: compilerName ? [compilerName] : [] + } + + Probes.BinaryProbe { + id: pluginProbe + names: pluginName ? [pluginName] : [] + } + + FileTagger { + patterns: ["*.capnp"] + fileTags: ["capnproto.input"]; + } + + validate: { + HelperFunctions.validateCompiler(compilerName, compilerPath); + HelperFunctions.validatePlugin(pluginName, pluginPath); + } +} diff --git a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs new file mode 100644 index 000000000..e8c61dc89 --- /dev/null +++ b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com) +** 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. +** +****************************************************************************/ + +import "../capnprotobase.qbs" as CapnProtoBase +import "../capnproto.js" as HelperFunctions + +CapnProtoBase { + property bool useRpc: false + + Depends { name: "cpp" } + Depends { name: "capnp" } + Depends { name: "capnp-rpc"; condition: useRpc } + + pluginName: "capnpc-c++" + + cpp.systemIncludePaths: _outputDir + cpp.cxxLanguageVersion: "c++14" + + Rule { + inputs: ["capnproto.input"] + outputFileTags: ["hpp", "cpp"] + outputArtifacts: { + var outputDir = HelperFunctions.getOutputDir(input.capnproto.cpp, input); + var result = [ + HelperFunctions.artifact(outputDir, input, "hpp", ".capnp.h"), + HelperFunctions.artifact(outputDir, input, "cpp", ".capnp.c++") + ]; + return result; + } + + prepare: { + var result = HelperFunctions.doPrepare( + input.capnproto.cpp, product, input, outputs, "cpp"); + return result; + } + } +} diff --git a/tests/auto/blackbox/testdata/capnproto/bar.capnp b/tests/auto/blackbox/testdata/capnproto/bar.capnp new file mode 100644 index 000000000..a0e8a0f8c --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/bar.capnp @@ -0,0 +1,8 @@ +@0xc967c84bcca70a1d; + +using Foo = import "foo.capnp"; + +struct Bar { + foo @0 :Foo.Foo; + # Use type "Foo" defined in foo.capnp. +} diff --git a/tests/auto/blackbox/testdata/capnproto/baz.capnp b/tests/auto/blackbox/testdata/capnproto/baz.capnp new file mode 100644 index 000000000..8b2fe4faf --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/baz.capnp @@ -0,0 +1,8 @@ +@0xc967c84bcca70a1d; + +using Foo = import "/imports/foo.capnp"; + +struct Baz { + foo @0 :Foo.Foo; + # Use type "Foo" defined in foo.capnp. +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp new file mode 100644 index 000000000..0e8979eec --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp @@ -0,0 +1,14 @@ +#include "baz.capnp.h" + +#include + +int main() +{ + ::capnp::MallocMessageBuilder message; + + auto baz = message.initRoot(); + auto foo = baz.initFoo(); + foo.setStr("hello"); + + return 0; +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs new file mode 100644 index 000000000..ee0903f73 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs @@ -0,0 +1,18 @@ +CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + if (!capnproto.cpp.present) + console.info("capnproto is not present"); + return result && capnproto.cpp.present; + } + cpp.minimumMacosVersion: "10.8" + capnproto.cpp.importPaths: "." + files: [ + "baz.capnp", + "capnproto_absolute_import.cpp", + "imports/foo.capnp", + ] +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp new file mode 100644 index 000000000..b9f729955 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp @@ -0,0 +1,13 @@ +#include "foo.capnp.h" + +#include + +int main() +{ + ::capnp::MallocMessageBuilder message; + + auto foo = message.initRoot(); + foo.setStr("hello"); + + return 0; +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs new file mode 100644 index 000000000..d7ee1b4c9 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs @@ -0,0 +1,16 @@ +CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + if (!capnproto.cpp.present) + console.info("capnproto is not present"); + return result && capnproto.cpp.present; + } + cpp.minimumMacosVersion: "10.8" + files: [ + "capnproto_cpp.cpp", + "foo.capnp" + ] +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp new file mode 100644 index 000000000..5116bd3d6 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp @@ -0,0 +1,14 @@ +#include "bar.capnp.h" + +#include + +int main() +{ + ::capnp::MallocMessageBuilder message; + + auto bar = message.initRoot(); + auto foo = bar.initFoo(); + foo.setStr("hello"); + + return 0; +} diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs new file mode 100644 index 000000000..7c1991d8f --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs @@ -0,0 +1,17 @@ +CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + if (!capnproto.cpp.present) + console.info("capnproto is not present"); + return result && capnproto.cpp.present; + } + cpp.minimumMacosVersion: "10.8" + files: [ + "bar.capnp", + "capnproto_relative_import.cpp", + "foo.capnp", + ] +} diff --git a/tests/auto/blackbox/testdata/capnproto/foo.capnp b/tests/auto/blackbox/testdata/capnproto/foo.capnp new file mode 100644 index 000000000..146a2969f --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/foo.capnp @@ -0,0 +1,6 @@ +@0x8a2efe67220790be; + +struct Foo { + num @0 :UInt32; + str @1 :Text; +} diff --git a/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp b/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp new file mode 100644 index 000000000..d3fcdb4e3 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp @@ -0,0 +1,25 @@ +#include "greeter.capnp.h" + +#include + +#include + +int main(int argc, char *argv[]) +{ + const char address[] = "localhost:5050"; + capnp::EzRpcClient client(address); + Greeter::Client greeter = client.getMain(); + + auto& waitScope = client.getWaitScope(); + + for (int i = 0; i < 2; ++i) { + auto request = greeter.sayHelloRequest(); + request.initRequest().setName("hello workd"); + auto promise = request.send(); + + auto response = promise.wait(waitScope); + std::cout << response.getResponse().getName().cStr() << std::endl; + } + + return 0; +} diff --git a/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp b/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp new file mode 100644 index 000000000..a7f482cc8 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp @@ -0,0 +1,27 @@ +#include "greeter.capnp.h" + +#include +#include + +#include + +class GreeterImpl final: public Greeter::Server +{ +public: + ::kj::Promise sayHello(SayHelloContext context) override + { + auto response = context.getResults().initResponse(); + response.setName(context.getParams().getRequest().getName()); + return kj::READY_NOW; + }; +}; + +int main(int argc, char *argv[]) +{ + const char address[] = "localhost:5050"; + capnp::EzRpcServer server(kj::heap(), address); + + auto& waitScope = server.getWaitScope(); + // Run forever, accepting connections and handling requests. + kj::NEVER_DONE.wait(waitScope); +} diff --git a/tests/auto/blackbox/testdata/capnproto/greeter.capnp b/tests/auto/blackbox/testdata/capnproto/greeter.capnp new file mode 100644 index 000000000..b9188f634 --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/greeter.capnp @@ -0,0 +1,13 @@ +@0x85150b117366d14b; + +struct HelloRequest { + name @0 :Text; +} + +struct HelloResponse { + name @0 :Text; +} + +interface Greeter { + sayHello @0 (request: HelloRequest) -> (response: HelloResponse); +} diff --git a/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs new file mode 100644 index 000000000..cf95b968b --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs @@ -0,0 +1,32 @@ +Project { + CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + if (!capnproto.cpp.present) + console.info("capnproto is not present"); + return result && capnproto.cpp.present; + } + name: "server" + consoleApplication: true + cpp.minimumMacosVersion: "10.8" + capnproto.cpp.useRpc: true + files: [ + "greeter.capnp", + "greeter-server.cpp" + ] + } + CppApplication { + Depends { name: "capnproto.cpp"; required: false } + name: "client" + consoleApplication: true + capnproto.cpp.useRpc: true + cpp.minimumMacosVersion: "10.8" + files: [ + "greeter.capnp", + "greeter-client.cpp" + ] + } +} diff --git a/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp b/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp new file mode 100644 index 000000000..146a2969f --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp @@ -0,0 +1,6 @@ +@0x8a2efe67220790be; + +struct Foo { + num @0 :UInt32; + str @1 :Text; +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 1a593c73c..20269d25c 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -739,6 +739,32 @@ void TestBlackbox::buildVariantDefaults() QCOMPARE(runQbs(params), 0); } +void TestBlackbox::capnproto() +{ + QFETCH(QString, projectFile); + QDir::setCurrent(testDataDir + "/capnproto"); + rmDirR(relativeBuildDir()); + + QbsRunParameters params{QStringLiteral("resolve"), {QStringLiteral("-f"), projectFile}}; + if (m_qbsStdout.contains("targetPlatform differs from hostPlatform")) + QSKIP("Cannot run binaries in cross-compiled build"); + if (m_qbsStdout.contains("capnproto is not present")) + QSKIP("capnproto is not present"); + + params.command.clear(); + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::capnproto_data() +{ + QTest::addColumn("projectFile"); + + QTest::newRow("cpp") << QStringLiteral("capnproto_cpp.qbs"); + QTest::newRow("greeter cpp (grpc)") << QStringLiteral("greeter_cpp.qbs"); + QTest::newRow("relative import") << QStringLiteral("capnproto_relative_import.qbs"); + QTest::newRow("absolute import") << QStringLiteral("capnproto_absolute_import.qbs"); +} + void TestBlackbox::changedFiles_data() { QTest::addColumn("useChangedFilesForInitialBuild"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 8a5d69a02..e958a113c 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -62,6 +62,8 @@ private slots: void buildGraphVersions(); void buildVariantDefaults_data(); void buildVariantDefaults(); + void capnproto(); + void capnproto_data(); void changedFiles_data(); void changedFiles(); void changedInputsFromDependencies(); -- cgit v1.2.3 From 072837660d43b0f83fa9ae405a4c36c0f3aa4519 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sat, 11 Jul 2020 17:39:14 +0300 Subject: Android: Do not fail if project contains multiple shared libraries Do not fail the detection of the main binary if the first two candidates do not match the product name. Simplify the logic to exclude all non-matching candidates except the first one. Change-Id: I5046d7e103a9130c602aa8991bb9853c6ff593dd Reviewed-by: Christian Kandeler --- share/qbs/module-providers/Qt/templates/android_support.qbs | 5 ++--- 1 file changed, 2 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 2835a9336..410b05ff9 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -102,9 +102,8 @@ Module { targetArchitecture = theBinary.Android.ndk.abi; continue; } - if (theBinary.product.name === product.name - && candidate.product.name !== product.name) { - continue; // We already have a better match. + if (candidate.product.name !== product.name) { + continue; // This is not going to be a match } if (candidate.product.name === product.name && theBinary.product.name !== product.name) { -- cgit v1.2.3 From 1d2e98101d835bf699984a9770eeb055efcad2c3 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 21 Jul 2020 10:50:51 +0200 Subject: Fix CONFIG variable being read incorrectly in setup-qt.js QT_CONFIG was read in the line before CONFIG, and the regex matched it: QT_CONFIG += private_tests shared shared [...] CONFIG += shared shared debug sanitize_address sanitizer This would lead to address sanitizer being ignored and requiring workarounds. Use a stricter regex that doesn't allow arbitrary characters before the key. The regex can be tested manually here: https://regex101.com/r/aYSWwG/2 Change-Id: I8b6a509d0fdd9500c527497fa3545646f24c42d1 Fixes: QBS-1387 Reviewed-by: Oswald Buddenhagen Reviewed-by: Ivan Komissarov --- share/qbs/module-providers/Qt/setup-qt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index f4ec55328..c7b722563 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -107,7 +107,7 @@ function readFileContent(filePath) { // TODO: Don't do the split every time... function configVariable(configContent, key) { var configContentLines = configContent.split('\n'); - var regexp = new RegExp("\\s*" + key + "\\s*\\+{0,1}=(.*)"); + var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)"); for (var i = 0; i < configContentLines.length; ++i) { var line = configContentLines[i]; var match = regexp.exec(line); -- cgit v1.2.3 From 4bd119f1063a2e73085b47772f1e6838ecf172ed Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Tue, 21 Jul 2020 21:22:13 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for IAR ARM Change-Id: Ie4dee42345d4ca1d3280b1b65155d0dc173beffd Reviewed-by: Christian Kandeler --- .../testdata-baremetal/one-object-asm-application/arm-iar.s | 7 +++++++ .../one-object-asm-application/one-object-asm-application.qbs | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s new file mode 100644 index 000000000..0a13a5dc2 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s @@ -0,0 +1,7 @@ + PUBLIC main + SECTION `.text`:CODE:NOROOT(1) + THUMB +main: + MOVS R0, #+0 + BX LR + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index cd1413d6f..0bf07b0d7 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -8,6 +8,8 @@ BareMetalApplication { if (qbs.architecture === "mcs51") return true; } else if (qbs.toolchainType === "iar") { + if (qbs.architecture.startsWith("arm")) + return true; if (qbs.architecture === "mcs51") return true; } else if (qbs.toolchainType === "sdcc") { @@ -36,6 +38,14 @@ BareMetalApplication { cpp.linkerPath: cpp.compilerPathByLanguage["c"] } + Properties { + condition: qbs.toolchainType === "iar" + && qbs.architecture.startsWith("arm") + cpp.entryPoint: "main" + } + + cpp.linkerPath: original + files: [(qbs.architecture.startsWith("arm") ? "arm" : qbs.architecture) + "-" + qbs.toolchainType + ".s"] } -- cgit v1.2.3 From de4cf2c1f78e7a90a80464ebb46558e0fdd85259 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 15:40:48 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for IAR STM8 ... also the changes in the BareMetal{Application|StaticLibrary}.qbs files allows to pass all other IAR STM8 tests. Change-Id: Ic4e36e0e1a19863d2c9172a17e4bcfc5da7e3a34 Reviewed-by: Ivan Komissarov --- tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs | 8 ++++++++ tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs | 8 ++++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ .../testdata-baremetal/one-object-asm-application/stm8-iar.s | 7 +++++++ 4 files changed, 25 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs index d802937d1..1183c9036 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs @@ -1,4 +1,12 @@ CppApplication { + Properties { + condition: qbs.toolchain.contains("iar") + && qbs.architecture === "stm8" + cpp.driverLinkerFlags: [ + "--config_def", "_CSTACK_SIZE=0x100", + "--config_def", "_HEAP_SIZE=0x100", + ] + } Properties { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs index 01493a5ec..6f985c84b 100644 --- a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs +++ b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs @@ -1,4 +1,12 @@ StaticLibrary { + Properties { + condition: qbs.toolchain.contains("iar") + && qbs.architecture === "stm8" + cpp.driverLinkerFlags: [ + "--config_def", "_CSTACK_SIZE=0x100", + "--config_def", "_HEAP_SIZE=0x100", + ] + } Properties { condition: qbs.toolchain.contains("keil") && qbs.architecture.startsWith("arm") diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 0bf07b0d7..1aece3b13 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -12,6 +12,8 @@ BareMetalApplication { return true; if (qbs.architecture === "mcs51") return true; + if (qbs.architecture === "stm8") + return true; } else if (qbs.toolchainType === "sdcc") { if (qbs.architecture === "mcs51") return true; diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s new file mode 100644 index 000000000..674e20de6 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s @@ -0,0 +1,7 @@ + PUBLIC main + SECTION `.near_func.text`:CODE:REORDER:NOROOT(0) + CODE +main: + CLRW X + RET + END -- cgit v1.2.3 From eabe75763cabe5519986d1a11952dcefe15f2c68 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 15:53:28 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for IAR MSP430 Change-Id: I7d0d6906c7b54e1c936e23f4860174f45d89d787 Reviewed-by: Ivan Komissarov --- .../testdata-baremetal/one-object-asm-application/msp430-iar.s | 6 ++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s new file mode 100644 index 000000000..fbabe3ba8 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s @@ -0,0 +1,6 @@ + PUBLIC main + RSEG `CODE`:CODE:REORDER:NOROOT(1) +main: + MOV.W #0x0, R12 + RET + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 1aece3b13..0ffc5c3cc 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -14,6 +14,8 @@ BareMetalApplication { return true; if (qbs.architecture === "stm8") return true; + if (qbs.architecture === "msp430") + return true; } else if (qbs.toolchainType === "sdcc") { if (qbs.architecture === "mcs51") return true; -- cgit v1.2.3 From fec1e844780f3280d931e48008062640d61d4e77 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 20:39:00 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for IAR AVR Change-Id: Ide33b5ad139ed18f45e9feff6093ae903d828ee7 Reviewed-by: Ivan Komissarov --- .../testdata-baremetal/one-object-asm-application/avr-iar.s | 7 +++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s new file mode 100644 index 000000000..49e9d476e --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s @@ -0,0 +1,7 @@ + PUBLIC main + RSEG CODE:CODE:NOROOT(1) +main: + LDI R16, 0 + LDI R17, 0 + RET + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 0ffc5c3cc..f92a36866 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -14,6 +14,8 @@ BareMetalApplication { return true; if (qbs.architecture === "stm8") return true; + if (qbs.architecture === "avr") + return true; if (qbs.architecture === "msp430") return true; } else if (qbs.toolchainType === "sdcc") { -- cgit v1.2.3 From 909c7e34af983e48535d6e87fdb8fd49b3170c09 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 20:50:00 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for GCC XTENSA Change-Id: Ic01a2720a9184daa80c83ed8d53809f6f50d9258 Reviewed-by: Ivan Komissarov --- .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ .../one-object-asm-application/xtensa-gcc.s | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index f92a36866..7a7f9dfe4 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -30,6 +30,8 @@ BareMetalApplication { return true; if (qbs.architecture === "msp430") return true; + if (qbs.architecture === "xtensa") + return true; } console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s new file mode 100644 index 000000000..c21000905 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s @@ -0,0 +1,11 @@ + .global main + .type main, @function +main: + addi sp, sp, -16 + s32i.n a15, sp, 12 + mov.n a15, sp + movi.n a2, 0 + mov.n sp, a15 + l32i.n a15, sp, 12 + addi sp, sp, 16 + ret.n -- cgit v1.2.3 From 00e74af75c0d473bc639c5204f5cf9db2aadd575 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 21:48:56 +0300 Subject: baremetal: Skip 'preinclude-headers' test for KEIL C251 and C166 ... because this toolchains does not support the pre-include headers. Change-Id: I3a56389d9bdc7ca05a8e274caa9f01fb9fb52afc Reviewed-by: Ivan Komissarov --- .../testdata-baremetal/preinclude-headers/preinclude-headers.qbs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs index 088af3340..0ded6ff15 100644 --- a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs +++ b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs @@ -3,7 +3,9 @@ import "../BareMetalApplication.qbs" as BareMetalApplication BareMetalApplication { condition: { if (qbs.toolchainType === "keil") { - if (qbs.architecture === "mcs51") { + if (qbs.architecture === "mcs51" + || qbs.architecture === "mcs251" + || qbs.architecture === "c166") { console.info("unsupported toolset: %%" + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%"); return false; -- cgit v1.2.3 From 668305d1414e222c2060b211a41c2642350ec04f Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 22:19:49 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for KEIL MCS251 Change-Id: I2b6b4618e2ddf2cc58f4c2f6c9776a5d6fac9b9f Reviewed-by: Ivan Komissarov --- .../testdata-baremetal/one-object-asm-application/mcs251-keil.s | 8 ++++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s new file mode 100644 index 000000000..312cc9680 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s @@ -0,0 +1,8 @@ +PUBLIC main +MAIN_SEG SEGMENT CODE + RSEG MAIN_SEG +main PROC + XRL WR6,WR6 + RET + ENDP + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 7a7f9dfe4..722133320 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -7,6 +7,8 @@ BareMetalApplication { return true; if (qbs.architecture === "mcs51") return true; + if (qbs.architecture === "mcs251") + return true; } else if (qbs.toolchainType === "iar") { if (qbs.architecture.startsWith("arm")) return true; -- cgit v1.2.3 From 6314d18adc786d3b2e2d2e469707fb0bc07ee803 Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Thu, 23 Jul 2020 22:30:36 +0300 Subject: baremetal: Pass 'one-object-asm-application' test for KEIL C166 Change-Id: I8e5ffc2340135e53021d95c3079f6dc458dc1459 Reviewed-by: Ivan Komissarov --- .../testdata-baremetal/one-object-asm-application/c166-keil.s | 7 +++++++ .../one-object-asm-application/one-object-asm-application.qbs | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s new file mode 100644 index 000000000..394bc2ae4 --- /dev/null +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s @@ -0,0 +1,7 @@ +MAIN_SEG SECTION CODE WORD 'NCODE' +main PROC NEAR + MOV R4, #0 + RET +main ENDP +MAIN_SEG ENDS + END diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs index 722133320..f556fc093 100644 --- a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs +++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs @@ -9,6 +9,8 @@ BareMetalApplication { return true; if (qbs.architecture === "mcs251") return true; + if (qbs.architecture === "c166") + return true; } else if (qbs.toolchainType === "iar") { if (qbs.architecture.startsWith("arm")) return true; -- cgit v1.2.3 From 8a948b3389faa2a3949d382d0880551c3f6347fd Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Wed, 22 Jul 2020 09:42:15 +0200 Subject: Fix setting isBundle:false on macOS in config-ui.qbs Despite the fact that the second Properties item is not evaluated on macOS, this "works" because isBundle is set to undefined (which is false) due to a bug with Properties. Set isBundle:false explicitly for macOS as well. This amends 1eff548646 Change-Id: Ifd5bc3b656a0eff02914e4fe43945b0e8b0f04fe Reviewed-by: Christian Kandeler --- src/app/config-ui/config-ui.qbs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/config-ui/config-ui.qbs b/src/app/config-ui/config-ui.qbs index 2222e6a17..8b5e37559 100644 --- a/src/app/config-ui/config-ui.qbs +++ b/src/app/config-ui/config-ui.qbs @@ -24,6 +24,7 @@ QbsApp { Properties { condition: qbs.targetOS.contains("macos") cpp.frameworks: ["ApplicationServices", "Cocoa"] + bundle.isBundle: false } Properties { -- cgit v1.2.3 From 0ac5a1467433cafe98e01598de25f155f24fcb2f Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Wed, 15 Jul 2020 12:46:48 +0200 Subject: doc: Provide a complete example in the "Installing Files" section Fixes: QBS-1350 Change-Id: I86cf583ea8cd54041c17a8656913702aab7fc35d Reviewed-by: Christian Kandeler --- doc/qbs.qdoc | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index db9a64af4..e91424685 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -1525,18 +1525,47 @@ \endcode In this example, we want to install a couple of QML files and an executable. - The actual installation is then done like this (using the default profile): + When building, \QBS installs artifacts into the default root folder, namely + \c{/install-root}. The \l{qbs::installPrefix}{qbs.installPrefix} and + \l{qbs::installDir}{qbs.installDir} properties are appended to the root folder. \code - qbs --clean-install-root qbs.installRoot:/tmp/myProjectRoot + $ qbs build qbs.installPrefix:/usr \endcode + In this example, the executable will be installed into the \c{/install-root/usr/bin} + folder and the QML files will be installed into the + \c{/install-root/usr/share/myproject} folder. - Here, we want the \c installDir properties from the project file to be - interpreted relative to the directory \c{/tmp/myProjectRoot}, and we want - that directory to be removed first. + To skip installation during the build, use the \c --no-install option. - If the \l{qbs::installRoot}{qbs.installRoot} property is not given, a - default is used, namely \c{/install-root}. + To override the default location, use the \c --install-root option of the \c{qbs install} + command: + \code + $ qbs build --no-install qbs.installPrefix:/usr + # qbs install --no-build --install-root / + \endcode + In this example, artifacts will be installed directly into the \c /usr folder. Since the + \c{qbs install} command implies \c build, we use the \c --no-build parameter to ensure that + we do not accidentally rebuild the project, thereby changing the artifacts owner to \c root. + + Sometimes, it makes sense to install the application into a temporary root folder, keeping the + same folder structure within that root folder as in the examples above; for instance, + when building a Linux package such as \c deb or \c rmp. To install the application into the + \c /tmp/myProjectRoot folder, use the following command: + + \code + $ qbs install --install-root /tmp/myProjectRoot + \endcode + + In this example, the executable will be installed into the \c{/tmp/myProjectRoot/usr/bin} folder + and QML files will be installed into the \c{/tmp/myProjectRoot/usr/share/myproject} folder. + + To remove all files from the install root prior to installing, use the \c --clean-install-root + parameter: + + \code + $ qbs install --clean-install-root --install-root /tmp/myProjectRoot + \endcode For more information about how the installation path is constructed, see \l {Installation Properties}. -- cgit v1.2.3