diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6c9ce03..0718cdee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,7 +103,8 @@ jobs: zlib1g-dev \ ghostscript \ poppler-utils \ - mupdf-tools + mupdf-tools \ + file - name: Build & test libppd (native) if: matrix.use-qemu == false @@ -111,7 +112,9 @@ jobs: set -ex cd "$REPO_DIR" ./autogen.sh - ./configure + # --enable-ppdc-utils: the downstream libppd-2-dev autopkgtest needs + # the staged `ppdc` to compile its test.drv into a PPD. + ./configure --enable-ppdc-utils make -j$(nproc) V=1 make check V=1 VERBOSE=1 || { echo "==== test-suite.log ====" @@ -123,6 +126,17 @@ jobs: exit 1 } + - name: Autopkgtest (DESTDIR staging, native) + if: matrix.use-qemu == false + run: | + set -ex + cd "$REPO_DIR" + # Full downstream suite: libppd-2-dev (compile/link/run) + + # libppd-2-ppd-handling. Both point at the staged tree via + # environment overrides, so no privilege or path redirection is + # needed. + make test-autopkgtest V=1 + # ----------------------------------------------------------------------- # EMULATED LEG (armhf via QEMU armv7, riscv64 via QEMU) # ----------------------------------------------------------------------- @@ -166,12 +180,14 @@ jobs: zlib1g-dev \ ghostscript \ poppler-utils \ - mupdf-tools + mupdf-tools \ + file run: | set -ex cd /workspace ./autogen.sh - ./configure + # --enable-ppdc-utils: needed by the libppd-2-dev autopkgtest. + ./configure --enable-ppdc-utils make -j$(nproc) V=1 make check V=1 VERBOSE=1 || { echo "==== test-suite.log ====" @@ -183,6 +199,12 @@ jobs: exit 1 } + # Full downstream autopkgtest suite. Both tests resolve the + # staged build tree through environment overrides (no absolute + # paths, no privilege, no bind mounts), so the same suite that + # runs on the native legs runs unchanged under QEMU emulation. + make test-autopkgtest V=1 + # ----------------------------------------------------------------------- # ARTIFACT UPLOAD (all four legs, only on failure) # ----------------------------------------------------------------------- diff --git a/Makefile.am b/Makefile.am index 16a10f0d..f06c5ece 100644 --- a/Makefile.am +++ b/Makefile.am @@ -367,4 +367,104 @@ ppdpo_CFLAGS = \ distclean-local: rm -rf *.cache *~ +clean-local: + rm -rf $(CIROOT) + SUBDIRS = + +# ============================================================ +# Staged-install downstream autopkgtests (DESTDIR staging) +# ============================================================ +# Install the just-built tree into an ephemeral root ($(CIROOT)) with +# `make install DESTDIR=`, top up the bits that `make install` does not ship +# but the downstream Debian autopkgtests require, fix up the staged .pc, then +# run the unmodified downstream scripts against that tree. +CIROOT = $(abs_top_builddir)/_ciroot + +EXTRA_DIST += \ + ci/autopkgtest/run.sh \ + ci/autopkgtest/debian-tests/control \ + ci/autopkgtest/debian-tests/libppd-2-dev \ + ci/autopkgtest/debian-tests/libppd-2-ppd-handling + +# Fix A: check_PROGRAMS are build-only under Automake and are never copied by +# `make install`. The downstream PPD-handling test executes the test driver +# as an *installed* binary (/usr/bin/testppd), so build it and stage it into +# $(CIROOT)$(bindir). Uses libtool --mode=install so the binary is relinked +# cleanly against $(libdir) (LD_LIBRARY_PATH then forces the staged library). +STAGED_TEST_BINARIES = testppd + +install-test-programs: $(STAGED_TEST_BINARIES) + $(MKDIR_P) "$(CIROOT)$(bindir)" + @for p in $(STAGED_TEST_BINARIES); do \ + echo " STAGE $$p -> $(CIROOT)$(bindir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) "$$p" "$(CIROOT)$(bindir)/$$p" || exit 1; \ + done + +# Fix C (data seeding): the downstream PPD-handling test copies its PPDs from +# the installed $(datadir)/ppd/testppd directory, which upstream `make install` +# does not ship. Seed every test PPD the current testppd binary opens +# (test.ppd, test2.ppd, sbo_test.ppd) so the unmodified script finds them. +STAGED_TEST_PPDS = \ + ppd/test.ppd \ + ppd/test2.ppd \ + ppd/sbo_test.ppd + +install-test-data: + $(MKDIR_P) "$(CIROOT)$(datadir)/ppd/testppd" + @for f in $(STAGED_TEST_PPDS); do \ + echo " STAGE $$f -> $(CIROOT)$(datadir)/ppd/testppd/"; \ + $(INSTALL_DATA) "$(srcdir)/$$f" "$(CIROOT)$(datadir)/ppd/testppd/" || exit 1; \ + done + +# Build everything, DESTDIR-install it, top up the test-only programs/data, +# then rewrite the staged libppd.pc prefix so a downstream consumer resolves +# THIS build (not a system copy) while the system dependency .pc files +# (libcupsfilters, cups) keep resolving normally. +stage-ciroot: all + rm -rf "$(CIROOT)" + $(MAKE) $(AM_MAKEFLAGS) install DESTDIR="$(CIROOT)" + $(MAKE) $(AM_MAKEFLAGS) install-test-programs install-test-data + @pc="$(CIROOT)$(libdir)/pkgconfig/libppd.pc"; \ + if [ -f "$$pc" ]; then \ + sed -i.bak -E "s#^(prefix|exec_prefix|libdir|includedir)=(/.*)#\1=$(CIROOT)\2#" "$$pc"; \ + rm -f "$$pc.bak"; \ + echo "stage-ciroot: rewrote staged-tree paths in $$pc"; \ + fi + +# Common environment for the wrapper. PPDC_DATADIR / CUPS_DATADIR point ppdc +# at the staged *.defs so `ppdc test.drv` resolves / +# from the staging tree. LIBPPD_TESTPPD / LIBPPD_PPDDIR point the PPD-handling +# test at the staged binary and data instead of the system /usr paths, so it +# needs no absolute-path redirection (no root, no proot) and runs identically +# on native and QEMU-emulated architectures. +# NB: libppd configures with a split prefix (prefix=/ but exec_prefix=/usr and +# everything installs under /usr), so the staging root the wrapper points at is +# $(CIROOT)$(exec_prefix), not $(CIROOT)$(prefix). +CIROOT_ENV = \ + CIROOT="$(CIROOT)" \ + CIPREFIX="$(exec_prefix)" \ + TOP_BUILDDIR="$(abs_top_builddir)" \ + PPDC_DATADIR="$(CIROOT)$(datadir)/ppdc" \ + CUPS_DATADIR="$(CIROOT)$(datadir)" \ + LIBPPD_TESTPPD="$(CIROOT)$(bindir)/testppd" \ + LIBPPD_PPDDIR="$(CIROOT)$(datadir)/ppd/testppd" + +# Full downstream suite in a single wrapper invocation, so the run produces one +# aggregate PASS/FAIL summary across both tests. Neither test needs privilege +# or path redirection, so this is identical on every architecture. +test-autopkgtest: stage-ciroot + $(CIROOT_ENV) \ + $(SHELL) $(srcdir)/ci/autopkgtest/run.sh libppd-2-dev libppd-2-ppd-handling + +# Single-test convenience targets (handy for debugging one case in isolation). +test-autopkgtest-dev: stage-ciroot + $(CIROOT_ENV) \ + $(SHELL) $(srcdir)/ci/autopkgtest/run.sh libppd-2-dev + +test-autopkgtest-ppd: stage-ciroot + $(CIROOT_ENV) \ + $(SHELL) $(srcdir)/ci/autopkgtest/run.sh libppd-2-ppd-handling + +.PHONY: stage-ciroot install-test-programs install-test-data \ + test-autopkgtest test-autopkgtest-dev test-autopkgtest-ppd diff --git a/ci/autopkgtest/debian-tests/control b/ci/autopkgtest/debian-tests/control new file mode 100644 index 00000000..f39e44b7 --- /dev/null +++ b/ci/autopkgtest/debian-tests/control @@ -0,0 +1,12 @@ +Tests: libppd-2-dev +Depends: build-essential, + libcups2-dev, + libcupsfilters-dev, + libppd-dev, + pkg-config, + libppd-utils, +Restrictions: allow-stderr + +Tests: libppd-2-ppd-handling +Depends: libppd-tests +Restrictions: allow-stderr diff --git a/ci/autopkgtest/debian-tests/libppd-2-dev b/ci/autopkgtest/debian-tests/libppd-2-dev new file mode 100755 index 00000000..962b2fee --- /dev/null +++ b/ci/autopkgtest/debian-tests/libppd-2-dev @@ -0,0 +1,71 @@ +#!/bin/sh +# autopkgtest check: Build and run a program against libppd, to verify +# that the headers and pkg-config file are installed correctly +# (C) 2012 Canonical Ltd. +# (C) 2018-2019 Simon McVittie +# (C) 2023 Till Kamppeter +# Authors: Martin Pitt, Simon McVittie, Till Kamppeter + +set -eux + +package=libppd +WORKDIR="$(mktemp -d)" +export HOME="$WORKDIR" +export XDG_RUNTIME_DIR="$WORKDIR" +trap 'cd /; rm -rf "$WORKDIR"' 0 INT QUIT ABRT PIPE TERM + +if [ -n "${DEB_HOST_GNU_TYPE:-}" ]; then + CROSS_COMPILE="$DEB_HOST_GNU_TYPE-" +else + CROSS_COMPILE= +fi + +cd "$WORKDIR" +cat < test.c +// All header files of libppd API +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Actually use something from the library, so that it gets actually linked + cf_filter_universal_parameter_t universal_parameters; + memset(&universal_parameters, 0, sizeof(cf_filter_universal_parameter_t)); + return (ppdFilterCUPSWrapper(argc, argv, ppdFilterUniversal, + &universal_parameters, NULL)); +} +EOF + +cat < test.drv +#include +#include +Font * +Manufacturer "Test" +ModelName "TestPrinter" +Version 1.0 +Filter image/urf 100 - +*MediaSize Letter +MediaSize A4 +ColorDevice true +ColorModel Gray/Grayscale w chunky 0 +*ColorModel RGB/Color rgb chunky 0 +*Resolution - 8 0 0 0 "600dpi/600 DPI" +Resolution - 8 0 0 0 "1200dpi/1200 DPI" +PCFileName "test.ppd" +EOF + +ppdc test.drv +echo "create test PPD ($package): OK" +export PPD=./ppd/test.ppd +export CONTENT_TYPE=text/plain +export FINAL_CONTENT_TYPE=application/vnd.cups-pdf +# Deliberately word-splitting pkg-config's output: +# shellcheck disable=SC2046 +${CROSS_COMPILE}g++ -o "${package}-test" test.c $(${CROSS_COMPILE}pkg-config --cflags --libs "$package") $(${CROSS_COMPILE}pkg-config --cflags --libs "libcupsfilters") +echo "build ($package): OK" +[ -x "${package}-test" ] +echo -en 'Test1\nTest2\nTest3\n' | ./${package}-test 1 1 1 1 'PageSize=A4 Resolution=1200dpi' > test.pdf +echo "run ($package): OK" +file test.pdf | grep -q 'PDF document' +echo "check output file ($package): OK" diff --git a/ci/autopkgtest/debian-tests/libppd-2-ppd-handling b/ci/autopkgtest/debian-tests/libppd-2-ppd-handling new file mode 100755 index 00000000..f3c19c45 --- /dev/null +++ b/ci/autopkgtest/debian-tests/libppd-2-ppd-handling @@ -0,0 +1,27 @@ +#!/bin/sh +# autopkgtest check: Run "make check" test program "testppd" using libppd +# installed on the system. +# (C) 2012 Canonical Ltd. +# (C) 2023 Till Kamppeter +# Authors: Martin Pitt, Till Kamppeter + +set -eux + +package=libppd +WORKDIR="$(mktemp -d)" +export HOME="$WORKDIR" +export XDG_RUNTIME_DIR="$WORKDIR" +trap 'cd /; rm -rf "$WORKDIR"' 0 INT QUIT ABRT PIPE TERM + +# Location of the installed test program and its PPD data. Default to the +# system paths used when the package is installed (the normal Debian +# autopkgtest case); allow a staged build tree to override them so the test +# assumes no absolute paths and runs unprivileged on every architecture. +: "${LIBPPD_TESTPPD:=/usr/bin/testppd}" +: "${LIBPPD_PPDDIR:=/usr/share/ppd/testppd}" + +# Copy test PPD files into right place +cp -r "$LIBPPD_PPDDIR" ppd +# Run the test program +"$LIBPPD_TESTPPD" +echo "run test program on system's $package: OK" diff --git a/ci/autopkgtest/run.sh b/ci/autopkgtest/run.sh new file mode 100755 index 00000000..616438fe --- /dev/null +++ b/ci/autopkgtest/run.sh @@ -0,0 +1,98 @@ +#!/bin/sh +# ci/autopkgtest/run.sh +# +# Universal DESTDIR-staging wrapper for the downstream Debian autopkgtests. +# Points PATH / LD_LIBRARY_PATH / PKG_CONFIG_PATH at the staged install tree +# ($CIROOT) produced by `make stage-ciroot`, then runs the unmodified +# downstream scripts vendored under ci/autopkgtest/debian-tests/. +# +# The downstream scripts take environment overrides (e.g. LIBPPD_TESTPPD, +# LIBPPD_PPDDIR) that default to the system /usr paths but can be redirected +# into the staging tree. That keeps every test free of absolute-path +# assumptions, so the wrapper needs no privilege and no bind mounts and runs +# identically on native and QEMU-emulated architectures. +# +# Env in: +# CIROOT staging root (default: $PWD/_ciroot) +# CIPREFIX configured prefix (default: /usr) +# TOP_BUILDDIR build tree (default: $PWD) +# Any extra exported variables (e.g. PPDC_DATADIR, CUPS_DATADIR, +# LIBPPD_TESTPPD, LIBPPD_PPDDIR) are passed straight through to the scripts. +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +TESTS_DIR="$SCRIPT_DIR/debian-tests" + +: "${CIROOT:=$PWD/_ciroot}" +: "${CIPREFIX:=/usr}" +: "${TOP_BUILDDIR:=$PWD}" + +if [ ! -d "$CIROOT" ]; then + echo "run.sh: staging root not found: $CIROOT (run 'make stage-ciroot' first)" >&2 + exit 1 +fi + +ROOT="$CIROOT$CIPREFIX" +MULTIARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null \ + || gcc -dumpmachine 2>/dev/null || echo "") + +PATH="$ROOT/bin:$ROOT/sbin:$TOP_BUILDDIR:$TOP_BUILDDIR/.libs:$PATH" +LD_LIBRARY_PATH="$ROOT/lib${MULTIARCH:+:$ROOT/lib/$MULTIARCH}:$TOP_BUILDDIR/.libs${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +PKG_CONFIG_PATH="$ROOT/lib/pkgconfig${MULTIARCH:+:$ROOT/lib/$MULTIARCH/pkgconfig}:$ROOT/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" +export PATH LD_LIBRARY_PATH PKG_CONFIG_PATH + +if [ "$#" -eq 0 ]; then + echo "run.sh: usage: run.sh [test-name...]" >&2 + exit 2 +fi + +rc=0 +total=0 +n_pass=0 +n_fail=0 +results="" +for name in "$@"; do + total=$((total + 1)) + script="$TESTS_DIR/$name" + if [ ! -f "$script" ]; then + echo "run.sh: no such test: $script" >&2 + n_fail=$((n_fail + 1)) + results="$results +FAIL: $name (not found)" + rc=1 + continue + fi + chmod +x "$script" 2>/dev/null || true + workdir=$(mktemp -d) + echo "=== autopkgtest: $name (CIROOT=$CIROOT, prefix=$CIPREFIX) ===" + if ( cd "$workdir" && "$script" ); then + echo "=== PASS: $name ===" + n_pass=$((n_pass + 1)) + results="$results +PASS: $name" + else + ec=$? + echo "=== FAIL: $name (exit $ec) ===" >&2 + n_fail=$((n_fail + 1)) + results="$results +FAIL: $name (exit $ec)" + rc=1 + fi + rm -rf "$workdir" +done + +# --------------------------------------------------------------------------- +# Aggregate summary (mirrors the Automake `make check` test-suite summary) +# --------------------------------------------------------------------------- +echo "============================================================================" +echo "Downstream autopkgtest summary" +echo "============================================================================" +printf '# TOTAL: %d\n' "$total" +printf '# PASS: %d\n' "$n_pass" +printf '# FAIL: %d\n' "$n_fail" +echo "----------------------------------------------------------------------------" +# Per-test breakdown (strip the leading blank line from the accumulator). +printf '%s\n' "$results" | sed '/^$/d' +echo "============================================================================" + +exit $rc diff --git a/libppd.pc.in b/libppd.pc.in index 481a293f..d4acaf47 100644 --- a/libppd.pc.in +++ b/libppd.pc.in @@ -9,4 +9,4 @@ Version: @VERSION@ Libs: -L${libdir} -lppd Libs.private: @CUPS_LIBS@ -Cflags: -I${includedir}/ppd +Cflags: -I${includedir} -I${includedir}/ppd