Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez apprendre à ajouter des fichiers et des modifications aux fichiers générés pour une extension.

Le temps de lecture de ce didacticiel est estimé à 25 minutes.

Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier fichiers.svm_plugin en utilisant ce code :

PLUGIN files

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	std::string r("<");
	r += RAW_STRING(s) + ">";
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Fichiers

Dans des extensions contenant beaucoup de code, il peut être intéressant d'organiser le code en dehors du fichier source produit par le générateur d'extension.

Ajout de fichiers

Modifiez le code de l'extension :

PLUGIN files

includes:
%{
#include <src/business.h>
%}

file: "src/business.h"
%{
#pragma once

#include <string>

struct Business
{
	static std::string compute(const std::string& s)
	{
		return std::string("<")+s+">";
	}
};

%}

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	auto r = Business::compute(RAW_STRING(s));
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Générez l'extension. Vous pouvez la compiler, mais cela n'est pas nécessaire.

Au lieu de la compilation, entrez dans le répertoire des fichiers de l'extension : un fichier src/business.h a été créé et il contient le code donné dans le bloc de la directive file.

Plusieurs directives file peuvent être ajoutées dans la partie générale de l'extension.

Commentaires

Le fichier précédemment généré a une particularité dans l'extension : il se retrouve copié tel quel, sans adjonction de la licence d'utilisation.

Il est possible d'indiquer que le fichier contient un certain type de contenu dans la directive file, et cela active l'ajout de la licence au début du fichier.

Modifiez le code de l'extension :

PLUGIN files

includes:
%{
#include <src/business.h>
%}

file source: "src/business.h"
%{
#pragma once

#include <string>

struct Business
{
	static std::string compute(const std::string& s)
	{
		return std::string("<")+s+">";
	}
};

%}

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	auto r = Business::compute(RAW_STRING(s));
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Générez l'extension. Vous pouvez la compiler, mais cela n'est toujours pas nécessaire.

Ouvrez le fichier src/business.h : il contient maintenant un commentaire au début contenant la licence d'utilisation.

Ici, le type de contenu est noté comme étant source, ce qui active les commentaires en mode C/C++. En réalité, d'autres types sont disponibles par défaut :

Il est également possible de personnaliser les commentaires produits pour ces types de fichiers, ainsi que de créer des types de contenus personnalisés.

Modifiez le code de l'extension :

PLUGIN files

includes:
%{
#include <src/business.h>
%}

file source: "src/business.h"
%{
#pragma once

#include <string>

struct Business
{
	static std::string compute(const std::string& s)
	{
		return std::string("<")+s+">";
	}
};

%}

comment source: "// ==========" "// " "// =========="

comment xml: "<!--" "  " "-->"

file xml: "example.xml"
%{
<a>
	<b>text</b>
</a>
%}

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	auto r = Business::compute(RAW_STRING(s));
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Générez l'extension. Vous pouvez la compiler, mais cela n'est toujours pas nécessaire.

Ouvrez les fichiers example.xml et src/business.h :

  1. dans le fichier XML, le commentaire respecte la syntaxe du XML,
  2. dans le fichier C++, le commentaire est différent de la valeur par défaut, et se conforme plus aux commentaires unilignes du C++.

Patchs

En plus d'ajouter des fichiers, il peut être également intéressant de modifier les fichiers produits par le générateur d'extension.

Syntaxe

Modifiez le code de l'extension :

PLUGIN files

includes:
%{
#include <src/business.h>
%}

file source: "src/business.h"
%{
#pragma once

#include <string>

struct Business
{
	static std::string compute(const std::string& s);
};

%}

file source: "src/business.cpp"
%{
#include <src/business.h>

std::string Business::compute(const std::string& s)
{
	return std::string("<")+s+">";
}
%}

patch: "Makefile.local"
%{
--- Makefile.local.orig 2023-01-01 00:00:00.000000000 +0200
+++ Makefile.local      2023-01-01 00:00:00.000000000 +0200
@@ -20,7 +20,7 @@

 OPTIONS=

-DEPENDENCIES=
+DEPENDENCIES=src/business.o
 GENERATED=

 all: libsvmfiles.so
%}

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	auto r = Business::compute(RAW_STRING(s));
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Générez l'extension. Vous pouvez la compiler, mais cela n'est toujours pas nécessaire.

La directive patch va automatiquement modifier le fichier Makefile.local après génération pour ajouter la compilation du fichier src/business.o, maintenant devenu nécessaire à la compilation de l'extension.

Vous pouvez noter que la directive demande :

  1. une chaine contenant le chemin complet du fichier à modifier,
  2. un bloc contenant la différence entre le fichier généré et celui désiré, obtenu grâce à la commande diff -u.

Un fichier source d'extension peut contenir jusqu'à un patch à appliquer par fichier généré.

Génération assistée

Maintenir les patchs n'est pas une chose simple, car cela demande certaines précautions, comme conserver le fichier d'origine et réaliser la différence depuis le répertoire généré.

Pour simplifier la rédaction et la maintenance des patchs, il est conseillé :

  1. Pour les nouveaux patchs à créer, de l'ajouter au fichier source de l'extension avec un corps vide %{}.
  2. Invoquer le générateur d'extension en ajoutant l'option -p. Dans ce cas, le générateur va :
    1. conserver les fichiers générés sans les modifications dans un fichier .orig,
    2. modifier les fichiers avec les patchs existants dans le fichier source de l'extension,
    3. ajouter un script update_patches.
  3. Modifier les fichiers pour lesquels un patch doit être créé tels que souhaités dans leur version finale, sans toucher le fichier .orig associé.
  4. Remplacer dans le fichier source de l'extension toutes les directives patch par le résultat du script update_patches. Le script s'assure que la différence est calculée depuis le bon répertoire, en prenant comme référence les fichiers d'origine.

Appliquez cette méthode sur le fichier source de l'extension jusqu'à obtenir (aux dates et heures près) :

PLUGIN files

includes:
%{
#include <src/business.h>
%}

file source: "src/business.h"
%{
#pragma once

#include <string>

struct Business
{
	static std::string compute(const std::string& s);
};

%}

file source: "src/business.cpp"
%{
#include <src/business.h>

std::string Business::compute(const std::string& s)
{
	return std::string("<")+s+">";
}
%}

patch: "Makefile.local"
%{
--- Makefile.local.orig 2023-01-01 00:00:00.000000000 +0200
+++ Makefile.local      2023-01-01 00:00:00.000000000 +0200
@@ -20,7 +20,7 @@

 OPTIONS=

-DEPENDENCIES=
+DEPENDENCIES=src/business.o
 GENERATED=

 all: libsvmfiles.so
%}

patch: "src/Makefile.am"
%{
--- src/Makefile.am.orig	2023-01-01 00:00:00.000000000 +0200
+++ src/Makefile.am     2023-01-01 00:00:00.000000000 +0200
@@ -22,6 +22,6 @@

 noinst_LTLIBRARIES=libplugin.la

-libplugin_la_SOURCES=plugin.cpp plugin.h
+libplugin_la_SOURCES=plugin.cpp plugin.h business.cpp business.h
 libplugin_la_LIBADD=
 libplugin_la_LDFLAGS=-no-undefined
%}

DEFINE

INSTRUCTION files.instruction STR -> STR
%{
	auto s = ARGV_VALUE(0,string);
	auto r = Business::compute(RAW_STRING(s));
	return ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
%}

Vous pouvez alors générer une dernière fois l'extension et la compiler. La compilation fonctionne également en utilisant autotools.

Conclusion

Vous venez de voir comment ajouter des fichiers et modifier des fichiers des extensions.

Ces outils permettent d'écrire des extensions ayant un code fonctionnel beaucoup plus important et déporté du fichier de base qui devient juste l'interface de l'extension. Et cela tout en conservant l'atomicité du fichier de génération des extensions.