#!/usr/bin/env bash # # Rebuild Fedora's libcamera with this board's patches on top. # # libcamera has no plugin mechanism for CameraSensorHelper: the per-sensor gain # models are compiled into libcamera.so, so teaching the software ISP about the # Surface Pro 12"'s front sensor means shipping a rebuilt package. The patches # in patches/ are held against Fedora's source RPM rather than an upstream git # checkout, so the result stays a drop-in replacement for the distribution # package -- same name, same subpackages, same soname. # # Usage: ./build.sh [-o OUTPUT_DIR] [-w WORK_DIR] [-n] # # -o where the built binary RPMs are copied (default: ./RPMS) # -w scratch directory, reused between runs (default: ./.libcamera-build) # -n resolve only: print the binary RPM file names the build would produce, # one per line, and exit without building. Lets a caller check whether # the packages are already published before paying for a build. # # Environment: # # RELEASE_SUFFIX what to append to Fedora's release (default: sp12in1) set -euo pipefail script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) patch_dir="${script_dir}/patches" suffix=${RELEASE_SUFFIX:-sp12in1} output_dir="$(pwd)/RPMS" work_dir="$(pwd)/.libcamera-build" resolve_only=false while getopts ':o:w:nh' opt; do case "${opt}" in o) output_dir=${OPTARG} ;; w) work_dir=${OPTARG} ;; n) resolve_only=true ;; h) sed -n '2,30p' "${BASH_SOURCE[0]}"; exit 0 ;; *) echo "unknown option -${OPTARG}" >&2; exit 2 ;; esac done topdir="${work_dir}/rpmbuild" spec="${topdir}/SPECS/libcamera.spec" if [[ ! -f ${spec} ]]; then rm -rf "${work_dir}" mkdir -p "${work_dir}" "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} ( cd "${work_dir}" && dnf download --source libcamera ) srpm=$(echo "${work_dir}"/libcamera-*.src.rpm) [[ -f ${srpm} ]] || { echo "no libcamera source RPM was downloaded" >&2; exit 1; } rpm --define "_topdir ${topdir}" -i "${srpm}" shopt -s nullglob patches=("${patch_dir}"/*.patch) shopt -u nullglob [[ ${#patches[@]} -gt 0 ]] || { echo "no patches in ${patch_dir}" >&2; exit 1; } # The spec uses %autosetup -p1, so a PatchNN: line is the whole of it -- # nothing has to call %patch. Number them after the last one Fedora ships. last_num=$(grep -oE '^Patch[0-9]+:' "${spec}" | grep -oE '[0-9]+' | sort -n | tail -1) last_num=${last_num:-0} last_line=$(grep -nE '^Patch[0-9]+:' "${spec}" | tail -1 | cut -d: -f1) [[ -n ${last_line} ]] || { echo "no Patch lines in ${spec} to append after" >&2; exit 1; } insert="${work_dir}/patch-lines" : > "${insert}" for patch in "${patches[@]}"; do cp "${patch}" "${topdir}/SOURCES/" last_num=$((10#${last_num} + 1)) printf 'Patch%02d: %s\n' "${last_num}" "$(basename "${patch}")" >> "${insert}" done sed -i "${last_line}r ${insert}" "${spec}" sed -i -E "0,/^Release:/s/^(Release:[[:space:]]*[^%]*)(%\{\?dist\})/\1.${suffix}\2/" "${spec}" grep -qE "^Release:.*\.${suffix}%\{\?dist\}" "${spec}" || { echo "failed to add the .${suffix} release suffix to ${spec}" >&2 exit 1 } fi mapfile -t rpm_files < <(rpmspec -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n' "${spec}") [[ ${#rpm_files[@]} -gt 0 ]] || { echo "rpmspec -q produced no packages" >&2; exit 1; } if [[ ${resolve_only} == true ]]; then printf '%s\n' "${rpm_files[@]}" exit 0 fi dnf --assumeyes builddep "${spec}" rpmbuild --define "_topdir ${topdir}" -bb "${spec}" mkdir -p "${output_dir}" for rpm_file in "${rpm_files[@]}"; do built=$(find "${topdir}/RPMS" -type f -name "${rpm_file}" -print -quit) [[ -n ${built} ]] || { echo "rpmbuild did not produce ${rpm_file}" >&2; exit 1; } cp "${built}" "${output_dir}/" echo "${output_dir}/${rpm_file}" done