diff --git a/bpm_utils/package_utils.go b/bpm_utils/package_utils.go index a203ff9..24eaf4b 100644 --- a/bpm_utils/package_utils.go +++ b/bpm_utils/package_utils.go @@ -22,6 +22,8 @@ type PackageInfo struct { Name string Description string Version string + Url string + License string Arch string Type string Depends []string @@ -29,6 +31,39 @@ type PackageInfo struct { Provides []string } +func GetPackageInfoRaw(filename string) (string, error) { + if _, err := os.Stat(filename); os.IsNotExist(err) { + return "", err + } + file, err := os.Open(filename) + if err != nil { + return "", err + } + archive, err := gzip.NewReader(file) + if err != nil { + return "", err + } + tr := tar.NewReader(archive) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return "", err + } + if header.Name == "pkg.info" { + bs, _ := io.ReadAll(tr) + err := file.Close() + if err != nil { + return "", err + } + return string(bs), nil + } + } + return "", errors.New("pkg.info not found in archive") +} + func ReadPackage(filename string) (*PackageInfo, error) { if _, err := os.Stat(filename); os.IsNotExist(err) { return nil, err @@ -71,6 +106,8 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) Name: "", Description: "", Version: "", + Url: "", + License: "", Arch: "", Type: "", Depends: nil, @@ -82,6 +119,9 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) if len(strings.TrimSpace(line)) == 0 { continue } + if line[0] == '#' { + continue + } split := strings.SplitN(line, ":", 2) if len(split) != 2 { return nil, errors.New("invalid pkg.info format at line " + strconv.Itoa(num)) @@ -95,6 +135,10 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) pkgInfo.Description = split[1] case "version": pkgInfo.Version = split[1] + case "url": + pkgInfo.Url = split[1] + case "license": + pkgInfo.License = split[1] case "architecture": pkgInfo.Arch = split[1] case "type": @@ -131,6 +175,12 @@ func CreateInfoFile(pkgInfo PackageInfo) string { ret = ret + "name: " + pkgInfo.Name + "\n" ret = ret + "description: " + pkgInfo.Description + "\n" ret = ret + "version: " + pkgInfo.Version + "\n" + if pkgInfo.Url != "" { + ret = ret + "url: " + pkgInfo.Url + "\n" + } + if pkgInfo.License != "" { + ret = ret + "license: " + pkgInfo.License + "\n" + } ret = ret + "architecture: " + pkgInfo.Arch + "\n" ret = ret + "type: " + pkgInfo.Type + "\n" if len(pkgInfo.Depends) > 0 { @@ -168,7 +218,9 @@ func InstallPackage(filename, installDir string, force bool) error { return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", ")) } } + if pkgInfo.Type == "binary" { + seenHardlinks := make(map[string]string) for { header, err := tr.Next() if err == io.EOF { @@ -190,6 +242,10 @@ func InstallPackage(filename, installDir string, force bool) error { fmt.Println("Creating Directory: " + extractFilename) } case tar.TypeReg: + err := os.Remove(extractFilename) + if err != nil && !os.IsNotExist(err) { + return err + } outFile, err := os.Create(extractFilename) fmt.Println("Creating File: " + extractFilename) files = append(files, strings.TrimPrefix(header.Name, "files/")) @@ -207,17 +263,36 @@ func InstallPackage(filename, installDir string, force bool) error { return err } case tar.TypeSymlink: - fmt.Println("Creating Symlink: "+extractFilename, " -> "+header.Linkname) + fmt.Println("Creating Symlink: " + extractFilename + " -> " + header.Linkname) files = append(files, strings.TrimPrefix(header.Name, "files/")) - err := os.Symlink(header.Linkname, extractFilename) + err := os.Remove(extractFilename) + if err != nil && !os.IsNotExist(err) { + return err + } + err = os.Symlink(header.Linkname, extractFilename) if err != nil { return err } + case tar.TypeLink: + fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(installDir, strings.TrimPrefix(header.Linkname, "files/"))) + files = append(files, strings.TrimPrefix(header.Name, "files/")) + seenHardlinks[extractFilename] = path.Join(strings.TrimPrefix(header.Linkname, "files/")) + err := os.Remove(extractFilename) + if err != nil && !os.IsNotExist(err) { + return err + } default: return errors.New("ExtractTarGz: unknown type: " + strconv.Itoa(int(header.Typeflag)) + " in " + extractFilename) } } } + for extractFilename, destination := range seenHardlinks { + fmt.Println("Creating Hard Link: " + extractFilename + " -> " + path.Join(installDir, destination)) + err := os.Link(path.Join(installDir, destination), extractFilename) + if err != nil { + return err + } + } } else if pkgInfo.Type == "source" { for { header, err := tr.Next() @@ -232,7 +307,7 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { return err } - temp, err := os.MkdirTemp("/tmp/", "bpm_source-") + temp, err := os.MkdirTemp("/var/tmp/", "bpm_source-") fmt.Println("Creating temp directory at: " + temp) if err != nil { return err @@ -364,7 +439,11 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { return err } - _, err = f.WriteString(CreateInfoFile(*pkgInfo)) + raw, err := GetPackageInfoRaw(filename) + if err != nil { + return err + } + _, err = f.WriteString(raw) if err != nil { return err } @@ -441,8 +520,25 @@ func CheckDependencies(pkgInfo *PackageInfo, rootDir string) []string { if !item.IsDir() { continue } - if slices.Contains(unresolved, item.Name()) { - unresolved = stringSliceRemove(unresolved, item.Name()) + _, err := os.Stat(path.Join(installedDir, item.Name(), "/info")) + if err != nil { + return nil + } + bs, err := os.ReadFile(path.Join(installedDir, item.Name(), "/info")) + if err != nil { + return nil + } + info, err := ReadPackageInfo(string(bs), false) + if err != nil { + return nil + } + if slices.Contains(unresolved, info.Name) { + unresolved = stringSliceRemove(unresolved, info.Name) + } + for _, prov := range info.Provides { + if slices.Contains(unresolved, prov) { + unresolved = stringSliceRemove(unresolved, prov) + } } } return unresolved @@ -464,8 +560,25 @@ func CheckMakeDependencies(pkgInfo *PackageInfo, rootDir string) []string { if !item.IsDir() { continue } - if slices.Contains(unresolved, item.Name()) { - unresolved = stringSliceRemove(unresolved, item.Name()) + _, err := os.Stat(path.Join(installedDir, item.Name(), "/info")) + if err != nil { + return nil + } + bs, err := os.ReadFile(path.Join(installedDir, item.Name(), "/info")) + if err != nil { + return nil + } + info, err := ReadPackageInfo(string(bs), false) + if err != nil { + return nil + } + if slices.Contains(unresolved, info.Name) { + unresolved = stringSliceRemove(unresolved, info.Name) + } + for _, prov := range info.Provides { + if slices.Contains(unresolved, prov) { + unresolved = stringSliceRemove(unresolved, prov) + } } } return unresolved diff --git a/main.go b/main.go index 19df223..ba339d6 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ import ( /* A simple-to-use package manager */ /* ---------------------------------- */ -var bpmVer = "0.0.8" +var bpmVer = "0.0.9" var rootDir = "/" func main() { @@ -168,6 +168,21 @@ func resolveCommand() { } } } + if !slices.Contains(flags, "y") { + reader := bufio.NewReader(os.Stdin) + if pkgInfo.Type == "source" { + fmt.Print("Would you like to view the source.sh file of this package? [Y\\n] ") + text, _ := reader.ReadString('\n') + if strings.TrimSpace(strings.ToLower(text)) != "n" && strings.TrimSpace(strings.ToLower(text)) != "no" { + script, err := bpm_utils.GetSourceScript(file) + if err != nil { + log.Fatalf("Could not read source script\nError: %s\n", err) + } + fmt.Println(script) + fmt.Println("-------EOF-------") + } + } + } if bpm_utils.IsPackageInstalled(pkgInfo.Name, rootDir) { if !slices.Contains(flags, "y") { installedInfo := bpm_utils.GetPackageInfo(pkgInfo.Name, rootDir, false) @@ -194,20 +209,7 @@ func resolveCommand() { } } else if !slices.Contains(flags, "y") { reader := bufio.NewReader(os.Stdin) - if pkgInfo.Type == "source" { - fmt.Print("Would you like to view the source.sh file of this package? [Y\\n]") - text, _ := reader.ReadString('\n') - if strings.TrimSpace(strings.ToLower(text)) != "n" && strings.TrimSpace(strings.ToLower(text)) != "no" { - script, err := bpm_utils.GetSourceScript(file) - if err != nil { - log.Fatalf("Could not read source script\nError: %s\n", err) - } - fmt.Println(script) - fmt.Println("-------EOF-------") - } - } fmt.Printf("Do you wish to %s this package? [y\\N] ", verb) - text, _ := reader.ReadString('\n') if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name) @@ -217,9 +219,15 @@ func resolveCommand() { err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f")) if err != nil { + if pkgInfo.Type == "source" { + fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") + } log.Fatalf("Could not install package\nError: %s\n", err) } fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name) + if pkgInfo.Type == "source" { + fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") + } } case remove: flags, i := resolveFlags() diff --git a/test_packages/x86_64/bpm-utils/bpm-utils.bpm b/test_packages/x86_64/bpm-utils/bpm-utils.bpm index 02067e6..e56d284 100644 Binary files a/test_packages/x86_64/bpm-utils/bpm-utils.bpm and b/test_packages/x86_64/bpm-utils/bpm-utils.bpm differ diff --git a/test_packages/x86_64/bpm-utils/pkg.info b/test_packages/x86_64/bpm-utils/pkg.info index b5a85b4..b3f12f5 100644 --- a/test_packages/x86_64/bpm-utils/pkg.info +++ b/test_packages/x86_64/bpm-utils/pkg.info @@ -1,5 +1,7 @@ name: bpm-utils description: Utilities to create BPM packages version: 1.0.0 +url: https://gitlab.com/bubble-package-manager/bpm/ +license: GPL3 architecture: x86_64 type: binary \ No newline at end of file diff --git a/test_packages/x86_64/bpm/bpm.bpm b/test_packages/x86_64/bpm/bpm.bpm index 98f1107..2b29caf 100644 Binary files a/test_packages/x86_64/bpm/bpm.bpm and b/test_packages/x86_64/bpm/bpm.bpm differ diff --git a/test_packages/x86_64/bpm/files/usr/bin/bpm b/test_packages/x86_64/bpm/files/usr/bin/bpm index 2c08237..df081ce 100755 Binary files a/test_packages/x86_64/bpm/files/usr/bin/bpm and b/test_packages/x86_64/bpm/files/usr/bin/bpm differ diff --git a/test_packages/x86_64/bpm/pkg.info b/test_packages/x86_64/bpm/pkg.info index 95068da..973453a 100644 --- a/test_packages/x86_64/bpm/pkg.info +++ b/test_packages/x86_64/bpm/pkg.info @@ -1,5 +1,7 @@ name: bpm description: The Bubble Package Manager -version: 0.0.8 +version: 0.0.9 +url: https://gitlab.com/bubble-package-manager/bpm/ +license: GPL3 architecture: x86_64 type: binary diff --git a/test_packages/x86_64/hello/hello.bpm b/test_packages/x86_64/hello/hello.bpm index 43d53be..613e2e1 100644 Binary files a/test_packages/x86_64/hello/hello.bpm and b/test_packages/x86_64/hello/hello.bpm differ diff --git a/test_packages/x86_64/htop-src/htop-src.bpm b/test_packages/x86_64/htop-src/htop-src.bpm index 3bc1a92..3066ff3 100644 Binary files a/test_packages/x86_64/htop-src/htop-src.bpm and b/test_packages/x86_64/htop-src/htop-src.bpm differ diff --git a/test_packages/x86_64/htop-src/pkg.info b/test_packages/x86_64/htop-src/pkg.info index 0709896..bff70ef 100644 --- a/test_packages/x86_64/htop-src/pkg.info +++ b/test_packages/x86_64/htop-src/pkg.info @@ -1,5 +1,8 @@ name: htop description: An interactive process viewer version: 3.3.0 +url: https://github.com/htop-dev/htop +license: GPL2 architecture: x86_64 type: source +#make_depends: git \ No newline at end of file diff --git a/test_packages/x86_64/htop-src/source.sh b/test_packages/x86_64/htop-src/source.sh index 2dda643..7ce67df 100644 --- a/test_packages/x86_64/htop-src/source.sh +++ b/test_packages/x86_64/htop-src/source.sh @@ -1,5 +1,5 @@ # This file is read and executed by BPM to compile htop. It will run inside a temporary folder in /tmp during execution -echo "Building htop..." +echo "Compiling htop..." # Creating 'source' directory mkdir source # Cloning the git repository into the 'source' directory