366 lines
9.9 KiB
Go
366 lines
9.9 KiB
Go
package workdir
|
|
|
|
import (
|
|
"cmp"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"maps"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
|
|
"github.com/Jguer/yay/v13/pkg/dep"
|
|
"github.com/Jguer/yay/v13/pkg/runtime"
|
|
settingslua "github.com/Jguer/yay/v13/pkg/settings/lua"
|
|
|
|
gosrc "github.com/Morganamilo/go-srcinfo"
|
|
mapset "github.com/deckarep/golang-set/v2"
|
|
)
|
|
|
|
func (preper *Preparer) runPreDownloadSourcesHooks(ctx context.Context, run *runtime.Runtime, w io.Writer,
|
|
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
|
|
targets []map[string]*dep.InstallInfo,
|
|
) error {
|
|
if err := runAURPreInstallLuaHooks(run, pkgbuildDirsByBase, installed, targets); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, hookFn := range preper.hooks {
|
|
if hookFn.Type != PreDownloadSourcesHook {
|
|
continue
|
|
}
|
|
|
|
if err := hookFn.Hookfn(ctx, run, w, pkgbuildDirsByBase, installed); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runAURPreInstallLuaHooks(run *runtime.Runtime, pkgbuildDirsByBase map[string]string,
|
|
installed mapset.Set[string], targets []map[string]*dep.InstallInfo,
|
|
) error {
|
|
if run == nil || run.Lua == nil || !run.Lua.HasAutocmd(settingslua.EventAURPreInstall) {
|
|
return nil
|
|
}
|
|
|
|
events, err := aurPreInstallEvents(pkgbuildDirsByBase, installed, targets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := range events {
|
|
if err := run.Lua.RunAURPreInstall(&events[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func aurPreInstallEvents(pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
|
|
targets []map[string]*dep.InstallInfo,
|
|
) ([]settingslua.AURPreInstallEvent, error) {
|
|
packagesByBase := aurTargetPackagesByBase(targets)
|
|
bases := sortedAURBases(pkgbuildDirsByBase)
|
|
|
|
events := make([]settingslua.AURPreInstallEvent, 0, len(bases))
|
|
for _, base := range bases {
|
|
event, err := aurPreInstallEvent(base, pkgbuildDirsByBase[base],
|
|
packagesByBase[base], installed, targets)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
events = append(events, event)
|
|
}
|
|
|
|
return events, nil
|
|
}
|
|
|
|
func sortedAURBases(pkgbuildDirsByBase map[string]string) []string {
|
|
return slices.Sorted(maps.Keys(pkgbuildDirsByBase))
|
|
}
|
|
|
|
func aurPreInstallEvent(base, path string, packages []settingslua.AURPreInstallPackage,
|
|
installed mapset.Set[string], targets []map[string]*dep.InstallInfo,
|
|
) (settingslua.AURPreInstallEvent, error) {
|
|
return aurPackageEvent(settingslua.EventAURPreInstall, base, path, packages, installed, targets)
|
|
}
|
|
|
|
func aurPackageEvent(eventName, base, path string, packages []settingslua.AURPreInstallPackage,
|
|
installed mapset.Set[string], targets []map[string]*dep.InstallInfo,
|
|
) (settingslua.AURPreInstallEvent, error) {
|
|
dir, pkgbuildPath, srcinfoPath := aurPreInstallPaths(path)
|
|
|
|
pkgbuildBytes, err := os.ReadFile(pkgbuildPath)
|
|
if err != nil {
|
|
return settingslua.AURPreInstallEvent{},
|
|
fmt.Errorf("%s %s: read PKGBUILD: %w", eventName, base, err)
|
|
}
|
|
|
|
srcinfo, err := gosrc.ParseFile(srcinfoPath)
|
|
if err != nil {
|
|
return settingslua.AURPreInstallEvent{},
|
|
fmt.Errorf("%s %s: parse .SRCINFO: %w", eventName, base, err)
|
|
}
|
|
|
|
if len(packages) == 0 {
|
|
packages = packagesFromSRCINFO(srcinfo)
|
|
}
|
|
slices.SortFunc(packages, func(a, b settingslua.AURPreInstallPackage) int { return cmp.Compare(a.Name, b.Name) })
|
|
|
|
return settingslua.AURPreInstallEvent{
|
|
Base: base,
|
|
Dir: dir,
|
|
PKGBUILDPath: pkgbuildPath,
|
|
SRCINFOPath: srcinfoPath,
|
|
PKGBUILD: string(pkgbuildBytes),
|
|
Version: srcinfo.Version(),
|
|
LastModified: aurPreInstallLastModified(base, targets),
|
|
Installed: aurPreInstallInstalled(packages, srcinfo, installed),
|
|
Packages: packages,
|
|
SRCINFO: srcinfoEventData(srcinfo),
|
|
}, nil
|
|
}
|
|
|
|
func aurPreInstallPaths(path string) (dir, pkgbuildPath, srcinfoPath string) {
|
|
info, err := os.Stat(path)
|
|
if err == nil && !info.IsDir() {
|
|
dir = filepath.Dir(path)
|
|
srcinfoPath = path
|
|
pkgbuildPath = filepath.Join(dir, "PKGBUILD")
|
|
|
|
return dir, pkgbuildPath, srcinfoPath
|
|
}
|
|
|
|
dir = path
|
|
pkgbuildPath = filepath.Join(dir, "PKGBUILD")
|
|
srcinfoPath = filepath.Join(dir, ".SRCINFO")
|
|
|
|
return dir, pkgbuildPath, srcinfoPath
|
|
}
|
|
|
|
func aurTargetPackagesByBase(targets []map[string]*dep.InstallInfo) map[string][]settingslua.AURPreInstallPackage {
|
|
packages := map[string][]settingslua.AURPreInstallPackage{}
|
|
|
|
for _, layer := range targets {
|
|
for name, info := range layer {
|
|
if info == nil || info.AURBase == "" {
|
|
continue
|
|
}
|
|
|
|
if info.Source != dep.AUR && info.Source != dep.SrcInfo {
|
|
continue
|
|
}
|
|
|
|
base := info.AURBase
|
|
packages[base] = append(packages[base], settingslua.AURPreInstallPackage{
|
|
Name: name,
|
|
Version: info.Version,
|
|
LocalVersion: info.LocalVersion,
|
|
Reason: luaReason(info.Reason),
|
|
Upgrade: info.Upgrade,
|
|
Devel: info.Devel,
|
|
})
|
|
}
|
|
}
|
|
|
|
return packages
|
|
}
|
|
|
|
func packagesFromSRCINFO(srcinfo *gosrc.Srcinfo) []settingslua.AURPreInstallPackage {
|
|
packages := make([]settingslua.AURPreInstallPackage, 0, len(srcinfo.Packages))
|
|
for i := range srcinfo.Packages {
|
|
packages = append(packages, settingslua.AURPreInstallPackage{
|
|
Name: srcinfo.Packages[i].Pkgname,
|
|
Version: srcinfo.Version(),
|
|
})
|
|
}
|
|
|
|
return packages
|
|
}
|
|
|
|
func aurPreInstallInstalled(packages []settingslua.AURPreInstallPackage, srcinfo *gosrc.Srcinfo,
|
|
installed mapset.Set[string],
|
|
) bool {
|
|
isInstalled := false
|
|
for _, pkg := range packages {
|
|
if pkg.LocalVersion != "" || installed.Contains(pkg.Name) {
|
|
isInstalled = true
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := range srcinfo.Packages {
|
|
if installed.Contains(srcinfo.Packages[i].Pkgname) {
|
|
isInstalled = true
|
|
break
|
|
}
|
|
}
|
|
|
|
return isInstalled
|
|
}
|
|
|
|
func aurPreInstallLastModified(base string, targets []map[string]*dep.InstallInfo) int64 {
|
|
var lastModified int64
|
|
|
|
for _, layer := range targets {
|
|
for _, info := range layer {
|
|
if info == nil || info.AURBase == "" {
|
|
continue
|
|
}
|
|
|
|
if info.AURBase == base && info.LastModified > lastModified {
|
|
lastModified = info.LastModified
|
|
}
|
|
}
|
|
}
|
|
|
|
return lastModified
|
|
}
|
|
|
|
func luaReason(reason dep.Reason) string {
|
|
switch reason {
|
|
case dep.Explicit:
|
|
return "explicit"
|
|
case dep.Dep:
|
|
return "dependency"
|
|
case dep.MakeDep:
|
|
return "make_dependency"
|
|
case dep.CheckDep:
|
|
return "check_dependency"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
func srcinfoEventData(srcinfo *gosrc.Srcinfo) settingslua.AURPreInstallSRCINFO {
|
|
pkgdesc, url := srcinfoDescriptionAndURL(srcinfo)
|
|
packageArch := packageStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []string {
|
|
return pkg.Arch
|
|
})
|
|
packageLicense := packageStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []string {
|
|
return pkg.License
|
|
})
|
|
packageDepends := packageArchStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []gosrc.ArchString {
|
|
return pkg.Depends
|
|
})
|
|
packageOptDepends := packageArchStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []gosrc.ArchString {
|
|
return pkg.OptDepends
|
|
})
|
|
packageProvides := packageArchStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []gosrc.ArchString {
|
|
return pkg.Provides
|
|
})
|
|
packageConflicts := packageArchStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []gosrc.ArchString {
|
|
return pkg.Conflicts
|
|
})
|
|
packageReplaces := packageArchStringValues(srcinfo.Packages, func(pkg *gosrc.Package) []gosrc.ArchString {
|
|
return pkg.Replaces
|
|
})
|
|
|
|
return settingslua.AURPreInstallSRCINFO{
|
|
Pkgbase: srcinfo.Pkgbase,
|
|
Pkgver: srcinfo.Pkgver,
|
|
Pkgrel: srcinfo.Pkgrel,
|
|
Epoch: srcinfo.Epoch,
|
|
Version: srcinfo.Version(),
|
|
Pkgdesc: pkgdesc,
|
|
URL: url,
|
|
Arch: mergedStringValues(srcinfo.Arch, packageArch...),
|
|
License: mergedStringValues(srcinfo.License, packageLicense...),
|
|
Depends: mergedArchStringValues(srcinfo.Depends, packageDepends...),
|
|
MakeDepends: archStringValues(srcinfo.MakeDepends),
|
|
CheckDepends: archStringValues(srcinfo.CheckDepends),
|
|
OptDepends: mergedArchStringValues(srcinfo.OptDepends, packageOptDepends...),
|
|
Provides: mergedArchStringValues(srcinfo.Provides, packageProvides...),
|
|
Conflicts: mergedArchStringValues(srcinfo.Conflicts, packageConflicts...),
|
|
Replaces: mergedArchStringValues(srcinfo.Replaces, packageReplaces...),
|
|
}
|
|
}
|
|
|
|
func srcinfoDescriptionAndURL(srcinfo *gosrc.Srcinfo) (pkgdesc, url string) {
|
|
pkgdesc = srcinfo.Pkgdesc
|
|
url = srcinfo.URL
|
|
if pkgdesc != "" && url != "" {
|
|
return pkgdesc, url
|
|
}
|
|
|
|
for i := range srcinfo.Packages {
|
|
pkg := &srcinfo.Packages[i]
|
|
if pkgdesc == "" {
|
|
pkgdesc = pkg.Pkgdesc
|
|
}
|
|
if url == "" {
|
|
url = pkg.URL
|
|
}
|
|
if pkgdesc != "" && url != "" {
|
|
break
|
|
}
|
|
}
|
|
|
|
return pkgdesc, url
|
|
}
|
|
|
|
func packageStringValues(packages []gosrc.Package, selectValues func(*gosrc.Package) []string) [][]string {
|
|
values := make([][]string, 0, len(packages))
|
|
for i := range packages {
|
|
values = append(values, selectValues(&packages[i]))
|
|
}
|
|
|
|
return values
|
|
}
|
|
|
|
func packageArchStringValues(packages []gosrc.Package, selectValues func(*gosrc.Package) []gosrc.ArchString) [][]gosrc.ArchString {
|
|
values := make([][]gosrc.ArchString, 0, len(packages))
|
|
for i := range packages {
|
|
values = append(values, selectValues(&packages[i]))
|
|
}
|
|
|
|
return values
|
|
}
|
|
|
|
func mergedStringValues(global []string, packageValues ...[]string) []string {
|
|
out := make([]string, 0, len(global))
|
|
seen := map[string]struct{}{}
|
|
|
|
appendValue := func(value string) {
|
|
if _, ok := seen[value]; ok {
|
|
return
|
|
}
|
|
seen[value] = struct{}{}
|
|
out = append(out, value)
|
|
}
|
|
|
|
for _, value := range global {
|
|
appendValue(value)
|
|
}
|
|
for _, values := range packageValues {
|
|
for _, value := range values {
|
|
appendValue(value)
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func mergedArchStringValues(global []gosrc.ArchString, packageValues ...[]gosrc.ArchString) []string {
|
|
values := make([][]string, 0, len(packageValues))
|
|
for _, packageValue := range packageValues {
|
|
values = append(values, archStringValues(packageValue))
|
|
}
|
|
|
|
return mergedStringValues(archStringValues(global), values...)
|
|
}
|
|
|
|
func archStringValues(values []gosrc.ArchString) []string {
|
|
out := make([]string, 0, len(values))
|
|
for _, value := range values {
|
|
out = append(out, value.Value)
|
|
}
|
|
|
|
return out
|
|
}
|