aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-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
14 files changed, 1368 insertions, 0 deletions
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]
+}