Skip to content

Commit c2d3548

Browse files
committed
dlopen-notes: add mode to generate rpm Provides/Requires
This can be used to insert the appropriate deps into the spec file: $ ./dlopen-notes.py .../libsystemd-shared-256.so --rpm-requires zstd,qrencode --rpm-recommends tpm Requires: libqrencode.so.4()(64bit) Requires: libzstd.so.1()(64bit) Recommends: libtss2-esys.so.0()(64bit) Recommends: libtss2-rc.so.0()(64bit) Recommends: libtss2-mu.so.0()(64bit) Most likely, it needs to be used manually. Rpm doesn't really have a notion of dynamically generated deps at the package level :( (There is the fileattr mechanism, https://rpm-software-management.github.io/rpm/manual/dependency_generators.html, but that is per-file. It has the shortcoming that it assumes that the generated deps depend only on the file itself, and not the subpackage that the file lands in, and there is no notion of package configuration. We _could_ use that, but it seems a poor fit. Also, before rpm 4.20, fileattr generators could not be provided by the package being built, they needed to be installed in the filesystem. I don't think we're at the point where it makes sense to make this "global". 4.15 added %generate_buildrequires. Obviously that only works for BuildRequires. Also, the mechanism with generating src.rpms in a loop is rather convoluted and heavyweight.) I'll open an RFE in rpm. For now, the generator can be used manually in systemd. That is still nicer than the previous method of looking at the sources.
1 parent 2aa0091 commit c2d3548

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

dlopen-notes.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ def filter_features(features, filter):
131131
sys.exit('Some features not found:', ', '.join(missing))
132132
return ans
133133

134+
@listify
135+
def generate_rpm(elffiles, stanza, filter):
136+
# Produces output like:
137+
# Requires: libqrencode.so.4()(64bit)
138+
# Requires: libzstd.so.1()(64bit)
139+
for elffile in elffiles:
140+
suffix = '()(64bit)' if elffile.elffile.elfclass == 64 else ''
141+
for note in elffile.notes():
142+
if note['feature'] in filter or not filter:
143+
soname = next(iter(note['soname'])) # we take the first — most recommended — soname
144+
yield f"{stanza}: {soname}{suffix}"
145+
134146
def make_parser():
135147
p = argparse.ArgumentParser(
136148
description=__doc__,
@@ -157,6 +169,24 @@ def make_parser():
157169
metavar='FEATURE1,FEATURE2',
158170
help='Describe features, can be specified multiple times',
159171
)
172+
p.add_argument(
173+
'--rpm-requires',
174+
nargs='?',
175+
const=[],
176+
type=lambda s: s.split(','),
177+
action='extend',
178+
metavar='FEATURE1,FEATURE2',
179+
help='Generate rpm Requires for listed features',
180+
)
181+
p.add_argument(
182+
'--rpm-recommends',
183+
nargs='?',
184+
const=[],
185+
type=lambda s: s.split(','),
186+
action='extend',
187+
metavar='FEATURE1,FEATURE2',
188+
help='Generate rpm Recommends for listed features',
189+
)
160190
p.add_argument(
161191
'filenames',
162192
nargs='+',
@@ -173,7 +203,11 @@ def make_parser():
173203
def parse_args():
174204
args = make_parser().parse_args()
175205

176-
if not args.raw and args.features is None and not args.sonames:
206+
if (not args.raw
207+
and not args.sonames
208+
and args.features is None
209+
and args.rpm_requires is None
210+
and args.rpm_recommends is None):
177211
# Make --raw the default if no action is specified.
178212
args.raw = True
179213

@@ -196,6 +230,14 @@ def parse_args():
196230
indent=2,
197231
default=lambda prio: prio.name))
198232

233+
if args.rpm_requires is not None:
234+
lines = generate_rpm(elffiles, 'Requires', args.rpm_requires)
235+
print('\n'.join(lines))
236+
237+
if args.rpm_recommends is not None:
238+
lines = generate_rpm(elffiles, 'Recommends', args.rpm_recommends)
239+
print('\n'.join(lines))
240+
199241
if args.sonames:
200242
sonames = group_by_soname(elffiles)
201243
for soname in sorted(sonames.keys()):

test/test.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# SPDX-License-Identifier: CC0-1.0
22

3-
from _notes import ELFFileReader, group_by_soname
3+
from _notes import ELFFileReader, group_by_soname, generate_rpm
44

5-
def test_notes():
5+
def test_sonames():
66
expected = {
77
'libfido2.so.1': 'required',
88
'liblz4.so.1': 'recommended',
@@ -11,5 +11,25 @@ def test_notes():
1111
'libtss2-esys.so.0': 'recommended',
1212
'libtss2-mu.so.0': 'recommended',
1313
}
14-
notes = [ELFFileReader('notes')]
15-
assert group_by_soname(notes) == expected
14+
notes = ELFFileReader('notes')
15+
assert group_by_soname([notes]) == expected
16+
17+
def test_requires():
18+
notes = ELFFileReader('notes')
19+
20+
expected = {
21+
32: [
22+
'Suggests: libpcre2-8.so.0',
23+
'Suggests: libtss2-mu.so.0',
24+
'Suggests: libtss2-esys.so.0',
25+
],
26+
64: [
27+
'Suggests: libpcre2-8.so.0()(64bit)',
28+
'Suggests: libtss2-mu.so.0()(64bit)',
29+
'Suggests: libtss2-esys.so.0()(64bit)',
30+
],
31+
}
32+
33+
lines = generate_rpm([notes], 'Suggests', ('pcre2', 'tpm'))
34+
expect = expected[notes.elffile.elfclass]
35+
assert lines == expect

0 commit comments

Comments
 (0)