Chapter 9. Create components: the UObject architecture

Table of Contents

UObject
The basics
Adding attributes
Binding functions and events
Timers
Advanced types for binaries
The "load" attribute
The "remote" attribute
The colormap example
The practical side: how to use create an UObject?
How to install a sdk to build/link components for your robot?
How to use umake to create engines and components?
How to distribute one of your components and make them available to others?

The UObject architecture is the most advanced way to extend URBI and integrates powerful components in the language. It's currently limited to C++ but should generalize to other languages in the future. The idea is to take a C++ class and, after a few small modifications, to be able to plug this class in the URBI language so that one can access its methods and attributes as if they were pure URBI objects. A few words about terminology: the UObject architecture enables to add a component to the language, and this component will be seen as an object.

There are actually two ways of integrating your C++ class inside URBI:

  • Mode plugin: You can plug the object directly in URBI (link it to the URBI Engine) and it will be part of the binary code of the URBI Engine.

  • Mode remote: You can run it as an autonomous remote process that will connect itself to your URBI Engine and transparently add the object to the language, just like in the plugin mode, but remotely.

In both cases, we provide the necessary tools to make the link (described below). The good news is that the C++ source code of your object is exactly the same in both cases, and the way you use it inside URBI is also transparent. So, you can decide to plug/remote-run a component at will (hopefully in the future, you will be able to relocate the object at runtime, but not for now).

We will now see how to turn your C++ class into an UObject class, and we will see then how to connect the methods and attributes of the C++ class to URBI.

UObject

The basics

Let's create a colormap object, composed of colormap.cpp and colormap.hh. The colormap.hh should start like this:

#include <urbi/uobject.hh>
using namespace urbi;

class colormap : public UObject
{
  public:
  colormap(std::string);
  ...
};

Whatever constructor you previously had should be renamed init. The default constructor myclassname(std::string) which is appropriate for UObjects must be used instead. For example, you might define the constructor init which takes a RGB point as a color definition like this:

public:
  colormap(std::string);
  int init (int r, int g, int b);
  ...

For the moment, that's all what you need on the class definition side. Let's have a look at the main code in colormap.cpp:

#include "colormap.hh"

UStart(colormap);

colormap::colormap(std::string s) : UObject(s)
{
  UBindFunction(colormap, init);
}

int colormap::init(int r, int g, int b)
{
  return 0;
}

...

Two new things here: you have to invoke the "magic" line UStart(myobject) in order to let the system know about it. Then, you must make sure that the default constructor calls the UObject constructor and passes the string, and also bind the init function to make it visible and export it in URBI. This is required if you want the init constructor to be called by URBI upon a new object creation. The init method should return 0 upon success, anything else in case of failure (you can also return void which is considered as a success).

There is nothing else to know, at this stage you already have a exportable object called 'colormap' with a method 'init'. Now, you can compile it and get a binary code ready to link.

Let's assume than you have linked this code to the URBI Engine, to make it a component in plugin mode (we will see how later). Now, how to use this new colormap object? Well, not much has to be done: it's already there. Remember that in URBI there is no difference between a class and an instance (prototype-based language), so defining colormap is enough to have a functionnal colormap object. You can try to evaluate it to see this:

colormap;
[139464:notag] OBJ [load:1.000000]

NB: By default, there is an exported load attribute in UObject, let's ignore it for the moment.

Let's define a subclass of colormap. This action will call the init constructor on the C++ side and spawn a new instance of the C++ colormap class, but of course this is all done automatically and you don't have to take care of that:

ball = new colormap(123,45,12);
ball;
[139464:notag] OBJ [load:1.000000]

You see that the syntax to create a new object in URBI is identical to the C++ syntax. Each time is was possible, we have kept the familiar C/C++ syntax in URBI, because there is no point to waste time learning stuffs we already know (as long as there is no confusion in term of semantics).

Adding attributes

Our colormap object is not much fun so far. To make it more useful, we can start to add attributes to the object and bind them to URBI. To add a x variable, we will simply add UVar x; inside the class definition:

#include <urbi/uobject.hh>
using namespace urbi;

class colormap : public UObject
{
public:
  colormap(std::string);

  UVar x; // definition of the exported variable
  ...
};

and then add the binding code in the init method:

int colormap::init(int r, int g, int b)
{
  UBindVar(colormap, x);
  ...
}

Actually, you can put your binding code (UBindVar) anywhere you want, in particular it can be in the C++ object constructor or in the object init method. If you put it in the C++ constructor, it will make the variable available to the base instance (the one that is there at start and that you don't have to 'new'), or if you put it in the 'init' method, only 'newed' objects will have it. This is useful if the base instance is useless because you need to derive it to specify it. In that case you put all your bindings in the 'init' method only and the base instance is just a sort of ghost instance. Note that UObject::derived is a boolean that tells you if your class has been derived with a 'new' or if it is the base class.

You can check, now the colormap.x and ball.x will be there.

To assign a value to x from within your C++ class, simply use it as a normal variable, UObject will do the rest for you:

x = 42;
  or
x= "hello";

The = operator in C++ has been redefined for UVar, so that you don't have to worry and you can assign values to x as you would do it from within URBI.

Now, how to read the variable? We've tried to keep things simple again: you can simply use a C-style casting to get a value in the appropriate C++ type. For the moment, there is not exception raised if an error occurs, so be careful to what you are doing:

x = 42;
printf("Value of x: %d n",(int)x);

x is called a "hook" to the URBI colormap.x variable. Actually, you can define hooks on any variable you like by defining your own UVar instance wherever you like (it will be automatically binded, no need to use UBindVar, the UVar constructor does it). Here are a few examples:

UVar("camera.val");
UVar("camera","val");
UVar* myvar = new UVar("headPan","val");

The reason why you have to call UBindVar for a UVar defined in the body of your class is that this UVar is a non-dynamically allocated UVar called with the default UVar() constructor. Such a UVar doesn't know its name at this stage and the UBindVar macro simply tells it who it is. You don't need this stage with a direct call to the UVar(std::string) constructor who takes the name as its parameter.

Of course, your C++ object can contain many attributes that will not be exported to URBI and will remain "private" to the C++ class. To make an attribute available to URBI, you need to define it as a UVar or to "UBindVar" one that is part of your object definition.

One important thing that one wants to do with attributes is to monitor them for changes or accesses. This is done by assigning a callback function to the variable, specifying whether you want to be called back on changes or on accesses:

UNotifyChange(x,&colormap::mycallback);
UNotifyAccess(UVar("doo.daa",&colormap::myothercallback);
UNotifyChange("another.variable",&colormap::anothercallback);

Notify on change means that the callback will be called each time the variable is modified on the URBI side (for variables attached to sensors, it means "each time the sensor value is updated"). Notify on access means that the callback will be called each time someone evaluates the variable on the URBI side, so that you have a chance to update its value before the evaluation. In that case, you are advised to put a time-based caching mechanism in your callback if the variable is called frequently inside expressions.

You will typically put those "Notify" lines in the init function or in the constructor of your object, the choice of one over the other being dictated by the same rationale than with UBindVar. Notice that you must pass a pointeur to a function, which must be a method of your object. You have only two types of prototypes available for these callbacks:

UReturn mycallback();
UReturn mycallback(UVar&);

The first one is the simplest and obvious one: the function is called when the condition is met. The second one does the same thing but passes the UVar as a reference parameter so that you can use the same callback with several variables and get the one that is related to the current call.

Binding functions and events

Just like you did with attributes, you can easily bind a function to the mirrored URBI object. There is not much to do there, simply use the following construct:

int colormap::init(int r, int g, int b)
{
  UBindFunction(colormap, dostuff);
  ...
}

std::string colormap::dostuff(int, float)
{
  ...
}

This will make the method dostuff visible to the outside. You don't need to worry about parameters, they will be recognized an exported for you. For the moment, you cannot overload a function with this mecanism (and in particular, you cannot overload the init constructor).

Similarily, you can bind an event to a method of your object, so that this method will be called each time the corresponding event is emitted on the URBI side, and you will get the parameters on the way. Simply do:

UBindEvent(colormap, reacttothis);

You can also ask to be notified when the event terminates (as you know, events can last during a certain amount of time in URBI). For example, if you want to be notified by calling the endthis method of your object, simply use:

UBindEvent(colormap, reacttothis);
UBindEventEnd(colormap, reacttothis, endthis);

endthis must have a simple prototype like this one:

void colormap::endthis();

Timers

You can easily set timers to be called back at regular time intervals. The syntax is:

USetTimer(time_in_ms, &myobject::mycallback);

With mycallback being a method of your object with the following prototype:

UReturn myobject::mycallback();

You cannot use a callback function coming from outside of your object.

Advanced types for binaries

For integers, floats and strings the assignement and reading-by-casting of UVar is straitforward. For binary data, like images and sounds, you will need two appropriate types: UImage and USound. Here is a copy of their definition from uobject.hh


   ///Class encapsulating an image.
   class UImage {
     public:
       char                  *data;            ///< pointer to image data
       int                   size;             ///< image size in byte
       int                   width, height;    ///< size of the image
       UImageFormat          imageFormat;
   };

   ///Class encapsulating sound informations.
   class USound {
     public:
       char                  *data;            ///< pointer to sound data
       int                   size;             ///< total size in byte
       int                   channels;         ///< number of audio channels
       int                   rate;             ///< rate in Hertz
       int                   sampleSize;       ///< sample size in bit
       USoundFormat          soundFormat;      ///< format of the sound data
       USoundSampleFormat    sampleFormat;     ///< sample format
   }

You recognize them, they are the types used in liburbi, no surprise. If your UVar is an image, like "camera.raw", you can simply cast it to a UImage and you will retrieve the relevant information in the appropriate attributes, in particular the binary content will be in data and the size in size. Same thing for a sound.

Be careful if you use camera.val: it might be a jpeg-compressed binary and you should convert it with one of those functions, as described in section #x1-690007.9:

 int convertRGBtoYCrCb(const byte* source, int sourcelen, byte* dest);
 int convertYCrCbtoRGB(const byte* source, int sourcelen, byte* dest);
 int convertJPEGtoYCrCb(const byte* source, int sourcelen, byte* dest, int &size);
 int convertJPEGtoRGB(const byte* source, int sourcelen, byte* dest, int &size);

If you want to assign a sound to, let's say speaker.val, simply fill up a USound variable and assign it to the appropriate UVar, the = operator has been redefined to handle this. So far, we handle only wav format.

Writting anything to an uvar will copy the memory. USound and UImage do no memory management at all, so assigning an USound to an other just copies the pointer. If you want memory to be automatically managed, you can use UBinary, which deletes its buffer when its destructor is called. As a consequence, returning a USound in a bound function is problematic. It would be better to wrap the USound in a UBinary and return the UBinary.

The "load" attribute

We have already mentionned the load attribute that is defined as a UVar and bound by default in UObject. This attribute can be used to test in your C++ code whether the object is activated or not on the URBI side. In URBI, a call to "myobject on;" will put load to 1 and a call to "myobject off;" will put it to 0. So, you can easily test in your various functions if you have to do the computation or not, based on the value of load.

This is extremely useful if you want to be able to activate/disactivate some CPU hungry calculation that would otherwise run for nothing in the background. For example, you can turn the ball detection off in Aibo with:

ball off;

Note that this is a broadcastable construct: if you on/off a group, it will recursively propagates to every member of the group. That's exactly what happens behind the scene with a command like motors on;

NB: You also have "myobject switch;" to alternate between on and off.

The "remote" attribute

In UObject definition, the remote attribute is available to know whether your object is running as a remote component or a plugged one. This can be useful when you want to behave differently in both cases, typically handling the transfer of large amount of data or images with or without compression. The colormap example below make use of the remote attribute.

The colormap example

Here is a real example of a colormap object as it is used in the Aibo, to calculate the average position of a blob of color defined by a subspace of the YCrCb color space. You see how we bind a callback to source, which is usually camera. The actual callback is set to the .val or .raw attribute of the source object, depending on the status of the object, remote or not. In remote mode, we want to use jpeg compression and work with the resulting image value, whereas in plugged mode, we can use shared memory on the raw buffer to get a better image without artifacts, and avoid compressing/decompressing for nothing.

You also see how we simply assign values to the x and y attributes and other attributes describing the shape of the blob:

First the colormap.hh file (extracts only):

 #include <urbi/uobject.hh>
 using namespace urbi;

 class colormap : public UObject
 {
   public:

     colormap(std::string);
     int init(std::string,int,int,int,int,int,int,ufloat);

     UVar   x;
     UVar   y;
     UVar   visible;
     UVar   ratio;
     UVar   threshold;
     UVar   orientation;
     UVar   elongation;
     UVar   ymin, ymax, cbmin, cbmax, crmin, crmax;

     UReturn newImage(UVar&);
 };

Here, we use ufloat instead of float because ufloat can be adapted to 32bits or 64bits or even no-FPU motherboards and thus it is more suitable for embedded applications.

Now, the main code:

 #include "colormap.hh"

 UStart(colormap);

 //! colormap constructor.
 colormap::colormap(std::string s) :
   UObject(s)
 {
   UBindFunction(colormap,init);
 }

 //! colormap init function
 int
 colormap::init(std::string source,
                int _Ymin,
                int _Ymax,
                int _Cbmin,
                int _Cbmax,
                int _Crmin,
                int _Crmax,
                ufloat _threshold)
 {
   UBindVar(colormap,x);
   UBindVar(colormap,y);
   UBindVar(colormap,visible);
   UBindVar(colormap,ratio);
   UBindVar(colormap,threshold);
   UBindVar(colormap,orientation);
   UBindVar(colormap,elongation);
   UBindVar(colormap,ymin);
   UBindVar(colormap,ymax);
   UBindVar(colormap,cbmin);
   UBindVar(colormap,cbmax);
   UBindVar(colormap,crmin);
   UBindVar(colormap,crmax);

   if (remote)
     UNotifyChange(source+".val",&colormap::newImage);
   else
     UNotifyChange(source+".raw",&colormap::newImage);




   // initialization
   ymin        = _Ymin;
   ymax        = _Ymax;
   cbmin       = _Cbmin;
   cbmax       = _Cbmax;
   crmin       = _Crmin;
   crmax       = _Crmax;
   threshold   = _threshold;
   x           = -1;
   y           = -1;
   visible     = 0;
   orientation = 0;
   elongation  = 0;
   ratio       = 0;

   return 0;
 }

 //! colormap image update
 UReturn
 colormap::newImage(UVar& img)
 {
   if ((ufloat)load < 0.5) return(1);

   UImage img1 = (UImage)img;  //ptr copy

   if (remote)
     convertYCrCb(img1); // this function is available in UObject 1.0 only

   int w = img1.width;
   int h = img1.height;

   //lets cache things
   int ymax = this->ymax; int ymin = this->ymin;
   int crmin = this->crmin; int crmax = this->crmax;
   int cbmin = this->cbmin; int cbmax = this->cbmax;

   long long x=0,y=0,xx=0,yy=0,xy=0;
   int size = 0;
   for (int i=0;i<w;i++)
     for (int j=0;j<h;j++) {

       unsigned char lum = img1.data[(i+j*w)*3];
       unsigned char cb  = img1.data[(i+j*w)*3+1];
       unsigned char cr  = img1.data[(i+j*w)*3+2];




       if ( (lum  >= ymin) &&
            (lum  <= ymax) &&
            (cb >= cbmin) &&
            (cb <= cbmax) &&
            (cr >= crmin) &&
            (cr <= crmax) ) {
         size++;
         x += i;
         y += j;
         xx += i*i;
         yy += j*j;
         xy += i*j;
       }
     }

   this->ratio = ((ufloat)size)/((ufloat)(w*h));
   if (size > (int)((ufloat)threshold * (ufloat)(w*h))) {

     this->visible = 1;
     this->x = 0.5 - ((double)x /
         ((double)size * (double)w));
     this->y = 0.5 - ((double)y /
         ((double)size * (double)h));

     //orientation: first eighenvector of covariance matrice
     double m00 = (double)xx - (double)(x*x)/(double)(size);
     double m11 = (double)yy - (double)(y*y)/(double)(size);
     double m01 = (double)xy - (double)(x*y)/(double)(size);

     //bigest eighenvalue
     double l = (m00+m11)/2.0 + 0.5*sqrt((m00+m11)*
                (m00+m11)-4*(m00*m11-m01*m01));

     //first eighenvector orientation
     double angle = atan2(l-m00, m01);
     this->orientation = angle* 180.0 /M_PI;

     //variance on new axis => elongation
     double angle2 = angle + M_PI/2.0;
     double X = x*cos(angle)+y*sin(angle);
     double Y = x*cos(angle2)+y*sin(angle2);
     double XX = xx*cos(angle)*cos(angle)+yy*sin(angle)*sin(angle)+
                 2.0*xy*cos(angle)*sin(angle);
     double YY = xx*cos(angle2)*cos(angle2)+yy*sin(angle2)*sin(angle2)+
                 2.0*xy*cos(angle2)*sin(angle2);
     double vX = XX - X*X/(double)size;



     double vY = YY - Y*Y/(double)size;

     this->elongation = sqrt(vX/vY);

   }
   else {
     this->x=-1;
     this->y=-1;
     this->visible = 0;
   }

   return(1);
 }

The colormap object is then plugged in the URBI Engine and it is used to create a ball detector in the URBI.INI file:

ball = new colormap("camera",0,255,120,190,150,230,0.0015);

The practical side: how to use create an UObject?

You'll have to install an appropriate SDK (see hereafter), then to use umake in unix environments or visual sudio™.

We have seen that you can create two different types of UObjects: remote UObjects and plugged in UObjects. At the moment, with an Urbi-SDK, you can create remote UObjects only. To create a new engine with plugged-in UObjects, you must use the urbiengine-SDK which allows you also to create remote UObjects.

How to install a sdk to build/link components for your robot?

To build or use components for a given URBI server, you need to install the engine SDK corresponding to the server. Download it from the URBI website (or from the robot manufacturer's website). the installation procedure depends on the format of the downloaded file and of the OS:

minGW

If you are using a mingw under windows, unzip the package in the root mingwdir. In a mingw console, type:

cd /
unzip DOWNLOADED_SDK.zip

Visual sudio/Visual C++ express edition

If you are using visual c++ express or visual studio under windows, unzip the package in the any dir you want using your favorite zip tool. You will find inside an "include" dir and a "lib" dir. Provide this directories in your visual project respectively in the include dir list and in the link path list.

mac, zip file

Usually x86 and powerPC versions are available. In a console, type:

cd /
unzip DOWNLOADED_SDK.zip

rpm

If you are using a rpm based linux distribution (redhat, mandrake, fedora...), you can run as root in the directory where is your downloaded package:

rpm -ivh downloaded_SDK.rpm

You can also use your favorite graphical or command line package installer.

deb

If you are using a deb based linux distribution (debian, ubuntu...) you can run as root in the directory where is your downloaded package:

dpkg --install downloaded_SDK.rpm

with ubuntun there is no root account, use:

sudo dpkg --install downloaded_SDK.rpm

You can also use your favorite graphical or command line package installer.

tarball

If you are using a deb or rpm based linux distribution, you can either use the tar binary package. As root:

cd /
tar -xvzf downloaded_SDK.tgz

or

cd /
tar -xvjf downloaded_SDK.tar.bz2

sources

If you want to use (if available) a source package, use the standard commands:

tar -xvzf downloaded_SDK.tgz
cd new_dir
./configure;make
sudo make install

Some robots requires build chains. For exemple, with aibo, you will need to have an installed OPENR-SDK. There is a good tutorial on how to install it there: http://aibostuff.iofreak.com/wiki.php?n=Open-R.UbuntuInstall

How to use umake to create engines and components?

Plugins and remote components are built the same way, using umake.

Basic usage

To compile all source files in the current directory (and its subdirectories) and link them with the Remote SDK, simply type umake. To compile all source files in the current directory and produce a library, type umake-lib. To compile all source files in the current directory and link with the Engine SDK, type umake-engine.

$ ls

foo.cc foo.hh

$ umake

/usr/local/gostai/core/linux/libtool
--tag=CXX --mode=compile g++ -O2 -pthread
-I/usr/local/gostai/core/include -c foo.cc -o foo.lo g++ -O2 -pthread
-I/usr/local/gostai/core/include -c foo.cc -o foo.o
/usr/local/gostai/core/linux/libtool --mode=link --tag=CXX g++ -O2
-pthread -L/usr/local/gostai/core/linux/remote -o
urbiengine-linux-remote ./foo.o
/usr/local/gostai/core/linux/remote/*.la mkdir .libs libtool: link:
warning: library
`/usr/local/gostai/core/linux/remote/libkernel-remote.la' was moved.
libtool: link: warning: library
`/usr/local/gostai/core/linux/remote/libkernel-remote.la' was moved.
g++ -O2 -pthread -o urbiengine-linux-remote ./foo.o
-L/usr/local/gostai/core/linux/remote
/usr/local/gostai/core/linux/remote/libkernel-remote.a
-L/tmp/urbi/gostai/core/linux/remote

$ ls

foo.cc foo.hh foo.lo foo.o urbiengine-linux-remote

Specifying the source

You can pass to umake a list of files and directory. Files can be sources, headers and libraries. Directory will be searched and all the sources and libraries they contain will be included in the build.

$ ls -R .

.:
uobj1  uobj2

./uobj1:
myuobj1.cc

./uobj2:
myuobj2.cc

$ umake uobj1 uobj2

/usr/local/gostai/core/linux/libtool --tag=CXX --mode=compile g++ -O2
 -pthread -I/usr/local/gostai/core/include -c uobj1/myuobj1.cc -o uobj1/1.lo
 g++ -O2 -pthread -I/usr/local/gostai/core/include -c uobj1/myuobj1.cc -o
 uobj1/1.o /usr/local/gostai/core/linux/libtool --tag=CXX
 --mode=compile g++ -O2 -pthread -I/usr/local/gostai/core/include -c
 uobj2/myuobj2.cc -o uobj2/2.lo g++ -O2 -pthread
 -I/usr/local/gostai/core/include -c uobj2/myuobj2.cc -o uobj2/2.o
 /usr/local/gostai/core/linux/libtool --mode=link --tag=CXX g++ -O2
 -pthread -L/usr/local/gostai/core/linux/remote -o
 urbiengine-linux-remote uobj1/1.o uobj2/2.o
 /usr/local/gostai/core/linux/remote/*.la mkdir .libs libtool: link:
 warning: library
 `/usr/local/gostai/core/linux/remote/libkernel-remote.la' was moved.
 libtool: link: warning: library
 `/usr/local/gostai/core/linux/remote/libkernel-remote.la' was moved.
 g++ -O2 -pthread -o urbiengine-linux-remote uobj1/1.o uobj2/2.o
 -L/usr/local/gostai/core/linux/remote
 /usr/local/gostai/core/linux/remote/libkernel-remote.a
 -L/tmp/urbi/gostai/core/linux/remote

$ ls -R .

.: uobj1 uobj2  urbiengine-linux-remote

./uobj1:
myuobj1.cc

./uobj2:
myuobj2.cc

Specifying a different host or SDK

To compile for a different host, you can use the -H host option. A SDK for the specified host must be installed. To specify the SDK to use, -C sdk.

$ ls
foo.cc foo.hh
$ umake --core webots

/usr/local/gostai/core/linux/libtool --tag=CXX --mode=compile g++ -O2
-pthread -I/usr/local/gostai/core/include -c foo.cc -o foo.lo g++ -O2
-pthread -I/usr/local/gostai/core/include -c foo.cc -o foo.o
/usr/local/gostai/core/linux/libtool --mode=link --tag=CXX g++ -O2
-pthread -L/usr/local/gostai/core/linux/webots -o
urbiengine-linux-webots ./foo.o
/usr/local/gostai/core/linux/webots/*.la libtool: link: warning:
library `/usr/local/gostai/core/linux/webots/liburbicore.la' was
moved.  libtool: link: warning: library
`/usr/local/gostai/core/linux/webots/liburbicore.la' was moved.  g++
-O2 -pthread -o urbiengine-linux-webots ./foo.o
-L/usr/local/gostai/core/linux/webots
/usr/local/gostai/core/linux/webots/liburbicore.a
-L/tmp/urbi/gostai/core/linux/webots
-L/tmp/urbi/gostai/kernel/linux/engine -L/usr/local/webots/lib
-lController

$ ls

foo.cc foo.hh foo.lo foo.o urbiengine-linux-webots

Specifying the output file

The option -o can be used to set the output file name. It defaults to urbiengine-HOST-CORE when building engines, and uobject-HOST.a when building libraries.

$ ls
foo.cc foo.hh
$ umake --core webots -o urbi

/usr/local/gostai/core/linux/libtool --tag=CXX --mode=compile g++ -O2
-pthread -I/usr/local/gostai/core/include -c foo.cc -o foo.lo g++ -O2
-pthread -I/usr/local/gostai/core/include -c foo.cc -o foo.o
/usr/local/gostai/core/linux/libtool --mode=link --tag=CXX g++ -O2
-pthread -L/usr/local/gostai/core/linux/webots -o
urbiengine-linux-webots ./foo.o
/usr/local/gostai/core/linux/webots/*.la libtool: link: warning:
library `/usr/local/gostai/core/linux/webots/liburbicore.la' was
moved.  libtool: link: warning: library
`/usr/local/gostai/core/linux/webots/liburbicore.la' was moved.  g++
-O2 -pthread -o urbiengine-linux-webots ./foo.o
-L/usr/local/gostai/core/linux/webots
/usr/local/gostai/core/linux/webots/liburbicore.a
-L/tmp/urbi/gostai/core/linux/webots
-L/tmp/urbi/gostai/kernel/linux/engine -L/usr/local/webots/lib
-lController

$ ls
foo.cc foo.hh foo.lo foo.o urbi
Passing parameters to make

All the umake options of the form 'var=value' will be passed to make. To pass flages to the compiler and the linker, use the variables EXTRA_CPPFLAGS and EXTRA_LDFLAGS.

$ ls foo.cc foo.hh $ umake --core webots *.cc
EXTRA_CPPFLAGS=-I/usr/local/webots/include -o urbi -V

/usr/local/bin/umake: run.  /usr/local/bin/umake: libs=''
/usr/local/bin/umake: sources=' 'foo.cc'' /usr/local/bin/umake:
headers='' /usr/local/bin/umake: objects=''foo.o''
/usr/local/bin/umake: make options='
'EXTRA_CPPFLAGS=-I/usr/local/webots/include
-I/home/thomas/project/liburbi-cpp/trunk/lib' OUTBIN=urbi
URBI_ENV=webots prefix=/usr/local' /usr/local/bin/umake: invoking make
-f /usr/local/gostai/core/linux/param.mk urbi
/usr/local/gostai/core/linux/libtool --mode=link --tag=CXX g++ -O2
-pthread -L/usr/local/gostai/core/linux/webots -o urbi foo.o
/usr/local/gostai/core/linux/webots/*.la libtool: link: warning:
library `/usr/local/gostai/core/linux/webots/liburbicore.la' was
moved.  libtool: link: warning: library
`/usr/local/gostai/core/linux/webots/liburbicore.la' was moved.  g++
-O2 -pthread -o urbi foo.o -L/usr/local/gostai/core/linux/webots
/usr/local/gostai/core/linux/webots/liburbicore.a
-L/tmp/urbi/gostai/core/linux/webots
-L/tmp/urbi/gostai/kernel/linux/engine -L/usr/local/webots/lib
-lController /usr/local/bin/umake: done.

$ ls
foo.cc foo.hh foo.lo foo.o urbi
Examples

Suppose you have a module whose sources are in the current directory.

  • umake produces urbiengine-linux-remote, a remote object executable

  • umake-lib -o mymodule produces mymodule.a, a library that can be linked using umake to a sdk.

  • umake-engine -o urbi . libs/othermodule.a produces urbi, an URBI server containing the module othermodule.a and the module made by compiling the sources in the current directory

  • umake-engine --core=aibo --host=mipsel-linux produces urbi, an URBI engine for aibo which can run on the aibo host. This is the same as umake-aibo

  • umake-engine -H arm EXTRA_CPPFLAGS=/usr/local/myLib/include produces urbiengine-arm, an URBI engine for arm architecture containing your object, if you have the arm-engine URBI SDK installed. It will search for headers in the /usr/local/myLib/include directory.

How to distribute one of your components and make them available to others?

You can either distribute the sources of your component, or the library file (.a) generated by the build process as described above (umake-lib). People will then be able to link it to an URBI server as explained above. Please note that the library is architecture dependant: a component compiled for the Aibo can't be relinked as a remote module, the sources have to be recompiled. We strongly advice to publish both the source code for rebuilding purposes if your license allows it, AND one or several binary versions for people who just want to link it and use it as a remote component.

The website http://www.urbiforge.com is a platform to exhange components and URBI scripts. You can upload your work there so that the community can benefit from it.