Files
ccpkgs/pkgs/opengl/insert-virtualgl.sh
Tyson Whitehead dcea1444c3 opengl: VirtualGL fixup require basename in library filter
Prevents triggering on valid files like .solvespace-wrapped.
2021-11-24 18:03:38 -05:00

264 lines
6.3 KiB
Bash
Executable File

# 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" <<EOF
#! @bash@
# exec -a "\$0" "$(patchelf --print-interpreter "${file%/*}/.${file##*/}.vgl")" \${VGL_DISPLAY:+--preload "@virtualglLib@"} "${file%/*}/.${file##*/}.vgl" "\$@"
# EOF
# chmod +x "$file"
# VGL version may need fixing up to ensure libglfaker.so gets loaded at the start
printf 'Wrapping and patching for VirtualGL: %q\n' "$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" <<EOF
#! @bash@
if [ -z "\$VGL_DISPLAY" ]; then
exec -a "\$0" "${file%/*}/.${file##*/}.vgl-no" "\$@"
else
exec -a "\$0" "${file%/*}/.${file##*/}.vgl-yes" "\$@"
fi
EOF
chmod +x "$file"
done
}
# Setup hook
#
# Find all executables that depend on libGL and replace with a wrapper script that either executes the original
# executable or a copy with an added libvglfaker.so dependency depending on whether VGL_DISPLAY is set or not.
#
# Only run when we are a runtime dependency (i.e., our host matches).
#
VGL_autoAddVGL() {
declare opts=$(shopt -p) output
set +o pipefail
echo "Inserting VirtualGL into OpenGL executables in $prefix..." >&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