srcinfo.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package dep
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "strconv"
  9. aurc "github.com/Jguer/aur"
  10. gosrc "github.com/Morganamilo/go-srcinfo"
  11. "github.com/leonelquinteros/gotext"
  12. "github.com/Jguer/yay/v12/pkg/db"
  13. "github.com/Jguer/yay/v12/pkg/dep/topo"
  14. "github.com/Jguer/yay/v12/pkg/intrange"
  15. aur "github.com/Jguer/yay/v12/pkg/query"
  16. "github.com/Jguer/yay/v12/pkg/settings"
  17. "github.com/Jguer/yay/v12/pkg/settings/exe"
  18. "github.com/Jguer/yay/v12/pkg/text"
  19. )
  20. var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory"))
  21. var _ SourceHandler = &SRCINFOHandler{}
  22. type SRCINFOHandler struct {
  23. cfg *settings.Configuration
  24. log *text.Logger
  25. db db.Executor
  26. cmdBuilder exe.ICmdBuilder
  27. noConfirm bool
  28. foundTargets []string
  29. aurHandler *AURHandler
  30. }
  31. func (g *SRCINFOHandler) Test(target Target) bool {
  32. path := filepath.Join(g.cfg.BuildDir, target.Name)
  33. if _, err := os.Stat(path); err == nil {
  34. g.foundTargets = append(g.foundTargets, path)
  35. return true
  36. }
  37. return false
  38. }
  39. func (g *SRCINFOHandler) Graph(ctx context.Context, graph *topo.Graph[string, *InstallInfo]) error {
  40. _, err := g.GraphFromSrcInfoDirs(ctx, graph, g.foundTargets)
  41. return err
  42. }
  43. func (g *SRCINFOHandler) GraphFromSrcInfoDirs(ctx context.Context, graph *topo.Graph[string, *InstallInfo],
  44. srcInfosDirs []string,
  45. ) (*topo.Graph[string, *InstallInfo], error) {
  46. if graph == nil {
  47. graph = NewGraph()
  48. }
  49. srcInfos := map[string]*gosrc.Srcinfo{}
  50. for _, targetDir := range srcInfosDirs {
  51. if err := srcinfoExists(ctx, g.cmdBuilder, targetDir); err != nil {
  52. return nil, err
  53. }
  54. pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO"))
  55. if err != nil {
  56. return nil, fmt.Errorf("%s: %w", gotext.Get("failed to parse .SRCINFO"), err)
  57. }
  58. srcInfos[targetDir] = pkgbuild
  59. }
  60. aurPkgsAdded := []*aurc.Pkg{}
  61. for pkgBuildDir, pkgbuild := range srcInfos {
  62. pkgBuildDir := pkgBuildDir
  63. aurPkgs, err := makeAURPKGFromSrcinfo(g.db, pkgbuild)
  64. if err != nil {
  65. return nil, err
  66. }
  67. if len(aurPkgs) > 1 {
  68. var errPick error
  69. aurPkgs, errPick = g.pickSrcInfoPkgs(aurPkgs)
  70. if errPick != nil {
  71. return nil, errPick
  72. }
  73. }
  74. for _, pkg := range aurPkgs {
  75. pkg := pkg
  76. reason := Explicit
  77. if pkg := g.db.LocalPackage(pkg.Name); pkg != nil {
  78. reason = Reason(pkg.Reason())
  79. }
  80. graph.AddNode(pkg.Name)
  81. g.aurHandler.AddAurPkgProvides(pkg, graph)
  82. validateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
  83. Color: colorMap[reason],
  84. Background: bgColorMap[AUR],
  85. Value: &InstallInfo{
  86. Source: SrcInfo,
  87. Reason: reason,
  88. SrcinfoPath: &pkgBuildDir,
  89. AURBase: &pkg.PackageBase,
  90. Version: pkg.Version,
  91. },
  92. })
  93. }
  94. aurPkgsAdded = append(aurPkgsAdded, aurPkgs...)
  95. }
  96. g.aurHandler.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
  97. return graph, nil
  98. }
  99. func srcinfoExists(ctx context.Context,
  100. cmdBuilder exe.ICmdBuilder, targetDir string,
  101. ) error {
  102. srcInfoDir := filepath.Join(targetDir, ".SRCINFO")
  103. pkgbuildDir := filepath.Join(targetDir, "PKGBUILD")
  104. if _, err := os.Stat(srcInfoDir); err == nil {
  105. if _, err := os.Stat(pkgbuildDir); err == nil {
  106. return nil
  107. }
  108. }
  109. if _, err := os.Stat(pkgbuildDir); err == nil {
  110. // run makepkg to generate .SRCINFO
  111. srcinfo, stderr, err := cmdBuilder.Capture(cmdBuilder.BuildMakepkgCmd(ctx, targetDir, "--printsrcinfo"))
  112. if err != nil {
  113. return fmt.Errorf("unable to generate .SRCINFO: %w - %s", err, stderr)
  114. }
  115. if err := os.WriteFile(srcInfoDir, []byte(srcinfo), 0o600); err != nil {
  116. return fmt.Errorf("unable to write .SRCINFO: %w", err)
  117. }
  118. return nil
  119. }
  120. return fmt.Errorf("%w: %s", ErrNoBuildFiles, targetDir)
  121. }
  122. func (g *SRCINFOHandler) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) {
  123. final := make([]*aurc.Pkg, 0, len(pkgs))
  124. for i := range pkgs {
  125. g.log.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) +
  126. " " + text.Cyan(pkgs[i].Version))
  127. g.log.Println(" " + pkgs[i].Description)
  128. }
  129. g.log.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):")
  130. numberBuf, err := g.log.GetInput("", g.noConfirm)
  131. if err != nil {
  132. return nil, err
  133. }
  134. include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
  135. isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0
  136. for i := 1; i <= len(pkgs); i++ {
  137. target := i - 1
  138. if isInclude && !include.Get(i) {
  139. final = append(final, pkgs[target])
  140. }
  141. if !isInclude && (exclude.Get(i)) {
  142. final = append(final, pkgs[target])
  143. }
  144. }
  145. return final, nil
  146. }
  147. func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) {
  148. pkgs := make([]*aur.Pkg, 0, 1)
  149. alpmArch, err := dbExecutor.AlpmArchitectures()
  150. if err != nil {
  151. return nil, err
  152. }
  153. alpmArch = append(alpmArch, "") // srcinfo assumes no value as ""
  154. getDesc := func(pkg *gosrc.Package) string {
  155. if pkg.Pkgdesc != "" {
  156. return pkg.Pkgdesc
  157. }
  158. return srcInfo.Pkgdesc
  159. }
  160. for i := range srcInfo.Packages {
  161. pkg := &srcInfo.Packages[i]
  162. pkgs = append(pkgs, &aur.Pkg{
  163. ID: 0,
  164. Name: pkg.Pkgname,
  165. PackageBaseID: 0,
  166. PackageBase: srcInfo.Pkgbase,
  167. Version: srcInfo.Version(),
  168. Description: getDesc(pkg),
  169. URL: pkg.URL,
  170. Depends: append(archStringToString(alpmArch, pkg.Depends),
  171. archStringToString(alpmArch, srcInfo.Package.Depends)...),
  172. MakeDepends: archStringToString(alpmArch, srcInfo.PackageBase.MakeDepends),
  173. CheckDepends: archStringToString(alpmArch, srcInfo.PackageBase.CheckDepends),
  174. Conflicts: append(archStringToString(alpmArch, pkg.Conflicts),
  175. archStringToString(alpmArch, srcInfo.Package.Conflicts)...),
  176. Provides: append(archStringToString(alpmArch, pkg.Provides),
  177. archStringToString(alpmArch, srcInfo.Package.Provides)...),
  178. Replaces: append(archStringToString(alpmArch, pkg.Replaces),
  179. archStringToString(alpmArch, srcInfo.Package.Replaces)...),
  180. OptDepends: []string{},
  181. Groups: pkg.Groups,
  182. License: pkg.License,
  183. Keywords: []string{},
  184. })
  185. }
  186. return pkgs, nil
  187. }
  188. func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string {
  189. pkgs := make([]string, 0, len(archString))
  190. for _, arch := range archString {
  191. if db.ArchIsSupported(alpmArches, arch.Arch) {
  192. pkgs = append(pkgs, arch.Value)
  193. }
  194. }
  195. return pkgs
  196. }