-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathdependencies.py
More file actions
executable file
·191 lines (162 loc) · 6.62 KB
/
dependencies.py
File metadata and controls
executable file
·191 lines (162 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/env python3
# Parse Cargo.toml files for each plugin to collect their external dependencies.
# Meson will lookup those dependencies using pkg-config to be able to link
# static Rust plugins into gst-full.
from argparse import ArgumentParser
from pathlib import Path
import sys
try:
# Python11 stdlib
import tomllib
except ImportError:
import tomli as tomllib
PARSER = ArgumentParser()
PARSER.add_argument('src_dir', type=Path)
PARSER.add_argument('plugins', nargs='*')
PARSER.add_argument('--features', action="store_true", help="Get list of features to activate")
PARSER.add_argument('--gst-version', help="GStreamer version used")
PARSER.add_argument('--feature-deps', action="store_true", help="Get list of feature dependencies per plugin")
# Map plugin name to directory name, for those that does not match.
RENAMES = {
'rsanalytics': 'analytics',
'rsaudiofx': 'audiofx',
'rsaudioparsers': 'audioparsers',
'rsfile': 'file',
'rsflv': 'flavors',
'rsrtp': 'rtp',
'rsrtsp': 'rtsp',
'rsudp': 'udp',
'rswebp': 'webp',
'rsonvif': 'onvif',
'rstracers': 'tracers',
'rsvalidate': 'validate',
'rsclosedcaption': 'closedcaption',
'rswebrtc': 'webrtc',
'rspng': 'png',
'rsvideofx': 'videofx',
'rsinter': 'inter',
'textahead': 'ahead',
'textwrap': 'wrap',
'textaccumulate': 'accumulate',
}
class CargoAnalyzer:
def __init__(self, src_dir=None):
self.src_dir = src_dir
self.plugins = None
self.features = False
self.gst_version = "1.18"
self.feature_deps = False
self.crates = None
def load_crates(self):
with (self.src_dir / 'Cargo.toml').open('rb') as f:
self.crates = tomllib.load(f)['workspace']['members']
def get_feature_deps(self):
res = dict()
for crate in self.crates:
crate_path = opts.src_dir / crate / 'Cargo.toml'
with crate_path.open('rb') as f:
data = tomllib.load(f)
if 'lib' not in data or 'dependencies' not in data:
continue
name = data['lib']['name']
deps = set()
for dep_name, dep_info in data['dependencies'].items():
if not dep_name.startswith('gst') and 'workspace' in dep_info and dep_info['workspace']:
deps.add(dep_name)
if (len(deps)):
res[name] = deps
return res
def extract_version(self, feature_name):
if feature_name.startswith('v') and not feature_name == 'vulkan':
verindex = 1
elif feature_name.startswith('gst') and len(feature_name) > 3 and feature_name[3].isdigit():
verindex = 3
else:
return None
parts = feature_name[verindex:].split("_")
if len(parts) != 2:
return None
try:
return (int(parts[0]), int(parts[1]))
except ValueError:
return None
def extract_features(self, cargo_data):
features = cargo_data['features']
print(f'Available features: {features!r}', file=sys.stderr)
wanted_features = set()
gst_version_major = int(self.gst_version.split('.')[0])
gst_version_minor = int(self.gst_version.split('.')[1])
if gst_version_minor % 2:
gst_version_minor += 1
for (name, value) in features.items():
version = self.extract_version(name)
if version is None:
continue
(majver, minver) = version
if gst_version_major < majver or gst_version_minor < minver:
continue
wanted_features |= set([name])
wanted_features |= set(value)
if name.startswith("gst"):
# Required for some reason for rswebrtc which has a specific feature
wanted_features |= {f"{cargo_data['package']['name']}/{name}"}
print(f'Enabling features: {wanted_features!r}', file=sys.stderr)
return wanted_features
def get_library_names(self, packages):
"""
Convert package names to library names by parsing Cargo.toml files.
Returns a set of library name variants (with and without 'lib' prefix)
for filtering libraries and pkg-config files.
"""
lib_names = set()
for package in packages:
# Find the crate by matching the package name directly
for crate in self.crates:
crate_path = self.src_dir / crate / 'Cargo.toml'
if not crate_path.exists():
continue
# Parse the Cargo.toml to check package name
with crate_path.open('rb') as f:
data = tomllib.load(f)
# Match against the actual package name in Cargo.toml
if data.get('package', {}).get('name') == package:
if 'lib' in data and 'name' in data['lib']:
lib_name = data['lib']['name']
# Add library name variants for filtering
lib_names.add(f'lib{lib_name}') # e.g., libgstrsvalidate
lib_names.add(lib_name) # e.g., gstrsvalidate (for .pc files)
break
return lib_names
def run(self):
res = set()
for name in opts.plugins:
if name.startswith('gst'):
name = name[3:]
name = RENAMES.get(name, name)
crate_path = None
for crate in self.crates:
if Path(crate).name == name:
crate_path = opts.src_dir / crate / 'Cargo.toml'
assert crate_path
with crate_path.open('rb') as f:
data = tomllib.load(f)
if opts.features:
res |= self.extract_features(data)
else:
try:
requires = data['package']['metadata']['capi']['pkg_config']['requires_private']
except KeyError:
continue
res.update([i.strip().replace('>', "|>").replace('<', "|<").replace("==", "|==") for i in requires.split(',')])
return res
if __name__ == "__main__":
analyzer = CargoAnalyzer()
opts = PARSER.parse_args(namespace=analyzer)
if opts.feature_deps and (opts.features or opts.plugins):
sys.exit('error: --feature-deps must not be used with --features or plugins')
analyzer.load_crates()
if opts.feature_deps:
for name, deps in analyzer.get_feature_deps().items():
print('{}: {}'.format(name, ','.join(deps)))
else:
print(','.join(analyzer.run()))