Intégration dynamique de bibliothèques Go (.so)

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 GOPATH est cruciale ; l'option trimpath lors de la compilation peut aider à résoudre d'éventuels conflits.
  • Une fois chargé, un module ne peut pas être déchargé.
  • Les fichiers .so gé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 NomFonction avant 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-shared pour générer une bibliothèque C.
  • Communication Inter-Processus (IPC) : Utiliser des mécanismes comme gRPC.
  • ctypes Python : Utiliser le module ctypes de Python pour appeler des fonctions dans des bibliothèques C standard.

Options pour le système de plugins Go :

  • Mode plugin de Go : Continuer à utiliser le mode plugin natif 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.

Étiquettes: Go plugin dynamic loading shared library golang

Publié le 6 juin à 19h07