- A shared library written in vala, including
- shared object (.so), and how to install it
- headers (.h), and
- bindings (.vapi)
- A command-line tool that uses the library by direct-linking
- A dbus server that uses the library by direct linking
- makes the library available to other dbus-aware applications
- self-terminating process after use (not a daemon)
- A command-line tool that uses the library via dbus and the dbus-server.
The first half of the example is based on the official vala tutorial library example,
I have added dbus connectivity and a bit more explanation that I found helpful.
1) Create an empty directory. These steps create a lot of files!
    $ mkdir test_library
    $ cd test_library
2) Create the library. Save this file as libtest.vala:
// BEGIN
public class MyLib : Object {
    public string hello() {
      return "Hello World from MyLib";
   }
   public int sum(int x, int y) {
      return x + y;
   }
}
// END
3) Create the .c, .h (C header), and .vapi (vala binding) files:
   -C --ccode   Output C code instead of compiled
   -H --header=file  Output C header file
      --library=name Assign name to library
      --vapi         Assign name to vapi file
   -b --basedir=dir  Use dir as the base source dir (For me, this is unnecessary)
    $ valac -C -H libtest.h --library libtest libtest.vala --basedir ./
4) Compile the library using:
   -shared       Create a .so shared object
   -fPIC         Position Independent Code (PIC) suitable for use in a shared library
   $(pkg-config --cflags gobject-2.0)
                 Output: -I/usr/include/glib-2.0
                         -I/usr/lib/i386-linux-gnu/glib-2.0/include
   -o filename   Output filename
     $ gcc -shared -fPIC -o libtest.so $(pkg-config --cflags --libs gobject-2.0) libtest.c
5) Create the command-line application that uses the library by linking.
Save this file as hello.vala:
// BEGIN
void main() {
    var test = new MyLib();
    // MyLib hello()
    stdout.printf("%s\n", test.hello());
    // MyLib sum()
    int x = 4, y = 5;
    stdout.printf("The sum of %d and %d is %d\n", x, y, test.sum(x, y));
}
// END
6) Compile the command-line application.
Using vala, there need to be TWO links:
- The vala link to a .vapi file (in this example, test.vapi)
- The gcc link to a C library (in this example, -X -ltestto add libtest.so)
   - The vala link to a .vapi file (in this example, test.vapi)
   - The gcc link to a C library (in this example, -X -ltest to add libtest.so)
   -X --Xcc=-I.    Pass the -I. (include current directory) to gcc.
                   GCC will look for shared libraries in the current directory first
   -X --Xcc=-L.    Pass the -L. (add current directory to search path) to gcc.
   -X --Xcc=-ltest Link library "libtest" which we just created in the current directory. 
   If we had the libtest.so in the local directory, we could use:
    $ valac -X -I. -X -L. -X -ltest -o hello hello.vala libtest.vapi
7) Test the command-line application that uses the library:
The library is in the local directory (uninstalled):
     $ LD_LIBRARY_PATH=$PWD ./hello
     Hello World from MyLib
     The sum of 4 and 5 is 9
8) We cannot easily use the local library for dbus.
So install the library to the expected location.
Copy the library to /usr/lib using:
$ sudo cp libtest.so /usr/lib/
9) Test the (installed) command-line application:
     $ ./hello
     Hello World from MyLib
     The sum of 4 and 5 is 9
10) Create the dbus server that uses the library.
A server listens for requests from other applications. This server acts as a gateway: It translates other application requests for library methods into a library call, and then sends the response back across dbus to the original requestor.
Save this file as dbus_server.vala:
// BEGIN
// Source: https://live.gnome.org/Vala/DBusServerSample#Server
[DBus (name = "org.example.Demo")]   // dbus interface name
public class DemoServer : Object {
    /* Functions that access the library on the Demo interface
     * Note the LACK of \n in the return strings.
     * Vala automatically mangles my_function_name into the
     *   standard Dbus camelcase MyFunctionName
     * So a function is "hello()" here, but "Hello" on Dbus 
     */
    public string sum () { 
        var test = new MyLib();
        int x = 4, y = 5;
        return "The sum of %d and %d is %d".printf( x, y, test.sum(x, y));
        // Note the LACK of \n in the return strings
    }
    public string hello () { 
        var test = new MyLib();
        return "%s".printf( test.hello());
    }
}
[DBus (name = "org.example.DemoError")]
public errordomain DemoError { SOME_ERROR }
/* Dbus functions */
void on_bus_aquired (DBusConnection conn) {
    try {
        string path = "/org/example/demo";
        conn.register_object (path, new DemoServer ());
    } catch (IOError e) { 
    stderr.printf ("Could not register service\n"); }
}
void main () {
    GLib.MainLoop loop           = new GLib.MainLoop ();
    GLib.TimeoutSource time      = new TimeoutSource(3000);   // 3 sec
    GLib.BusType bus             = BusType.SESSION;
    string destination           = "org.example.demo";
    GLib.BusNameOwnerFlags flags = BusNameOwnerFlags.NONE;
    Bus.own_name ( bus, destination, flags,
                  on_bus_aquired,
                  () => {},
                  () => stderr.printf ("Could not aquire name\n"));
    // Use timeout to quit the loop and exit the program
    time.set_callback( () => { loop.quit(); return false; });
    time.attach( loop.get_context() );  // Attach timeout to loop
    loop.run ();                // Listen for dbus connections
}
// END
11) Compile the dbus server:
   Dbus requires the gio package (--pkg gio-2.0)
   -X --Xcc=-I.    Pass the -I. (include current directory) to gcc.
                   GCC will look for shared libraries in the current directory first
   -X --Xcc=-L.    Pass the -L. (add current directory to search path) to gcc.
   -X --Xcc=-ltest Link library "libtest" which we just created in the current directory. 
   Since the library is installed, we can ignore those local directory flags:
      $ valac --pkg gio-2.0 -X -ltest dbus_server.vala libtest.vapi
12) Create a dbus .service file, so dbus knows how to find our new dbus_server.
Save this file as dbus.service. Your third line will vary - use the real path:
// BEGIN [D-BUS Service] Name=org.example.demo Exec=/home/me/vala/library/dbus_server // END
13) Install the dbus service file:
$ sudo cp dbus.service /usr/share/dbus-1/services/org.example.demo.service
14) Test the dbus server using an existing command-line application, dbus-send.
This is a command-line application using the library via dbus, using dbus_server for it's intended purpose as a gatetay.
The sender is different each time because dbus-send creates a new process and connection each time it is used.
The destination is different because the server terminates after 3 seconds.
      $ dbus-send --session --type=method_call --print-reply \
        --dest="org.example.demo" /org/example/demo org.example.Demo.Hello
      method return sender=:1.3173 -> dest=:1.3172 reply_serial=2
      string "Hello World from MyLib"
      $ dbus-send --session --type=method_call --print-reply \
        --dest="org.example.demo" /org/example/demo org.example.Demo.Sum
      method return sender=:1.3175 -> dest=:1.3174 reply_serial=2
      string "The sum of 4 and 5 is 9"
15) Clean up:
Remove the dbus service file:
Remove the test library from /usr/lib
     $ sudo rm /usr/share/dbus-1/services/org.example.demo.service
     $ sudo rm /usr/lib/libtest.so
Thanks man, I've been trying to brush up on my Vala and this really helped me out :)
ReplyDeleteStarting work on a web framework, and I used this guide to build and link the SCGI library I needed.