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 with just the execution of 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)

Table of content

1. Toolbox composition

The root directory has the generic name of the toolbox (here mytoolbox), it contains 8 sub-directories:

and 4 files:

2. 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 to load the necessary libraries and the Scilab help files.

2.1 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

2.1.1 The macros builder

The builder (see below script) creates a library variable, (named here: mytoolboxlib= toolbox name +'lib') from functions .sci include in the directory macros, and saves it in the file lib. The builder code is generic, it's done in 2 steps, the first to locate buildmacros.sce script (see help get_absolute_file_path function), and the second to generate 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.

2.1.2 The macros loader

The loader (see below script) loads the library lib included in the directory macros. Like the builder, the code is generic, the first step to locate loadmacros.sce script, and the second to load 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

2.2 scr 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 to create the corresponding interface program included in the sci_gateway directory.

2.2.1 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.

2.2.2 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.

Example a:
We consider an C routine vectsum which returns the sum of two vectors. I suppose that the name of the corresponding primitive is sumab and the associated interface program name is sci_sumab. By convention all the interfaces programs 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);
		
/* 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)    
	if(n1!=1 && m1!=1)   
	{
		cerro("inputs arguments must be vectors");    
		return(0); 
	}
		 
		
/* 4 - Create a new variable corresponding to the output argument */ 
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 certains functions (see example2) 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.

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:


Step 4: 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)

Step 5: 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 6: 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))


Example b:
In the second example we describe the interface program named sci_fun of the Scilab primitive named fun.
This function call the C routines fun1 and fun2 and has 2 syntaxes which are:

First syntax:
--> [X, Y ]=fun(A);
Given a vector A, this function returns the positives components of A in a vector X and the sum of its positives components in a scalar Y.

Second syntax:
--> [X ]=fun(A);
Given a vector A, this function returns the positives components of A in a vector X.

The number of outputs arguments (i.e Lhs value) is variable: for the first syntax Lhs=1, for the second syntax Lhs=2. The number of intputs arguments (i.e Rhs value) is constant: Rhs=1 (first and second syntax).
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); 

	/* 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);

	/* 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);
		*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;
}

2.2.3 Primitives builder

Now the src and the sci_gateway directories contain all the necessary files (fun1.c, fun2.c, sci_fun.c, vectsum.c, sci_sumab.c) to create the builder (see below template) 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 interfaces programs names (second column)

2.3 The help directory

This directory included .xml files, a buildhelp and a loadhelp scripts.
On Unix/Linux systems: 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

2.3.1 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_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>

2.3.2 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 

2.3.3 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_

3. 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

4. Upload your toolbox