# Quote the given arguement # VGL_quote() { printf '%s' "${*@Q}" } # Stream producers # # These produce a stream of escaped new-line terminated items # # Stream the given arguments # VGL_sourceS() { (( $# == 0 )) || printf '%q\n' "$@" } # Stream the results of find (correctly escapes all filenames) # VGL_findS() { declare options=("$@") file find "${options[@]}" -print0 | while read -d '' -r file; do VGL_sourceS "$file" done } # Stream the list of directly required libraries from an elf file # VGL_elfLibsS() { patchelf --print-needed "$1" } # Stream the resolved libraries from an elf file # VGL_elfLibsResolvedS() { declare file=$1 line ldd "$file" | while read line; do line=${line#$'\t'} case "$line" in *' => not found') line=${line% => not found} ;; *' => '*' ('*')') line=${line#* => } line=${line% (*)} ;; *' ('*')') line=${line% (*)} ;; esac VGL_sourceS "$line" done } # Stream the list of rpaths from an elf file # VGL_elfRPathS() { declare file=$1 entry patchelf --print-rpath "$file" | while read -d ':' -r entry; do [[ -z $entry ]] || VGL_sourceS "$entry" done [[ -z $entry ]] || VGL_sourceS "$entry" } # Stream transformers # # Take in a set of options and transform to a stream of new-line terminated items # # Remove all items for which the given command returns false # VGL_testKeepS() { declare command=$1 next while read next; do set -- "$next" if (eval "$command"); then VGL_sourceS "$next" fi done } # Remove all items which match one of the given values # VGL_valuesRemoveS() { declare -A values declare value for value in "$@"; do values[$value]= done while read value; do [[ ${values[$value]+yes} ]] \ || VGL_sourceS "$value" done } # Add addition to end unless test succeeds on at least one # VGL_testSuffixNoneS() { declare command=$1 addition=$2 eval "declare items=($(cat))" VGL_sourceS "${items[@]}" VGL_sourceS "${items[@]}" | VGL_isTestAnyS "$command" \ || VGL_sourceS "$addition" } # Stream consumers # # These consume a stream of escaped new-line terminated items (termination is required for processing) # # Return true iff given command returns true for some item # VGL_isTestAnyS() { declare command="$1" next while read next; do set -- "$next" if (eval "$command"); then return 0 fi done return 1 } # Return true iff given command returns true for all item # VGL_isTestAllS() { declare command="$1" next while read next; do set -- "$next" if ! (eval "$command"); then return 1 fi done return 0 } # Tests for the filter transformer # # Check for an elf file (binary or library) # VGL_isElfBin() { declare file=$1 patchelf --print-interpreter "$file" &>/dev/null } # Check for libGL # VGL_isLibGL() { declare file=$1 case "$file" in libGL.so* | */libGL.so*) return 0 ;; *) return 1 ;; esac } # Update elf files # # Run given stream element on rpath stream and write result to file if differs from original # VGL_elfFilterRPathS() { declare file=$1 command=$2 eval "declare pathsOld=($(VGL_elfRPathS "$file"))" eval "declare pathsNew=($(VGL_sourceS "${pathsOld[@]}" | eval "$command"))" [[ ${pathsNew[@]@Q} = ${pathsOld[@]@Q} ]] \ || if (( ${#pathsNew[@]} > 0 )); then ( IFS=:; patchelf --set-rpath "${pathsNew[*]}" "$file" ) else ( patchelf --remove-rpath "$file" ) fi } # Run given stream element on directly required libraries stream and write result to file if differs from original # VGL_elfFilterLibsS() { declare file=$1 command=$2 eval "declare libsOld=($(VGL_elfLibsS "$file"))" eval "declare libsNew=($(VGL_sourceS "${libsOld[@]}" | eval "$command"))" eval "declare libs=($(VGL_sourceS "${libsOld[@]}" | VGL_valuesRemoveS "${libsNew[@]}"))" (( ${#libs[@]} < 1 )) \ || ( eval "patchelf$(printf ' --remove-needed %q' "${libs[@]}") ${file@Q}") eval "declare libs=($(VGL_sourceS "${libsNew[@]}" | VGL_valuesRemoveS "${libsOld[@]}"))" (( ${#libs[@]} < 1 )) \ || ( eval "patchelf$(printf ' --add-needed %q' "${libs[@]}") ${file@Q}") } # Patch elf file to be able to use OpenGL properly (not a stream function so can be called in normal way) # VGL_patch() { declare files=("$@") file for file in "${files[@]}"; do # glibc 2.3 ld.so supports a --preload option that would make this just a simple wrapper # # mv "$file" "${file%/*}/.${file##*/}.vgl" # cat > "$file" <&2 cp -a "$file" "${file%/*}/.${file##*/}.vgl-yes" VGL_elfFilterLibsS "${file%/*}/.${file##*/}.vgl-yes" 'VGL_testSuffixNoneS '"$(VGL_quote '[[ $1 =~ ^(.*/)?libvglfaker.so ]]')"' @virtualglLib@' # Non-VGL is fine as we link it against mesa_glxgallium mv "$file" "${file%/*}/.${file##*/}.vgl-no" # Replace with chooser script based on whether VGL_DISPLAY is set or not cat > "$file" <&2 for output in $outputs; do VGL_findS "${!output}" -type f \ | VGL_testKeepS 'VGL_isElfBin "$1" && ! [[ ${1##*/} == .*.vgl-* ]] && ! [[ ${1##*/} == ?*.so ]]' \ | VGL_testKeepS 'VGL_elfLibsResolvedS "$1" | VGL_isTestAnyS '"$(VGL_quote 'VGL_isLibGL "$1"')" \ | VGL_isTestAllS 'VGL_patch "$1"' done eval "$opts" } if (( hostOffset == 0 )); then fixupOutputHooks+=(VGL_autoAddVGL) fi