diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e84ed0..f24dcec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,8 +19,12 @@ jobs: with: go-version: 1.21 - - name: Build + - name: Build vib run: | go get ./... - go build -v -trimpath ./... - \ No newline at end of file + make build + + - name: Build plugins + run: | + go get ./... + make build-plugins diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 89c3b25..082e3c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,12 +18,20 @@ jobs: go-version: 1.21 - name: Build + run: | + Go get ./... + make build + + - name: Build plugins run: | go get ./... - go build -o vib -trimpath + make build-plugins + tar cvf plugins.tar.xz build/plugins - name: Upload a Release Asset if: github.repository == 'Vanilla-OS/Vib' uses: softprops/action-gh-release@v2 with: - files: vib + files: | + build/vib + plugins.tar.xz diff --git a/.gitignore b/.gitignore index 91096a9..8129444 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ example/downloads/* example/sources/* test/* -vib +build docs/website/dist/* docs/website/node_modules/* go.work +*~ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ead2b2a --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +.POSIX: + +PREFIX=/usr/ +DESTDIR=/ +BINARY_NAME=vib + +all: build # plugins + +build: + mkdir -p build + sed 's|$$INSTALLPREFIX$$|${PREFIX}|g' core/plugins.in > core/plugins.go + go build -a -o build/${BINARY_NAME} + +plugins: FORCE + mkdir -p build/plugins + $(MAKE) -C plugins/ + +install: + install -Dm755 -t ${DESTDIR}/${PREFIX}/bin/ ./${BINARY_NAME} + +install-plugins: + install -Dm644 -t ${DESTDIR}/${PREFIX}/share/vib/plugins/ ./build/plugins/*.so + +clean: + rm -r build + rm core/plugins.go + +FORCE: diff --git a/core/build.go b/core/build.go index 45b1a79..789b437 100644 --- a/core/build.go +++ b/core/build.go @@ -285,16 +285,8 @@ func BuildModule(recipe *api.Recipe, moduleInterface interface{}) (string, error } moduleBuilders := map[string]func(interface{}, *api.Recipe) (string, error){ - "apt": BuildAptModule, - "dnf": BuildDnfModule, - "cmake": BuildCMakeModule, - "dpkg": BuildDpkgModule, - "dpkg-buildpackage": BuildDpkgBuildPkgModule, - "go": BuildGoModule, - "make": BuildMakeModule, - "meson": BuildMesonModule, "shell": BuildShellModule, - "includes": func(interface{}, *api.Recipe) (string, error) { return "", nil }, + "includes": func(interface{}, *api.Recipe) (string, error) { return "", nil }, } if moduleBuilder, ok := moduleBuilders[module.Type]; ok { diff --git a/core/dnf.go b/core/dnf.go deleted file mode 100644 index b5c57dc..0000000 --- a/core/dnf.go +++ /dev/null @@ -1,71 +0,0 @@ -package core - -import ( - "bufio" - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/mitchellh/mapstructure" - "github.com/vanilla-os/vib/api" -) - -type DnfModule struct { - Name string `json:"name"` - Type string `json:"type"` - Source api.Source `json:"source"` -} - -// BuildDnfModule builds a module that installs packages -// using the dnf package manager -func BuildDnfModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module DnfModule - err := mapstructure.Decode(moduleInterface, &module) - if err != nil { - return "", err - } - if len(module.Source.Packages) > 0 { - packages := "" - for _, pkg := range module.Source.Packages { - packages += pkg + " " - } - - return fmt.Sprintf("dnf install -y %s && dnf clean packages", packages), nil - } - - if len(module.Source.Paths) > 0 { - cmd := "" - - for i, path := range module.Source.Paths { - instPath := filepath.Join(recipe.ParentPath, path+".inst") - pkgs := "" - file, err := os.Open(instPath) - if err != nil { - return "", err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - pkgs += scanner.Text() + " " - } - - if err := scanner.Err(); err != nil { - return "", err - } - - cmd += fmt.Sprintf("dnf install -y %s ", pkgs) - - if i != len(module.Source.Paths)-1 { - cmd += "&& " - } else { - cmd += "&& dnf clean packages" - } - } - - return cmd, nil - } - - return "", errors.New("no packages or paths specified") -} diff --git a/core/dpkg.go b/core/dpkg.go deleted file mode 100644 index 79918a3..0000000 --- a/core/dpkg.go +++ /dev/null @@ -1,38 +0,0 @@ -package core - -import ( - "fmt" - - "github.com/mitchellh/mapstructure" - "github.com/vanilla-os/vib/api" -) - -type DpkgModule struct { - Name string `json:"name"` - Type string `json:"string"` - Source api.Source -} - -// BuildDpkgModule builds a module that installs a .deb package -func BuildDpkgModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module CMakeModule - err := mapstructure.Decode(moduleInterface, &module) - if err != nil { - return "", err - } - err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) - if err != nil { - return "", err - } - err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) - if err != nil { - return "", err - } - cmd := "" - for _, path := range module.Source.Paths { - cmd += fmt.Sprintf(" dpkg -i /sources/%s/%s && apt install -f && ", module.Name, path) - } - - cmd += " && apt clean" - return cmd, nil -} diff --git a/core/make.go b/core/make.go deleted file mode 100644 index 59bca94..0000000 --- a/core/make.go +++ /dev/null @@ -1,31 +0,0 @@ -package core - -import ( - "github.com/mitchellh/mapstructure" - "github.com/vanilla-os/vib/api" -) - -type MakeModule struct { - Name string `json:"name"` - Type string `json:"type"` - Source api.Source -} - -// BuildMakeModule builds a module that builds a Make project -func BuildMakeModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module MakeModule - err := mapstructure.Decode(moduleInterface, &module) - if err != nil { - return "", err - } - err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) - if err != nil { - return "", err - } - err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) - if err != nil { - return "", err - } - - return "cd /sources/" + api.GetSourcePath(module.Source, module.Name) + " && make && make install", nil -} diff --git a/core/plugins.go b/core/plugins.in similarity index 67% rename from core/plugins.go rename to core/plugins.in index fd66d1b..b4338dd 100644 --- a/core/plugins.go +++ b/core/plugins.in @@ -9,6 +9,7 @@ import ( "github.com/ebitengine/purego" "github.com/vanilla-os/vib/api" ) +import "os" var openedPlugins map[string]Plugin @@ -23,10 +24,25 @@ func LoadPlugin(name string, module interface{}, recipe *api.Recipe) (string, er fmt.Println("Loading new plugin") buildModule = Plugin{Name: name} - loadedPlugin, err := purego.Dlopen(fmt.Sprintf("%s/%s.so", recipe.PluginPath, name), purego.RTLD_NOW|purego.RTLD_GLOBAL) - if err != nil { - panic(err) + localPluginPath := fmt.Sprintf("%s/%s.so", recipe.PluginPath, name) + + globalPluginPath := fmt.Sprintf("$INSTALLPREFIX$/share/vib/plugins/%s.so", name) + + // 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) + if err != nil { + panic(err) // yayyy panics <3 + } + } else { + loadedPlugin, err = purego.Dlopen(localPluginPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } } + var buildFunction func(*C.char, *C.char) string purego.RegisterLibFunc(&buildFunction, loadedPlugin, "BuildModule") buildModule.BuildFunc = buildFunction diff --git a/core/shell.go b/core/shell.go index c4f03aa..d8abd0d 100644 --- a/core/shell.go +++ b/core/shell.go @@ -1,8 +1,9 @@ -package core +package main import ( "errors" "strings" + "fmt" "github.com/mitchellh/mapstructure" "github.com/vanilla-os/vib/api" diff --git a/plugins/Makefile b/plugins/Makefile new file mode 100644 index 0000000..7baef56 --- /dev/null +++ b/plugins/Makefile @@ -0,0 +1,10 @@ + +.PHONY: all + +PLUGS := $(wildcard *.go) +OBJS := $(PLUGS:go=so) + +all: $(OBJS) + +$(OBJS): %.so: %.go + go build -buildmode=c-shared -a -o ../build/plugins/$@ $< diff --git a/core/apt.go b/plugins/apt.go similarity index 63% rename from core/apt.go rename to plugins/apt.go index f348e3e..f470cbb 100644 --- a/core/apt.go +++ b/plugins/apt.go @@ -1,16 +1,15 @@ -package core +package main import ( + "C" "bufio" - "errors" "fmt" "os" "path/filepath" - "github.com/mitchellh/mapstructure" - "github.com/vanilla-os/vib/api" ) +import "encoding/json" type AptModule struct { Name string `json:"name"` @@ -28,11 +27,20 @@ type AptOptions struct { // BuildAptModule builds a module that installs packages // using the apt package manager -func BuildAptModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module AptModule - err := mapstructure.Decode(moduleInterface, &module) +// +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *AptModule + var recipe *api.Recipe + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } args := "" @@ -55,7 +63,7 @@ func BuildAptModule(moduleInterface interface{}, recipe *api.Recipe) (string, er packages += pkg + " " } - return fmt.Sprintf("apt-get install -y %s %s && apt-get clean", args, packages), nil + return C.CString(fmt.Sprintf("apt install -y %s %s && apt clean", args, packages)) } if len(module.Source.Paths) > 0 { @@ -66,7 +74,7 @@ func BuildAptModule(moduleInterface interface{}, recipe *api.Recipe) (string, er packages := "" file, err := os.Open(instPath) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } defer file.Close() @@ -76,20 +84,23 @@ func BuildAptModule(moduleInterface interface{}, recipe *api.Recipe) (string, er } if err := scanner.Err(); err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } - cmd += fmt.Sprintf("apt install -y %s %s", args, packages) + cmd += fmt.Sprintf("apt-get install -y %s %s", args, packages) if i != len(module.Source.Paths)-1 { cmd += "&& " } else { - cmd += "&& apt clean" + cmd += "&& apt-get clean" } } - return cmd, nil + return C.CString(cmd) } - return "", errors.New("no packages or paths specified") + return C.CString("ERROR: no packages or paths specified") } + +func main() {} + diff --git a/core/cmake.go b/plugins/cmake.go similarity index 59% rename from core/cmake.go rename to plugins/cmake.go index e676ff1..6ff6e97 100644 --- a/core/cmake.go +++ b/plugins/cmake.go @@ -1,11 +1,11 @@ -package core +package main import ( + "C" + "encoding/json" "fmt" "path/filepath" - "github.com/mitchellh/mapstructure" - "github.com/vanilla-os/vib/api" ) @@ -18,19 +18,29 @@ type CMakeModule struct { } // BuildCMakeModule builds a module that builds a CMake project -func BuildCMakeModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module CMakeModule - err := mapstructure.Decode(moduleInterface, &module) +// +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *CMakeModule + var recipe *api.Recipe + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } buildVars := map[string]string{} for k, v := range module.BuildVars { @@ -48,5 +58,7 @@ func BuildCMakeModule(moduleInterface interface{}, recipe *api.Recipe) (string, buildFlags, ) - return cmd, nil + return C.CString(cmd) } + +func main() { fmt.Println("This plugin is not meant to run standalone!") } diff --git a/core/dpkg-buildpackage.go b/plugins/dpkg-buildpackage.go similarity index 54% rename from core/dpkg-buildpackage.go rename to plugins/dpkg-buildpackage.go index 6f22f18..10b3c0a 100644 --- a/core/dpkg-buildpackage.go +++ b/plugins/dpkg-buildpackage.go @@ -1,10 +1,11 @@ -package core +package main import ( + "C" "fmt" "path/filepath" + "encoding/json" - "github.com/mitchellh/mapstructure" "github.com/vanilla-os/vib/api" ) @@ -16,20 +17,29 @@ type DpkgBuildModule struct { // BuildDpkgModule builds a module that builds a dpkg project // and installs the resulting .deb package -func BuildDpkgBuildPkgModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module DpkgBuildModule - err := mapstructure.Decode(moduleInterface, &module) +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *DpkgBuildModule + var recipe *api.Recipe + + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } cmd := fmt.Sprintf( @@ -42,5 +52,9 @@ func BuildDpkgBuildPkgModule(moduleInterface interface{}, recipe *api.Recipe) (s } cmd += " && apt clean" - return cmd, nil + return C.CString(cmd) } + + + +func main() { fmt.Println("This plugin is not meant to run standalone!"); } diff --git a/core/go.go b/plugins/go.go similarity index 62% rename from core/go.go rename to plugins/go.go index 6645134..4887729 100644 --- a/core/go.go +++ b/plugins/go.go @@ -1,11 +1,12 @@ -package core +package main import ( + "C" "fmt" - "github.com/mitchellh/mapstructure" "github.com/vanilla-os/vib/api" ) +import "encoding/json" type GoModule struct { Name string `json:"name"` @@ -18,19 +19,29 @@ type GoModule struct { // BuildGoModule builds a module that builds a Go project // buildVars are used to customize the build command // like setting the output binary name and location -func BuildGoModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module GoModule - err := mapstructure.Decode(moduleInterface, &module) +// +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *GoModule + var recipe *api.Recipe + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } buildVars := map[string]string{} @@ -55,5 +66,7 @@ func BuildGoModule(moduleInterface interface{}, recipe *api.Recipe) (string, err buildVars["GO_OUTPUT_BIN"], ) - return cmd, nil + return C.CString(cmd) } + +func main() { fmt.Println("This plugin is not meant to run standalone!") } diff --git a/plugins/make.go b/plugins/make.go new file mode 100644 index 0000000..3068373 --- /dev/null +++ b/plugins/make.go @@ -0,0 +1,47 @@ +package main + +import ( + "C" + "encoding/json" + "fmt" + + "github.com/vanilla-os/vib/api" +) + +type MakeModule struct { + Name string `json:"name"` + Type string `json:"type"` + Source api.Source +} + +// BuildMakeModule builds a module that builds a Make project +// +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *MakeModule + var recipe *api.Recipe + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + cmd := "cd /sources/" + api.GetSourcePath(module.Source, module.Name) + " && make && make install" + return C.CString(cmd) +} + +func main() { fmt.Println("This plugin is not meant to run standalone!") } diff --git a/core/meson.go b/plugins/meson.go similarity index 55% rename from core/meson.go rename to plugins/meson.go index 8fb5078..3c77e03 100644 --- a/core/meson.go +++ b/plugins/meson.go @@ -1,9 +1,10 @@ -package core +package main import ( + "C" + "encoding/json" "fmt" - "github.com/mitchellh/mapstructure" "github.com/vanilla-os/vib/api" ) @@ -14,19 +15,29 @@ type MesonModule struct { } // BuildMesonModule builds a module that builds a Meson project -func BuildMesonModule(moduleInterface interface{}, recipe *api.Recipe) (string, error) { - var module MesonModule - err := mapstructure.Decode(moduleInterface, &module) +// +//export BuildModule +func BuildModule(moduleInterface *C.char, recipeInterface *C.char) *C.char { + var module *MesonModule + var recipe *api.Recipe + + err := json.Unmarshal([]byte(C.GoString(moduleInterface)), &module) + if err != nil { + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) + } + + err = json.Unmarshal([]byte(C.GoString(recipeInterface)), &recipe) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } + err = api.DownloadSource(recipe.DownloadsPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, module.Source, module.Name) if err != nil { - return "", err + return C.CString(fmt.Sprintf("ERROR: %s", err.Error())) } // Since the downloaded source goes through checksum verification already @@ -40,5 +51,7 @@ func BuildMesonModule(moduleInterface interface{}, recipe *api.Recipe) (string, tmpDir, ) - return cmd, nil + return C.CString(cmd) } + +func main() { fmt.Println("This plugin is not meant to run standalone!") }