Skip to content

Commit

Permalink
Merge pull request NixOS#133537 from Yarny0/foomatic-db
Browse files Browse the repository at this point in the history
foomatic-db ppd files
  • Loading branch information
doronbehar authored Nov 6, 2022
2 parents b8a5755 + 0d36fdc commit c568890
Show file tree
Hide file tree
Showing 17 changed files with 791 additions and 15 deletions.
9 changes: 9 additions & 0 deletions nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,15 @@
maintainer to update the package.
</para>
</listitem>
<listitem>
<para>
ppd files in <literal>pkgs.cups-drv-rastertosag-gdi</literal>
are now gzipped. If you refer to such a ppd file with its path
(e.g. via
<link xlink:href="options.html#opt-hardware.printers.ensurePrinters">hardware.printers.ensurePrinters</link>)
you will need to append <literal>.gz</literal> to the path.
</para>
</listitem>
<listitem>
<para>
xow package removed along with the
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2211.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ Available as [services.patroni](options.html#opt-services.patroni.enable).

- riak package removed along with `services.riak` module, due to lack of maintainer to update the package.

- ppd files in `pkgs.cups-drv-rastertosag-gdi` are now gzipped. If you refer to such a ppd file with its path (e.g. via [hardware.printers.ensurePrinters](options.html#opt-hardware.printers.ensurePrinters)) you will need to append `.gz` to the path.

- xow package removed along with the `hardware.xow` module, due to the project being deprecated in favor of `xone`, which is available via the `hardware.xone` module.

- dd-agent package removed along with the `services.dd-agent` module, due to the project being deprecated in favor of `datadog-agent`, which is available via the `services.datadog-agent` module.
Expand Down
25 changes: 25 additions & 0 deletions pkgs/build-support/setup-hooks/patch-ppd-files/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{ lib
, makeSetupHook
, which
, callPackage
}:

let
patchPpdFilesHook = makeSetupHook
{
name = "patch-ppd-files";
substitutions.which = lib.attrsets.getBin which;
substitutions.awkscript = ./patch-ppd-lines.awk;
}
./patch-ppd-hook.sh;
in

patchPpdFilesHook.overrideAttrs (
lib.trivial.flip
lib.attrsets.recursiveUpdate
{
passthru.tests.test = callPackage ./test.nix {};
meta.description = "setup hook to patch executable paths in ppd files";
meta.maintainers = [ lib.maintainers.yarny ];
}
)
183 changes: 183 additions & 0 deletions pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
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 $outputs; 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

}
50 changes: 50 additions & 0 deletions pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-lines.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
BEGIN {

# ppd file keys are separated from their values by a colon,
# but "options" may reside between the key name and the colon;
# options are separated from the key by spaces
# (we also permit tabs to be on the safe side)
FS = "[: \t]";

# escape regex characters in the old and new command strings
gsub(/[]\\.^$(){}|*+?[]/, "\\\\&", old);
gsub(/\\/, "\\\\&", new);
# ...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")
new = "\\1" new "\\2";
old = "(^|[;&| \\t\"`(])" old "($|[);&| \\t\"`<>])";
# note that a similar regex is build in the shell script to
# filter out unaffected files before this awk script is called;
# if the regex here is changed, the shell script should also be checked

# list of PPD keys that contain executable names or scripts, see
# https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Printing/LSB-Printing/ppdext.html
# https://www.cups.org/doc/spec-ppd.html
cmds["*APAutoSetupTool"] = "";
cmds["*APPrinterLowInkTool"] = "";
cmds["*FoomaticRIPCommandLine"] = "";
cmds["*FoomaticRIPPostPipe"] = "";
cmds["*cupsFilter"] = "";
cmds["*cupsFilter2"] = "";
cmds["*cupsPreFilter"] = "";

}

# since comments always start with "*%",
# this mechanism also properly recognizes (and ignores) them

{

# if the current line starts a new key,
# check if it is a command-containing key;
# also reset the `isCmd` flag if a new file begins
if ($0 ~ /^\*/ || FNR == 1) { isCmd = ($1 in cmds) }

# replace commands if the current keys might contain commands
if (isCmd) { $0 = gensub(old, new, "g") }

print

}
40 changes: 40 additions & 0 deletions pkgs/build-support/setup-hooks/patch-ppd-files/test.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{ substituteAll
, diffutils
, stdenv
, patchPpdFilesHook
}:

let
input = substituteAll {
src = ./test.ppd;
keep = "cmp";
patch = "cmp";
pathkeep = "/bin/cmp";
pathpatch = "/bin/cmp";
};

output = substituteAll {
src = ./test.ppd;
keep = "cmp";
patch = "${diffutils}/bin/cmp";
pathkeep = "/bin/cmp";
pathpatch = "${diffutils}/bin/cmp";
};
in

stdenv.mkDerivation {
name = "${patchPpdFilesHook.name}-test";
buildInputs = [ diffutils ];
nativeBuildInputs = [ diffutils patchPpdFilesHook ];
dontUnpack = true;
dontInstall = true;
ppdFileCommands = [ "cmp" ];
preFixup = ''
install -D "${input}" "${placeholder "out"}/share/cups/model/test.ppd"
install -D "${input}" "${placeholder "out"}/share/ppds/test.ppd"
'';
postFixup = ''
diff --color --report-identical-files "${output}" "${placeholder "out"}/share/cups/model/test.ppd"
diff --color --report-identical-files "${output}" "${placeholder "out"}/share/ppds/test.ppd"
'';
}
22 changes: 22 additions & 0 deletions pkgs/build-support/setup-hooks/patch-ppd-files/test.ppd
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
*% This comment: might look like a command @keep@
*% but it should be left untouched
*SomeKey: do not replace this @keep@
*APAutoSetupTool: do replace this @patch@
*FoomaticRIPCommandLine: "patch also @patch@
in a multi-line command @patch@
and another line @patch@
*SomeKey: "stop patching on new non-command key @keep@
and remember the key in the next line @keep@"
*cupsFilter option: recognize keys with options @patch@
*cupsFilter : handle strange spacing;@patch@
*cupsFilter : handle tabulator @patch@
*cupsFilter: patch common paths @pathpatch@
*cupsFilter: patch quoted commands "@patch@"
*cupsFilter: patch commands in subshell (@patch@)
*cupsFilter: patch commands in subshell `@pathpatch@`
*cupsFilter: keep uncommon paths /fancy/@pathkeep@
*cupsFilter: keep entangled commands-@keep@
*cupsFilter: keep entangled commands\@keep@
*cupsFilter: keep entangled commands @keep@()
*cupsFilter: keep entangled commands @pathkeep@-cmd
*%cupsFilter: This comment should also be left as is @pathkeep@
9 changes: 7 additions & 2 deletions pkgs/misc/cups/drivers/cups-drv-rastertosag-gdi/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
, fetchpatch
, cups
, python3Packages
, patchPpdFilesHook
}:

python3Packages.buildPythonApplication rec {
Expand All @@ -20,9 +21,9 @@ python3Packages.buildPythonApplication rec {
})
];
format = "other";
nativeBuildInputs = [ (lib.getBin cups) ];
nativeBuildInputs = [ (lib.getBin cups) patchPpdFilesHook ];
# The source image also brings pre-built ppd files,
# be we prefer to generate from source where possible, so
# but we prefer to generate from source where possible, so
# the following line generates ppd files from the drv file.
postBuild = ''
ppdc -v -d . -I "${cups}/share/cups/ppdc" rastertosag-gdi.drv
Expand All @@ -35,6 +36,10 @@ python3Packages.buildPythonApplication rec {
ln -vst "${placeholder "out"}/lib/cups/filter/" "${placeholder "out"}/bin/rastertosag-gdi"
runHook postInstall
'';
ppdFileCommands = [ "rastertosag-gdi" ];
postFixup = ''
gzip -9nv "${placeholder "out"}/share/cups/model/rastertosag-gdi"/*.ppd
'';
meta = {
description = "CUPS driver for Ricoh Aficio SP 1000S and SP 1100S printers";
downloadPage = "https://www.openprinting.org/download/printing/rastertosag-gdi/";
Expand Down
Loading

0 comments on commit c568890

Please sign in to comment.