mercredi 3 avril 2013

Writing portable makefiles

Edit 2016-10-21: I notice this post comes on first page of Google "portable makefile" request, so I thought I'd might add some context. This post was written when I was struggling with this kind of stuff, and should be taken as a "proof of concept" post. For me, this is definitely over, as I (almost) completely quit using Windows, being for several years now a happy GNU/Linux user. Readers must be aware that although some tricks are given here, it is certainly not the best approach for setting up a portable build system. If you are in that situation, the best way to go is probably CMake, as it is today the de facto standard tool.

This note is about GNU Make makefiles syntax, and how to write them to keep them OS-independent as much as possible.

1- Introduction: computers and file systems

When it comes to computers and their associated filesystem, there are two worlds on earth.
One that considers that a path to a file spells this way:
path/to/the/file
and the other world that considers that the correct syntax is:
path\to\the\file

This may sound silly (and it is) but it can lead to some complications. Not only because these two worlds use a different symbol, but mostly because they both have a special meaning for the other symbol.

To put it clearly, on a Linux machine (that uses the '/' path separator), the backslash has a special meaning in some situations (shell scripts, makefiles, ...) meaning "I have no more room on this line, lets keep on and continue the current command on next line" (and that trick is very valuable for readability). And it follows what is a convention in C and C++ source files.
 
In the other world (MS Windows), the default shell (cmd.exe) interprets the slash character as the option separator. For example: del /F path\to\file.txt

And, no, at least in XP, the Windows shell DOES NOT accept both path/to/file and path\to\file, as it is frequently said in many places. Try to do something like del path/to/file to check. Maybe this has changed with Windows 7, 8, 11 or 42, I'm not really interested, but with Windows XP's shell, it does-not-work.The cause of that misunderstanding is probably that system calls (that is, the functions you call from inside a program), DO accept forward slashes or backslashes in paths).

Anyway, the two shells (Linux/bash and Windows/cmd.exe) are sooo different, only insane people would consider trying to write a "compatible" script, running equally on both systems (1).

However, there is one situation where a same command semantic must be executed equally in those two different environments: makefiles

Basically, a makefile is a set of commands that are executed by the shell.
Say for one target, we want to erase some file, even "read-only" ones. On one environment, this must be done with the following command:
rm -f path/to/file
while on the other, it will be:
del /F path\to\file

The question is: how can a write that command in my makefile so that it expands in these two different syntaxes at runtime? And more generally, how do I write portable makefiles ?

2 - Handling command names

First, lets manage the different command names (and their options). That's the easiest. Just define a variable holding the name of the command, that will hold different values depending on platform. The easiest way to detect the platform is to check for the existence of a Windows-only environment variable, say ComSpec (but some sources relie on SystemRoot that can be used too).

ifdef ComSpec
    RM=del /F /Q
else
    RM=rm -f
endif


This will be in the upper part of the makefile, before any recipes. Then, in the recipes, just use $(RM) in place of the command.

3 - Handling paths

Secondly, you need to handle the path separator. Two situations need to be handled:
- paths to explicit files (the example above),
- automatic paths, build using make's wildcards and substitution functions.

Remember that you need to care for this only for system calls. Whatever the platform, GNU Make, gcc or other "regular" development tools handle very well paths with forward slashes, whatever the platform. To make it clear, say we have these lines that follow the classical target-prerequisite-command scheme:
mytarget: path/to/file
   $(SOMECOMMAND) path/to/file


The first line will do fine, but the second line will generate an error on Windows if SOMECOMMAND expands to a built-in shell command.

3.1 - Processing explicit paths

First, for the explicit paths, we can proceed with the same trick: define a variable holding the required separator ('\' or '/'), then use this variable in the commands.

ifdef ComSpec
    PATHSEP2=\

else
    PATHSEP2=/
endif

Ha. Unfortunatly, this does not work, because the backslash is interpreted by make as the "keep on same line!" request, and not as a character. Ok, so we need to escape that backslash, in order to fool make:

ifdef ComSpec
    PATHSEP2=\\
else
    PATHSEP2=/
endif

Funilly, this works half ways: the definition is accepted, but the variable holds the two backslashes! Fortunatly, the Windows shell accepts paths that looks like path\\to\\file (don't ask me why...)

Almost done. This still does not work: the above definition adds a trailing space at the end of the variable, i.e. its usage in:
path$(PATHSEP2)file
will expand into:
path/ file  (or  path\ file  on Windows)
and that will not be ok, for sure!

So finally, we need to add the following definition and function call, that removes that ugly trailing space:
PATHSEP=$(strip $(PATHSEP2))

That way, an explicit erasing command in a makefile (for example) can be portably written as:
$(RM) path$(PATHSEP)to$(PATHSEP)file

Ok, now how about paths that are automatically build.

3.2 - Processing generated paths

For example, you usually define a variable holding all the object files, that is build from all the source files. If these are in a folder named src, and the object files are in a folder named obj, then you can define the list of the source files with:
SRC_FILES=$(wildcard src/*.cpp)

and the list of corresponding object files with (2):
OBJ_FILES=$(patsubst src/%.cpp,obj/%.o,$(SRC_FILES))

But heres comes the problem, trying to erase all the object files with:
$(RM) $(OBJ_FILES)
will expand on Windows as something like:
del /F obj/file1.o obj/file2.o obj/file3.o
and that will throw an error, because the shell will consider that what is behind the slash as some option.

Two solutions can be used:
  • either use PATHSEP in the "patsubst" function call above:
OBJ_FILES=$(patsubst src/%.cpp,obj$(PATHSEP)%.o,$(SRC_FILES))
  • either use the "subst function", that replaces some pattern in a string with another:
OBJ_FILES_CORRECT=$(subst \,/,$(OBJ_FILES))

But this latter solution implies the creation of another variable, which can be error-prone in dense makefiles.

4 - Command separator

Another problem that needs to be handled is the command separator. In many make tutorials, you see commands written this way:
cd MyFolder; SomeCommand Some Arguments
which means: "get down into folder MyFolder, and execute SomeCommand with Some Arguments"
This is an invalid syntax on Windows where the command separator is &.
So, again, the variable trick:

ifndef ComSpec
    CMDSEP=;
else
    CMDSEP=&
endif


and the above command will be written:
cd MyFolder $(CMDSEP) SomeCommand Some Arguments

5 - Debugging makefiles

Portable makefiles are also tougher to debug that ordinary makefile. You will run into countless issues, and each one might require some special treatment.

As you may know, you can prefix each make command with the special symbol '@', that will suppress the default echo to terminal. This is useful for the makefile user, that doesn't want to see all the steps of the build process: he just wants to get the job done, as quickly as possible.
But while writing the makefile (an debugging it), you will need all this information, so you don't use that prefix, of course...
   ... until you're done ! Then you want to deliver a nice experience to the makefile user, and you carefully edit your code to prefix each command with '@'
And then... Ah. Something got broken in the makefile when this new build step was added, but where exactly ? So you start again removing all these stupid '@' characters that you spend so much time to add!

Don't. Instead use again the good'ol variable trick. Instead of using the special character '@', prefix right away all of your commands with $(L). For example:
obj/%.o : src/%.cpp
    $(L)$(CXX) -o $@ -c $< $(CFLAGS)


And add the following lines in the first part of the makefile:
ifeq "$(LOG)" ""
    LOG=no
endif

ifeq "$(LOG)" "no"
    L=@
endif



This way, launching make with no special option will run silently, and in case of trouble, just tell your mate to run:
make <target-name> LOG=yes
and all the commands that are launched will (magically) appear on screen (3).

6 - Conclusion

These are some hints that can help you design more portable makefiles. I'll finish with one remark. For "big" projects, maybe you should rely on "makefile generators", that is, programs that do all these low-level tasks (and much more). The most known are CMake and the GNU set of tools, but others can be used.

Finally, one quote from "Managing projects with GNU Make": "... there is no such thing as perfect portability, so it is our job to balance effort versus portability."


(1) Unless you use a non-native modern script langage such as Python of course.
(2) In real life, the folders name would also be stored in variables, i.e. :
OBJ_FILES=$(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
(3) Or in a text file if you redirect it, and that is usually a good idea when output starts to get large.

samedi 30 mars 2013

Octave/Ubuntu: problems installing additional packages

This post started out as a question on SO, but as I finally found the answer, I though it might interest other people.

Consider the following situation: you need to do some function data-fitting, you don't {wan't to use / have access to} Matlab, and think that Octave might be an alternative.

First problem: your version of Octave on you current Ubuntu 12.04 is slightly outdated, and sudo apt-get install doesn't seem to have a more recent version.

You can upgrade using an unofficial ppa, as described on this page.


Then, Octave actually doesn't have data-fitting material. It is provided as additional packages (see here). And according to this page, it is the optim package that you need.

Once downloaded, running the following line in the Octave shell
pkg install optim-1.2.2.tar.gz
tells you that there are additional packages required (`miscellaneous`, `struct` and `general`). And at one point you might hit the following error (or something near), complaining about something called mkoctfile:

make: /usr/bin/mkoctfile: Command not found
    make: *** [__exit__.oct] Error 127
    'make' returned the following error: make: Entering directory `/tmp/oct-P11IKL/general/src'
    /usr/bin/mkoctfile __exit__.cc
    make: Leaving directory `/tmp/oct-P11IKL/general/src'
    error: called from `pkg>configure_make' in file /usr/share/octave/3.6.2/m/pkg/pkg.m         near line 1391, column 9
    error: called from:
    error:   /usr/share/octave/3.6.2/m/pkg/pkg.m at line 834, column 5
    error:   /usr/share/octave/3.6.2/m/pkg/pkg.m at line 383, column 9


If you search about this, you might find this question, where the answer (unaccepted) says that you should sudo apt-get install octave-signal

Don't ! Depending on your ppa settings, this might revert your Octave installation to 3.2, which is not desirable.

The solution requires to install the Octave development packages with :
sudo apt-get install octave-pkg-dev

Finally, It seems that installation of some (?) packages writes stuff in /usr/share/octave/, which can't be done by user (and 'sudo' can't be run from Octave 's shell).
So the easiest it to switch as root before starting Octave (with su), then install the packages, then quit Octave.



samedi 29 décembre 2012

Loterie 4-1-9

A quelques heures de 2013, je ne résiste pas au plaisir de partager ce morceau d'anthologie que je viens de recevoir à l'instant.Malgré les différents filtres, on en reçoit tous un peu, de qualité plus ou moins variable. Encore il y a quelques années, je m'amusait à les collectionner dans une boite dédiée de mon client courrier, avec l'objectif initial de constituer un corpus pour un travail futur. Devant le peu d'originalité déployée (et aussi la quantité reçue), ça.m'a passé, et en général, rien qu'au titre, j'ai déjà pressé la touche "Suppr"...

Mais pour celui-ci, ça vaut le coup d'oeil. Le texte associé est plutôt banal (je ne vous le recopie pas, vous l'avez tous déjà lu ailleurs), mais l'image envoyée montre que le gars y a mis tout son coeur! Texte en anglais, photo de Bill Gates, "joli" dégradé de couleur sur le titre, et puis surtout, presque aucune faute d'orthographe, je n'en ai vu qu'une (je vous laisse la chercher...)


Ca serait presque drôle, on peut se dire qu'un gamin (nigérian ?) a pu acquérir quelques compétences en infographie grâce à ça (bon, en même temps, il n'y en pas pour des heures...), mais en même temps, ça laisse rêveur. Y a-t-il réellement, fin 2012, des gens qui peuvent gober un truc pareil ? Si oui, ça témoigne d'un sacré problème. Vaste sujet...

lundi 23 avril 2012

Impression de planches d'étiquette avec Latex (publipostage)

Je m'occupe d'une association avec laquelle nous faisons de temps en temps du mailing "classique" (par la poste!) en générant des planches d'étiquettes autocollantes à partir d'un fichier de contacts maintenu sous la forme d'un fichier "tableur".
Pendant longtemps, je me suis acharné, (voire épuisé!) avec les "fonctionnalités" de publipostage des suites bureautiques classiques.
Cette fonctionalité, pourtant à priori basique, est en effet difficile à manier, du moins sur les versions que j'ai pu essayer (Office 2003 et OOo 3.3). On passe des heures à créer un modèle, à cliquer, à en enregistrer les versions successives, à identifier le format parmi les formats prédéfinis, à cliquer, à ouvrir la base de données (qu'il faut aller rechercher dans l'arborescence à chaque fois...), à fusionner, à recommencer parce qu'on a un message disant "impossible de trouver la source de données" ou quelque chose du genre, à recliquer, à re-naviguer jusqu'au dossier, à cliquer encore, à re-générer, à essayer autre chose, à re-naviquer, à re, à re, etc.
L'enfer! Pour quelque chose qui au final, peut fonctionner, mais surtout a un comportement erratique: un coup, c'est bon, un coup, c'est pas bon....

Bref. Insupportable.

A coté de ça, on trouve aussi des logiciels, libres ou propriétaires, qui vous proposent de faire peu ou prou la même chose. Mais bon, encore un bidule à installer, qui ne va pas forcémment être exactement adapté à ce que vous voulez...

Je refléchissais depuis quelque temps sur la possibilité d'une solution automatisée 'en 1 clic', utilisant des solutions éprouvées, libres et multiplateforme, mais faute de temps, j'avais laissé ça un peu de coté. Etant utilisateur quotididien de Latex, j'ai presque par hasard recherché s'il n'y avait pas un package qui m'aiderait dans cette tâche. Et là, Bingo!
Premier hit: le package "labels", qui fait... tout!
Pour la faire courte, et pour les familiers de Latex, il suffit du petit source suivant. Pour les autres, c'est peut-être une excellente occasion de se mettre à Latex ?

\documentclass{article}
\usepackage{labels}

\begin{document}
\begin{labels}
\input names.dat
\end{labels}
\end{document}

Et le tour est joué: après compilation, vous disposez d'un pdf avec les étiquettes, à condition que vous fournissiez dans le dossier courant le fichier names.dat contenant les noms et adresses au bon format (voir ci-dessous). Evidemment, en général, on devra personnaliser un peu, ajouter babel pour l'alphabet occidental, le nombre de lignes et de colonnes, etc. On peut évidemment régler les marges et autres espacements avec les commandes adéquates, la doc est très complète là dessus, et l'archive de téléchargement contient en bonus une multitude d'exemples.

# my_labels.tex

\documentclass[a4paper,12pt]{article}
\usepackage[french]{babel}
\usepackage[utf8]{inputenc}% ou iso8859-15 en général sur windows
\usepackage{labels}

\LabelCols=2
\LabelRows=7

\begin{document}
\bfseries
\begin{labels}
\input names.dat
\end{labels}
\end{document}

Pour le format des données, ce package n'attend pas de format particulier: il se contente d'imprimer sur les étiquettes le texte fourni, ligne par ligne, avec comme séparateur d'étiquette un simple saut de ligne. Comme par exemple (tiré de la doc):

Professor R. Bercov, Chair
Department of Mathematics
University of Alberta
Edmonton, Alberta
Canada T6G 2G1

Chair of the Search Committee
Department of Mathematics
and Statistics
University of Regina
Regina, Saskatchewan
Canada S4S 0A2

...

Il faudra donc commencer par exporter les données au format csv, puis procéder à un petit traitement pour générer ce fichier, et ensuite lancer Latex dessus. Sur plateforme Linux standard l'ensemble des outils nécessaires est normalement disponible, pour Windows, c'est faisable, mais un peu plus délicat.

D'abord, il faut expurger préalablement le fichier .csv des guillemets qui peuvent avoir été générés par le tableur (Il semble qu'OOo génère systématiquement ces guillemets autour des cellulles contenant du texte). On peut faire ça à la main via un "rechercher/remplacer" dans son éditeur favori, mais c'est plus élégant et surtout plus fiable de faire ça avec un outil dédié: sed

Depuis le shell, la commande
sed s/XXXX/YYYY/ input_file > output_file
va remplacer dans le fichier d'entrée input_file la première occurence de 'XXXX' par 'YYYY'. La sortie de programme se fait par défaut sur la "sortie standard" (la console), et il faut donc la rediriger vers le fichier souhaité (output_file) via le caractère de redirection '>'.

Pour remplacer toutes les occurences, on ajoute un 'g':
sed s/XXXX/YYYY/g input_file > output_file
Pour éliminer les occurences d'une chaine, il suffit de laisser YYYY vide.

En général, la première ligne du fichier contient le nom des colonnes, et il faut donc la supprimer. sed permet ceci avec la syntaxe:
sed 1d input_file > output_file
Pour regrouper les 2 commandes en une ligne, il faut ajouter l'option -e indiquant que la chaine qui suit est une commande:
sed -e 1d -e s/XXXX/YYYY/g input_file > output_file

Au final la commande est donc:
sed -e 1d -e s/\"//g ooo_export.csv > adresses.csv
(il faut "backslasher" le guillemet)

Un exemple de script bash est donné ci-dessous, qui enchaîne les 3 passes:
- preprocessing du fichier csv,
- génération du fichier d'adresses,
- appel de pdflatex pour la génération du pdf final.
Il faudra aussi adapter l'ordre des champs à ce que vous avez dans votre fichier d'origine.

#! /bin/bash

outfile=names.dat
sed -e 1d -e s/\"//g ooo_export.csv > adresses.csv

echo "" > $outfile

# var. spéciale: séparateur de champ
IFS=';'

while read LINE; do
 echo "$LINE";
 a=( $LINE )
 LNAME=${a[0]}
 FNAME=${a[1]}
 ADR=${a[2]}
 CODE=${a[3]}
 CITY=${a[4]}

 echo "$FNAME $LNAME"  >> $outfile
 echo "$ADR"  >> $outfile
 echo "$CODE $CITY"  >> $outfile
 echo "" >> $outfile

done < adresses.csv

pdflatex -interaction=batchmode my_labels.tex

Attention:
On a parfois dans les fichiers d'adresses des champs du style "Gérard & Jacqueline". Latex interprétant le '&' comme un caractère spécial, mieux vaut le supprimer de votre fichier source et remplacer par "Gérard et Jacqueline". Autre solution : "Gérard \& Jacqueline".

La suite ? Réaliser un export en ligne de commande du fichier d'adresses originel... Mais OOo ne semble pas disposer de cette fonctionnalité là, il faudrait passer par d'autres outils tiers.

Edit 10/2012: en fait, l'export en .ods  (Open Document Spreadsheet) depuis le fichier Excel de départ permet de disposer d'un fichier xml du contenu, dont on peut extraire le contenu. OpenDocument Fellowship propose des outils pour manipuler les fichiers Open Document, mais qui semblent à ce jour ne concerner que les fichiers odt (de type "texte", donc, et pas les feuilles de calcul ods).

L'autre approche consisterait à écrire un parser adéquat, qui récupère  les champs et les copie dans le fichier décrit ci-dessus.  pyxml semble l'outil le plus adapté, une piste à suivre...

Edit 02/2015: LibreOffice 4.2 (et OpenOffice ?) propose désormais un outil en ligne de commande qui permet d'exporter un fichier .ods en .csv, avec la syntaxe suivante:
libreoffice --headless --convert-to csv *.ods

lundi 2 avril 2012

GNU Make and the foreach() function

Make is a nice tool for building stuff. Software, of course, but not only, I use it also for building pdf files from Latex sources. However, its usage is not obvious at all, and many pitfalls lie in front of the newcomer. And the manual is not a great help when it comes to learning new features. Joyfully, there are bunches of tutorials out there.

However, some advanced tricks are rarely covered. Here is one trick that can be useful in some situations. Beware, it assumes you already know the basics about Make (GNU flavor), in case not, please go read some tutorial and come back in a while...

Lets say you have a folder containing some source files of type ".in", say foo.in and boo.in. These files need to be processed with some tool (call it "some_tool" for now) to build the output files. This (awesome) thing has a rather classical syntax:
some_tool in_file out_file [option]

Say you need to build these with two different configurations to produce two output files for each input files, say one of type "X" and one of type "Y". Here, from the two input files, you need to build:
foo_X.out
foo_Y.out
boo_X.out
boo_Y.out

How can Make help you so you only build the ones that are needed ? For the example, say that each build process takes several hours, so you really don't want to build them if it's not needed.

First create a variable holding all the input files:
infiles := $(wildcard *.in) 

Then, add a generic rule telling how to build a ".out" file:
%.out: %.in 
    @some_tool $< $@ 
Yes, remember these special variables: $< is the first dependency, and $@ is the target (%.out here), so that will generate the correct command-line. Oh, and for the different output flavors (say, by adding some option), just dupe the generic rule:
%_X.out: %.in
 @some_tool $< $@ X
%_Y.out: %.in
 @some_tool $< $@ Y
Finally, you will try to build the main (all) rule. Its dependencies are all the input files (thats easy, its $(infiles) ) and the output files, so that each of them gets produced only in case it is not present. Ah. Here comes some trouble. How do you create a variable holding all the expected targets ? Well, first, lets get the files names without extension. Easy:
filenames :=$(basename $(infiles))

Okay, and now, how do I automatically generate a string holding all the output files names ? This is a job for the foreach() function. Its description in the manual is (IMHO) mostly unreadable, so lets see this through a simpler example:
what = $(foreach a, ham cheese salad, john likes $(a) )
all:
    @echo "what=$(what)"
produces:
what= john likes ham  john likes cheese  john likes salad
Usually, this is used using some variables, and it would be written:
types= ham cheese salad
what = $(foreach a, $(types), john likes $(a) )

Mmmh, I'm sure you're starting to see the point. The 'types' will be our different output flavors, say, something like:
types= X Y
outfile := $(foreach a, $(types), boo_$(a).out )
     @echo "outfile=$(outfile)"
that will produce:
outfile= boo_X.out boo_Y.out
But this has to be done for each input files, and the following fails:
types= X Y
outfiles := $(foreach a, $(types), $(filenames)_$(a).out )
It gets expanded to:
boo foo_X.out boo foo_Y.out

So the solution is to use nested foreach() loops, just as you would do with some regular procedural programming language.
types:=X Y
outfiles := $(foreach a,$(filenames), \
     $(foreach b,$(types), \
         $(a)_$(b).out ) ) 
Then, the main rule can just get written as follows:
all: $(infiles) $(outfiles)
    @echo "That's all, folks !"

For further reading, check this series of articles on advanced topics on Make.

vendredi 17 février 2012

Changing the monitor on which the GNOME panels are placed

In my job, it is quite common these days to have a dual display setup on your laptop. Either your are at the office and have a larger LDC monitor, either your are teaching or doing some presentation and you have a beamer projector connected.

With the GNOME 2 desktop, the UI is based on 2 toolbars (called "panels") on top and bottom of the screen, that are very handy for launching common apps. With two screens, what happens is that these panels are usually placed on one screen, where you would rather have them on the other one.

The desktop provides a graphical way to move them around with the mouse, by holding the "alt" key down, and moving them around. But this is rather difficult in some lighting situations, when the projector image is bad and you hardly see your mouse pointer. Worse, you might end up with a vertical panel, and then, due to the lack of free space in the panel to click on, you're doomed to use either the graphical editor, either the command line tool.

The other solution is to edit this item directly in the gnome registry. Oh, sorry, I know, GNOME has no registry, only Windows has... Although it can look quite similar as viewed from the corresponding graphical editors, the implementation beneath is completely different. While Windows stores all its configuration information in two HUGE files, on Linux the information is spread over many many files, usually under /etc. It's only the way the information is displayed that makes it look like Windows registry (a tree of pairs key/values). And it is called GConf repository instead of registry.

For the desktops panels, the items to consider are stored under the keys /apps/panel/toplevels/ keys, one is called bottom_panel_screen0, the other top_panel_screen0 (at least on my computer, check on yours).

This can be done graphically with the gconf-editor tool, but when you have the audience waiting, it can be complicated finding and clicking on correct items... The best idea is to use the command-line tool provided, and to have somewhere two scripts, one for switching panels to monitor 0, one for switching to monitor 1.

So just copy/paste these lines in two files that you store whereyouwant, and add two symlinks on them on your desktop. Then, once you are in front of the audience and you have trouble with the panels, just reduce all the opened windows and double-click on one of the links. Changes should apply in less than a second.

file switch_panel_0.sh
#! /bin/bash
# switch top/bottom panels to screen 0
gconftool-2 --set "/apps/panel/toplevels/bottom_panel_screen0/monitor" --type int 0
gconftool-2 --set "/apps/panel/toplevels/top_panel_screen0/monitor" --type int 0

file switch_panel_1.sh:
#! /bin/bash
# switch top/bottom panels to screen 1
gconftool-2 --set "/apps/panel/toplevels/bottom_panel_screen0/monitor" --type int 1
gconftool-2 --set "/apps/panel/toplevels/top_panel_screen0/monitor" --type int 1

To create the symlinks (remember to adjust the path to where you put the files), just "cd" to your desktop folder ($HOME/Desktop usually) and type:
>ln -s $HOME/scripts/screen/switch_panel_0.sh panel_0
>ln -s $HOME/scripts/screen/switch_panel_1.sh panel_1

And don't forget to make your files executable! (chmod)

vendredi 2 décembre 2011

SLIFIS: fuzzy logic C++ library first release

I have just uploaded on sourceforge the SLIFIS library, a long-standing project on which I can say I have spend quite some time... It is a C++ library designed to be used inside scientific software when you need some fuzzy logic stuff (membership functions, fuzzy inference system, ...)

It all started in 2008-2009, when I was in that situation, and had trouble finding what would fit my needs. Since then, the software was more or less in a standby situation, but I fixed some stuff recently, and decided to make a public release. A lot of things still need to be worked on, I hope to clean up things a bit and will keep an eye on this.

Checkout the (reduced) doc here, and the download project page is here.

At present, Windows build is not up to date, as Linux is now my main dev platform, but it shouldn't be too hard to make it work.

As usual with open source, absolutely no warranty is given..., blah blah blah.

Feedback is welcome.