2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
A simple client-server scenario
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Björn Döbel and Norman Feske
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
Abstract
|
|
|
|
########
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
This tutorial will give you a step-by-step introduction for creating your first
|
|
|
|
little client-server application scenario using the Genode OS Framework. We will create
|
2011-12-22 15:19:25 +00:00
|
|
|
a server that provides two functions to its clients and a client that uses
|
|
|
|
these functions. The code samples in this section are not necessarily complete.
|
2016-05-20 12:49:45 +00:00
|
|
|
You can can find the complete source code at the _repos/hello_tutorial_
|
|
|
|
directory within Genode's source tree.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Prerequisites
|
|
|
|
#############
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
We assume that you have acquainted yourself with the basic concepts of
|
|
|
|
Genode and have read the "Getting started" section of the Genode Foundations
|
|
|
|
book. Our can download the book from [http://genode.org].
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Setting up the build environment
|
|
|
|
################################
|
|
|
|
|
|
|
|
The Genode build system enables developers to create software in different
|
|
|
|
repositories that don't need to interfere with the rest of the Genode tree. We
|
|
|
|
will do this for our example now. In the Genode root directory, we create the
|
|
|
|
following subdirectory structure:
|
|
|
|
|
|
|
|
! hello_tutorial
|
|
|
|
! hello_tutorial/include
|
|
|
|
! hello_tutorial/include/hello_session
|
|
|
|
! hello_tutorial/src
|
|
|
|
! hello_tutorial/src/hello
|
|
|
|
! hello_tutorial/src/hello/server
|
|
|
|
! hello_tutorial/src/hello/client
|
|
|
|
|
|
|
|
In the remaining document when referring to non-absolute directories, these are
|
2016-05-20 12:49:45 +00:00
|
|
|
local to _hello_tutorial_.
|
|
|
|
Now we tell the Genode build system that there is a new repository. Therefore
|
|
|
|
we add the path to our new repository to _build/etc/build.conf_:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
! REPOSITORIES += /path/to/your/hello_tutorial
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Later we will place build description files into the tutorial subdirectories
|
|
|
|
so that the build system can figure out what is needed to build your custom
|
|
|
|
components. You can then build these components from the _build_ directory
|
|
|
|
using one of the following commands:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
! make hello
|
|
|
|
! make hello/server
|
|
|
|
! make hello/client
|
|
|
|
|
|
|
|
The first command builds both the client and the server whereas the latter two
|
|
|
|
commands build only the specific target respectively.
|
|
|
|
|
|
|
|
Defining an interface
|
|
|
|
#####################
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
In our example, we are going to implement a server providing two functions:
|
|
|
|
:'void say_hello()': makes the server print a message, and
|
2011-12-22 15:19:25 +00:00
|
|
|
:'int add(int a, int b)': adds two integers and returns the result.
|
|
|
|
|
|
|
|
The interface of a Genode service is called a _session_. We will define it as a
|
|
|
|
C++ class in 'include/hello_session/hello_session.h'
|
|
|
|
|
|
|
|
!#include <session/session.h>
|
|
|
|
!#include <base/rpc.h>
|
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { struct Session; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!struct Hello::Session : Genode::Session
|
|
|
|
!{
|
|
|
|
! static const char *service_name() { return "Hello"; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
! virtual void say_hello() = 0;
|
|
|
|
! virtual int add(int a, int b) = 0;
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
! GENODE_RPC(Rpc_say_hello, void, say_hello);
|
|
|
|
! GENODE_RPC(Rpc_add, int, add, int, int);
|
|
|
|
! GENODE_RPC_INTERFACE(Rpc_say_hello, Rpc_add);
|
|
|
|
!};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
As a good practice, we place the Hello service into a dedicated namespace. The
|
|
|
|
_Hello::Session_ class defines the public interface for our service as well as
|
|
|
|
the meta information that Genode needs to perform remote procedure calls (RPC)
|
2016-05-20 12:49:45 +00:00
|
|
|
across component boundaries.
|
|
|
|
Furthermore, we use the interface to specify the name of the service by
|
|
|
|
providing the 'service_name' method. This method will later be used by both
|
|
|
|
the server for announcing the service at its parent and the client for
|
|
|
|
requesting the creation of a "Hello" session.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
The 'GENODE_RPC' macro is used to declare an RPC function. Its first argument
|
|
|
|
is a type name that is used to refer to the RPC function. The type name can
|
2016-05-20 12:49:45 +00:00
|
|
|
be chosen freely. However, it is a good practice to prefix the type name
|
2011-12-22 15:19:25 +00:00
|
|
|
with 'Rpc_'. The remaining arguments are the return type of the RPC function,
|
|
|
|
the server-side name of the RPC implementation, and the function arguments.
|
2016-05-20 12:49:45 +00:00
|
|
|
The 'GENODE_RPC_INTERFACE' macro declares the list of RPC functions that the
|
2011-12-22 15:19:25 +00:00
|
|
|
RPC interface is comprised of. Under the hood, the 'GENODE_RPC*' macros enrich
|
|
|
|
the compound class with the type information used to automatically generate the
|
|
|
|
RPC communication code at compile time. They do not add any members to the
|
|
|
|
'Session' struct.
|
|
|
|
|
|
|
|
|
|
|
|
Writing server code
|
|
|
|
###################
|
|
|
|
|
|
|
|
Now let's write a server providing the interface defined by _Hello::Session_.
|
2015-01-14 18:00:53 +00:00
|
|
|
We will put all of this code in 'src/hello/server/main.cc'
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Implementing the server side
|
|
|
|
============================
|
|
|
|
|
|
|
|
We place the implementation of the session interface into a class called
|
|
|
|
'Session_component' derived from the 'Rpc_object' class template. By
|
|
|
|
instantiating this template class with the session interface as argument, the
|
|
|
|
'Session_component' class gets equipped with the communication code that
|
|
|
|
will make the server's functions accessible via RPC.
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
!#include <base/log.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
!#include <hello_session/hello_session.h>
|
|
|
|
!#include <base/rpc_server.h>
|
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { struct Session_component; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!struct Hello::Session_component : Genode::Rpc_object<Session>
|
|
|
|
!{
|
|
|
|
! void say_hello() {
|
|
|
|
! Genode::log("I am here... Hello."); }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
! int add(int a, int b) {
|
|
|
|
! return a + b; }
|
|
|
|
!};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Getting ready to start
|
|
|
|
======================
|
|
|
|
|
|
|
|
The server component won't help us much as long as we don't use it in a server
|
|
|
|
application. Starting a service with Genode works as follows:
|
|
|
|
* Create and announce a root capability to our parent.
|
|
|
|
* When a client requests our service, the parent invokes the root capability to
|
|
|
|
create session objects and session capabilities. These are then used by the
|
|
|
|
client to communicate with the server.
|
|
|
|
|
|
|
|
The class 'Hello::Root_component' is derived from Genode's 'Root_component'
|
2016-05-20 12:49:45 +00:00
|
|
|
class template. This class defines a '_create_session' method, which is called
|
2011-12-22 15:19:25 +00:00
|
|
|
each time a client wants to establish a connection to the server. This function
|
|
|
|
is responsible for parsing the parameter string the client hands over to the
|
2016-05-20 12:49:45 +00:00
|
|
|
server and for creating a 'Hello::Session_component' object from these
|
|
|
|
parameters.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
!#include <base/log.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
!#include <root/component.h>
|
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { class Root_component; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!class Hello::Root_component
|
|
|
|
!:
|
|
|
|
! public Genode::Root_component<Session_component>
|
|
|
|
!{
|
|
|
|
! protected:
|
|
|
|
!
|
|
|
|
! Session_component *_create_session(const char *args)
|
|
|
|
! {
|
|
|
|
! Genode::log("creating hello session");
|
|
|
|
! return new (md_alloc()) Session_component();
|
|
|
|
! }
|
|
|
|
!
|
|
|
|
! public:
|
|
|
|
!
|
|
|
|
! Root_component(Genode::Entrypoint &ep,
|
|
|
|
! Genode::Allocator &alloc)
|
|
|
|
! :
|
|
|
|
! Genode::Root_component<Session_component>(ep, alloc)
|
|
|
|
! {
|
|
|
|
! Genode::log("creating root component");
|
|
|
|
! }
|
|
|
|
!};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Now we only need the actual application code that instantiates the root
|
|
|
|
component and the service to our parent. It is good practice to represent
|
|
|
|
the applications as a class called 'Main' with its constructor taking the
|
|
|
|
component's environment as argument.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
!#include <base/component.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { struct Main; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!struct Hello::Main
|
2011-12-22 15:19:25 +00:00
|
|
|
!{
|
2016-05-20 12:49:45 +00:00
|
|
|
! Genode::Env &env;
|
|
|
|
!
|
|
|
|
! Genode::Sliced_heap sliced_heap { env.ram(), env.rm() };
|
|
|
|
!
|
|
|
|
! Hello::Root_component root { env.ep(), sliced_heap };
|
|
|
|
!
|
|
|
|
! Main(Genode::Env &env) : env(env)
|
|
|
|
! {
|
|
|
|
! env.parent().announce(env.ep().manage(root));
|
|
|
|
! }
|
|
|
|
!};
|
|
|
|
|
|
|
|
The sliced heap is used for the dynamic allocation of session objects.
|
|
|
|
It interacts with the component's RAM session to obtain the backing store
|
|
|
|
for the allocations, and the component's region map to make
|
|
|
|
backing store visible within its virtual address space.
|
|
|
|
|
|
|
|
The announcement of the service is performed by the body of the constructor by
|
|
|
|
creating a capability for the root component as return value of the 'manage'
|
|
|
|
method, and passing this capability to the parent.
|
|
|
|
|
|
|
|
The 'Component::construct' function of the hello server simply constructs a singleton
|
|
|
|
instance of 'Hello::Main' as a _static_ local variable.
|
|
|
|
|
|
|
|
!Genode::size_t Component::stack_size() { return 64*1024; }
|
|
|
|
!
|
|
|
|
!void Component::construct(Genode::Env &env)
|
|
|
|
!{
|
|
|
|
! static Hello::Main main(env);
|
2011-12-22 15:19:25 +00:00
|
|
|
!}
|
|
|
|
|
|
|
|
|
|
|
|
Making it fly
|
|
|
|
=============
|
|
|
|
|
|
|
|
In order to run our application, we need to perform two more steps:
|
|
|
|
|
|
|
|
Tell the Genode build system that we want to build 'hello_server'. Therefore we
|
|
|
|
create a 'target.mk' file in 'src/hello/server':
|
|
|
|
|
|
|
|
! TARGET = hello_server
|
|
|
|
! SRC_CC = main.cc
|
Merge base libraries into a single library
This patch simplifies the way of how Genode's base libraries are
organized. Originally, the base API was implemented in the form of many
small libraries such as 'thread', 'env', 'server', etc. Most of them
used to consist of only a small number of files. Because those libraries
are incorporated in any build, the checking of their inter-dependencies
made the build process more verbose than desired. Also, the number of
libraries and their roles (core only, non-core only, shared by both core
and non-core) were not easy to capture.
Hereby, the base libraries have been reduced to the following few
libraries:
- startup.mk contains the startup code for normal Genode processes.
On some platform, core is able to use the library as well.
- base-common.mk contains the parts of the base library that are
identical by core and non-core processes.
- base.mk contains the complete base API implementation for non-core
processes
Consequently, the 'LIBS' declaration in 'target.mk' files becomes
simpler as well. In the most simple case, only the 'base' library must
be mentioned.
Fixes #18
2013-02-14 11:39:51 +00:00
|
|
|
! LIBS = base
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
To tell the init component to start the new program, we have to add a '<start>'
|
2013-09-29 10:22:12 +00:00
|
|
|
entry to init's 'config' file, which is located at 'build/bin/config'.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
! <config>
|
2013-09-29 10:22:12 +00:00
|
|
|
! <parent-provides>
|
|
|
|
! <service name="LOG"/>
|
|
|
|
! </parent-provides>
|
|
|
|
! <default-route>
|
|
|
|
! <any-service> <parent/> <any-child/> </any-service>
|
|
|
|
! </default-route>
|
2011-12-22 15:19:25 +00:00
|
|
|
! <start name="hello_server">
|
2013-09-29 10:22:12 +00:00
|
|
|
! <resource name="RAM" quantum="1M"/>
|
2011-12-22 15:19:25 +00:00
|
|
|
! <provides><service name="Hello"/></provides>
|
|
|
|
! </start>
|
|
|
|
! </config>
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
For information about the configuring concept, please refer to the
|
|
|
|
"System configuration" section of the Genode Foundations book.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Writing client code
|
|
|
|
###################
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
In the next part, we are going to have a look at the client-side implementation.
|
2011-12-22 15:19:25 +00:00
|
|
|
The most basic steps here are:
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
* Obtain a capability for the "Hello" service from our parent
|
2011-12-22 15:19:25 +00:00
|
|
|
* Invoke RPCs via the obtained capability
|
|
|
|
|
|
|
|
|
|
|
|
A client object
|
|
|
|
===============
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
We will encapsulate the Genode RPC interface in a 'Hello::Session_client' class.
|
2011-12-22 15:19:25 +00:00
|
|
|
This class derives from 'Hello:Session' and implements a client-side object.
|
|
|
|
Therefore edit 'include/hello_session/client.h':
|
|
|
|
|
|
|
|
!#include <hello_session/hello_session.h>
|
|
|
|
!#include <base/rpc_client.h>
|
2016-05-20 12:49:45 +00:00
|
|
|
!#include <base/log.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { struct Session_client; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!struct Hello::Session_client : Genode::Rpc_client<Session>
|
|
|
|
!{
|
|
|
|
! Session_client(Genode::Capability<Session> cap)
|
|
|
|
! : Genode::Rpc_client<Session>(cap) { }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
! void say_hello()
|
|
|
|
! {
|
|
|
|
! Genode::log("issue RPC for saying hello");
|
|
|
|
! call<Rpc_say_hello>();
|
|
|
|
! Genode::log("returned from 'say_hello' RPC call");
|
|
|
|
! }
|
|
|
|
!
|
|
|
|
! int add(int a, int b)
|
|
|
|
! {
|
|
|
|
! return call<Rpc_add>(a, b);
|
|
|
|
! }
|
|
|
|
!};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
A 'Hello::Session_client' object takes a 'Capability' as constructor argument.
|
|
|
|
This capability is tagged with the session type and gets passed to the
|
|
|
|
inherited 'Rpc_client' class. This class contains the client-side communication
|
|
|
|
code via the 'call' template function. The template argument for 'call' is the
|
|
|
|
RPC type as declared in the session interface.
|
|
|
|
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
A connection object
|
|
|
|
===================
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Whereas the 'Hello::Session_client' is able to perform RPC calls to an RPC
|
|
|
|
object when given a capability for such an object, the question of how
|
|
|
|
the client obtains this capability is still open.
|
|
|
|
Here, the so-called connection object enters the picture. A connection
|
|
|
|
object has the purposes:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
* It transforms session-specific parameters into a format that can be
|
|
|
|
passed to the server along with the session request. The connection
|
|
|
|
object thereby hides the details of how the session parameters are
|
|
|
|
represented "on the wire".
|
2015-01-14 18:00:53 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
* It issues a session request to the parent and retrieves a session
|
|
|
|
capability as response.
|
|
|
|
|
|
|
|
* It acts as a session-client object such that the session's RPC functions
|
|
|
|
can directly be called on the connection object.
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
By convention, the wrapper is called 'connection.h' and placed in the directory
|
|
|
|
of the session interface. For our case, the file
|
|
|
|
'include/hello_session/connection.h' looks like this:
|
|
|
|
|
|
|
|
!#include <hello_session/client.h>
|
|
|
|
!#include <base/connection.h>
|
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!namespace Hello { struct Connection; }
|
2011-12-22 15:19:25 +00:00
|
|
|
!
|
2016-05-20 12:49:45 +00:00
|
|
|
!struct Hello::Connection : Genode::Connection<Session>, Session_client
|
|
|
|
!{
|
|
|
|
! Connection(Genode::Env &env)
|
|
|
|
! :
|
|
|
|
! /* create session */
|
|
|
|
! Genode::Connection<Hello::Session>(env, session(env.parent(),
|
|
|
|
! "ram_quota=4K")),
|
|
|
|
! /* initialize RPC interface */
|
|
|
|
! Session_client(cap()) { }
|
|
|
|
!};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Client implementation
|
|
|
|
=====================
|
|
|
|
|
|
|
|
The client-side implementation using the 'Hello::Connection' object is pretty
|
|
|
|
straightforward. Put this code into 'src/hello/client/main.cc':
|
|
|
|
|
|
|
|
!#include <base/component.h>
|
|
|
|
!#include <base/log.h>
|
|
|
|
!#include <hello_session/connection.h>
|
|
|
|
!
|
|
|
|
!Genode::size_t Component::stack_size() { return 64*1024; }
|
|
|
|
!
|
|
|
|
!void Component::construct(Genode::Env &env)
|
|
|
|
!{
|
|
|
|
! Hello::Connection hello(env);
|
|
|
|
!
|
|
|
|
! hello.say_hello();
|
|
|
|
!
|
|
|
|
! int const sum = hello.add(2, 5);
|
|
|
|
! Genode::log("added 2 + 5 = ", sum);
|
|
|
|
!
|
|
|
|
! Genode::log("hello test completed");
|
|
|
|
!}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
Ready, set, go...
|
|
|
|
=================
|
|
|
|
|
|
|
|
Add a 'target.mk' file with the following content to 'src/hello/client/':
|
|
|
|
|
|
|
|
! TARGET = hello_client
|
|
|
|
! SRC_CC = main.cc
|
Merge base libraries into a single library
This patch simplifies the way of how Genode's base libraries are
organized. Originally, the base API was implemented in the form of many
small libraries such as 'thread', 'env', 'server', etc. Most of them
used to consist of only a small number of files. Because those libraries
are incorporated in any build, the checking of their inter-dependencies
made the build process more verbose than desired. Also, the number of
libraries and their roles (core only, non-core only, shared by both core
and non-core) were not easy to capture.
Hereby, the base libraries have been reduced to the following few
libraries:
- startup.mk contains the startup code for normal Genode processes.
On some platform, core is able to use the library as well.
- base-common.mk contains the parts of the base library that are
identical by core and non-core processes.
- base.mk contains the complete base API implementation for non-core
processes
Consequently, the 'LIBS' declaration in 'target.mk' files becomes
simpler as well. In the most simple case, only the 'base' library must
be mentioned.
Fixes #18
2013-02-14 11:39:51 +00:00
|
|
|
! LIBS = base
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-05-20 12:49:45 +00:00
|
|
|
Extend your init _config_ as follows to also start the hello-client component:
|
|
|
|
|
|
|
|
! <start name="hello_client">
|
|
|
|
! <resource name="RAM" quantum="1M"/>
|
|
|
|
! </start>
|
|
|
|
|
|
|
|
|
|
|
|
Creating a run script to automate your work flow
|
|
|
|
================================================
|
|
|
|
|
|
|
|
The procedure of building, configuring, integrating, and executing Genode
|
|
|
|
system scenarios across different kernels can be automated using a run
|
|
|
|
script, which can be executed directly from within your build directory.
|
|
|
|
A run script for the hello client-server scenario should be placed
|
|
|
|
at the _run/hello.run_ and look as follows:
|
|
|
|
|
2016-12-01 18:00:11 +00:00
|
|
|
!build { core ld.lib.so init hello }
|
2016-05-20 12:49:45 +00:00
|
|
|
!
|
|
|
|
!create_boot_directory
|
|
|
|
!
|
|
|
|
!install_config {
|
|
|
|
!<config>
|
|
|
|
! <parent-provides>
|
|
|
|
! <service name="LOG"/>
|
|
|
|
! </parent-provides>
|
|
|
|
! <default-route>
|
|
|
|
! <any-service> <parent/> <any-child/> </any-service>
|
|
|
|
! </default-route>
|
|
|
|
! <start name="hello_server">
|
|
|
|
! <resource name="RAM" quantum="1M"/>
|
|
|
|
! <provides> <service name="Hello"/> </provides>
|
|
|
|
! </start>
|
|
|
|
! <start name="hello_client">
|
|
|
|
! <resource name="RAM" quantum="1M"/>
|
|
|
|
! </start>
|
|
|
|
!</config>}
|
|
|
|
!
|
|
|
|
!build_boot_image { core init hello_client hello_server }
|
|
|
|
!
|
|
|
|
!append qemu_args " -nographic "
|
|
|
|
!
|
|
|
|
!run_genode_until "hello test completed.*\n" 10
|
|
|
|
|
|
|
|
When executed via 'make run/hello', it performs the given steps in sequence.
|
|
|
|
Note that the run script is kernel-agnostic. Hence, you can execute the system
|
|
|
|
scenario on all the different kernels supported by Genode without any
|
|
|
|
modification. The regular expression specified to the 'run_genode_until' step
|
|
|
|
is used as pattern for detecting the success of the step. If the log output
|
|
|
|
produced by the scenario matches the pattern, the run script completes
|
|
|
|
successfully. If the pattern does not appear within the specified time (in
|
|
|
|
this example ten seconds), the run script aborts with an error. By creating
|
|
|
|
the run script, we have not just automated our work flow but have actually
|
|
|
|
created an automated test case for our components.
|