Showing posts with label vala. Show all posts
Showing posts with label vala. Show all posts

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
{ Read More }