Skip to content

Commit c30481b

Browse files
committed
Add lockfile command to bazeldnf
In order to allow for easy generation of the lockfile to use with the rpmtree tags in the bazel module extension we need to add an additional command for this purpose. The `lockfile` subcommand is derived from a combination the `rpmtree` subcommand in conjunction with some work from Manuel Naranjo on a `bzlmod` subcommand.
1 parent 0523988 commit c30481b

File tree

8 files changed

+655
-9
lines changed

8 files changed

+655
-9
lines changed

cmd/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
load("@rules_go//go:def.bzl", "go_binary", "go_library")
1+
load("@rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
22
load("//bazeldnf:toolchain.bzl", "bazeldnf_toolchain")
33

44
go_library(
55
name = "cmd_lib",
66
srcs = [
77
"bazeldnf.go",
8+
"config_helper.go",
89
"fetch.go",
910
"filter.go",
1011
"init.go",
1112
"ldd.go",
13+
"lockfile.go",
1214
"prune.go",
1315
"reduce.go",
1416
"resolve.go",
@@ -40,6 +42,7 @@ go_library(
4042
"@com_github_sirupsen_logrus//:logrus",
4143
"@com_github_spf13_cobra//:cobra",
4244
"@org_golang_x_crypto//openpgp",
45+
"@org_golang_x_exp//maps",
4346
],
4447
)
4548

@@ -60,3 +63,14 @@ toolchain(
6063
toolchain = ":host-toolchain",
6164
toolchain_type = "//bazeldnf:toolchain_type",
6265
)
66+
67+
go_test(
68+
name = "cmd_test",
69+
srcs = ["config_helper_test.go"],
70+
embed = [":cmd_lib"],
71+
deps = [
72+
"//pkg/api",
73+
"//pkg/api/bazeldnf",
74+
"@com_github_onsi_gomega//:gomega",
75+
],
76+
)

cmd/config_helper.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package main
2+
3+
import (
4+
"cmp"
5+
"fmt"
6+
"slices"
7+
8+
"github.com/rmohr/bazeldnf/pkg/api"
9+
"github.com/rmohr/bazeldnf/pkg/api/bazeldnf"
10+
"github.com/sirupsen/logrus"
11+
"golang.org/x/exp/maps"
12+
)
13+
14+
func keys[K cmp.Ordered, V any](m map[K]V) []K {
15+
keys := maps.Keys(m)
16+
slices.Sort(keys)
17+
return keys
18+
}
19+
20+
func toConfig(install, forceIgnored []*api.Package, targets []string, cmdline []string) (*bazeldnf.Config, error) {
21+
ignored := make(map[string]bool)
22+
for _, forceIgnoredPackage := range forceIgnored {
23+
ignored[forceIgnoredPackage.Name] = true
24+
}
25+
26+
allPackages := make(map[string]*bazeldnf.RPM)
27+
repositories := make(map[string][]string)
28+
for _, installPackage := range install {
29+
repositories[installPackage.Repository.Name] = installPackage.Repository.Mirrors
30+
31+
deps := make([]string, 0, len(installPackage.Format.Requires.Entries))
32+
for _, entry := range installPackage.Format.Requires.Entries {
33+
deps = append(deps, entry.Name)
34+
}
35+
36+
slices.Sort(deps)
37+
38+
allPackages[installPackage.Name] = &bazeldnf.RPM{
39+
Name: installPackage.Name,
40+
SHA256: installPackage.Checksum.Text,
41+
URLs: []string{installPackage.Location.Href},
42+
Repository: installPackage.Repository.Name,
43+
Dependencies: deps,
44+
}
45+
}
46+
47+
providers := collectProviders(forceIgnored, install)
48+
packageNames := keys(allPackages)
49+
sortedPackages := make([]*bazeldnf.RPM, 0, len(packageNames))
50+
for _, name := range packageNames {
51+
pkg := allPackages[name]
52+
deps, err := collectDependencies(name, pkg.Dependencies, providers, ignored)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
pkg.SetDependencies(deps)
58+
59+
sortedPackages = append(sortedPackages, pkg)
60+
}
61+
62+
lockFile := bazeldnf.Config{
63+
CommandLineArguments: cmdline,
64+
ForceIgnored: keys(ignored),
65+
RPMs: sortedPackages,
66+
Repositories: repositories,
67+
Targets: targets,
68+
}
69+
70+
return &lockFile, nil
71+
}
72+
73+
func collectProviders(pkgSets ...[]*api.Package) map[string]string {
74+
providers := map[string]string{}
75+
for _, pkgSet := range pkgSets {
76+
for _, pkg := range pkgSet {
77+
for _, entry := range pkg.Format.Provides.Entries {
78+
providers[entry.Name] = pkg.Name
79+
}
80+
81+
for _, entry := range pkg.Format.Files {
82+
providers[entry.Text] = pkg.Name
83+
}
84+
}
85+
}
86+
87+
return providers
88+
}
89+
90+
func collectDependencies(pkg string, requires []string, providers map[string]string, ignored map[string]bool) ([]string, error) {
91+
depSet := make(map[string]bool)
92+
for _, req := range requires {
93+
if ignored[req] {
94+
logrus.Debugf("Ignoring dependency %s", req)
95+
continue
96+
}
97+
logrus.Debugf("Resolving dependency %s", req)
98+
provider, ok := providers[req]
99+
if !ok {
100+
return nil, fmt.Errorf("could not find provider for %s", req)
101+
}
102+
logrus.Debugf("Found provider %s for %s", provider, req)
103+
if ignored[provider] {
104+
logrus.Debugf("Ignoring provider %s for %s", provider, req)
105+
continue
106+
}
107+
depSet[provider] = true
108+
}
109+
110+
deps := keys(depSet)
111+
112+
found := map[string]bool{pkg: true}
113+
114+
// RPMs may have circular dependencies, even depend on themselves.
115+
// we need to ignore such dependencies
116+
nonCyclicDeps := make([]string, 0, len(deps))
117+
for _, dep := range deps {
118+
if found[dep] {
119+
continue
120+
}
121+
122+
nonCyclicDeps = append(nonCyclicDeps, dep)
123+
}
124+
125+
return nonCyclicDeps, nil
126+
}

0 commit comments

Comments
 (0)