Guide for the toolboxes contributions (general application)
This article describes a standart 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 mytoolbox. (The standarisation below is not mandatory)
Contents
Toolbox composition
The root directory has the generic name of the toolbox (here mytoolbox), it contains 8 sub-directories:
macros: Scilab macros (i.e the functions written in Scilab code with the extension .sci), buildmacros and loadmacros scripts
src: source code (all .c and .f files), a buildsrc script
sci_gateway: interfaces programs, a buildsci_gateway
help: english and french help sub-directories named respectively en_US and fr_FR which contains all the .xml help files, buildhelp and loadhelp scripts
etc: files used to initialize the toolboxe when it is loaded
unit tests: .tst files (scripts to test your toolbox)
demos: different examples to illustrate your toolbox
includes: .h files
and 4 files:
readme.txt: toolbox description and installation
builder.sce: the main builder
loader.sce: the main loader
license.txt
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 and the loader included in the macros directory are named respectively buildmacros.sce and loadmacros.sce
A macros is a function written in Scilab code (included in .sci file).
We consider that the macros directory of the toolbox mytoolbox contains just one .sci file, the function foo1 (see above script): Given a matrix A, this function returns the vector X the positives components of the A diagonal. foo1.sci
function [X]=foo1(A)
// This function returns the positive components of the A diagonal // Check the type and the size of A
if type(A)<>1 then
error("type of input argument must be a double");
end
if size(A,1)<>size(A,2) then
error("input argument must be a square matrix");
end //Extraction of the positive components X=[]; for i=1:size(A,1)
if A(i,i)>0 then
X($+1)=A(i,i);
end
end
endfunction
The macros builder
The builder (see below script) creates a library variable, (named here: mytoolboxlib= toolbox name +'lib') from functions .sci included in the directory macros, and saves it in the file lib. The builder code is generic, it is executed in 2 steps, first step is used to locate buildmacros.sce script (see help get_absolute_file_path function), and second step generates the library (see help genlib function).
buildmacros.sce
mode(-1)
toolboxname='mytoolbox'
pathB=get_absolute_file_path('buildmacros.sce')
disp('Building macros in ' +pathB)
genlib(toolboxname+'lib',pathB,%t)
clear pathB genlib toolboxname
To customize this template, replace the characters string 'mytoolbox' by the name of your toolbox.
The macros loader
The loader (see below script) loads the library lib included in the directory macros. Like the builder, the code is generic, in the first step it locates loadmacros.sce script, and in the second step it loads the library (see help load function)
loadmacros.sce
mode(-1)
pathL=get_absolute_file_path('loadmacros.sce')
disp('Loading macros in ' +pathL)
load(pathL+'/lib')
clear pathL
src and sci_gateway directories
A primitive is a Scilab function which calls a function written in C or fortran code, using an interface program. So for each Scilab primitive we must create the corresponding interface program included in the sci_gateway directory.
Interface C/Scilab
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 CheckRhs and CheckLhs functions), and gets the rhs arguments address which are in Scilab internal stack to give this information at the interfaced function.
We don't describe all the possibilities of the interface programs, for more explanations see the directory SCI/examples.
Examples of interfaces programs
These examples (written in C code) are described step by step, they enable to write many interfaces, so it's important to understand them, and to know how to customize them for your toolboxes.
First example
We consider an C routine vectsum which returns the sum of two vectors. We suppose that the name of the corresponding primitive is sumab and the associated interface program name is sci_sumab. By convention all interface program names begin by the "sci_" character strings. The both following scripts represent the C code of vectsum and sci_sumab when we call the primitive sumab in a Scilab window as follows:
--> Y=sumab(A,B )
vectsum.c
void vectsum(int n, double * a, double * b, double * y)
{
int k;
for (k = 0; k < n; ++k) y[k] = a[k] + b[k];
}
sci_sumab.c
#include "stack-c.h"
extern int vectsum(int n, double * a, double * b, double * y);
void sci_sumab(char *fname)
{
int l1, m1, n1, l2, m2, n2, l3, n;
/* 1 - Check the number of inputs/outputs arguments */
int minlhs=1, maxlhs=1, minrhs=2, maxrhs=2;
CheckRhs(minrhs,maxrhs) ;
CheckLhs(minlhs,maxlhs) ;
/* 2 - Check inputs arguments type, and get the size and the address in the Scilab stack of the inputs arguments */
GetRhsVar(1, "d", &m1, &n1, &l1);
GetRhsVar(2, "d", &m2, &n2, &l2);
// GetRhsVar(1, MATRIX_OF_DOUBLE_DATATYPE, &m1, &n1, &l1); // In Scilab 5
// GetRhsVar(2, MATRIX_OF_DOUBLE_DATATYPE, &m2, &n2, &l2); // In Scilab 5
/* 3 - Check that the inputs arguments have the same size */
/* it's possible to use the chekdims and getscalar functions to make these checks*/
n=m2*n2;
if( n1!=n2 || m1!=m2)
{
cerro("inputs arguments must have the same size");
return 0;
}
if ( (n1!=0 && m1!=0) && (n1!=1 && m1!=1) )
{
cerro("inputs arguments must be vectors"); return(0);
}
/* 4 - Create a new variable corresponding to the output argument */
CreateVar(3,MATRIX_OF_DOUBLE_DATATYPE,&m2,&n2,&l3);
// CreateVar(3,"d",&m2,&n2,&l3);
/* 5 -call vectsum routine: returns in stk(l3) the sum of a and b*/
vectsum(n,stk(l1),stk(l2),stk(l3));
/* 6 - Specif ouput argument */
LhsVar(1) = 3;
return 0;
}
Step 1: call CheckRhsVar(minrhs,maxrhs) and CheckLhsVar(minlhs,maxlhs) instructions
CheckRhsVar 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 vectsum are constant, so minrhs=maxrhs=2 and minlhs=maxlhs=1, but for other functions (see second example) 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 CheckRhsVar and CheckLhsVar to check the number of inputs/outputs.
Step 2: call GetRhsVar(1,"d",&m1,&n1,&l1) instruction
GetRhsVar function checks that the type of inputs arguments of sumab are correct, and gets their size and their address in the Scilab stack. We describe below all arguments of GetRhsVar function:
1 : corresponds to the position on the stack of the first input argument of sumab, i.e A, (2 corresponds to B,...)
"d": sets the type of the created variable, here double. In Scilab 5, this has been changed. It would be MATRIX_OF_DOUBLE_DATATYPE. See Scilab Internal Datatypes
m1 : gets the row number of A (m2 for B)
n1 : gets the column number of A (n2 for B )
l1 : gets the address of A in Scilab stack (l2 for B )
Step 3: call CreateVar(3,"d",&m2,&n2,&l3) instruction
CreateVar function creates in the Stack at the 3th position a variable which corresponds to the output argument of vectsum (here Y)
3 : position of the created variable in the stack. This position (here 3) must follows the position of the last input argument (here two) of sumab
"d": sets the type of the created variable, here double. In Scilab 5, this has been changed. It would be MATRIX_OF_DOUBLE_DATATYPE. See Scilab Internal Datatypes
m2: sets the row number of the created variable (here equals to the row number of the second input argument B: m2)
n2: sets the column number of the first created variable (here equal to the column number of the second input argument B: n2)
l3: gets the address of the created variable in the Scilab stack
Step 4: call vectsum(n,stk(l1),stk(l2),stk(l3)) instruction
The C function vectsum returns in stk(l3) the sum of stk(l1) and stk(l2) (i.e a and b)
Step 5: call LhsVar(1) = 3 instruction
The first output argument (here Y) of sumab takes the value of the variable placed in the 3th position on the stack (i.e stk(l3))
Second example
In this example we describe the interface program named sci_fun of the Scilab primitive named fun.
This function calls the C routines fun1 and fun2 and has two different calling sequences which are:
First calling sequence: --> [X, Y ]=fun(A);
Given a vector A, this function returns the positive components of A in a vector X and the sum of its positive components in a scalar Y.
Second calling sequence: --> [X ]=fun(A);
Given a vector A, this function returns the positive components of A in a vector X.
The number of output arguments (i.e Lhs value) is variable: for the first case Lhs=1, for the second case Lhs=2. The number of input arguments (i.e Rhs value) is constant: Rhs=1 (first and second case).
So the interface program must check that: 1<=Lhs<=2 (set minlhs=1, maxlhs=2) and Rhs=1 (set minrhs=maxrhs=1)
fun1.c (the C function fun1 creates the vector X and the scalar Y. It calls the C function fun2 to get the needed size of X in order to allocate the corresponding memory place)
extern void fun2(double *, int, int *);
void fun1(double * a, int na, int * nx, double ** x , double * y)
{
int i, k1=0;
y=0; fun2(a, na, nx);
x=(double *)malloc((*nx)*sizeof(double));
y=0;
for(i=0;i<na;i++) if(a[i]>0)
{
(*x+k1)=a[i];
y += a[i]; k1++;
};
}
fun2.c
void fun2(double * a, int na, int * nx)
{
int i;
nx=0;
for(i=0;i<na;i++) if (a[i]>0) (*nx)++;
}
sci_fun.c
#include "stack-c.h"
extern void fun1(double * , int, int *, double **, double *);
int sci_fun(char *fname)
{
int la, ma, na, m=1, nx, i, lx, ls; double * x, s;
/* 1 - Check the number of inputs and outputs arguments */
/* You can use the variables: Lhs and Rhs */
int minlhs=1, maxlhs=2, minrhs=1, maxrhs=1;
CheckRhs(minrhs,maxrhs) ;
CheckLhs(minlhs,maxlhs) ;
/* 2 - Check the rhs type, get the rows number (ma) and the columns number (na) of rhs, and its address (la) in the Scilab stack (first position) */
GetRhsVar(1, "d", &ma, &na, &la);
// GetRhsVar(1, MATRIX_OF_DOUBLE_DATATYPE, &ma, &na, &la); // in Scilab 5
/* 3 - Check rhs is a vector */
if(ma!=0 && na!=0 )
{
if(ma!=1 && na!=1)
{
cerro("input argument must be a vector"); return(0);
}
}
fun1(stk(la), na*ma, &nx, &x, &s);
/* 4 - Create the place for the first output argument x ( a vector of doubles, size: 1*nx ) to the address lx in the Scilab stack (second position) */
CreateVar(2, "d", &m, &nx, &lx);
// CreateVar(2, MATRIX_OF_DOUBLE_DATATYPE, &m, &nx, &lx); // in Scilab 5
/* if there are two outputs variables then: Create the place for the second output s ( a double, size 1*1) to the address ls in the Scilab stack (third position) */
/* get the value of s, and put it in the Scilab stack */
if(Lhs==2)
{
CreateVar(3, "d", &m, &m, &ls);
// CreateVar(3, MATRIX_OF_DOUBLE_DATATYPE, &m, &m, &ls); // in Scilab 5
stk(ls)=s;
}
/* get the components of x, and put them in the Scilab stack */
for(i=0;i<nx;i++) stk(lx)[i]=x[i];
/* free memory */
free(x);
/* 5 - Specification of outputs variables */
LhsVar(1) = 2;
if(Lhs==2) LhsVar(2) = 3;
return 0;
}
Primitives builder
Now the src and the sci_gateway directories contain all necessary files (fun1.c, fun2.c, sci_fun.c, vectsum.c, sci_sumab.c) to create the builder (see template below) for the primitives fun and sumab.
We need to write two builders:
One the hand, in the src directory, this builder (named buildersrc) creates a shared libraries (see help ilib_for_link function) corresponding to the C functions.
And the other hand, in the interface directory, this builder (named buildsci_gateway) creates the new shared libraries to link the compiled C or Fortran new Scilab interface routines (thanks to src libraries), and generates a loader (see help ilib_build function). This loader file calls the addinter function to load dynamically the shared library (see help addinter function)
buildsrc.sce
ilib_for_link('mytoolboxsrc',['fun1.o' 'fun2.o','vectsum.o'],[],"c")
buildsci_gateway.sce
// must be run from this directory ilib_name = 'libmytoolbox' // interface library name files = ['sci_fun.o', 'sci_sumab.o']; // objects files libs = ["../src/libmytoolboxsrc"] // other libs needed for linking table = [ 'fun', 'sci_fun';'sumab','sci_sumab']; // table of (scilab_name,interface-name) // do not modify below ilib_build(ilib_name,table,files,libs)
The ilib_name value is the interface library name, the vector files contains all the object interface files, the vector libs contains the libraries needed for linking (here the library included in the src directory), the variable table contains the primitives names (first column) and the corresponding interface program names (second column)
The help directory
This directory included .xml files, a buildhelp and a loadhelp scripts. To create the manual pages you need 'sabcmd', an XML parser which is part of the Sablotron package. here the link to download it: http://www.scilab.org/download/index_download.php?page=related_tool.html
Creation of .xml files
Here a template which shows you how to write the .xml help files. You should just fill the different items (Langage, title, type, date, short description, ...) for the .xml files of your functions (here foo1.xml, fun.xml, sumab.xml) and put them in the help directory.
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <!DOCTYPE MAN SYSTEM "/home/scilab/scilab-3.0/man/manrev.dtd"> <MAN> <LANGUAGE>eng</LANGUAGE> <TITLE>sumab</TITLE> <TYPE>Scilab Function </TYPE> <DATE>20-Mar-2006</DATE> <SHORT_DESCRIPTION name="add function name"> add short decription here</SHORT_DESCRIPTION> <CALLING_SEQUENCE> <CALLING_SEQUENCE_ITEM>add function syntax</CALLING_SEQUENCE_ITEM> </CALLING_SEQUENCE> <PARAM> <PARAM_INDENT> <PARAM_ITEM> <PARAM_NAME>add param name</PARAM_NAME> <PARAM_DESCRIPTION> <SP> : add here the parameter description </SP> </PARAM_DESCRIPTION> </PARAM_ITEM> <PARAM_ITEM> <PARAM_NAME>add param name</PARAM_NAME> <PARAM_DESCRIPTION> <SP> : add here the parameter description </SP> </PARAM_DESCRIPTION> </PARAM_ITEM> </PARAM_INDENT> </PARAM> <DESCRIPTION> <DESCRIPTION_INDENT> <DESCRIPTION_ITEM> <P> Add here a paragraph of the function description. Other paragraph can be added </P> </DESCRIPTION_ITEM> <DESCRIPTION_ITEM> <P> Add here a paragraph of the function description </P> </DESCRIPTION_ITEM> </DESCRIPTION_INDENT> </DESCRIPTION> <EXAMPLE><![CDATA[ Add here scilab instructions and comments ]]></EXAMPLE> <SEE_ALSO> <SEE_ALSO_ITEM> <LINK> add a key here</LINK> </SEE_ALSO_ITEM> <SEE_ALSO_ITEM> <LINK> add a key here</LINK> </SEE_ALSO_ITEM> </SEE_ALSO> <BIBLIO> Add here the function bibliography if any </BIBLIO> <AUTHORS> <AUTHORS_ITEM label='enter here the author name'> Add here the author references </AUTHORS_ITEM> </AUTHORS> <USED_FUNCTIONS> Add here the used function name and references </USED_FUNCTIONS> </MAN>
The help builder
The builder (named buildhelp) creates a whatis.htm file which is a short description of the functions, and translates the xml files to html (see help xmltohtml function)
buildhelp.sce
mode(-1) //force silent execution
path=get_absolute_file_path('builhelp.sce');//get the absolute path of this file
add_help_chapter("Title1",path);//add help chapter
xmltohtml(path,"Title1")
//clear the variable stack
clear path add_help_chapter get_absolute_file_path
The help loader
The loader (named loadhelp) adds your help functions files in the help Scilab browser
loadhelp.sce
mode(-1) //force silent execution
path=get_absolute_file_path('loadhelp.sce');//get the absolute path of this file
add_help_chapter("Title1",path);//add help chapter
clear path add_help_chapter get_absolute_file_path
The main builder and loader
The builder and loader are generic, they execute all sub-builder(s) and sub-loader(s), here the both scripts:
builder.sce
mode(-1);
mainpathB=get_absolute_file_path('builder.sce');
chdir(mainpathB);
if isdir('src') then
chdir('src');
exec('buildsrc.sce');
chdir('..');
end
if isdir('sci_gateway') then
chdir('sci_gateway');
exec('buildsci_gateway.sce');
chdir('..');
end
if isdir('macros') then
chdir('macros');
exec('buildmacros.sce');
chdir('..');
end
if isdir('help') then
chdir('help');
exec('buildhelp.sce');
chdir('..');
end
clear mainpathB
loader.sce
mode(-1);
mainpathL=get_absolute_file_path('loader.sce');
chdir(mainpathL);
if isdir('sci_gateway') then
chdir('sci_gateway');
exec('loader.sce');
chdir('..');
end
if isdir('macros') then
chdir('macros');
exec('loadmacros.sce');
chdir('..');
end
if isdir('help') then
chdir('help');
exec('loadhelp.sce');
chdir('..');
end
clear mainpathL
Upload your toolbox
Read the instructions about how to contribute , see the link: http://www.scilab.org/contrib/index_contrib.php?page=howto.html
- Archive and Compress your toolbox
Complete the submission form document and add your package (here mytoolbox.tar.gz and mytoolbox.zip), see the link: http://www.scilab.fr/contrib/index_contrib.php?page=upload.html
