Ce guide détaille l'intégration dynamique de modules compilés en Go (format .so).
Prérequis et considérations importantes :
- La version du compilateur Go doit être identique pour le programme hôte et le module chargé.
- Les versions des dépendanecs communes (bibliothèques tierces) doivent également correspondre.
- L'uniformité de l'environnement
GOPATHest cruciale ; l'optiontrimpathlors de la compilation peut aider à résoudre d'éventuels conflits. - Une fois chargé, un module ne peut pas être déchargé.
- Les fichiers
.sogénérés peuvent avoir une taille conséquente.
Structure du projet :
goso/
├── modules/
│ └── hello/
│ └── hello.go
├── main.go
└── build.sh
1. Création du code du module :
Pour ajouter une nouvelle fonction exportable :
- Ajoutez le commentaire
//export NomFonctionavant la déclaration de la fonction. - Assurez-vous que la fonction est exportable (son nom commence par une majuscule).
// hello.go
package main
import "C"
import "fmt"
//export SayHello
func SayHello(name string) string {
return fmt.Sprintf("Bonjour, %s!", name)
}
//export Add
func Add(valeur1, valeur2 int) int {
return valeur1 + valeur2
}
// La fonction main est requise mais ne sera pas exécutée lors du chargement en tant que plugin.
func main() {}
2. Développement du programme hôte :
// main.go
package main
import (
"fmt"
"plugin"
"reflect"
)
// inspectModule charge et examine un fichier .so.
func inspectModule(chemin string) {
// Ouverture du fichier .so
plug, err := plugin.Open(chemin)
if err != nil {
fmt.Printf("Erreur lors du chargement du module '%s': %v\n", chemin, err)
return
}
fmt.Printf("\nAnalyse du module : %s\n", chemin)
fmt.Println("Symboles exportés :")
// Recherche et récupération des fonctions exportées
symBonjour, err := plug.Lookup("SayHello")
if err == nil {
// Utilisation de la réflexion pour obtenir des informations sur la fonction
typeFunc := reflect.TypeOf(symBonjour)
fmt.Printf("Fonction : SayHello\n")
fmt.Printf(" Signature : %v\n", typeFunc.String())
}
symAddition, err := plug.Lookup("Add")
if err == nil {
typeFunc := reflect.TypeOf(symAddition)
fmt.Printf("Fonction : Add\n")
fmt.Printf(" Signature : %v\n", typeFunc.String())
}
// Test d'appel des fonctions
if symBonjour != nil {
// Vérification du type de la fonction et appel
if fn, ok := symBonjour.(func(string) string); ok {
resultat := fn("Alice")
fmt.Printf("\nTest d'appel SayHello : %s\n", resultat)
}
}
if symAddition != nil {
// Vérification du type de la fonction et appel
if fn, ok := symAddition.(func(int, int) int); ok {
resultat := fn(10, 7)
fmt.Printf("Test d'appel Add : 10 + 7 = %d\n", resultat)
}
}
}
func main() {
// Appel de la fonction pour inspecter le module .so
inspectModule("modules/hello.so")
}
3. Script de compilation :
#!/bin/bash
# Création du répertoire de sortie pour les modules
mkdir -p modules
# Compilation du module hello
echo "Compilation du module hello..."
cd modules/hello
go build -buildmode=plugin -o ../hello.so
cd ../..
echo "Compilation terminée."
Interopérabilité Go / Python via .so :
Les fichiers .so compilés par Go et Python ne sont pas directement interopérables. Une approche consiste à passer par une couche C. Par exemple, vous pouvez compiler le code Go en tant que bibliothèque C partagée (c-shared) pour permettre l'appel depuis Python via ctypes.
Alternatives pour l'interopérabilité Go/Python :
- CGO : Compiler le code Go avec
go build -buildmode=c-sharedpour générer une bibliothèque C. - Communication Inter-Processus (IPC) : Utiliser des mécanismes comme gRPC.
ctypesPython : Utiliser le modulectypesde Python pour appeler des fonctions dans des bibliothèques C standard.
Options pour le système de plugins Go :
- Mode
pluginde Go : Continuer à utiliser le modepluginnatif de Go, en acceptant la taille des fichiers résultants.
Options pour une taille de fichier minimale :
- Bibliothèques C/C++ : Envisager de développer les bibliothèques dynamiques en C ou C++.
- Autres solutions légères : Explorer d'autres langages ou aproches conçus pour des déploiements légers.