// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // plugin.go package expr import ( "fmt" "io" "os" "plugin" "strings" "git.portale-stac.it/go-pkg/expr/kern" ) var pluginRegister map[string]*plugin.Plugin func registerPlugin(name string, p *plugin.Plugin) (err error) { if pluginExists(name) { err = fmt.Errorf("plugin %q already loaded", name) } else { pluginRegister[name] = p } return } func pluginExists(name string) (exists bool) { _, exists = pluginRegister[name] return } func makePluginName(name string) (decorated string) { var template string if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") { template = "expr-%s-plugin%s" } else { template = "expr-%s-plugin%s.debug" } decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION) return } func importPlugin(ctx kern.ExprContext, dirList []string, name string) (err error) { var filePath string var p *plugin.Plugin var sym plugin.Symbol var moduleName string var importFunc func(kern.ExprContext) var ok bool decoratedName := makePluginName(name) if filePath, err = makeFilepath(decoratedName, dirList); err != nil { return } if p, err = plugin.Open(filePath); err != nil { return } if sym, err = p.Lookup("MODULE_NAME"); err != nil { return } if moduleName = *sym.(*string); moduleName == "" { err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName) return } if sym, err = p.Lookup("DEPENDENCIES"); err != nil { return } if deps := *sym.(*[]string); len(deps) > 0 { // var count int if err = loadModules(ctx, dirList, deps); err != nil { return } } if sym, err = p.Lookup("ImportFuncs"); err != nil { return } if importFunc, ok = sym.(func(kern.ExprContext)); !ok { err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName) return } registerPlugin(moduleName, p) if globalCtx := ctx.GetGlobal(); globalCtx != nil { importFunc(globalCtx) } return } func importPluginFromSearchPath(ctx kern.ExprContext, name any) (count int, err error) { var moduleSpec any var it kern.Iterator dirList := buildSearchDirList(ctx, "plugin", ENV_EXPR_PLUGIN_PATH) count = 0 if it, err = NewIterator(name); err != nil { return } for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { if module, ok := moduleSpec.(string); ok { if err = importPlugin(ctx, dirList, module); err != nil { break } count++ } else { err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, kern.TypeName(moduleSpec)) break } } if err == io.EOF { err = nil } return } func loadModules(ctx kern.ExprContext, dirList []string, moduleNames []string) (err error) { for _, name := range moduleNames { if err1 := importPlugin(ctx, dirList, name); err1 != nil { if !ImportInContext(ctx, name) { err = err1 break } } } return } func init() { pluginRegister = make(map[string]*plugin.Plugin) }