diff --git a/Makefile b/Makefile index a7e283c..60337db 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ all: build # plugins build: mkdir -p build - sed 's|%INSTALLPREFIX%|${PREFIX}|g' core/plugins.in > core/plugins.go + sed "s|%INSTALLPREFIX%|${PREFIX}|g" core/plugins.in > core/plugins.go go build -a -o build/${BINARY_NAME} build-plugins: FORCE diff --git a/core/plugins.in b/core/plugins.in index 2adf306..8a778c5 100644 --- a/core/plugins.in +++ b/core/plugins.in @@ -10,6 +10,7 @@ import ( "github.com/vanilla-os/vib/api" ) import ( + "errors" "encoding/base64" "os" "syscall" @@ -34,23 +35,61 @@ func decodeBuildCmds(cmds string) ([]string, error) { func LoadPlugin(name string, plugintype api.PluginType, recipe *api.Recipe) (uintptr, api.PluginInfo, error) { fmt.Println("Loading new plugin") - localPluginPath := fmt.Sprintf("%s/%s.so", recipe.PluginPath, name) + projectPluginPath := fmt.Sprintf("%s/%s.so", recipe.PluginPath, name) - globalPluginPath := fmt.Sprintf("%INSTALLPREFIX%/share/vib/plugins/%s.so", name) + installPrefixPath := fmt.Sprintf("%INSTALLPREFIX%/share/vib/plugins/%s.so", name) + + globalPluginPathsEnv, isGPPEDefined := os.LookupEnv("XDG_DATA_DIRS") + if !isGPPEDefined || globalPluginPathsEnv == "" { + globalPluginPathsEnv = "/usr/local/share:/usr/share" + } + + globalPluginPaths_split := strings.Split(globalPluginPathsEnv, ":") + + for index := range globalPluginPaths_split { + // Resolve each directory to a *possible* plugin file path. + globalPluginPaths_split[index] = fmt.Sprintf("%s/vib/plugins/%s.so", globalPluginPaths_split[index], name) + } + + // Specify all the paths where the plugin file might be stored. + // Give priority to the projects "plugins" directory, then + // follow INSTALLPREFIX and $XDG_DATA_DIRS, respectively. + var allPluginPaths = append([]string{projectPluginPath, installPrefixPath}, globalPluginPaths_split...) + var lastIndex = len(allPluginPaths) - 1 - // Prefer local plugins before global ones var loadedPlugin uintptr - _, err := os.Stat(localPluginPath) - if os.IsNotExist(err) { - loadedPlugin, err = purego.Dlopen(globalPluginPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) + + // LoadPlugin() is run once for every plugin, therefore + // the size of the array is limited to the same number + // of paths to search. + var _errors = make([]error, len(allPluginPaths)) + + for index, path := range allPluginPaths { + _, err := os.Stat(path) if err != nil { - panic(err) // yayyy panics <3 + _errors = append(_errors, err) + if index == lastIndex { + // If the last available path doesn't exist, + // panic with all the error messages. + panic(errors.Join(_errors...)) + } + + continue } - } else { - loadedPlugin, err = purego.Dlopen(localPluginPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) + + loadedPlugin, err = purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL) if err != nil { - panic(err) + _errors = append(_errors, err) + if index == lastIndex { + // If the last available plugin can't be loaded, + // panic with all the error messages. + panic(errors.Join(_errors...)) + } + + continue } + + break } infoLoc, err := purego.Dlsym(loadedPlugin, "PlugInfo") @@ -82,6 +121,7 @@ func LoadPlugin(name string, plugintype api.PluginType, recipe *api.Recipe) (uin return loadedPlugin, *pluginInfo, fmt.Errorf("ERROR: Plugin %s is not of type FinalizePlugin", name) } } + return loadedPlugin, *pluginInfo, nil }