1
0
mirror of https://github.com/Jguer/yay.git synced 2026-05-05 04:16:10 +02:00
yay/pkg/dep/srcinfo.go
2023-09-18 09:22:49 +02:00

245 lines
6.3 KiB
Go

package dep
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
aurc "github.com/Jguer/aur"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/intrange"
aur "github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory"))
var _ SourceHandler = &SRCINFOHandler{}
type SRCINFOHandler struct {
cfg *settings.Configuration
log *text.Logger
db db.Executor
cmdBuilder exe.ICmdBuilder
noConfirm bool
foundTargets []string
aurHandler *AURHandler
}
func (g *SRCINFOHandler) Test(target Target) bool {
path := filepath.Join(g.cfg.BuildDir, target.Name)
if _, err := os.Stat(path); err == nil {
g.foundTargets = append(g.foundTargets, path)
return true
}
return false
}
func (g *SRCINFOHandler) Graph(ctx context.Context, graph *topo.Graph[string, *InstallInfo]) error {
_, err := g.GraphFromSrcInfoDirs(ctx, graph, g.foundTargets)
return err
}
func (g *SRCINFOHandler) GraphFromSrcInfoDirs(ctx context.Context, graph *topo.Graph[string, *InstallInfo],
srcInfosDirs []string,
) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil {
graph = NewGraph()
}
srcInfos := map[string]*gosrc.Srcinfo{}
for _, targetDir := range srcInfosDirs {
if err := srcinfoExists(ctx, g.cmdBuilder, targetDir); err != nil {
return nil, err
}
pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO"))
if err != nil {
return nil, fmt.Errorf("%s: %w", gotext.Get("failed to parse .SRCINFO"), err)
}
srcInfos[targetDir] = pkgbuild
}
aurPkgsAdded := []*aurc.Pkg{}
for pkgBuildDir, pkgbuild := range srcInfos {
pkgBuildDir := pkgBuildDir
aurPkgs, err := makeAURPKGFromSrcinfo(g.db, pkgbuild)
if err != nil {
return nil, err
}
if len(aurPkgs) > 1 {
var errPick error
aurPkgs, errPick = g.pickSrcInfoPkgs(aurPkgs)
if errPick != nil {
return nil, errPick
}
}
for _, pkg := range aurPkgs {
pkg := pkg
reason := Explicit
if pkg := g.db.LocalPackage(pkg.Name); pkg != nil {
reason = Reason(pkg.Reason())
}
graph.AddNode(pkg.Name)
g.aurHandler.AddAurPkgProvides(pkg, graph)
validateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[reason],
Background: bgColorMap[AUR],
Value: &InstallInfo{
Source: SrcInfo,
Reason: reason,
SrcinfoPath: &pkgBuildDir,
AURBase: &pkg.PackageBase,
Version: pkg.Version,
},
})
}
aurPkgsAdded = append(aurPkgsAdded, aurPkgs...)
}
g.aurHandler.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
return graph, nil
}
func srcinfoExists(ctx context.Context,
cmdBuilder exe.ICmdBuilder, targetDir string,
) error {
srcInfoDir := filepath.Join(targetDir, ".SRCINFO")
pkgbuildDir := filepath.Join(targetDir, "PKGBUILD")
if _, err := os.Stat(srcInfoDir); err == nil {
if _, err := os.Stat(pkgbuildDir); err == nil {
return nil
}
}
if _, err := os.Stat(pkgbuildDir); err == nil {
// run makepkg to generate .SRCINFO
srcinfo, stderr, err := cmdBuilder.Capture(cmdBuilder.BuildMakepkgCmd(ctx, targetDir, "--printsrcinfo"))
if err != nil {
return fmt.Errorf("unable to generate .SRCINFO: %w - %s", err, stderr)
}
if err := os.WriteFile(srcInfoDir, []byte(srcinfo), 0o600); err != nil {
return fmt.Errorf("unable to write .SRCINFO: %w", err)
}
return nil
}
return fmt.Errorf("%w: %s", ErrNoBuildFiles, targetDir)
}
func (g *SRCINFOHandler) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) {
final := make([]*aurc.Pkg, 0, len(pkgs))
for i := range pkgs {
g.log.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) +
" " + text.Cyan(pkgs[i].Version))
g.log.Println(" " + pkgs[i].Description)
}
g.log.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):")
numberBuf, err := g.log.GetInput("", g.noConfirm)
if err != nil {
return nil, err
}
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0
for i := 1; i <= len(pkgs); i++ {
target := i - 1
if isInclude && !include.Get(i) {
final = append(final, pkgs[target])
}
if !isInclude && (exclude.Get(i)) {
final = append(final, pkgs[target])
}
}
return final, nil
}
func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) {
pkgs := make([]*aur.Pkg, 0, 1)
alpmArch, err := dbExecutor.AlpmArchitectures()
if err != nil {
return nil, err
}
alpmArch = append(alpmArch, "") // srcinfo assumes no value as ""
getDesc := func(pkg *gosrc.Package) string {
if pkg.Pkgdesc != "" {
return pkg.Pkgdesc
}
return srcInfo.Pkgdesc
}
for i := range srcInfo.Packages {
pkg := &srcInfo.Packages[i]
pkgs = append(pkgs, &aur.Pkg{
ID: 0,
Name: pkg.Pkgname,
PackageBaseID: 0,
PackageBase: srcInfo.Pkgbase,
Version: srcInfo.Version(),
Description: getDesc(pkg),
URL: pkg.URL,
Depends: append(archStringToString(alpmArch, pkg.Depends),
archStringToString(alpmArch, srcInfo.Package.Depends)...),
MakeDepends: archStringToString(alpmArch, srcInfo.PackageBase.MakeDepends),
CheckDepends: archStringToString(alpmArch, srcInfo.PackageBase.CheckDepends),
Conflicts: append(archStringToString(alpmArch, pkg.Conflicts),
archStringToString(alpmArch, srcInfo.Package.Conflicts)...),
Provides: append(archStringToString(alpmArch, pkg.Provides),
archStringToString(alpmArch, srcInfo.Package.Provides)...),
Replaces: append(archStringToString(alpmArch, pkg.Replaces),
archStringToString(alpmArch, srcInfo.Package.Replaces)...),
OptDepends: []string{},
Groups: pkg.Groups,
License: pkg.License,
Keywords: []string{},
})
}
return pkgs, nil
}
func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string {
pkgs := make([]string, 0, len(archString))
for _, arch := range archString {
if db.ArchIsSupported(alpmArches, arch.Arch) {
pkgs = append(pkgs, arch.Value)
}
}
return pkgs
}