Saturday, 29 July 2017

Creating libraries and linking to libraries in Vala

Creating libraries and linking to libraries in Vala


This is an example of how to create a library in vala, and how to access it using several different methods. This example includes:
  1. A shared library written in vala, including
    • shared object (.so), and how to install it
    • headers (.h), and
    • bindings (.vapi)
  2. A command-line tool that uses the library by direct-linking
  3. 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)
  4. 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 ", test.hello());

// MyLib sum()
int x = 4, y = 5;
stdout.printf("The sum of %d and %d is %d ", 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 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 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 "); }
}

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 "));

// 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 its 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
download
alternative link download

Like the Post? Do share with your Friends.