aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Qt Project <gerrit-noreply@qt-project.org>2020-07-27 14:47:46 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2020-07-27 14:47:46 +0000
commit34349e6574500e087ced56cc9504431b3e3b6469 (patch)
treed1e822daf5382d536d3b2c8940152526fd2a8f04
parent31a841ade7b1a3b10d5a132cc58a476295e2e0b5 (diff)
parenteb6adb2a65902ca507dd1fdcd97fde333f324068 (diff)
Merge "Merge branch 1.17 into master"
-rw-r--r--.travis.yml4
-rw-r--r--doc/howtos.qdoc37
-rw-r--r--doc/qbs.qdoc43
-rw-r--r--doc/reference/modules/capnprotocpp-module.qdoc116
-rw-r--r--doc/reference/modules/cpp-module.qdoc4
-rw-r--r--doc/reference/modules/qbs-module.qdoc2
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook.capnp55
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook.cpp288
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook_cpp.qbs11
-rw-r--r--examples/capnproto/calculator_cpp/calculator-client.cpp367
-rw-r--r--examples/capnproto/calculator_cpp/calculator-server.cpp215
-rw-r--r--examples/capnproto/calculator_cpp/calculator.capnp118
-rw-r--r--examples/capnproto/calculator_cpp/calculator_cpp.qbs26
-rw-r--r--examples/examples.qbs1
-rw-r--r--examples/rpaths/main.cpp45
-rw-r--r--examples/rpaths/objecta.cpp48
-rw-r--r--examples/rpaths/objecta.h47
-rw-r--r--examples/rpaths/objectb.cpp41
-rw-r--r--examples/rpaths/objectb.h48
-rw-r--r--examples/rpaths/rpaths.qbs58
-rw-r--r--share/qbs/imports/qbs/Probes/FrameworkProbe.qbs7
-rw-r--r--share/qbs/imports/qbs/Probes/PathProbe.qbs12
-rw-r--r--share/qbs/imports/qbs/Probes/path-probe.js6
-rw-r--r--share/qbs/module-providers/Qt/setup-qt.js2
-rw-r--r--share/qbs/module-providers/Qt/templates/android_support.qbs5
-rw-r--r--share/qbs/modules/bundle/BundleModule.qbs4
-rw-r--r--share/qbs/modules/bundle/bundle.js13
-rw-r--r--share/qbs/modules/capnproto/capnproto.js97
-rw-r--r--share/qbs/modules/capnproto/capnprotobase.qbs65
-rw-r--r--share/qbs/modules/capnproto/cpp/capnprotocpp.qbs64
-rw-r--r--share/qbs/modules/cpp/iar.js3
-rw-r--r--share/qbs/modules/cpp/keil.js3
-rw-r--r--share/qbs/modules/cpp/sdcc.js3
-rw-r--r--share/qbs/modules/cpp/sdcc.qbs9
-rw-r--r--share/qbs/modules/protobuf/cpp/protobufcpp.qbs9
-rw-r--r--share/qbs/modules/protobuf/objc/protobufobjc.qbs5
-rw-r--r--share/qbs/modules/protobuf/protobuf.js11
-rw-r--r--share/qbs/modules/xcode/xcode.js4
-rw-r--r--src/app/config-ui/config-ui.qbs1
-rw-r--r--src/lib/corelib/language/item.h1
-rw-r--r--src/lib/corelib/language/moduleloader.cpp16
-rw-r--r--src/plugins/generator/visualstudio/visualstudio.pro2
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs10
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs10
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs48
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs12
-rw-r--r--tests/auto/blackbox/testdata/capnproto/bar.capnp8
-rw-r--r--tests/auto/blackbox/testdata/capnproto/baz.capnp8
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp14
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs18
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp13
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs16
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp14
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs17
-rw-r--r--tests/auto/blackbox/testdata/capnproto/foo.capnp6
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter-client.cpp25
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter-server.cpp27
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter.capnp13
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs32
-rw-r--r--tests/auto/blackbox/testdata/capnproto/imports/foo.capnp6
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs10
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs5
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs3
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs3
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs4
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs11
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp37
-rw-r--r--tests/auto/blackbox/tst_blackbox.h3
-rw-r--r--tests/auto/blackbox/tst_blackboxbaremetal.cpp11
-rw-r--r--tests/auto/language/language.pro1
83 files changed, 2326 insertions, 65 deletions
diff --git a/.travis.yml b/.travis.yml
index 096b24e2c..25a23279e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -83,11 +83,13 @@ jobs:
addons:
homebrew:
packages:
+ - capnp
- ccache
- grpc
- icoutils
- makensis
- protobuf
+ - python3
- p7zip
update: true
env:
@@ -100,7 +102,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:
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/qbs.qdoc b/doc/qbs.qdoc
index 128c4d831..854a96aca 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{<build root>/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{<build root>/install-root/usr/bin}
+ folder and the QML files will be installed into the
+ \c{<build root>/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{<build root>/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}.
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 <foo.capnp.h>
+
+ int main(int argc, char* argv[]) {
+ ::capnp::MallocMessageBuilder message;
+
+ auto foo = message.initRoot<Foo>();
+ 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/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/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}
*/
/*!
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 <capnp/message.h>
+#include <capnp/serialize-packed.h>
+#include <iostream>
+
+using addressbook::Person;
+using addressbook::AddressBook;
+
+void writeAddressBook(int fd) {
+ ::capnp::MallocMessageBuilder message;
+
+ AddressBook::Builder addressBook = message.initRoot<AddressBook>();
+ ::capnp::List<Person>::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<Person::PhoneNumber>::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<AddressBook>();
+
+ 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 <capnp/message.h>
+#include <capnp/serialize-packed.h>
+#include <iostream>
+#include <capnp/schema.h>
+#include <capnp/dynamic.h>
+
+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<DynamicStruct>(schema);
+
+ DynamicList::Builder people =
+ addressBook.init("people", 2).as<DynamicList>();
+
+ DynamicStruct::Builder alice =
+ people[0].as<DynamicStruct>();
+ alice.set("id", 123);
+ alice.set("name", "Alice");
+ alice.set("email", "alice@example.com");
+ auto alicePhones = alice.init("phones", 1).as<DynamicList>();
+ auto phone0 = alicePhones[0].as<DynamicStruct>();
+ phone0.set("number", "555-1212");
+ phone0.set("type", "mobile");
+ alice.get("employment").as<DynamicStruct>()
+ .set("school", "MIT");
+
+ auto bob = people[1].as<DynamicStruct>();
+ 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<T>()!
+ List<Person::PhoneNumber>::Builder bobPhones =
+ bob.init("phones", 2).as<List<Person::PhoneNumber>>();
+ 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<DynamicStruct>()
+ .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<bool>() ? "true" : "false");
+ break;
+ case DynamicValue::INT:
+ std::cout << value.as<int64_t>();
+ break;
+ case DynamicValue::UINT:
+ std::cout << value.as<uint64_t>();
+ break;
+ case DynamicValue::FLOAT:
+ std::cout << value.as<double>();
+ break;
+ case DynamicValue::TEXT:
+ std::cout << '\"' << value.as<Text>().cStr() << '\"';
+ break;
+ case DynamicValue::LIST: {
+ std::cout << "[";
+ bool first = true;
+ for (auto element: value.as<DynamicList>()) {
+ if (first) {
+ first = false;
+ } else {
+ std::cout << ", ";
+ }
+ dynamicPrintValue(element);
+ }
+ std::cout << "]";
+ break;
+ }
+ case DynamicValue::ENUM: {
+ auto enumValue = value.as<DynamicEnum>();
+ 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<DynamicStruct>();
+ 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<DynamicStruct>(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<AddressBook>();
+ dynamicWriteAddressBook(1, schema);
+ } else if (strcmp(argv[1], "dread") == 0) {
+ StructSchema schema = Schema::from<AddressBook>();
+ 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 <capnp/ez-rpc.h>
+#include <kj/debug.h>
+#include <math.h>
+#include <iostream>
+
+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<void> 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<Calculator>();
+
+ // 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<PowerFunction>());
+ 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 <kj/debug.h>
+#include <capnp/ez-rpc.h>
+#include <capnp/message.h>
+#include <iostream>
+
+typedef unsigned int uint;
+
+kj::Promise<double> 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<Calculator::Value::ReadResults> result) {
+ return result.getValue();
+ });
+}
+
+kj::Promise<double> evaluateImpl(
+ Calculator::Expression::Reader expression,
+ capnp::List<double>::Reader params = capnp::List<double>::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<kj::Promise<double>> paramPromises =
+ KJ_MAP(param, call.getParams()) {
+ return evaluateImpl(param, params);
+ };
+
+ // Join the array of promises into a promise for an array.
+ kj::Promise<kj::Array<double>> joinedParams =
+ kj::joinPromises(kj::mv(paramPromises));
+
+ // When the parameters are complete, call the function.
+ return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable {
+ auto request = func.callRequest();
+ request.setParams(paramValues);
+ return request.send().then(
+ [](capnp::Response<Calculator::Function::CallResults>&& 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<void> 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<void> call(CallContext context) {
+ auto params = context.getParams().getParams();
+ KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
+
+ return evaluateImpl(body.getRoot<Calculator::Expression>(), 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<void> 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<void> evaluate(EvaluateContext context) override {
+ return evaluateImpl(context.getParams().getExpression())
+ .then([KJ_CPCAP(context)](double value) mutable {
+ context.getResults().setValue(kj::heap<ValueImpl>(value));
+ });
+ }
+
+ kj::Promise<void> defFunction(DefFunctionContext context) override {
+ auto params = context.getParams();
+ context.getResults().setFunc(kj::heap<FunctionImpl>(
+ params.getParamCount(), params.getBody()));
+ return kj::READY_NOW;
+ }
+
+ kj::Promise<void> getOperator(GetOperatorContext context) override {
+ context.getResults().setFunc(kj::heap<OperatorImpl>(
+ 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<CalculatorImpl>(), 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/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 <iostream>
+
+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 <string>
+
+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 <string>
+
+#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]
+}
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/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;
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/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);
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) {
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/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/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;
}
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/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);
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/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 5fd7f0202..8ab535abd 100644
--- a/share/qbs/modules/protobuf/protobuf.js
+++ b/share/qbs/modules/protobuf/protobuf.js
@@ -69,11 +69,10 @@ 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: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix,
+ filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix),
cpp: {
includePaths: [].concat(input.cpp.includePaths, outputDir),
warningLevel: "none",
@@ -81,11 +80,11 @@ 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: 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",
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";
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 {
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/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
diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs
index b262290bb..1183c9036 100644
--- a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs
+++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs
@@ -1,14 +1,24 @@
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")
&& cpp.compilerName.startsWith("armcc")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
cpp.driverFlags: ["--cpu", "cortex-m0"]
}
Properties {
condition: qbs.toolchain.contains("keil")
&& qbs.architecture.startsWith("arm")
&& cpp.compilerName.startsWith("armclang")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"]
}
Properties {
diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs
index b15f2ce9a..6f985c84b 100644
--- a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs
+++ b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs
@@ -1,14 +1,24 @@
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")
&& cpp.compilerName.startsWith("armcc")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
cpp.driverFlags: ["--cpu", "cortex-m0"]
}
Properties {
condition: qbs.toolchain.contains("keil")
&& qbs.architecture.startsWith("arm")
&& cpp.compilerName.startsWith("armclang")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
cpp.driverFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"]
}
Properties {
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/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/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/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/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/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/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/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/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/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 e98279d6c..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
@@ -3,15 +3,61 @@ import "../BareMetalApplication.qbs" as BareMetalApplication
BareMetalApplication {
condition: {
if (qbs.toolchainType === "keil") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
if (qbs.architecture === "mcs51")
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;
+ if (qbs.architecture === "mcs51")
+ 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") {
if (qbs.architecture === "mcs51")
return true;
+ if (qbs.architecture === "stm8")
+ return true;
+ } else if (qbs.toolchainType === "gcc") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
+ if (qbs.architecture === "avr")
+ return true;
+ if (qbs.architecture === "msp430")
+ return true;
+ if (qbs.architecture === "xtensa")
+ return true;
}
console.info("unsupported toolset: %%"
+ qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
return false;
}
- files: [qbs.architecture + "-" + qbs.toolchainType + ".s"]
+
+ 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"]
+ }
+
+ 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"]
}
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
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
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
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..0ded6ff15 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,18 @@
import "../BareMetalApplication.qbs" as BareMetalApplication
BareMetalApplication {
+ condition: {
+ if (qbs.toolchainType === "keil") {
+ if (qbs.architecture === "mcs51"
+ || qbs.architecture === "mcs251"
+ || qbs.architecture === "c166") {
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+ }
+ return true;
+ }
cpp.prefixHeaders: ["preinclude.h"]
files: ["main.c"]
}
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 <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto baz = message.initRoot<Baz>();
+ 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 <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto foo = message.initRoot<Foo>();
+ 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 <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto bar = message.initRoot<Bar>();
+ 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 <capnp/ez-rpc.h>
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ const char address[] = "localhost:5050";
+ capnp::EzRpcClient client(address);
+ Greeter::Client greeter = client.getMain<Greeter>();
+
+ 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 <capnp/ez-rpc.h>
+#include <capnp/message.h>
+
+#include <iostream>
+
+class GreeterImpl final: public Greeter::Server
+{
+public:
+ ::kj::Promise<void> 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<GreeterImpl>(), 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/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/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 c6e8b9731..66f195ff0 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<QString>("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<bool>("useChangedFilesForInitialBuild");
@@ -2935,6 +2961,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;
@@ -8111,6 +8138,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..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();
@@ -313,6 +315,7 @@ private slots:
void trackRemoveFile();
void trackRemoveFileTag();
void trackRemoveProduct();
+ void transitiveInvalidDependencies();
void transitiveOptionalDependencies();
void typescript();
void undefinedTargetPlatform();
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)
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)
}