From: Stefan Reinauer Date: Thu, 4 Jun 2026 14:59:48 +0000 (-0700) Subject: tools: add package dependency helpers X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=881c5015096aca150ab285eb39aa003544618cef;p=francis%2Fwinuae.git tools: add package dependency helpers Add helpers for Debian package assembly, Linux package metadata checks, and fetching the LZMA SDK used by archive support. These scripts keep external dependency and package steps explicit for CI and local release builds. --- diff --git a/tools/debian-build-package.sh b/tools/debian-build-package.sh new file mode 100755 index 00000000..f4a54bc0 --- /dev/null +++ b/tools/debian-build-package.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -euo pipefail + +source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +build_dir="${WINUAE_DEB_BUILD_DIR:-/tmp/winuae_deb_build}" +build_type="${WINUAE_DEB_BUILD_TYPE:-RelWithDebInfo}" +jobs="${WINUAE_DEB_JOBS:-}" +cmake_args=() + +usage() { + cat <] + +Options: + --build-dir DIR CMake build directory (default: ${build_dir}) + --build-type TYPE CMake build type (default: ${build_type}) + --jobs N Parallel build jobs (default: detected CPU count) + -h, --help Show this help + +Extra arguments after -- are passed to the CMake configure step. +EOF +} + +while (($#)); do + case "$1" in + --build-dir) + if (($# < 2)); then + echo "error: --build-dir requires a directory" >&2 + exit 2 + fi + build_dir="$2" + shift 2 + ;; + --build-type) + if (($# < 2)); then + echo "error: --build-type requires a value" >&2 + exit 2 + fi + build_type="$2" + shift 2 + ;; + --jobs|-j) + if (($# < 2)); then + echo "error: --jobs requires a value" >&2 + exit 2 + fi + jobs="$2" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + --) + shift + cmake_args+=("$@") + break + ;; + *) + echo "error: unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [ "$(uname -s)" != "Linux" ]; then + echo "error: Debian packages can only be built on Linux" >&2 + exit 1 +fi + +for tool in cmake cpack dpkg-deb dpkg-shlibdeps; do + if ! command -v "${tool}" >/dev/null 2>&1; then + echo "error: required tool not found: ${tool}" >&2 + exit 1 + fi +done + +if [ -z "${jobs}" ]; then + jobs="$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')" +fi + +cmake -S "${source_dir}" -B "${build_dir}" \ + -DCMAKE_BUILD_TYPE="${build_type}" \ + "${cmake_args[@]}" + +cmake --build "${build_dir}" \ + --target winuae_unix_linux_package_metadata_check +cmake --build "${build_dir}" --target package --parallel "${jobs}" + +mapfile -t debs < <( + find "${build_dir}" -maxdepth 1 -type f -name '*.deb' -printf '%T@ %p\n' | + sort -nr | + cut -d' ' -f2- +) +if ((${#debs[@]} == 0)); then + echo "error: package build completed but no .deb was found" >&2 + echo "error: checked ${build_dir}" >&2 + exit 1 +fi + +echo +echo "Built Debian package:" +ppc_qemu_enabled=false +if grep -Eq '^WINUAE_UNIX_WITH_PPC_QEMU:BOOL=(ON|TRUE|1)$' \ + "${build_dir}/CMakeCache.txt"; then + ppc_qemu_enabled=true +fi +for deb in "${debs[@]}"; do + echo " ${deb}" + dpkg-deb --field "${deb}" Package Version Architecture Depends + + contents="$(dpkg-deb --contents "${deb}")" + if ! grep -Eq ' \./usr/bin/winuae$' <<<"${contents}"; then + echo "error: package does not contain /usr/bin/winuae" >&2 + exit 1 + fi + if grep -Eq ' \./usr/bin/winuae_unix$' <<<"${contents}"; then + echo "error: package should not install /usr/bin/winuae_unix" >&2 + exit 1 + fi + if [[ "${ppc_qemu_enabled}" == true ]]; then + if ! grep -Eq ' \./usr/lib.*/winuae/plugins/qemu-uae\.so$' <<<"${contents}"; then + echo "error: package does not contain qemu-uae.so" >&2 + exit 1 + fi + fi +done diff --git a/tools/fetch-lzma-sdk.sh b/tools/fetch-lzma-sdk.sh new file mode 100755 index 00000000..9c9646bc --- /dev/null +++ b/tools/fetch-lzma-sdk.sh @@ -0,0 +1,171 @@ +#!/bin/sh +set -eu + +if [ "$#" -ne 4 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +target_dir=$1 +sdk_url=$2 +sdk_version=$3 +sdk_sha256=$4 + +if [ -f "$target_dir/C/7z/7zVersion.h" ] || + [ -f "$target_dir/7z/7zVersion.h" ]; then + exit 0 +fi + +cache_parent=$(dirname "$target_dir") +mkdir -p "$cache_parent" + +tmp_dir=$(mktemp -d "$cache_parent/.fetch-$sdk_version.XXXXXX") +cleanup() +{ + rm -rf "$tmp_dir" +} +trap cleanup EXIT HUP INT TERM + +archive_path="$tmp_dir/lzma-sdk.7z" +extract_dir="$tmp_dir/extract" +mkdir -p "$extract_dir" + +if command -v curl >/dev/null 2>&1; then + curl -fL "$sdk_url" -o "$archive_path" +elif command -v wget >/dev/null 2>&1; then + wget -O "$archive_path" "$sdk_url" +else + echo "curl or wget is required to fetch $sdk_url" >&2 + exit 1 +fi + +if [ -n "$sdk_sha256" ]; then + if command -v sha256sum >/dev/null 2>&1; then + archive_sha256=$(sha256sum "$archive_path" | awk '{print $1}') + elif command -v shasum >/dev/null 2>&1; then + archive_sha256=$(shasum -a 256 "$archive_path" | awk '{print $1}') + else + echo "sha256sum or shasum is required to verify $sdk_url" >&2 + exit 1 + fi + if [ "$archive_sha256" != "$sdk_sha256" ]; then + echo "downloaded SDK checksum mismatch for $sdk_url" >&2 + echo "expected: $sdk_sha256" >&2 + echo "actual: $archive_sha256" >&2 + exit 1 + fi +fi + +extract_archive() +{ + if command -v cmake >/dev/null 2>&1; then + if (cd "$extract_dir" && cmake -E tar xf "$archive_path") + then + return 0 + fi + fi + if command -v 7zz >/dev/null 2>&1; then + if 7zz x -y "-o$extract_dir" "$archive_path"; then + return 0 + fi + fi + if command -v 7z >/dev/null 2>&1; then + if 7z x -y "-o$extract_dir" "$archive_path"; then + return 0 + fi + fi + if command -v 7za >/dev/null 2>&1; then + if 7za x -y "-o$extract_dir" "$archive_path"; then + return 0 + fi + fi + if command -v bsdtar >/dev/null 2>&1; then + if (cd "$extract_dir" && bsdtar -xf "$archive_path"); then + return 0 + fi + fi + return 1 +} + +if ! extract_archive; then + echo "could not extract $archive_path; install cmake, 7zz, 7z, 7za, or bsdtar" >&2 + exit 1 +fi + +version_header= +for candidate in \ + "$extract_dir/C/7z/7zVersion.h" \ + "$extract_dir/C/7zVersion.h" \ + "$extract_dir/7z/7zVersion.h" \ + "$extract_dir/7zVersion.h" +do + if [ -f "$candidate" ]; then + version_header=$candidate + break + fi +done + +if [ -z "$version_header" ]; then + version_header=$(find "$extract_dir" -path '*/C/7z/7zVersion.h' -print -quit) +fi +if [ -z "$version_header" ]; then + version_header=$(find "$extract_dir" -path '*/C/7zVersion.h' -print -quit) +fi +if [ -z "$version_header" ]; then + version_header=$(find "$extract_dir" -path '*/7z/7zVersion.h' -print -quit) +fi +if [ -z "$version_header" ]; then + version_header=$(find "$extract_dir" -path '*/7zVersion.h' -print -quit) +fi +if [ -z "$version_header" ]; then + echo "downloaded SDK does not contain 7z/7zVersion.h" >&2 + exit 1 +fi + +if ! grep -F "MY_VERSION \"$sdk_version\"" "$version_header" >/dev/null; then + echo "downloaded SDK is not version $sdk_version: $version_header" >&2 + exit 1 +fi + +case "$version_header" in + */C/7z/7zVersion.h) + sdk_root=${version_header%/C/7z/7zVersion.h} + ;; + */C/7zVersion.h) + sdk_root=${version_header%/C/7zVersion.h} + ;; + */7z/7zVersion.h) + sdk_root=${version_header%/7z/7zVersion.h} + ;; + */7zVersion.h) + sdk_root=${version_header%/7zVersion.h} + ;; + *) + echo "unexpected SDK layout for $version_header" >&2 + exit 1 + ;; +esac + +if [ -f "$sdk_root/C/7zVersion.h" ]; then + mkdir -p "$sdk_root/C/7z" + find "$sdk_root/C" -maxdepth 1 -type f \ + \( -name '*.c' -o -name '*.h' -o -name '*.rc' \) \ + -exec mv {} "$sdk_root/C/7z/" \; +elif [ -f "$sdk_root/7zVersion.h" ]; then + mkdir -p "$sdk_root/7z" + find "$sdk_root" -maxdepth 1 -type f \ + \( -name '*.c' -o -name '*.h' -o -name '*.rc' \) \ + -exec mv {} "$sdk_root/7z/" \; +fi + +if [ ! -f "$sdk_root/C/7z/7zVersion.h" ] && + [ ! -f "$sdk_root/7z/7zVersion.h" ]; then + echo "could not normalize SDK layout under $sdk_root" >&2 + exit 1 +fi + +target_tmp="$target_dir.tmp.$$" +rm -rf "$target_tmp" +mv "$sdk_root" "$target_tmp" +rm -rf "$target_dir" +mv "$target_tmp" "$target_dir" diff --git a/tools/require-qemu-uae-plugin.sh b/tools/require-qemu-uae-plugin.sh new file mode 100755 index 00000000..07f12ca1 --- /dev/null +++ b/tools/require-qemu-uae-plugin.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -eu + +if [ "$#" -ne 1 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +plugin=$1 + +if [ ! -f "$plugin" ]; then + echo "error: Unix PPC support is enabled, but no qemu-uae.so" \ + "plugin is available for packaging." >&2 + echo "error: expected plugin: $plugin" >&2 + echo "error: set WINUAE_QEMU_UAE_PLUGIN_FILE or build the" \ + "winuae_unix_qemu_uae_plugin target." >&2 + exit 1 +fi diff --git a/tools/unix-check-linux-package-metadata.sh b/tools/unix-check-linux-package-metadata.sh new file mode 100755 index 00000000..1a683a07 --- /dev/null +++ b/tools/unix-check-linux-package-metadata.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +set -euo pipefail + +source_dir="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" + +desktop="${source_dir}/od-unix/share/applications/net.winuae.WinUAE.desktop" +mime="${source_dir}/od-unix/share/mime/packages/net.winuae.WinUAE.xml" +icon="${source_dir}/od-win32/resources/amiga.png" +cmake="${source_dir}/CMakeLists.txt" +readme="${source_dir}/README_unix.md" +deb_builder="${source_dir}/tools/debian-build-package.sh" +deb_postinst="${source_dir}/packaging/debian/postinst" +deb_postrm="${source_dir}/packaging/debian/postrm" + +require_file() { + local path="$1" + local label="$2" + if [[ ! -f "${path}" ]]; then + echo "error: missing ${label}: ${path}" >&2 + exit 1 + fi +} + +require_match() { + local pattern="$1" + local path="$2" + local label="$3" + if ! grep -Eq "${pattern}" "${path}"; then + echo "error: ${label} not found in ${path}" >&2 + exit 1 + fi +} + +require_file "${desktop}" "desktop entry" +require_file "${mime}" "MIME package" +require_file "${icon}" "Linux icon source" +require_file "${cmake}" "CMake package rules" +require_file "${readme}" "README package documentation" +require_file "${deb_builder}" "Debian package builder" +require_file "${deb_postinst}" "Debian postinst control script" +require_file "${deb_postrm}" "Debian postrm control script" + +require_match '^\[Desktop Entry\]$' "${desktop}" "desktop header" +require_match '^Type=Application$' "${desktop}" "desktop application type" +require_match '^Name=WinUAE$' "${desktop}" "desktop name" +require_match '^Exec=winuae %f$' "${desktop}" "desktop exec" +require_match '^Icon=winuae$' "${desktop}" "desktop icon" +require_match '^Categories=.*(^|;)Emulator(;|$)' "${desktop}" "desktop category" +require_match '^MimeType=application/x-winuae-config;$' "${desktop}" "desktop MIME type" + +require_match '' "${mime}" "MIME type" +require_match '' "${mime}" "MIME glob" + +if ! file "${icon}" | grep -q 'PNG image data'; then + echo "error: Linux icon source is not a PNG: ${icon}" >&2 + exit 1 +fi + +require_match 'install\(PROGRAMS "\$"' "${cmake}" "Linux executable install rule" +require_match 'RENAME winuae' "${cmake}" "Linux executable install name" +require_match 'WINUAE_UNIX_INSTALL_PLUGINDIR_RELATIVE' "${cmake}" "Linux plugin install directory" +require_match 'WINUAE_QEMU_UAE_PLUGIN_FILE' "${cmake}" "prebuilt QEMU-UAE plugin option" +require_match 'winuae_unix_qemu_uae_package_check' "${cmake}" "QEMU-UAE package requirement" +require_match 'install\(FILES "\$\{WINUAE_QEMU_UAE_PLUGIN_PATH\}"' "${cmake}" "QEMU-UAE plugin install rule" +require_match 'od-unix/share/applications/net\.winuae\.WinUAE\.desktop' "${cmake}" "desktop install rule" +require_match 'od-unix/share/mime/packages/net\.winuae\.WinUAE\.xml' "${cmake}" "MIME install rule" +require_match 'icons/hicolor/256x256/apps' "${cmake}" "application icon install rule" +require_match 'icons/hicolor/256x256/mimetypes' "${cmake}" "MIME icon install rule" +require_match 'set\(CPACK_GENERATOR "TGZ"\)' "${cmake}" "CPack TGZ generator" +require_match 'list\(APPEND CPACK_GENERATOR "DEB"\)' "${cmake}" "CPack DEB generator" +require_match 'CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON' "${cmake}" "CPack Debian shlibdeps" +require_match 'CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA' "${cmake}" "CPack Debian control scripts" +require_match 'packaging/debian/postinst' "${cmake}" "Debian postinst registration" +require_match 'packaging/debian/postrm' "${cmake}" "Debian postrm registration" +require_match 'add_dependencies\(package winuae_unix_qemu_uae_plugin\)' "${cmake}" "package plugin dependency" + +require_match 'cmake --install /tmp/winuae_cmake_build --prefix /opt/winuae' "${readme}" "Linux install instructions" +require_match 'cmake --build /tmp/winuae_cmake_build --target package' "${readme}" "Linux package instructions" +require_match 'tools/debian-build-package.sh' "${readme}" "Debian package helper instructions" +require_match 'installs the emulator as `winuae`' "${readme}" "Debian command name documentation" +require_match 'qemu-uae\.so' "${readme}" "QEMU-UAE package documentation" +require_match 'WINUAE_UNIX_WITH_PPC_QEMU:BOOL' "${deb_builder}" "Debian plugin requirement" +require_match '/usr/bin/winuae' "${deb_builder}" "Debian installed binary check" +require_match 'package does not contain qemu-uae\.so' "${deb_builder}" "Debian plugin contents check" + +echo "verified Linux package metadata"