Bigloo libraries are collections of global bindings (global variables and
global functions). Bigloo libraries are build on the top of the host
operating system (e.g. Unix) libraries. Because Bigloo uses modules, a
library is not only a bundle of compiled codes and memory locations. A
Bigloo library is split into several files:
- one heap that describes the locations of the library.
- several host library files.
- possibly, C header files.
- possibly, an initialization file.
Let's consider, for example, a library that implements the
format
Common Lisp facility. Let's suppose we name this library
bformat
and let's suppose that the library number is
1.0
. Using a Unix machine, the Bigloo library will consist of
the following files:
bformat.heap
: the heap file.
bformat.init
: the initialization file.
libbformat_s-1.0.a
, libbformat_s-1.0.so
,
libbformat_u-1.0.a
, libbformat_u-1.0.so
:
the Unix library files. The file names with a _u
are libraries compiled
in unsafe and optimized mode. By convention the library using
the _s
suffix are safe libraries, _p
are profiling
libraries, _d
debug libraries, and _e
eval libraries.
bformat.h
: an include file.
24.1 Compiling and linking with a library
|
From the user stand point, using a library can be made two ways:
- Using the Bigloo
-library lib-name
option where
lib-name
is the name of the Bigloo library (not the name of one
of the Unix files implementing the library). The name of the library
must be lower case. For instance:
$ bigloo foo.scm -library bformat
|
- Using the module clause
library
. This second solution prevent from
using a special compilation option. For instance, this module will
automatically compile and link with the bformat
library:
(module foo
(library bformat))
...
(format ...)
|
When a Bigloo library
lib
is used, Bigloo automatically
searches if a file called
lib.init
exists. If such a file
exits, it is loaded at compile-time. For instance, that file may be
used to specify compilation flags. The initialization file may affect
any of the global parameters of the Bigloo compiler. A Bigloo user
library might be needing additional system library. For instance, a
Bigloo library supporting SSL connections is likely to be needing the
a native library. Setting the compiler variable
*ld-port-options*
has this effect. For instance, one may define
an initialization file such as:
(cond-expand
(bigloo-compile
(set! *ld-post-options* (string-append "-lssl " *ld-post-options*)))
(bigloo-eval
#unspecified))
|
When a Bigloo library
lib
is used, the Bigloo linker
automatically looks at a library to be linked against the
application. The name of the file containing the library depends on
the operating system and the back-end used. For instance, under Unix,
for a library called
NAME, the Bigloo linker searches for a
file called
libNAME_[s|u]-VERSION.a
or
libNAME_[s|u]-VERSION.DYNLIB-SUFFIX
in the
compilation linker path when using the native back-end. It searches
for a file
NAME_[s|u]-VERSION.zip
when the JVM
back-end is used.
This default
NAME can be overridden in the initialization
file. The function
declare-library!
associates a
Bigloo library name and a system name.
declare-library! ident [attributes] | library procedure |
All the attributes are optional.
version: the version number of the library. This defaults
to the Bigloo version number.
basename: the base of the filename containing the library.
This default to the library name.
srfi: a list of symbols denoting the SRFI 0 features implemented
by this library. Registered SRFIs may be tested by the cond-expand
form (see SRFIs). This defaults to an empty list.
dlopen-init: a function to be invoked when the library is
dynamically loaded using the function dynamic-load . This defaults
to #f .
module-init: a module to be initialized when the library is
loaded. This defaults to #f .
eval-init: a module to be initialized for binding the library
exports in the interpreter. This defaults to #f .
class-init: the JVM or .NET class name containing the module
to be initialized. This defaults to #f .
eval-init: the JVM or .NET class name containing the module
to be initialized for eval. This defaults to #f .
init: a function to be invoked when a library is loaded.
This defaults to #f .
eval: a function to be invoked when a library is loaded for
the interpreter. This defaults to #f .
Examples:
- The following declares a library named
foo . When loaded
the Bigloo runtime system will seek file named libfoo_s-3.1a.so ,
libfoo_u-3.1a.so , and libfoo_e-3.1a.so .
- The following declares a library named
pthread . When loaded
the Bigloo runtime system will seek file named libbigloopth_s-1.1a.so ,
libbigloopth_u-1.1a.so , and libbigloopth_e-1.1a.so . Once
the library loaded, the SRFI-0 features pthread and srfi-18
will be bound. When loading the library, the two modules __pth_thread
and __pth_makelib will be initialized. In the JVM version these
modules are compiled in the classes "bigloo.pthread.pthread"
and "bigloo.pthread.make-lib" .
(declare-library! 'pthread
:basename "bigloopth"
:version "1.1a"
:srfi '(pthread srfi-18)
:module-init '__pth_thread
:module-eval '__pth_makelib
:class-init "bigloo.pthread.pthread"
:class-eval "bigloo.pthread.make-lib")
|
|
library-translation-table-add! ident name | library procedure |
library-translation-table-add! ident name version | library procedure |
library-translation-table-add! ident name version :dlopen-init initsym | library procedure |
The function library-translation-table-add! is obsolete. It should
no longer be used in new code. It is totally subsumed by
declare-library! . The function library-translation-table-add!
is still documented for enabling readers to understand old Bigloo source
code.
This function register a name for the library id . An optional
version can be specified. The optional named argument dlopen-init
gives the base name of the initialization entry point of a library.
Imagine that we would like to name our bformat library
bigloobformat . This can be achieved by adding the following
expression in the initialization file.
(library-translation-table-add! 'bformat "bigloobformat")
|
Using this translation, on a Unix platform, the library used during
the linking will be named:
libbigloobformat_s-<BIGLOO-VERSION>.a . In order to change the
<BIGLOO-VERSION> to another suffix, such as 1.0 , one may use:
(library-translation-table-add! 'bformat "bigloobformat" "1.0")
|
In such a case, the library searched will be named
libbigloobformat_s-1.0.a .
Specifying a #f prevents the insertion of any suffix. Hence,
(library-translation-table-add! 'bformat "bigloobformat" #f)
|
Instruments the compiler to look at a library named
libbigloobformat_s.a .
|
24.2 Library and inline functions
|
It is illegal for libraries to include inline functions that make use of
new foreign types. By new foreign type, we mean foreign types that are
defined inside the library. A library may contains inline functions but
that inline functions must not even call function using foreign types in
their prototypes. Including inline functions making use of foreign C
type will make the compiler to fail when compiling user code. The
compiler will fail prompting type errors. A library may contains non-inline
functions that make use of new foreign types.
The function
library-load
loads a library in the interpreter.
library-exists? ident . path | library procedure |
Checks if the library ident exists for the current back-end.
The regular Bigloo library paths are scanned unless optional path s
are sent to the function.
|
library-load ident . path | library procedure |
Loads a library in the interpreter. In addition to dynamically loading
the library, this function tries to the _e version of the library.
Libraries are searched in regular Bigloo library paths
unless optional path s are sent to the function.
This version may be used for automatically exporting binding to the
interpreter. In general, the _e library is a simple library
that contains only one module, the module that is used to build the
heap-file. For instance, let's consider an implementation of a library
for SSL programming. This library is composed of a single implementation
module __ssl_ssl . The library is build using a heap file:
(module __ssl_makelib
(import __ssl_ssl))
|
Changing this file for:
(module __ssl_makelib
(import __ssl_ssl)
(eval (export-all)))
|
Enables the construction of a _e library.
When the system loads a dynamic library, it initializes it.
For that it expects to find initialization entry points in the dynamic
libraries that are named after the libraries name. More precisely, for
the LIB_s library, the loader seeks the entry point named
"LIB_s" and for the LIB_e , it seeks "LIB_e" .
The name of the initialization entry of a library can be changed using
the declare-library! function. If that named is changed,
one module of the library must contain an option module clause
that sets the variable *dlopen-init* with the name of the initialization
entry point.
Since Bigloo 3.1a, the runtime system supports a better way for
initializing libraries. Initialization modules can be associated
with a library. When loaded, these modules are automatically initialized.
This new method fits harmoniously with the Bigloo initialization process
and it prevents users from annotating the source code of the library.
For instance, if a library initialization file contains the following
declaration:
(declare-library! 'foo :module-init 'foo)
|
Then, the library must implement the foo module.
(module foo
(import ...)
...)
|
In addition if the library binds variables, functions, or classes in the
interpreter then, an eval-init clause must be added to the
class declaration:
(declare-library! 'foo :module-init 'foo :eval-init 'foo-eval)
|
Then, the module foo-eval must be implemented in the
libfoo_e library.
(module foo-eval
(import ...)
(eval (export-all)))
|
|
The standard distribution contains examples of such construction. In
particular, the multi-threading libraries
pthread
and
fthread
use this facility.
It is possible to implement a "read-eval-print-loop" that is extended
with the facilities implemented inside a library. In order to make
the variables, functions, and classes of a library visible from the
interpreter, the eval
library
module clause has to be used.
(see
Module Declaration) For instance, here is a module that
implements a "repl" with the
format
facility available:
(module format-repl
(eval (library bformat))
(library bformat))
;; a dummy reference to a facility of the format library
(let ((dummy format))
(repl)
|
Bigloo libraries require several steps before being completed.
- The first step is to build a library heap. This is achieved
using a special compilation mode:
-mkaddheap -mkaddlib -addheap -heap-library <ident>
.
That is, for your library you have to create a heap associated source file
that imports all the binding you want in your library. The heap source file
must be excluded from the source files that will be used to build
the host library.
Suppose we have a unique source file for the bformat
library. The module
clause of this source file is:
(module __bformat
(export (format . args)
format:version
...
(eval (export format)
(export format:version)
...
|
Prior to compiling the library, we have to create the heap associated file
(let's name it make-lib.scm
). This file could be:
(module __make-lib
(import (__bformat "bformat.scm")))
|
Building it is simple:
bigloo -unsafe -q -mkaddheap -mkaddlib -heap-library bformat \
make-lib.scm -addheap bformat.heap
|
The options -mkaddheap
and -mkaddlib
tell Bigloo that it
is compiling an heap associated file. The option -addheap
tells
Bigloo the name of the heap file to be produced. The option
-heap-library
instructs the compiler for the library name to be
included inside the heap file. This name is used for checking versions
at run-time.
- The second step is to compile all the library source file. These
compilation must be done using the
-mkaddlib
compilation mode.
For example:
bigloo -O3 -unsafe -mkaddlib \
-cc gcc -fsharing -q -rm \
-unsafev bformat.scm -o bformat_u.o -c
bigloo -O3 -mkaddlib -g -cg -cc gcc \
-fsharing -q -rm \
-unsafev bformat.scm -o bformat.o -c
|
The first compilation produces the unsafe version the second the
produced the debugging version.
- The third step is to build the host operating system libraries. There
is no portable way to do this. This operation may looks like:
ar qcv libbiglooformat_s.a bformat.o
ranlib libbiglooformat_s.a
ld -G -o libbiglooformat_s.so bformat.o -lm -lc
ar qcv libbiglooformat_u.a bformat_u.o
ranlib libbiglooformat_u.a
ld -G -o libbiglooformat_u.so bformat_u.o -lm -lc
|
The last step is to create an initialization file
bformat.init
:
(declare-library! 'bformat :basename "bigloobformat" :version #f)
|
At this time, you are ready to use your library. The Bigloo distribution
contains library exemplar. Considering these examples as a departure point
for new libraries is probably a good idea.
24.6 A complete library example
|
For the means of an example let's suppose we want to design a Bigloo
library for 2d points. That library is made of three implementation
files: two C files,
point.h
and
point.c
and one Scheme
file
scm-point.scm
. Here are defined the three files:
point.h
:
struct point_2d {
double x, y;
};
|
point.c
:
#include <stdio.h>
#include "point.h"
int print_point_2d( struct point_2d *pt ) {
printf( "<point-2d: %g, %g>", pt->x, pt->y );
}
|
scm-point.scm
:
(module point
(include "point.sch")
(extern (include "point.h"))
(export (make-point::s-point_2d* ::double ::double)
(print-point ::s-point_2d*)
(point? ::obj))
(eval (export-all)))
(define (make-point::s-point_2d* x::double y::double)
(s-point_2d* x y))
(define (print-point p::s-point_2d*)
(print_point_2d p))
(define (point? obj::obj)
(s-point_2d*? obj)
obj)
|
We want our library to be composed of the whole exported Scheme
functions. Thus the file to build the heap library could look like:
make-lib.scm
:
(module __make-point-lib
(import (point "scm-point.scm"))
(eval (export-all)))
|
Let's suppose that the
point
library requires the
libposix
library. This means that any file linked with the
point
library
needs to be also linked with the
posix
library. Furthermore,
programs making use of the
point
library needs to include the
point.sch
file. That Scheme file needs in turn the C file
point.h
otherwise the produced C files won't compile. The need
for the
libposix
library and for the
point.h
file may be
specified inside the
point.init
file. For our current library,
the
point.init
file could look like:
(declare-library! 'point
:basename "point"
:srfi '(point)
:eval-init '__make-point-lib)
(set! *ld-options*
(string-append "-L/usr/lib " *ld-options*))
(set! *bigloo-user-lib*
(cons "-lposix" *bigloo-user-lib*))
(set! *additional-include-foreign*
(cons "point.h" *additional-include-foreign*))
(define-macro (point x y)
`(make-point ,x ,y))
|
This file updates some compilation variables (
*ld-options*
,
*bigloo-user-lib*
,
*additional-include-foreign*
) and
defines a macro:
point
. Because the
point.init
file will
be loaded each time a compilation require the
point
library is
spawned, user code are allowed to use the
point
macro. Here is an
example file making use of the
point
library:
(module example)
(let ((p (point 2.9 3.5)))
(print "point?: " (point? p))
(print "point?: " (point? 4))
(print-point p)
(print (eval `(point? ,p)))
(eval `(print-point ,p))
(print "done..."))
|
To conclude that example here is the
Makefile
used to compile
the
point
library, heap file and one example.
# bigloo flags
BIGLOO = bigloo
RELEASE = `$(BIGLOO) -eval '(begin (print *bigloo-version*) (exit 0))'`
BHEAPFLAGS = -unsafe -q -mkaddheap -mkaddlib -v2 -heap-library point
BCOMMONFLAGGS = -mkaddlib -fsharing -q $(VERBOSE) \
-copt '$(CCOMMONFLAGS)' -cc $(CC)
BSAFEFLAGS = $(BCOMMONFLAGGS) -cg -O3 -g -cg -unsafev \
-eval '(set! *indent* 4)' -rm
BUNSAFEFLAGS = $(BCOMMONFLAGS) -O4 -unsafe
# cigloo flags
CIGLOO = cigloo
# cflags
CC = gcc
CCOMMONFLAGS = -I.
CSAFEFLAGS = $(CCOMMONFLAGS)
CUNSAFEFLAGS = $(CCOMMONFLAGS) -O2
# library objects
SAFE_OBJECT = olib/scm-point.o olib/point.o
UNSAFE_OBJECT = olib_u/scm-point.o olib_u/point.o
all: heap lib example
heap: point.heap
point.heap: point.sch scm-point.scm
$(BIGLOO) $(BHEAPFLAGS) make-lib.scm -addheap point.heap
lib: lib_u lib.a
lib.a: olib $(SAFE_OBJECT)
ar qcv libpoint_s-$(RELEASE).a $(SAFE_OBJECT)
lib_u: olib_u $(UNSAFE_OBJECT)
ar qcv libpoint_u-$(RELEASE).a $(UNSAFE_OBJECT)
olib:
mkdir olib
olib_u:
mkdir olib_u
olib_u/scm-point.o olib/scm-point.o: scm-point.scm
$(BIGLOO) $(BSAFEFLAGS) $(<F) -o $*.o -c
olib_u/point.o olib/point.o: point.c
$(CC) $(CSAFEFLAGS) $(<F) -o $*.o -c
point.sch: point.h point.c
cigloo $^ > point.sch
example: heap lib
$(BIGLOO) -v2 -L . -library point \
-static-bigloo example.scm -o example
clean:
-/bin/rm -f point.heap
-/bin/rm -f point.sch scm-point.c
-/bin/rm -fr olib olib_u
-/bin/rm -f example example.c example.o
-/bin/rm -f libpoint_s-$(RELEASE).a libpoint_u-$(RELEASE).a
|
A Bigloo library may be composed of several Bigloo modules (even if in
our example only one module was used). The modules composing the library
are free to import each other. Nevertheless, someone designing a Bigloo
library should be aware that Bigloo importation creates dependences
between modules. A module
mod1
that imports a module
mod2
depends on
mod2
because
mod1
requires
mod2
to be
initialized (i.e.
mod1
calls to the initialization function of
mod2
). The result is that using
import
clauses inside
modules composing a library may create a lot of dependencies between the
object files that are used to build the associated Unix
library. Dependencies should be avoided because they make the Unix
linkers unable to produce small stand-alone programs. Instead of
import
clauses,
use
clauses should be
preferred.
Use
clauses do not create dependencies because a
module
mod1
that
use
s a second module
mod2
does not
require
mod2
to be initialized. Of course, it may happen
situations where the initialization is mandatory and thus, the
import
must not be replaced with a
use
clause. The source
code of the Bigloo library makes use of
import
and
use
clauses. The Bigloo standard library should be studied as an example.