Skip to content

Commit 945e0d5

Browse files
authored
Merge pull request #8 from rmohr/ignore
Add an option to ignore packages and their dependencies
2 parents 822e844 + f3e0fd9 commit 945e0d5

File tree

8 files changed

+152
-38
lines changed

8 files changed

+152
-38
lines changed

cmd/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ go_library(
2020
importpath = "github.com/rmohr/bazeldnf/cmd",
2121
visibility = ["//visibility:private"],
2222
deps = [
23+
"//cmd/template",
2324
"//pkg/api",
2425
"//pkg/api/bazeldnf",
2526
"//pkg/bazel",

cmd/resolve.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package main
22

33
import (
4-
"fmt"
4+
"os"
55

6+
"github.com/rmohr/bazeldnf/cmd/template"
67
"github.com/rmohr/bazeldnf/pkg/api/bazeldnf"
78
"github.com/rmohr/bazeldnf/pkg/reducer"
89
"github.com/rmohr/bazeldnf/pkg/repo"
@@ -18,6 +19,7 @@ type resolveOpts struct {
1819
arch string
1920
fedoraBaseSystem string
2021
repofiles []string
22+
forceIgnoreRegex []string
2123
}
2224

2325
var resolveopts = resolveOpts{}
@@ -50,7 +52,7 @@ func NewResolveCmd() *cobra.Command {
5052
}
5153
solver := sat.NewResolver(resolveopts.nobest)
5254
logrus.Info("Loading involved packages into the resolver.")
53-
err = solver.LoadInvolvedPackages(involved)
55+
err = solver.LoadInvolvedPackages(involved, resolveopts.forceIgnoreRegex)
5456
if err != nil {
5557
return err
5658
}
@@ -60,13 +62,13 @@ func NewResolveCmd() *cobra.Command {
6062
return err
6163
}
6264
logrus.Info("Solving.")
63-
install, _, err := solver.Resolve()
65+
install, _, forceIgnored, err := solver.Resolve()
6466
if err != nil {
6567
return err
6668
}
67-
fmt.Println(install)
68-
fmt.Println(len(install))
69-
logrus.Info("Done.")
69+
if err := template.Render(os.Stdout, install, forceIgnored); err != nil {
70+
return err
71+
}
7072
return nil
7173
},
7274
}
@@ -76,5 +78,6 @@ func NewResolveCmd() *cobra.Command {
7678
resolveCmd.Flags().StringVarP(&resolveopts.arch, "arch", "a", "x86_64", "target fedora architecture")
7779
resolveCmd.Flags().BoolVarP(&resolveopts.nobest, "nobest", "n", false, "allow picking versions which are not the newest")
7880
resolveCmd.Flags().StringArrayVarP(&resolveopts.repofiles, "repofile", "r", []string{"repo.yaml"}, "repository information file. Can be specified multiple times. Will be used by default if no explicit inputs are provided.")
81+
resolveCmd.Flags().StringArrayVar(&resolveopts.forceIgnoreRegex, "force-ignore-with-dependencies", []string{}, "Packages matching these regex patterns will not be installed. Allows force-removing unwanted dependencies. Be careful, this can lead to hidden missing dependencies.")
7982
return resolveCmd
8083
}

cmd/rpmtree.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package main
22

33
import (
4+
"os"
5+
6+
"github.com/rmohr/bazeldnf/cmd/template"
47
"github.com/rmohr/bazeldnf/pkg/bazel"
58
"github.com/rmohr/bazeldnf/pkg/reducer"
69
"github.com/rmohr/bazeldnf/pkg/repo"
@@ -19,6 +22,7 @@ type rpmtreeOpts struct {
1922
buildfile string
2023
name string
2124
public bool
25+
forceIgnoreRegex []string
2226
}
2327

2428
var rpmtreeopts = rpmtreeOpts{}
@@ -46,7 +50,7 @@ func NewRpmTreeCmd() *cobra.Command {
4650
}
4751
solver := sat.NewResolver(rpmtreeopts.nobest)
4852
logrus.Info("Loading involved packages into the rpmtreer.")
49-
err = solver.LoadInvolvedPackages(involved)
53+
err = solver.LoadInvolvedPackages(involved, rpmtreeopts.forceIgnoreRegex)
5054
if err != nil {
5155
return err
5256
}
@@ -56,7 +60,7 @@ func NewRpmTreeCmd() *cobra.Command {
5660
return err
5761
}
5862
logrus.Info("Solving.")
59-
install, _, err := solver.Resolve()
63+
install, _, forceIgnored, err := solver.Resolve()
6064
if err != nil {
6165
return err
6266
}
@@ -80,7 +84,9 @@ func NewRpmTreeCmd() *cobra.Command {
8084
if err != nil {
8185
return err
8286
}
83-
logrus.Info("Done.")
87+
if err := template.Render(os.Stdout, install, forceIgnored); err != nil {
88+
return err
89+
}
8490

8591
return nil
8692
},
@@ -94,6 +100,7 @@ func NewRpmTreeCmd() *cobra.Command {
94100
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.workspace, "workspace", "w", "WORKSPACE", "Bazel workspace file")
95101
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.buildfile, "buildfile", "b", "rpm/BUILD.bazel", "Build file for RPMs")
96102
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.name, "name", "", "", "rpmtree rule name")
103+
rpmtreeCmd.Flags().StringArrayVar(&rpmtreeopts.forceIgnoreRegex, "force-ignore-with-dependencies", []string{}, "Packages matching these regex patterns will not be installed. Allows force-removing unwanted dependencies. Be careful, this can lead to hidden missing dependencies.")
97104
rpmtreeCmd.MarkFlagRequired("name")
98105
return rpmtreeCmd
99106
}

cmd/template/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "template",
5+
srcs = ["install.go"],
6+
importpath = "github.com/rmohr/bazeldnf/cmd/template",
7+
visibility = ["//visibility:public"],
8+
deps = ["//pkg/api"],
9+
)

cmd/template/install.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package template
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"text/tabwriter"
7+
8+
"github.com/rmohr/bazeldnf/pkg/api"
9+
)
10+
11+
func Render(writer io.Writer, installed []*api.Package, forceIgnored []*api.Package) error {
12+
totalDownloadSize := 0
13+
totalInstallSize := 0
14+
15+
tabWriter := tabwriter.NewWriter(writer, 0, 8, 1, '\t', 0)
16+
if _, err := fmt.Fprintln(tabWriter, "Package\tVersion\tSize\tDownload Size"); err != nil {
17+
return fmt.Errorf("failed to write header: %v", err)
18+
}
19+
if _, err := fmt.Fprintln(tabWriter, "Installing:\t\t\t"); err != nil {
20+
return fmt.Errorf("failed to write header: %v", err)
21+
}
22+
for _, pkg := range installed {
23+
totalInstallSize += pkg.Size.Archive
24+
totalDownloadSize += pkg.Size.Package
25+
if _, err := fmt.Fprintf(tabWriter, " %v\t%v\t%s\t%s\n", pkg.Name, pkg.Version.String(), toReadableQuantity(pkg.Size.Archive), toReadableQuantity(pkg.Size.Package)); err != nil {
26+
return fmt.Errorf("failed to write entry: %v", err)
27+
}
28+
}
29+
if _, err := fmt.Fprintln(tabWriter, "Ignoring:\t\t\t"); err != nil {
30+
return fmt.Errorf("failed to write header: %v", err)
31+
}
32+
for _, pkg := range forceIgnored {
33+
if _, err := fmt.Fprintf(tabWriter, " %v\t%v\t%s\t%s\n", pkg.Name, pkg.Version.String(), toReadableQuantity(pkg.Size.Archive), toReadableQuantity(pkg.Size.Package)); err != nil {
34+
return fmt.Errorf("failed to write entry: %v", err)
35+
}
36+
}
37+
if _, err := fmt.Fprintln(tabWriter, "\t\t\t\nTransaction Summary:\t\t\t"); err != nil {
38+
return fmt.Errorf("failed to write header: %v", err)
39+
}
40+
if _, err := fmt.Fprintf(tabWriter, "Installing %d Packages \t\t\t\n", len(installed)); err != nil {
41+
return fmt.Errorf("failed to write header: %v", err)
42+
}
43+
if _, err := fmt.Fprintf(tabWriter, "Total download size: %s\t\t\t\n", toReadableQuantity(totalDownloadSize)); err != nil {
44+
return fmt.Errorf("failed to write header: %v", err)
45+
}
46+
if _, err := fmt.Fprintf(tabWriter, "Total install size: %s\t\t\t\n", toReadableQuantity(totalInstallSize)); err != nil {
47+
return fmt.Errorf("failed to write header: %v", err)
48+
}
49+
if err := tabWriter.Flush(); err != nil {
50+
return fmt.Errorf("failed to flush table: %v", err)
51+
}
52+
return nil
53+
}
54+
55+
func toReadableQuantity(bytes int) string {
56+
if bytes > 1000*1000*1000 {
57+
q := float64(bytes) / 1000 / 1000 / 1000
58+
return fmt.Sprintf("%.2f G", q)
59+
} else if bytes > 1000*1000 {
60+
q := float64(bytes) / 1000 / 1000
61+
return fmt.Sprintf("%.2f M", q)
62+
} else if bytes > 1000 {
63+
q := float64(bytes) / 1000
64+
return fmt.Sprintf("%.2f K", q)
65+
} else {
66+
return fmt.Sprintf("%d", bytes)
67+
}
68+
}

pkg/api/api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,9 @@ type Package struct {
233233
} `xml:"time"`
234234
Size struct {
235235
Text string `xml:",chardata"`
236-
Package string `xml:"package,attr"`
237-
Installed string `xml:"installed,attr"`
238-
Archive string `xml:"archive,attr"`
236+
Package int `xml:"package,attr"`
237+
Installed int `xml:"installed,attr"`
238+
Archive int `xml:"archive,attr"`
239239
} `xml:"size"`
240240
Location Location `xml:"location"`
241241
Format struct {

pkg/sat/sat.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ type Resolver struct {
7474

7575
bestPackages map[string]*api.Package
7676

77-
ands []bf.Formula
78-
unresolvable []unresolvable
79-
nobest bool
77+
ands []bf.Formula
78+
unresolvable []unresolvable
79+
forceIgnoreWithDependencies map[string]*api.Package
80+
nobest bool
8081
}
8182

8283
type unresolvable struct {
@@ -87,13 +88,14 @@ type unresolvable struct {
8788

8889
func NewResolver(nobest bool) *Resolver {
8990
return &Resolver{
90-
varsCount: 0,
91-
provides: map[string][]*Var{},
92-
packages: map[string][]*Var{},
93-
vars: map[string]*Var{},
94-
pkgProvides: map[VarContext][]*Var{},
95-
nobest: nobest,
96-
bestPackages: map[string]*api.Package{},
91+
varsCount: 0,
92+
provides: map[string][]*Var{},
93+
packages: map[string][]*Var{},
94+
vars: map[string]*Var{},
95+
pkgProvides: map[VarContext][]*Var{},
96+
nobest: nobest,
97+
bestPackages: map[string]*api.Package{},
98+
forceIgnoreWithDependencies: map[string]*api.Package{},
9799
}
98100
}
99101

@@ -102,14 +104,30 @@ func (r *Resolver) ticket() string {
102104
return "x" + strconv.Itoa(r.varsCount)
103105
}
104106

105-
func (r *Resolver) LoadInvolvedPackages(packages []*api.Package) error {
106-
// Deduplicate entries
107+
//LoadInvolvedPackages takes a list of all involved packages to install, as well as a list of regular
108+
// expressions which denoe packages which should be taken into account for solving the problem, but they
109+
// should then be ignored together with their requirements in the provided list of installed packages.
110+
func (r *Resolver) LoadInvolvedPackages(packages []*api.Package, ignoreRegex []string) error {
111+
// Deduplicate and detect excludes
107112
deduplicated := map[string]*api.Package{}
108113
for i, pkg := range packages {
109114
if _, exists := deduplicated[pkg.String()]; exists {
110115
logrus.Infof("Removing duplicate of %v.", pkg.String())
111116
}
112-
deduplicated[pkg.String()] = packages[i]
117+
fullName := pkg.String()
118+
if _, exists := deduplicated[fullName]; !exists {
119+
for _, rex := range ignoreRegex {
120+
if match, err := regexp.MatchString(rex, fullName); err != nil {
121+
return fmt.Errorf("failed to match package with regex '%v': %v", rex, err)
122+
} else if match {
123+
packages[i].Format.Requires.Entries = nil
124+
logrus.Warnf("Package %v is forcefully ignored by regex '%v'.", pkg.String(), rex)
125+
r.forceIgnoreWithDependencies[pkg.String()] = packages[i]
126+
break
127+
}
128+
}
129+
deduplicated[pkg.String()] = packages[i]
130+
}
113131
}
114132
packages = nil
115133
for k, _ := range deduplicated {
@@ -182,7 +200,7 @@ func (r *Resolver) ConstructRequirements(packages []string) error {
182200
return nil
183201
}
184202

185-
func (res *Resolver) Resolve() (install []*api.Package, excluded []*api.Package, err error) {
203+
func (res *Resolver) Resolve() (install []*api.Package, excluded []*api.Package, forceIgnoredWithDependencies []*api.Package, err error) {
186204
logrus.WithField("bf", bf.And(res.ands...)).Debug("Formula to solve")
187205

188206
satReader, satWriter := io.Pipe()
@@ -253,13 +271,13 @@ func (res *Resolver) Resolve() (install []*api.Package, excluded []*api.Package,
253271
logrus.Info("Loading the Partial weighted MAXSAT problem.")
254272
s, err := maxsat.ParseWCNF(pwMaxSatReader)
255273
if err != nil {
256-
return nil, nil, err
274+
return nil, nil, nil, err
257275
}
258276
if err := <-satErrChan; err != nil {
259-
return nil, nil, err
277+
return nil, nil, nil, err
260278
}
261279
if err := <-pwMaxSatErrChan; err != nil {
262-
return nil, nil, err
280+
return nil, nil, nil, err
263281
}
264282
satVars := <-varsChan
265283

@@ -270,12 +288,17 @@ func (res *Resolver) Resolve() (install []*api.Package, excluded []*api.Package,
270288
logrus.Infof("Solution with weight %v found.", solution.Weight)
271289
installMap := map[VarContext]*api.Package{}
272290
excludedMap := map[VarContext]*api.Package{}
291+
forceIgnoreMap := map[VarContext]*api.Package{}
273292
for k, v := range solution.Model {
274293
// Offset of `1`. The model index starts with 0, but the variable sequence starts with 1, since 0 is not allowed
275294
resVar := res.vars[satVars.satToPkg[strconv.Itoa(k+1)]]
276295
if resVar != nil && resVar.varType == VarTypePackage {
277296
if v {
278-
installMap[resVar.Context] = resVar.Package
297+
if _, exists := res.forceIgnoreWithDependencies[resVar.Package.String()]; !exists {
298+
installMap[resVar.Context] = resVar.Package
299+
} else {
300+
forceIgnoreMap[resVar.Context] = resVar.Package
301+
}
279302
} else {
280303
excludedMap[resVar.Context] = resVar.Package
281304
}
@@ -291,10 +314,13 @@ func (res *Resolver) Resolve() (install []*api.Package, excluded []*api.Package,
291314
for _, v := range excludedMap {
292315
excluded = append(excluded, v)
293316
}
294-
return install, excluded, nil
317+
for _, v := range forceIgnoreMap {
318+
forceIgnoredWithDependencies = append(forceIgnoredWithDependencies, v)
319+
}
320+
return install, excluded, forceIgnoredWithDependencies, nil
295321
}
296322
logrus.Info("No solution found.")
297-
return nil, nil, fmt.Errorf("no solution found")
323+
return nil, nil, nil, fmt.Errorf("no solution found")
298324
}
299325

300326
func (res *Resolver) MUS() (mus *explain.Problem, err error) {

pkg/sat/sat_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ func TestRecursive(t *testing.T) {
2727
for i, _ := range repo.Packages {
2828
packages = append(packages, &repo.Packages[i])
2929
}
30-
err = resolver.LoadInvolvedPackages(packages)
30+
err = resolver.LoadInvolvedPackages(packages, nil)
3131
g.Expect(err).ToNot(HaveOccurred())
3232
err = resolver.ConstructRequirements([]string{pkg.Name})
3333
g.Expect(err).ToNot(HaveOccurred())
34-
_, _, err := resolver.Resolve()
34+
_, _, _, err := resolver.Resolve()
3535
if err != nil {
3636
t.Fatalf("Failed to solve %s\n", pkg.Name)
3737
}
@@ -1229,11 +1229,11 @@ func Test(t *testing.T) {
12291229
for i, _ := range repo.Packages {
12301230
packages = append(packages, &repo.Packages[i])
12311231
}
1232-
err = resolver.LoadInvolvedPackages(packages)
1232+
err = resolver.LoadInvolvedPackages(packages, nil)
12331233
g.Expect(err).ToNot(HaveOccurred())
12341234
err = resolver.ConstructRequirements(tt.requires)
12351235
g.Expect(err).ToNot(HaveOccurred())
1236-
install, _, err := resolver.Resolve()
1236+
install, _, _, err := resolver.Resolve()
12371237
g.Expect(err).ToNot(HaveOccurred())
12381238
g.Expect(pkgToString(install)).To(ConsistOf(tt.installs))
12391239
})
@@ -1359,7 +1359,7 @@ func TestNewResolver(t *testing.T) {
13591359
}
13601360
t.Run(tt.name, func(t *testing.T) {
13611361
resolver := NewResolver(tt.nobest)
1362-
err := resolver.LoadInvolvedPackages(tt.packages)
1362+
err := resolver.LoadInvolvedPackages(tt.packages, nil)
13631363
if err != nil {
13641364
t.Fail()
13651365
}
@@ -1368,7 +1368,7 @@ func TestNewResolver(t *testing.T) {
13681368
fmt.Println(err)
13691369
t.Fail()
13701370
}
1371-
install, exclude, err := resolver.Resolve()
1371+
install, exclude, _, err := resolver.Resolve()
13721372
g := NewGomegaWithT(t)
13731373
if tt.solvable {
13741374
g.Expect(err).ToNot(HaveOccurred())

0 commit comments

Comments
 (0)