- 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 -ltest
to 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