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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
#!/bin/bash
# Automatically find Provides and Requires for typelib() gobject-introspection bindings.
# can be started with -R (Requires) and -P (Provides)
# Copyright 2011 by Dominique Leuenberger, Amsterdam, Netherlands (dimstar [at] opensuse.org)
# This file is released under the GPLv2 or later.
function split_name_version {
base=$1
tsymbol=${base%-*}
# Sometimes we get a Requires on Gdk.Settings.foo, because you can directly use imports.gi.Gdk.Settings.Foo in Javascript.
# We know that the symbol in this case is called Gdk, so we cut everything after the . away.
symbol=$(echo $tsymbol | awk -F. '{print $1}')
version=${base#*-}
# In case there is no '-' in the filename, then the split above 'fails' and version == symbol (thus: no version specified)
if [ "$tsymbol" = "$version" ]; then
unset version
fi
}
function split_name_version2 {
symbol=$(echo $1 | awk -F: '{sub(/^.*{/, "", $1); print $1}' | sed "s:[' ]::g")
version=$(echo $1 | awk -F: '{print $2}' | sed "s:[' ]::g")
}
# some javascript code imports gi like this (seen since GNOME 43, e.g. GNOME Maps)
# import 'gi://GeocodeGlib?version=2.0'
function split_name_versionjs_gi_name_version {
symbol=$(echo $1 | awk -F? '{print $1}')
version=$(echo $1 | awk -F? '/version=/ {print $2}' | sed 's/version=//')
}
function print_req_prov {
echo -n "typelib($symbol)"
if [ ! -z "$version" ]; then
echo " = ${version}"
else
echo ""
fi
}
function find_provides {
while read file; do
case $file in
*.typelib)
split_name_version $(basename $file | sed 's,.typelib$,,')
print_req_prov
;;
esac
done
}
function gresources_requires {
# GNOME is embedding .js files into ELF binaries for faster startup.
# As a result, we need to extract them and re-run the scanner over the
# embedded files.
# We extract all the gresources embedded in ELF binaries and start
# gi-find-deps.sh recusively over the extracted file list.
tmpdir=$(mktemp -d)
for resource in $($gresourcecmd list "$1" 2>/dev/null); do
mkdir -p $tmpdir/$(dirname $resource)
$gresourcecmd extract "$1" $resource > $tmpdir/$resource
done
find $tmpdir -type f | sort | sh $0 -R
rm -rf "$tmpdir"
}
function python_requires {
for module in $(grep -h -P "^\s*from gi\.repository import (\w+)" $1 | sed -e 's:#.*::' -e 's:raise ImportError.*::' -e 's:.*"from gi.repository import .*".*::' | sed -e 's,from gi.repository import,,' -r -e 's:\s+$::g' -e 's:\s+as\s+\w+::g' -e 's:,: :g'); do
split_name_version $module
print_req_prov
# Temporarly disabled... this is not true if the python code is written for python3... And there seems no real 'way' to identify this.
# echo "python-gobject >= 2.21.4"
done
for module in $(grep -h -P -o ".*(gi\.require_version\(['\"][^'\"]+['\"],\s*['\"][^'\"]+['\"]\))" $1 | sed -e 's:#.*::' -e 's:.*gi.require_version::' -e "s:[()\"' ]::g" -e 's:,:-:'); do
split_name_version $module
print_req_prov
done
# python glue layers (/gi/overrides) import their typelibs slightly different
for module in $(grep -h -P -o "=\s+(get_introspection_module\(['\"][^'\"]+['\"]\))" $1 | sed -e 's:#.*::' -e 's:=.*get_introspection_module::' -e "s:[()\"' ]::g"); do
split_name_version $module
print_req_prov
done
}
function javascript_requires {
# parse the new import style in 3.32
for module in $(grep -r -h -A2 'const {' $1 | paste -s -d ' ' | grep '} = imports.gi;' | sed 's/imports.gi;.*/imports.gi;/' | awk -F '[{}]' '{print $(NF>1?NF-1:"")}' | tr ',' '\n' | tr -d ' ' | awk -F ':' '{print $1}'); do
split_name_version $module
print_req_prov
done
# parse the old import style before 3.32
for module in $(grep -h -P -o "imports\.gi\.([^\s'\";]+)" $1 | grep -v "imports\.gi\.version" | sed -r -e 's,\s+$,,g' -e 's,imports.gi.,,'); do
split_name_version $module
print_req_prov
done
for module in $(grep -h -P -o "imports\.gi\.versions.([^\s'\";]+)\s*=\s*['\"].+['\"]" $1 | \
sed -e 's:imports.gi.versions.::' -e "s:['\"]::g" -e 's:=:-:' -e 's: ::g'); do
split_name_version $module
print_req_prov
done
# some javascript code imports gi like this (seen since GNOME 43, e.g. GNOME Maps)
# import 'gi://GeocodeGlib?version=2.0'
for module in $(grep -h -P -o "['\"]gi://([^'\"]+)" $1 | sed "s|['\"]gi://||"); do
split_name_versionjs_gi_name_version $module
print_req_prov
done
# This is, at the moment, specifically for Polari where a "const { Foo, Bar } = imports.gi;" is used.
for module in $(grep -h -E -o "\{ \w+(: \w+|, \w+)+ \} = imports.gi;" $1 | \
sed -r -e '0,/\w+:\s\w+/ s/:\s\w+//g' -e 's: = imports.gi;:: ; s:\{ :: ; s: \}:: ; s/,//g'); do
split_name_version $module
print_req_prov
done
# Remember files which contain a pkg.require() call
if pcre2grep -M "pkg.require\\(([^;])*" $1 > /dev/null; then
# the file contains a pkg.require(..) list... let's remember th is file for the in-depth scanner
if [ -n "$jspkg" ]; then
jspkg=$1:${jspkg}
else
jspkg=$1
fi
fi
# remember files which contain exlucde filters used against pkg.require()
if pcre2grep -M "const RECOGNIZED_MODULE_NAMES =([^;])*" $1 > /dev/null; then
# the file contains RECOGNIZED_MODULE_NAMES list. We remember the file name for the follow up filtering
if [ -n "$jspkgfilt" ]; then
jspkgfilt=$1:${jspkgfilt}
else
jspkgfilt=$1
fi
fi
}
function javascript_pkg_filter {
# For now this is a dummy function based on gnome-weather information
#for file in $jspkgfilt; do
# FILTER=($(pcre2grep -M "const RECOGNIZED_MODULE_NAMES =([^;])*" $file | grep -o "'.*'" | sed "s:'::g"))
#done
FILTER=('Lang' 'Mainloop' 'Signals' 'System' 'Params')
}
function javascript_pkg_requires {
# javascript files were found which specify pkg.require('..': '..'[,'..': '']); list
# This is used in some apps in order to have a 'centralized' point to specify all package dependencies.
# once we reach this function, we already know which file(s) contain the pkg.require(..) list.
oldIFS=$IFS
IFS=:
for file in "$jspkg"; do
IFS=$'\n'
PKGS=$(pcre2grep -M "pkg.require\\(([^;])*" $file | grep -o -E "'?.*'?: '.*'")
for pkg in $PKGS; do
split_name_version2 $pkg
found=0
for (( i=0 ; i<${#FILTER[@]} ; i++ )); do
if [ "$symbol" = "${FILTER[$i]}" ]; then
found=1
fi
done
if [ $found -eq 0 ]; then
print_req_prov
fi
done
IFS=:
done
IFS=$oldIFS
}
function typelib_requires {
split_name_version $(basename $1 | sed 's,.typelib$,,')
oldIFS=$IFS
IFS=$'\n'
for req in $(g-ir-inspect --print-shlibs --print-typelibs $symbol --version $version); do
case $req in
typelib:*)
module=${req#typelib: }
split_name_version $module
print_req_prov
;;
shlib:*)
echo "${req#shlib: }${shlib_64}"
;;
esac
done
IFS=$oldIFS
}
function find_requires {
# Currently, we detect:
# - in python:
# . from gi.repository import foo [Unversioned requirement of 'foo']
# . from gi.repository import foo-1.0 [versioned requirement]
# . gi.require_version('Gtk', '3.0') (To specify a version.. there is still an import needed)
# . And we do not stumble over:
# from gi.repository import foo as _bar
# from gi.repository import foo, bar
# - in JS:
# . imports.gi.foo; [unversioned requirement of 'foo']
# . imports.gi.foo-1.0; [versioned requirement of 'foo']
# . imports.gi.versions.Gtk = '3.0';
# . const { foo, bar } = imports.gi;
# . The imports can be listed on one line, and we catch them.
while read file; do
case $file in
*.js)
javascript_requires "$file"
;;
*.py)
python_requires "$file"
;;
*.typelib)
typelib_requires "$file"
;;
*.gresource)
gresources_requires "$file"
;;
*)
case $(file -b $file) in
*[Pp]ython*script*)
python_requires "$file"
;;
*JavaScript*source*)
javascript_requires "$file"
;;
*ELF*)
gresources_requires "$file"
;;
esac
;;
esac
done
# The pkg filter is a place holder. This should read the filter from the javascript files.
#if [ -n "$jspkgfilt" ]; then
javascript_pkg_filter
#fi
# in case the javascript parser above detected files which specify pkg.require, we enter the more in-depth scanning scheme for those files.
if [ -n "$jspkg" ]; then
javascript_pkg_requires
fi
}
function inList() {
for word in $1; do
[[ "$word" = "$2" ]] && return 0
done
return 1
}
# Confer with /usr/lib/rpm/platforms
x64bitarch="aarch64 mips64 mips64el mips64r6 mips64r6el loongarch64 sw64 ppc64 ppc64le riscv64 s390x sparc64 x86_64"
for path in \
$(for tlpath in \
$(find ${RPM_BUILD_ROOT}/usr/lib64 ${RPM_BUILD_ROOT}/usr/lib /usr/lib64 /usr/lib -name '*.typelib' 2>/dev/null); do
dirname $tlpath; done | sort --unique ); do
export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:$path
done
if which gresource >/dev/null 2>&1; then
gresourcecmd=$(which gresource 2>/dev/null)
else
grsourcecmd="false"
fi
if inList "$x64bitarch" "${HOSTTYPE}"; then
shlib_64="()(64bit)"
fi
case $1 in
-P)
find_provides
;;
-R)
find_requires
;;
esac
|