184 lines
7.4 KiB
Bash
184 lines
7.4 KiB
Bash
|
fixupOutputHooks+=(_patchPpdFileCommands4fixupOutputHooks)
|
||
|
|
||
|
|
||
|
|
||
|
# Install a hook for the `fixupPhase`:
|
||
|
# If the variable `ppdFileCommands` contains a list of
|
||
|
# executable names, the hook calls `patchPpdFileCommands`
|
||
|
# on each output's `/share/cups/model` and `/share/ppds`
|
||
|
# directories in order to replace calls to those executables.
|
||
|
|
||
|
_patchPpdFileCommands4fixupOutputHooks () {
|
||
|
[[ -n $ppdFileCommands ]] || return 0
|
||
|
if [[ -d $prefix/share/cups/model ]]; then
|
||
|
patchPpdFileCommands "$prefix/share/cups/model" $ppdFileCommands
|
||
|
fi
|
||
|
if [[ -d $prefix/share/ppds ]]; then
|
||
|
patchPpdFileCommands "$prefix/share/ppds" $ppdFileCommands
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
# patchPpdFileCommands PPD-ROOT PROGNAME...
|
||
|
#
|
||
|
# Look for ppd files in the directory PPD-ROOT.
|
||
|
# Descend into subdirectories, even if they are symlinks.
|
||
|
# However, ignore ppd files that don't belong to the same
|
||
|
# prefix ($NIX_STORE/$package_name) as PPD-ROOT-DIR does,
|
||
|
# to avoid stepping into other package's directories.
|
||
|
# ppd files may be gzipped; if the are,
|
||
|
# uncompress them, later recompress them.
|
||
|
# Skip symlinks to ppd files.
|
||
|
# PPD-ROOT may also be a single ppd file.
|
||
|
#
|
||
|
# Look for the PROGNAME executable in outputs and `buildInputs`,
|
||
|
# then look for PROGNAME invocations in the ppd files,
|
||
|
# without path or with common paths like `/usr/bin/$PROGNAME`.
|
||
|
# Replace those invocations with an absolute path to the
|
||
|
# corresponding executable from the outputs or `buildInputs`.
|
||
|
# Executables are searched where CUPS would search them,
|
||
|
# i.e., in `/bin` and `/lib/cups/filter`.
|
||
|
#
|
||
|
# As soon as an executable's path is replaced as
|
||
|
# described above, the package containing the binary
|
||
|
# is added to the list of propagated build inputs.
|
||
|
# This ensures the executable's package is still
|
||
|
# recognized as runtime dependency of the ppd file
|
||
|
# even if the ppd file is compressed lateron.
|
||
|
#
|
||
|
# PROGNAME may not contain spaces or tabs.
|
||
|
# The function will also likely fail or produce
|
||
|
# broken results if PROGNAME contains characters that
|
||
|
# require shell or regex escaping (e.g. a backslash).
|
||
|
|
||
|
patchPpdFileCommands () {
|
||
|
|
||
|
local bin binnew binold binoldgrep cupspath path ppdroot ppdrootprefix
|
||
|
|
||
|
# we will store some temporary data here
|
||
|
pushd "$(mktemp -d --tmpdir patch-ppd-file-commands.XXXX)"
|
||
|
|
||
|
# remember the ppd root path
|
||
|
[[ "$1" == $NIX_STORE/* ]] # ensure it's a store directory
|
||
|
ppdroot=$1
|
||
|
shift # now "$@" is the list of binaries
|
||
|
ppdrootprefix=${ppdroot%"/${ppdroot#"$NIX_STORE"/*/}"}
|
||
|
|
||
|
# create `cupspath` (where we should look for binaries),
|
||
|
# with these priorities
|
||
|
# * outputs of current build before buildInputs
|
||
|
# * `/lib/cups/filter' before `/bin`
|
||
|
# * add HOST_PATH at end, so we don't miss anything
|
||
|
for path in $(getAllOutputNames); do
|
||
|
addToSearchPath cupspath "${!path}/lib/cups/filter"
|
||
|
addToSearchPath cupspath "${!path}/bin"
|
||
|
done
|
||
|
for path in ${pkgsHostTarget+"${pkgsHostTarget[@]}"}; do
|
||
|
addToSearchPath cupspath "$path/lib/cups/filter"
|
||
|
addToSearchPath cupspath "$path/bin"
|
||
|
done
|
||
|
while read -r -d : path; do
|
||
|
addToSearchPath cupspath "$path"
|
||
|
done <<< "${HOST_PATH:+"${HOST_PATH}:"}"
|
||
|
|
||
|
# create list of compressed ppd files
|
||
|
# so we can recompress them later
|
||
|
find -L "$ppdroot" -type f -iname '*.ppd.gz' '!' -xtype l -print0 > gzipped
|
||
|
|
||
|
# decompress gzipped ppd files
|
||
|
echo "patchPpdFileCommands: decompressing $(grep -cz '^' < gzipped) gzipped ppd file(s) in $ppdroot"
|
||
|
xargs -0r -n 64 -P "$NIX_BUILD_CORES" gunzip < gzipped
|
||
|
|
||
|
# create list of all ppd files to be checked
|
||
|
find -L "$ppdroot" -type f -iname '*.ppd' '!' -xtype l -print0 > ppds
|
||
|
|
||
|
for bin in "$@"; do
|
||
|
|
||
|
# discover new path
|
||
|
binnew=$(PATH=$cupspath '@which@/bin/which' "$bin")
|
||
|
echo "patchPpdFileCommands: located binary $binnew"
|
||
|
|
||
|
# for each binary, we look for the name itself, but
|
||
|
# also for a couple of common paths that might be used
|
||
|
for binold in {/usr,}/{lib/cups/filter,sbin,bin}/"$bin" "$bin"; do
|
||
|
|
||
|
# escape regex characters in the old command string
|
||
|
binoldgrep=$(sed 's,[]$.*[\^],\\&,g' <<< "$binold")
|
||
|
# ...and surround old command with some regex
|
||
|
# that singles out shell command invocations
|
||
|
# to avoid replacing other strings that might contain the
|
||
|
# command name by accident (like "perl" in "perl-script")
|
||
|
binoldgrep='\(^\|[;&| '$'\t''"`(]\)'"$binoldgrep"'\($\|[);&| '$'\t''"`<>]\)'
|
||
|
# this string is used to *quickly* filter out
|
||
|
# unaffected files before the (slower) awk script runs;
|
||
|
# note that a similar regex is build in the awk script;
|
||
|
# if `binoldgrep` is changed, the awk script should also be checked
|
||
|
|
||
|
# create list of likely affected files
|
||
|
# (might yield exit status != 0 if there are no matches)
|
||
|
xargs -0r grep -lZ "$binoldgrep" < ppds > ppds-to-patch || true
|
||
|
|
||
|
echo "patchPpdFileCommands: $(grep -cz '^' < ppds-to-patch) ppd file(s) contain $binold"
|
||
|
|
||
|
# actually patch affected ppd files with awk;
|
||
|
# this takes some time but can be parallelized;
|
||
|
# speed up with LC_ALL=C, https://stackoverflow.com/a/33850386
|
||
|
LC_ALL=C xargs -0r -n 64 -P "$NIX_BUILD_CORES" \
|
||
|
awk -i inplace -v old="${binold//\\/\\\\}" -v new="${binnew//\\/\\\\}" -f "@awkscript@" \
|
||
|
< ppds-to-patch
|
||
|
|
||
|
done
|
||
|
|
||
|
# create list of affected files
|
||
|
xargs -0r grep -lZF "$binnew" < ppds > patched-ppds || true
|
||
|
|
||
|
echo "patchPpdFileCommands: $(grep -cz '^' < patched-ppds) ppd file(s) patched with $binnew"
|
||
|
|
||
|
# if the new command is contained in a file,
|
||
|
# remember the new path so we can add it to
|
||
|
# the list of propagated dependencies later
|
||
|
if [[ -s patched-ppds ]]; then
|
||
|
printf '%s\0' "${binnew%"/${binnew#"${NIX_STORE}"/*/}"}" >> dependencies
|
||
|
fi
|
||
|
|
||
|
done
|
||
|
|
||
|
# recompress ppd files that have been decompressed before
|
||
|
echo "patchPpdFileCommands: recompressing $(grep -cz '^' < gzipped) gzipped ppd file(s)"
|
||
|
# we can't just hand over the paths of the uncompressed files
|
||
|
# to gzip as it would add the lower-cased extension ".gz"
|
||
|
# even for files where the original was named ".GZ"
|
||
|
xargs -0r -n 1 -P "$NIX_BUILD_CORES" \
|
||
|
"$SHELL" -c 'gzip -9nS ".${0##*.}" "${0%.*}"' \
|
||
|
< gzipped
|
||
|
|
||
|
# enlist dependencies for propagation;
|
||
|
# this is needed in case ppd files are compressed later
|
||
|
# (Nix won't find dependency paths in compressed files)
|
||
|
if [[ -s dependencies ]]; then
|
||
|
|
||
|
# weed out duplicates from the dependency list first
|
||
|
sort -zu dependencies > sorted-dependencies
|
||
|
|
||
|
mkdir -p "$ppdrootprefix/nix-support"
|
||
|
while IFS= read -r -d '' path; do
|
||
|
printWords "$path" >> "$ppdrootprefix/nix-support/propagated-build-inputs"
|
||
|
# stdenv writes it's own `propagated-build-inputs`,
|
||
|
# based on the variable `propagatedBuildInputs`,
|
||
|
# but only to one output (`outputDev`).
|
||
|
# So we also add our dependencies to that variable.
|
||
|
# If our file survives as written above, great!
|
||
|
# If stdenv overwrits it,
|
||
|
# our dependencies will still be added to the file.
|
||
|
# The end result might contain too many
|
||
|
# propagated dependencies for multi-output packages,
|
||
|
# but never a broken package.
|
||
|
propagatedBuildInputs+=("$path")
|
||
|
done < sorted-dependencies
|
||
|
fi
|
||
|
|
||
|
popd
|
||
|
|
||
|
}
|