This article describes a standard method to create a Scilab toolbox. The objective is to help the contributors to build a toolbox easily, but also that the users are able to install it just by executing a main builder and a main loader script. We show in first time how to structure your toolbox (sub-directories contents, files, ...), in second time how to customize the templates to create the builder(s), the loader(s), and the Scilab help files. To finish we explain how to upload your contribution on the Scilab website. For this article the reference toolbox name is toolbox_skeleton.
You can find a template of toolbox in SCI/contrib/toolbox_skeleton/. To get this example on Windows, you must install "A toolbox skeleton (to extend Scilab)" component when you install Scilab.
Definitions
Script
Scilab scripts are text files with the extension .sce. They are used to store Scilab code which can be executed using the exec function.
Macros
Scilab macros are Scilab functions written in Scilab code and stored in a file with the extension .sci.
Primitive
Scilab primitives are Scilab functions which call a function written in C, C++ or fortran code, using an interface (gateway) program. So for each Scilab primitive we must create the corresponding interface program.
Builder
A builder file is a Scilab script used to build the toolbox i.e. to create a binary version which Scilab can load from a source version. This file is called builder*.sce (Examples: builder.sce, buildhelp.sce, ...).
Loader
A loader file is a Scilab script used to load the toolbox in Scilab. This file is called loader*.sce (Examples: loader.sce, loadhelp.sce, ...).
Toolbox structure
The root directory has the generic name of the toolbox (here toolbox_skeleton), it contains 8 sub-directories:
macros: toolbox macros, buildmacros.sce and loadmacros.sce scripts
src: source code (c, cpp, fortran sub-directories)
sci_gateway: gateway files (c, cpp, ... sub-directories)
help: english (respectively french, ...) help sub-directory named en_US (respectively fr_FR, ...) which contains all the .xml help files, buildhelp.sce script
etc: files used to initialize the toolbox when it is loaded (toolbox_skeleton.start) and when you exit Scilab (toolbox_skeleton.quit)
tests: .tst files (scripts to test your toolbox)
demos: some examples to illustrate your toolbox
includes: .h files that you want to publish with your toolbox
and 4 files:
readme.txt: toolbox description and installation guide
builder.sce: the main builder file
loader.sce: the main loader file (generated by builder.sce)
license.txt
To build this toolbox, you have to run this command in Scilab:
exec builder.sce
And then run the loader to load/launch the toolbox:
exec loader.sce
Then you can launch Scilab help browser to see the help pages about toolbox_skeleton:
help
Sub-builders and sub-loaders
The main builder and main loader scripts launch respectively the sub-builders and sub-loaders included in the sub-directories (macros, src, help, ...), to generate and load needed libraries and Scilab help files.
The macros directory
By convention the builder included in the macros directory is named buildmacros.sce.
In this example, we consider that the macros directory of the toolbox_skeleton toolbox contains just only one macro, the function scilab_sum (see above script). This function returns s the sum of A + B (A and B are the inputs of the function).
Example of Scilab macro: scilab_sum.sci
function s = scilab_sum(A, B) s = A + B; endfunction
The macros builder
The builder (see below script) creates a library variable from macros stored in the directory macros, and saves it in the file calledlib. The builder file contents is generic, it is executed in two steps:
first step locates buildmacros.sce script (see get_absolute_file_path function help page)
second step builds macros (see tbx_build_macros function help page).
The library generated is called: toolbox_skeletonlib i.e. yourtoolboxnamelib (yourtoolboxname must be 21-characters long max).
Contents of buildmacros.sce file:
tbx_build_macros(TOOLBOX_NAME, get_absolute_file_path('buildmacros.sce'));
clear tbx_build_macros;
The macros loader
The loader loads the library generated by the builder in the directory macros.
src and sci_gateway directories
As said before, a Scilab primitive is a Scilab function which calls a function written in C/C++ or fortran code through an interface. So for each Scilab primitive we must create the corresponding interface program stored in the sci_gateway directory. The source code of the function is stored in the src directory and its sub-directories (c, cpp, fortran, ...).
C/Scilab Interface
When a Scilab primitive is called, the interface program checks that the number, the type and the size of inputs/outputs arguments is correct (using functions such as CheckRhs and CheckLhs), and gets the input argument addresses which are in Scilab internal stack to give this information to the interfaced function.
We won't describe here all the possibilities of interface programs, for more explanations see the directory SCI/dynamic_link/examples.
Example of interface program
These examples (written in C code) are described step by step. They explain how to write interfaces, so it's important to understand them to know how to customize them for your toolboxes.
First example: C function
We consider a C function csum which returns the sum of two scalars. We suppose that the name of the corresponding primitive in Scilab is c_sum and the associated interface program name is sci_csum. By convention all interface program names begin with "sci_". The both following scripts represent the C code of csum and sci_csum.
The primitive c_sum can be called in Scilab as follows:
--> Y = c_sum(A,B)
Source code of csum.c:
int csum(double *a, double *b, double *c)
{
*c = *a + *b;
return 0;
}
Source code of sci_csum.c:
#include "stack-c.h"
/* ==================================================================== */
extern int csum(double *a,double *b,double *c);
/* ==================================================================== */
int sci_csum(char *fname)
{
int l1, m1, n1, l2, m2, n2, m3, n3,l3;
double a,b,c;
a = 0;
b = 0;
c = 0;
/* --> result = csum(3,8)
/* check that we have only 2 parameters input */
/* check that we have only 1 parameters output */
CheckRhs(2,2) ;
CheckLhs(1,1) ;
/* get first parameter and put in 'a' */
GetRhsVar(1, MATRIX_OF_DOUBLE_DATATYPE, &m1, &n1, &l1);
a = *stk(l1);
/* get second parameter and put in 'a' */
GetRhsVar(2, MATRIX_OF_DOUBLE_DATATYPE, &m2, &n2, &l2);
b= *stk(l2) ;
/* call csum subroutine */
csum(&a,&b,&c);
/* create a variable on scilab's stack */
m3=1;
n3=1;
CreateVar(Rhs+1,MATRIX_OF_DOUBLE_DATATYPE,&m3,&n3,&l3);
*stk(l3) = c;
LhsVar(1) = Rhs+1;
return 0;
}
Step 1: CheckRhs(minrhs,maxrhs) and CheckLhs(minlhs,maxlhs) instructions
CheckRhs function uses the arguments minrhs and maxrhs to check that:
minrhs <= number of input arguments <= maxrhs
The number of inputs and outputs arguments (respectively 2 and 1) of csum are constant, so minrhs=maxrhs=2 and minlhs=maxlhs=1, but for other functions they can be variable, in this case the variables minrhs/minlhs and maxrhs/maxlhs are different.
We can use directly the defined variables Rhs (number of inputs) and Lhs (number of outputs) instead of the functions CheckRhs and CheckLhs to check the number of inputs/outputs.
Step 2: GetRhsVar(1,MATRIX_OF_DOUBLE_DATATYPE,&m1,&n1,&l1) instruction
GetRhsVar function checks that the type of inputs arguments of c_sum are correct, and gets their size and address in Scilab stack. We describe below all arguments of GetRhsVar function:
1 : is the position on Scilab stack of the first input argument of csum, i.e A, (2 for B, ...)
MATRIX_OF_DOUBLE_DATATYPE: is the type of the variable read, here double. See Scilab Internal Datatypes
m1: gets the number of rows of A (m2 for B)
n1: gets the number of columns of A (n2 for B )
l1: gets the address of A in Scilab stack (l2 for B )
Step 3: csum(&a,&b,&c) instruction
Call to the C function csum which returns in c the sum of a and b.
Step 4: CreateVar(Rhs+1,MATRIX_OF_DOUBLE_DATATYPE,&m3,&n3,&l3) instruction
CreateVar function creates in the stack at the Rhs+1 th position a variable which corresponds to the output argument of csum. Here is a description of CreateVar function arguments:
Rhs+1: position of the created variable in the stack. This position (here 3) must follows the position of the last input argument (here 2) of c_sum
MATRIX_OF_DOUBLE_DATATYPE: sets the type of the created variable, here double. See Scilab Internal Datatypes
m3: sets the number of rows of the created variable here 1 (size of a scalar)
n3: sets the number of columns of the created variable here 1 (size of a scalar)
l3: gets the address of the created variable in the Scilab stack
Step 5: LhsVar(1) = Rhs+1 instruction
The first output argument (here Y) of c_sum takes the value of the variable placed in the Rhs+1 th position on the stack (i.e stk(l3)).
Second example: fortran function
We consider an fortran subroutine fsum which returns the sum of two scalars. We suppose that the name of the corresponding primitive in Scilab is fortran_sum and the associated interface program name is sci_fsum. By convention all interface program names begin by "sci_". The both following scripts represent the fortran code of fsum and sci_fsum.
The primitive fortran_sum in can be called in Scilab as follows:
--> Y = fortran_sum(A,B)
Source code of fsum.f:
c =================================
subroutine fsum(a,b,c)
c =================================
double precision a,b,c
c = a + b
end
c =================================
Source code of sci_fsum.c (we call a fortran subroutine from a C gateway):
#include "stack-c.h"
/* ==================================================================== */
extern int F2C(fsum)(double *a,double *b,double *c);
/* ==================================================================== */
int sci_fsum(char *fname)
{
int l1, m1, n1, l2, m2, n2, m3, n3,l3;
double a,b,c;
a = 0;
b = 0;
c = 0;
/* --> result = fortran_sum(3,8)
/* check that we have only 2 parameters input */
/* check that we have only 1 parameters output */
CheckRhs(2,2) ;
CheckLhs(1,1) ;
/* get first parameter and put in 'a' */
GetRhsVar(1, MATRIX_OF_DOUBLE_DATATYPE, &m1, &n1, &l1);
a = *stk(l1);
/* get second parameter and put in 'a' */
GetRhsVar(2, MATRIX_OF_DOUBLE_DATATYPE, &m2, &n2, &l2);
b= *stk(l2) ;
/* call fortran fsum subroutine */
F2C(fsum)(&a,&b,&c);
/* create a variable on scilab's stack */
m3=1;
n3=1;
CreateVar(Rhs+1,MATRIX_OF_DOUBLE_DATATYPE,&m3,&n3,&l3);
*stk(l3) = c;
LhsVar(1) = Rhs+1;
return 0;
}
This function is very similar to sci_csum.c
Primitives builder
Now the src and the sci_gateway directories contain all necessary files to create the builder (see template below) for the primitive c_sum.
We need to write two builders:
On one hand, in the src/c/ directory, this builder (named builder_c.sce) creates a shared library (see ilib_for_link function help page) corresponding to the C function.
And the other hand, in the sci_gateway/c/ directory, this builder (named builder_gateway_c.sce) creates the new shared libraries to link the compiled C or Fortran new Scilab interface routines and generates a loader (see ilib_build function help page). This loader file calls the addinter function to load dynamically the shared library (See addinter function help page).
Contents of builder_c.sce file:
tbx_build_src(['csum','csub'], ['csum.o','csub.o'], 'c', ..
get_absolute_file_path('builder_c.sce'));
clear tbx_build_src;
Contents of buildsci_gateway.sce file:
tbx_build_gateway('skeleton_c', ['c_sum','sci_csum';'c_sub','sci_csub'], ['sci_csum.o','sci_csub.o'], ..
get_absolute_file_path('builder_gateway_c.sce'), ..
['../../src/c/libcsum']);
clear tbx_build_gateway;
The help directory
This directory contains .xml files and a builder_help.sce script.
Creation of .xml files
Here is a template which shows you how to write the .xml help files. You should just fill the different items for your functions and put them in the help directory.
See http://wiki.scilab.org/howto/scilab_documentation_kit for more information about documentation.
Example of Scilab help file, c_sum.xml:
<?xml version="1.0" encoding="UTF-8"?>
<refentry version="5.0-subset Scilab" xml:id="c_sum" xml:lang="en"
xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns3="http://www.w3.org/1999/xhtml"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:db="http://docbook.org/ns/docbook">
<info>
<pubdate>$LastChangedDate: 2008-03-26 09:50:39 +0100 (mer., 26 mars 2008)$</pubdate>
</info>
<refnamediv>
<refname>c_sum</refname>
<refpurpose>sum from C</refpurpose>
</refnamediv>
<refsynopsisdiv>
<title>Calling Sequence</title>
<synopsis>a = c_sum(b,c)</synopsis>
</refsynopsisdiv>
<refsection>
<title>Description</title>
<para>Do a sum.</para>
<para>Add here a paragraph of the function description </para>
</refsection>
<refsection>
<title>Examples</title>
<programlisting role="example">c_sum(3,4)</programlisting>
</refsection>
<refsection>
<title>Authors</title>
<simplelist type="vert">
<member>YOUR NAME</member>
</simplelist>
</refsection>
</refentry>
The help builder
This file creates a file containing all your toolbox documentation so that it can be loaded in Scilab help browser.
Contents of build_help.sce:
help_lang_dir = get_absolute_file_path('build_help.sce');
tbx_build_help(TOOLBOX_TITLE, help_lang_dir);
clear help_lang_dir;
The main builder
This builder is generic, it executes all sub-builder(s), here is its contents:
mode(-1);
lines(0);
try
getversion('scilab');
catch
error(gettext('Scilab 5.0 or more is required.'));
end;
// ====================================================================
if ~with_module('development_tools') then
error(msprintf(gettext('%s module not installed.'),'development_tools'));
end
// ====================================================================
TOOLBOX_NAME = 'toolbox_skeleton';
TOOLBOX_TITLE = 'Toolbox Skeleton';
// ====================================================================
toolbox_dir = get_absolute_file_path('builder.sce');
tbx_builder_macros(toolbox_dir);
tbx_builder_src(toolbox_dir);
tbx_builder_gateway(toolbox_dir);
tbx_builder_help(toolbox_dir);
tbx_build_loader(TOOLBOX_NAME, toolbox_dir);
clear toolbox_dir TOOLBOX_NAME TOOLBOX_TITLE;
// ====================================================================
Upload your toolbox
Now your toolbox can be compiled and loaded, you can:
Read the instructions about how to contribute to Scilab with your work, see http://www.scilab.org/contrib/index_contrib.php?page=howto.html
- Archive and Compress your toolbox
Complete the submission form document and upload your package (here toolbox_skeleton.tar.gz and toolbox_skeleton.zip), see http://www.scilab.fr/contrib/index_contrib.php?page=upload.html
