#!/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: '{print $1}' | sed "s:[' ]::g") version=$(echo $1 | awk -F: '{print $2}' | sed "s:[' ]::g") } 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 { 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 # 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 pcregrep -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 pcregrep -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=($(pcregrep -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=$(pcregrep -M "pkg.require\\(([^;])*" $file | grep -o "'.*': '.*'") 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'; # . 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" ;; *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 } x64bitarch="x86_64 ppc64 ppc64le s390x ia64 aarch64 riscv64" 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