From: Stefan Reinauer Date: Fri, 12 Jun 2026 05:57:19 +0000 (-0700) Subject: cmake: own the qemu-uae plugin provisioning X-Git-Url: https://gitweb.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=e29606e2e31d2a3a5613f4decfce12bd1afd1bf5;p=francis%2Fwinuae.git cmake: own the qemu-uae plugin provisioning The mandatory qemu-uae.so plugin was only buildable from a sibling qemu-uae source tree; distro builds without one configured fine, built fine, and then failed at install time when the install rule referenced the never-built plugin (GitHub issue #6). Both CI workflows carried their own duplicated checkout/build/strip plumbing for the uae-ppc-plugin builder instead. Resolve the plugin at configure time, in order: prebuilt WINUAE_QEMU_UAE_PLUGIN_FILE, the sibling qemu-uae source tree, an uae-ppc-plugin builder checkout (WINUAE_QEMU_UAE_BUILDER_DIR, default sibling; WINUAE_QEMU_UAE_QEMU_TARBALL feeds it a local QEMU tarball for offline distribution builds), or a build-time clone of the builder (WINUAE_QEMU_UAE_BUILDER_FETCH, default ON). When nothing applies the configure fails with the list of options. On Linux the plugin target is part of the default build so plain build+install flows always bundle the plugin. Drive the builder through an OUTPUT-based custom command so repeated `all` builds and CPack preinstall do not rerun the external builder once qemu-uae.so is already present. The Ubuntu and macOS workflows drop their manual plugin build steps and pass the builder checkout to CMake; stripping moved into the builder wrapper script. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb901ae..640eff19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,13 @@ set(WINUAE_LZMA_SDK_SYSTEM_PATHS set(WINUAE_QEMU_UAE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../qemu-uae-v11.0" CACHE PATH "QEMU-UAE plugin source directory") set(WINUAE_QEMU_UAE_PLUGIN_FILE "" CACHE FILEPATH "Prebuilt qemu-uae.so plugin to install or bundle") +set(WINUAE_QEMU_UAE_BUILDER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../uae-ppc-plugin" + CACHE PATH "uae-ppc-plugin builder checkout used to build qemu-uae.so") +set(WINUAE_QEMU_UAE_BUILDER_URL "https://github.com/reinauer/uae-ppc-plugin.git" + CACHE STRING "Git URL cloned at build time when the builder checkout is missing") +option(WINUAE_QEMU_UAE_BUILDER_FETCH "Clone the uae-ppc-plugin builder when no local copy is found" ON) +set(WINUAE_QEMU_UAE_QEMU_TARBALL "" CACHE FILEPATH + "Local QEMU source tarball for offline qemu-uae plugin builds") set(WINUAE_UNIX_INSTALL_PLUGINDIR_RELATIVE "${CMAKE_INSTALL_LIBDIR}/winuae/plugins" CACHE STRING "Relative Unix install directory for runtime plugins") @@ -1703,30 +1710,84 @@ if(WINUAE_QEMU_UAE_PLUGIN_FILE) else() set(WINUAE_QEMU_UAE_PLUGIN_PATH "${CMAKE_BINARY_DIR}/qemu-uae.so") endif() +# On Linux the plugin is part of the default build so that plain +# `cmake --build` + `cmake --install` flows (distro packaging) always +# produce the mandatory qemu-uae.so. macOS keeps it behind the app/dmg +# and package targets. +set(WINUAE_QEMU_UAE_PLUGIN_ALL) +if(UNIX AND NOT APPLE) + set(WINUAE_QEMU_UAE_PLUGIN_ALL ALL) +endif() if(UNIX AND WINUAE_UNIX_WITH_PPC_QEMU AND WINUAE_UNIX_BUILD_QEMU_UAE_PLUGIN - AND EXISTS "${WINUAE_QEMU_UAE_SOURCE_DIR}/configure-qemu-uae") - add_custom_target(winuae_unix_qemu_uae_plugin - COMMAND "${CMAKE_COMMAND}" -E env - "WINUAE_MACOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" - "QEMU_UAE_DEPS_PREFIX=${WINUAE_QEMU_UAE_DEPS_PREFIX}" - "${CMAKE_CURRENT_SOURCE_DIR}/tools/build-qemu-uae.sh" - "${WINUAE_QEMU_UAE_SOURCE_DIR}" - "${WINUAE_QEMU_UAE_PLUGIN_PATH}" - WORKING_DIRECTORY "${WINUAE_QEMU_UAE_SOURCE_DIR}" - COMMENT "Building QEMU-UAE PPC plugin" - VERBATIM - ) + AND NOT WINUAE_QEMU_UAE_PLUGIN_FILE) + if(EXISTS "${WINUAE_QEMU_UAE_SOURCE_DIR}/configure-qemu-uae") + message(STATUS + "QEMU-UAE plugin: building from sibling source ${WINUAE_QEMU_UAE_SOURCE_DIR}") + add_custom_target(winuae_unix_qemu_uae_plugin ${WINUAE_QEMU_UAE_PLUGIN_ALL} + COMMAND "${CMAKE_COMMAND}" -E env + "WINUAE_MACOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + "QEMU_UAE_DEPS_PREFIX=${WINUAE_QEMU_UAE_DEPS_PREFIX}" + "${CMAKE_CURRENT_SOURCE_DIR}/tools/build-qemu-uae.sh" + "${WINUAE_QEMU_UAE_SOURCE_DIR}" + "${WINUAE_QEMU_UAE_PLUGIN_PATH}" + WORKING_DIRECTORY "${WINUAE_QEMU_UAE_SOURCE_DIR}" + COMMENT "Building QEMU-UAE PPC plugin" + VERBATIM + ) + elseif(EXISTS "${WINUAE_QEMU_UAE_BUILDER_DIR}/build-qemu-uae-plugin.sh" + OR WINUAE_QEMU_UAE_BUILDER_FETCH) + if(EXISTS "${WINUAE_QEMU_UAE_BUILDER_DIR}/build-qemu-uae-plugin.sh") + message(STATUS + "QEMU-UAE plugin: building through ${WINUAE_QEMU_UAE_BUILDER_DIR}") + else() + message(STATUS + "QEMU-UAE plugin: builder will be cloned from ${WINUAE_QEMU_UAE_BUILDER_URL}") + endif() + # OUTPUT-based so repeated `all` builds (e.g. the CPack preinstall + # step) skip the builder once the plugin exists; the external + # builder's configure step is not re-entrant. + add_custom_command(OUTPUT "${WINUAE_QEMU_UAE_PLUGIN_PATH}" + COMMAND "${CMAKE_COMMAND}" -E env + "WINUAE_MACOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + "QEMU_UAE_DEPS_PREFIX=${WINUAE_QEMU_UAE_DEPS_PREFIX}" + "QEMU_UAE_WORK_DIR=${CMAKE_BINARY_DIR}/_deps/qemu-uae" + "WINUAE_QEMU_UAE_BUILDER_DIR=${WINUAE_QEMU_UAE_BUILDER_DIR}" + "WINUAE_QEMU_UAE_BUILDER_URL=${WINUAE_QEMU_UAE_BUILDER_URL}" + "WINUAE_QEMU_UAE_BUILDER_FETCH=$" + "WINUAE_QEMU_UAE_QEMU_TARBALL=${WINUAE_QEMU_UAE_QEMU_TARBALL}" + "${CMAKE_CURRENT_SOURCE_DIR}/tools/build-qemu-uae-via-builder.sh" + "${WINUAE_QEMU_UAE_PLUGIN_PATH}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tools/build-qemu-uae-via-builder.sh" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building QEMU-UAE PPC plugin through uae-ppc-plugin" + VERBATIM + ) + add_custom_target(winuae_unix_qemu_uae_plugin ${WINUAE_QEMU_UAE_PLUGIN_ALL} + DEPENDS "${WINUAE_QEMU_UAE_PLUGIN_PATH}" + ) + endif() endif() -if(WINUAE_UNIX_WITH_PPC_QEMU) +if(UNIX AND WINUAE_UNIX_WITH_PPC_QEMU) if(WINUAE_QEMU_UAE_PLUGIN_FILE) if(NOT EXISTS "${WINUAE_QEMU_UAE_PLUGIN_PATH}") message(FATAL_ERROR "WINUAE_QEMU_UAE_PLUGIN_FILE does not exist: " "${WINUAE_QEMU_UAE_PLUGIN_PATH}") endif() + message(STATUS + "QEMU-UAE plugin: using prebuilt ${WINUAE_QEMU_UAE_PLUGIN_PATH}") + elseif(NOT TARGET winuae_unix_qemu_uae_plugin) + message(FATAL_ERROR + "WINUAE_UNIX_WITH_PPC_QEMU requires the QEMU-UAE PPC plugin, but no " + "way to provide qemu-uae.so was found. Provide one of:\n" + " -DWINUAE_QEMU_UAE_PLUGIN_FILE=/path/to/qemu-uae.so (prebuilt plugin)\n" + " -DWINUAE_QEMU_UAE_BUILDER_DIR=/path/to/uae-ppc-plugin (builder checkout; " + "offline builds also want -DWINUAE_QEMU_UAE_QEMU_TARBALL=/path/to/qemu-11.0.1.tar.xz)\n" + " -DWINUAE_QEMU_UAE_BUILDER_FETCH=ON to clone ${WINUAE_QEMU_UAE_BUILDER_URL} at build time\n" + " a qemu-uae source tree at ${WINUAE_QEMU_UAE_SOURCE_DIR}") endif() set(WINUAE_UNIX_QEMU_UAE_PACKAGE_CHECK_DEPS) if(TARGET winuae_unix_qemu_uae_plugin) diff --git a/README_unix.md b/README_unix.md index 368114d9..37092b87 100644 --- a/README_unix.md +++ b/README_unix.md @@ -184,17 +184,29 @@ when found under `WINUAE_LZMA_SDK_SYSTEM_PATHS` (or an explicit SHA-verified `lzma1604.7z` archive. Pass `-DWINUAE_LZMA_SDK_FETCH=OFF` for offline distribution builds. -To build a package from a prebuilt QEMU-UAE plugin, pass: - -```sh -tools/debian-build-package.sh -- \ - -DWINUAE_QEMU_UAE_PLUGIN_FILE=/path/to/qemu-uae.so \ - -DWINUAE_UNIX_BUILD_QEMU_UAE_PLUGIN=OFF -``` - -Package builds fail when `WINUAE_UNIX_WITH_PPC_QEMU=ON` and the resolved -`qemu-uae.so` path does not exist. Disable `WINUAE_UNIX_WITH_PPC_QEMU` for a -deliberately reduced package without PPC plugin support. +The `qemu-uae.so` PPC plugin is a mandatory part of Unix packages. CMake +resolves it at configure time, in this order, and fails the configure with +instructions when none applies: + +1. `-DWINUAE_QEMU_UAE_PLUGIN_FILE=/path/to/qemu-uae.so` — a prebuilt plugin. +2. A patched `qemu-uae` source tree at `WINUAE_QEMU_UAE_SOURCE_DIR` + (default: sibling `../qemu-uae-v11.0`) — the developer path. +3. The `uae-ppc-plugin` builder at `WINUAE_QEMU_UAE_BUILDER_DIR` + (default: sibling `../uae-ppc-plugin`). The builder downloads a + SHA-verified QEMU source tarball, applies the UAE patch deck, and builds + the plugin during the WinUAE build. For offline builds (distribution + packaging), declare the QEMU tarball as a package source and pass + `-DWINUAE_QEMU_UAE_QEMU_TARBALL=/path/to/qemu-11.0.1.tar.xz`. +4. When no builder checkout is found and `WINUAE_QEMU_UAE_BUILDER_FETCH` is + `ON` (the default), the builder is cloned at build time from + `WINUAE_QEMU_UAE_BUILDER_URL`. + +On Linux the plugin target is part of the default build, so plain +`cmake --build` + `cmake --install` flows always produce and install the +plugin. A distribution package build needs `meson`, `ninja`, GLib and libfdt +development headers for the embedded QEMU build, plus the builder checkout +and QEMU tarball as additional sources when the build host has no network +access. The Linux install/package metadata can be checked on any Unix host: @@ -280,11 +292,14 @@ CMake also exposes this as: cmake --build /tmp/winuae_cmake_build --target winuae_unix_qemu_uae_plugin -j ``` -`WINUAE_UNIX_BUILD_QEMU_UAE_PLUGIN` controls whether the sibling plugin target +`WINUAE_UNIX_BUILD_QEMU_UAE_PLUGIN` controls whether the plugin build target is enabled, and `WINUAE_QEMU_UAE_SOURCE_DIR` can point at a different `qemu-uae` source tree. `WINUAE_QEMU_UAE_PLUGIN_FILE` can point at an already -built plugin, which is useful for CI jobs that build the plugin through the -release-oriented `uae-ppc-plugin` wrapper. `WINUAE_QEMU_UAE_DEPS_PREFIX` +built plugin. Without a patched source tree, the target builds through the +release-oriented `uae-ppc-plugin` builder (`WINUAE_QEMU_UAE_BUILDER_DIR`, +cloned from `WINUAE_QEMU_UAE_BUILDER_URL` when missing and +`WINUAE_QEMU_UAE_BUILDER_FETCH` is enabled; `WINUAE_QEMU_UAE_QEMU_TARBALL` +supplies a local QEMU tarball for offline builds). `WINUAE_QEMU_UAE_DEPS_PREFIX` defaults to the private macOS dependency prefix and is passed to the plugin helper as `QEMU_UAE_DEPS_PREFIX`. Set `QEMU_UAE_NINJA=/path/to/ninja` if QEMU configure cannot find Ninja itself. On macOS, the app bundler copies diff --git a/tools/build-qemu-uae-via-builder.sh b/tools/build-qemu-uae-via-builder.sh new file mode 100755 index 00000000..ea70f39d --- /dev/null +++ b/tools/build-qemu-uae-via-builder.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat < + +Builds qemu-uae.so through the external uae-ppc-plugin builder, which +downloads a SHA-verified QEMU source tarball (or uses a local one), +applies the UAE patch deck, and builds the plugin. + +Environment: + WINUAE_QEMU_UAE_BUILDER_DIR uae-ppc-plugin checkout. When missing and + fetching is allowed, it is cloned. + WINUAE_QEMU_UAE_BUILDER_URL Clone URL. Defaults to the reinauer repo. + WINUAE_QEMU_UAE_BUILDER_FETCH 0 disables cloning a missing builder. + WINUAE_QEMU_UAE_BUILDER_CLONE_DIR + Clone destination. Defaults to + /uae-ppc-plugin. + WINUAE_QEMU_UAE_QEMU_TARBALL Local QEMU source tarball for offline + builds; passed to the builder as --tarball. + WINUAE_QEMU_UAE_STRIP 0 keeps symbols in the built plugin. + QEMU_UAE_WORK_DIR Builder working directory. + QEMU_UAE_DEPS_PREFIX Private GLib/libslirp dependency prefix. + WINUAE_MACOS_DEPLOYMENT_TARGET macOS deployment target for the plugin. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" || $# -lt 1 ]]; then + usage + [[ $# -lt 1 ]] && exit 2 + exit 0 +fi + +output_plugin="$1" +builder_dir="${WINUAE_QEMU_UAE_BUILDER_DIR:-}" +builder_url="${WINUAE_QEMU_UAE_BUILDER_URL:-https://github.com/reinauer/uae-ppc-plugin.git}" +builder_fetch="${WINUAE_QEMU_UAE_BUILDER_FETCH:-1}" +work_dir="${QEMU_UAE_WORK_DIR:-$(pwd)/qemu-uae-work}" +clone_dir="${WINUAE_QEMU_UAE_BUILDER_CLONE_DIR:-${work_dir}/uae-ppc-plugin}" +tarball="${WINUAE_QEMU_UAE_QEMU_TARBALL:-}" + +builder_script() { + printf '%s/build-qemu-uae-plugin.sh' "$1" +} + +if [[ -z "${builder_dir}" || ! -x "$(builder_script "${builder_dir}")" ]]; then + if [[ -x "$(builder_script "${clone_dir}")" ]]; then + builder_dir="${clone_dir}" + elif [[ "${builder_fetch}" != "0" ]]; then + echo "Cloning QEMU-UAE plugin builder from ${builder_url}" + mkdir -p "$(dirname "${clone_dir}")" + git clone --depth 1 "${builder_url}" "${clone_dir}" + builder_dir="${clone_dir}" + else + echo "error: uae-ppc-plugin builder not found" >&2 + echo "hint: set WINUAE_QEMU_UAE_BUILDER_DIR to a checkout of" >&2 + echo " ${builder_url}, or allow cloning" >&2 + exit 1 + fi +fi + +args=( --output "${output_plugin}" ) +if [[ -n "${tarball}" ]]; then + if [[ ! -f "${tarball}" ]]; then + echo "error: WINUAE_QEMU_UAE_QEMU_TARBALL does not exist: ${tarball}" >&2 + exit 1 + fi + args+=( --tarball "${tarball}" ) +fi + +export QEMU_UAE_WORK_DIR="${work_dir}" +"$(builder_script "${builder_dir}")" "${args[@]}" + +if [[ "${WINUAE_QEMU_UAE_STRIP:-1}" != "0" ]]; then + if [[ "$(uname -s)" == "Darwin" ]]; then + strip -x "${output_plugin}" + else + strip --strip-unneeded "${output_plugin}" + fi +fi