From 43b53de960128ab31aa1c74e8a30962350f236e1 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sat, 5 Sep 2026 00:37:42 -0500 Subject: [PATCH] Mac build is now universal. --- CHANGELOG | 10 ++++++++++ CMakeLists.txt | 29 +++++++++++++++++++---------- INSTALL | 21 +++++++++++---------- build-all.sh | 1 + cmake/Superbuild.cmake | 11 +++++++++-- cmake/zig/aarch64-macos.cmake | 32 +++++++++++++++++++++++++------- cmake/zig/zig.cmake | 7 ++++--- thirdparty/zlib/zutil.h | 2 +- 8 files changed, 80 insertions(+), 33 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index adf81f787..e51a2cd78 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -229,6 +229,16 @@ Fixes version header, and the manual are generated by CMake. The manual is embedded again and extracted as Singe/Manual.pdf. +- One command builds Singe from a clean checkout on a Debian based + system: ./build-all.sh installs the host packages, + then CMake fetches zig (the compiler for every target), the Pi's + platform packages and the macOS SDK into .builddir/toolchains and + builds every library and Singe itself. The results are a Linux binary + that runs on glibc 2.28 or newer, a Windows 10 or newer binary, a + 64-bit Raspberry Pi OS (bullseye or newer) binary and a macOS 13 or + newer Apple silicon binary. The toolchains repository is no longer + needed. See INSTALL. + SINGE 2.10 ========== diff --git a/CMakeLists.txt b/CMakeLists.txt index dac5d4a42..389973ad6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,6 +505,7 @@ elseif(KANGAROO_OS STREQUAL "macos") -Wl,-weak_framework,Metal -Wl,-weak_framework,QuartzCore -Wl,-weak_framework,CoreHaptics + -Wl,-weak_framework,UniformTypeIdentifiers -liconv -lc++ ) @@ -552,17 +553,24 @@ else() message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}") endif() -# FFmpeg's hardware decoding backends (VA-API, VDPAU, DRM, V4L2) are whatever the superbuild -# configured; its pkg-config files name the system libraries they need, so read them back. -if(KANGAROO_OS STREQUAL "linux" OR KANGAROO_OS STREQUAL "pi") +# FFmpeg's hardware decoding backends (VA-API, VDPAU, DRM, V4L2, VideoToolbox) are whatever the +# superbuild configured; its pkg-config files name the system libraries and frameworks they need, so +# read them back. +if(NOT KANGAROO_OS STREQUAL "windows") foreach(pc libavutil libavcodec) set(pcFile ${BUILD_DIR}/lib/pkgconfig/${pc}.pc) if(EXISTS ${pcFile}) file(STRINGS ${pcFile} pcLibs REGEX "^Libs:") string(REGEX REPLACE "^Libs: *" "" pcLibs "${pcLibs}") separate_arguments(pcLibs) + set(framework FALSE) foreach(token ${pcLibs}) - if(token MATCHES "^-l" AND NOT token MATCHES "^-l(avutil|avcodec|z|m|atomic|pthread)$") + if(framework) + list(APPEND SYSTEM_LIBS -Wl,-framework,${token}) + set(framework FALSE) + elseif(token STREQUAL "-framework") + set(framework TRUE) + elseif(token MATCHES "^-l" AND NOT token MATCHES "^-l(avutil|avcodec|z|m|atomic|pthread|iconv)$") list(APPEND SYSTEM_LIBS ${token}) endif() endforeach() @@ -600,12 +608,13 @@ set(STATIC_LIBS ${BUILD_DIR}/lib/libcrypto.a ${BUILD_DIR}/lib/libssl.a ) -if(NOT KANGAROO_OS STREQUAL "macos") - list(APPEND STATIC_LIBS - ${BUILD_DIR}/lib/libjpeg.a - ${BUILD_DIR}/lib/libpng16.a - ) -endif() +# SDL3_image builds its own jpeg and png decoders where the platform has none (macOS uses ImageIO +# for jpeg); link whichever archives it produced. +foreach(imageLib libjpeg.a libpng16.a) + if(EXISTS ${BUILD_DIR}/lib/${imageLib}) + list(APPEND STATIC_LIBS ${BUILD_DIR}/lib/${imageLib}) + endif() +endforeach() # System libraries follow the static ones so their symbols resolve for everything above. # Apple's linker has no --start-group, so the list is repeated there instead. diff --git a/INSTALL b/INSTALL index 7003d25d2..015a9968b 100644 --- a/INSTALL +++ b/INSTALL @@ -59,7 +59,7 @@ library (zlib, zstd, SDL3 and its satellites, OpenSSL, FFmpeg) into copied to .builddir/Singe-v--. Host packages (what build-all.sh installs): build-essential cmake -pkg-config perl nasm imagemagick lua5.4 ffmpeg asciidoctor +pkg-config perl nasm llvm imagemagick lua5.4 ffmpeg asciidoctor ruby-asciidoctor-pdf autoconf automake libtool libasound-dev libxi-dev libvdpau-dev libva-dev libdrm-dev libgl-dev libx11-dev. @@ -76,18 +76,19 @@ libraries itself, so nothing else is installed: A developer build with the host's own gcc, glibc and symbols is the linux-x86_64-gcc preset (binary Singe-v3.00-Linux-x86_64-gcc). -macOS builds with zig too, but needs an Apple SDK for the frameworks, -which Apple's licence does not allow a build script to fetch. Provide it -once as a tarball holding a single MacOSX*.sdk directory: +macOS (Apple silicon, macOS 13 or newer) builds with zig too, but needs +an Apple SDK for the frameworks. +Apple only ships it inside Xcode, so by default the build fetches the +MacOSX15.5 SDK tarball from the community mirror osxcross users share +(github.com/joseluisq/macosx-sdks), checks its SHA-256, and unpacks it to +.builddir/toolchains/MacOSX.sdk on first use. To avoid the download, +put a tarball holding a single MacOSX*.sdk directory at .builddir/toolchains/MacOSX.sdk.tar.xz -On a Mac with Xcode installed, osxcross's tools/gen_sdk_package.sh makes -exactly that tarball from the installed SDK. The build unpacks it to -.builddir/toolchains/MacOSX.sdk on first use. Alternatively point -SINGE_MACOS_SDK at an unpacked SDK, or set SINGE_MACOS_SDK_URL (and -SINGE_MACOS_SDK_SHA256) to have the build fetch the tarball from a -location of your choosing. Then: +(on a Mac with Xcode, osxcross's tools/gen_sdk_package.sh makes exactly +that), or point SINGE_MACOS_SDK at an unpacked SDK. SINGE_MACOS_SDK_URL +and SINGE_MACOS_SDK_SHA256 pick a different mirror. Then: ./build-all.sh macos aarch64 diff --git a/build-all.sh b/build-all.sh index 7a9c6cbea..f50d4ff81 100755 --- a/build-all.sh +++ b/build-all.sh @@ -61,6 +61,7 @@ sudo apt-get install -y \ pkg-config \ perl \ nasm \ + llvm \ imagemagick \ lua5.4 \ ffmpeg \ diff --git a/cmake/Superbuild.cmake b/cmake/Superbuild.cmake index 4b9325e81..eeca60232 100644 --- a/cmake/Superbuild.cmake +++ b/cmake/Superbuild.cmake @@ -254,10 +254,17 @@ endif() # With a classic cross toolchain every tool is -; with zig each is named outright. if(SINGE_ZIG_TARGET) set(ffmpegTools --enable-cross-compile --cc=${SB_CC} --cxx=${SB_CXX} --ar=${CMAKE_AR} --ranlib=${SB_RANLIB} --windres=${CMAKE_RC_COMPILER}) - # The host's binutils nm reads x86 ELF and PE objects but not aarch64 or Mach-O ones; FFmpeg only - # uses nm to learn the symbol prefix, which is empty everywhere but macOS, so the others go without. + # FFmpeg only uses nm to learn the symbol prefix. The host's binutils nm reads x86 ELF and PE + # objects but not aarch64 or Mach-O ones; the prefix is empty on Linux either way, and on macOS + # (where it is an underscore) llvm-nm from Debian's llvm package reads Mach-O. if(KANGAROO_ARCH STREQUAL "x86_64") list(APPEND ffmpegTools --nm=nm) + elseif(KANGAROO_OS STREQUAL "macos") + find_program(SINGE_LLVM_NM NAMES llvm-nm llvm-nm-21 llvm-nm-20 llvm-nm-19 llvm-nm-18 llvm-nm-17 llvm-nm-16 llvm-nm-15) + if(NOT SINGE_LLVM_NM) + message(FATAL_ERROR "llvm-nm is needed for the macOS FFmpeg build (apt-get install llvm).") + endif() + list(APPEND ffmpegTools --nm=${SINGE_LLVM_NM}) endif() else() set(ffmpegTools --cross-prefix=${SINGE_TRIPLE}- --cc=${SB_CC} --cxx=${SB_CXX} --ranlib=${SB_RANLIB}) diff --git a/cmake/zig/aarch64-macos.cmake b/cmake/zig/aarch64-macos.cmake index 57eeab5f1..a301c3b19 100644 --- a/cmake/zig/aarch64-macos.cmake +++ b/cmake/zig/aarch64-macos.cmake @@ -23,18 +23,23 @@ # script fetch. The SDK is expected, in this order: # 1. an unpacked SDK at SINGE_MACOS_SDK (default .builddir/toolchains/MacOSX.sdk); # 2. a tarball at .builddir/toolchains/MacOSX.sdk.tar.xz, unpacked on first use; -# 3. SINGE_MACOS_SDK_URL (with SINGE_MACOS_SDK_SHA256), downloaded and unpacked on first use. -# See INSTALL for how to make the tarball from Xcode. +# 3. SINGE_MACOS_SDK_URL (with SINGE_MACOS_SDK_SHA256), downloaded and unpacked on first use; +# the default points at the community mirror osxcross users share (github.com/joseluisq/ +# macosx-sdks), pinned to one SDK release and its published SHA-256. +# See INSTALL for how to make the tarball from Xcode instead. include(${CMAKE_CURRENT_LIST_DIR}/zig.cmake) set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_SYSTEM_PROCESSOR arm64) +# Oldest macOS the binary runs on (Ventura: Intel Macs from 2017 and every Apple silicon Mac). +set(SINGE_MACOS_VERSION 13.0) + get_filename_component(sdkToolchains ${CMAKE_CURRENT_LIST_DIR}/../../.builddir/toolchains ABSOLUTE) set(SINGE_MACOS_SDK ${sdkToolchains}/MacOSX.sdk CACHE PATH "Unpacked Apple SDK for the macOS build") -set(SINGE_MACOS_SDK_URL "" CACHE STRING "Where to fetch the Apple SDK tarball from, if anywhere") -set(SINGE_MACOS_SDK_SHA256 "" CACHE STRING "SHA-256 of that tarball") +set(SINGE_MACOS_SDK_URL "https://github.com/joseluisq/macosx-sdks/releases/download/15.5/MacOSX15.5.sdk.tar.xz" CACHE STRING "Where to fetch the Apple SDK tarball from; empty to never download") +set(SINGE_MACOS_SDK_SHA256 "c15cf0f3f17d714d1aa5a642da8e118db53d79429eb015771ba816aa7c6c1cbd" CACHE STRING "SHA-256 of that tarball; empty to skip the check") set(sdkTarball ${sdkToolchains}/MacOSX.sdk.tar.xz) if(NOT EXISTS ${SINGE_MACOS_SDK}/System/Library/Frameworks/Cocoa.framework) if((NOT EXISTS ${sdkTarball}) AND SINGE_MACOS_SDK_URL) @@ -68,9 +73,22 @@ if(NOT EXISTS ${SINGE_MACOS_SDK}/System/Library/Frameworks/Cocoa.framework) message(FATAL_ERROR "No Apple SDK at ${SINGE_MACOS_SDK}. Put an SDK tarball at ${sdkTarball} (see INSTALL), or set SINGE_MACOS_SDK to an unpacked SDK, or SINGE_MACOS_SDK_URL to fetch one.") endif() -# The SDK's headers come after zig's own libc headers; frameworks and stub libraries from the SDK. -singeZigToolchain(aarch64-macos "--sysroot=${SINGE_MACOS_SDK} -idirafter ${SINGE_MACOS_SDK}/usr/include -F${SINGE_MACOS_SDK}/System/Library/Frameworks -L${SINGE_MACOS_SDK}/usr/lib") -set(CMAKE_OSX_SYSROOT ${SINGE_MACOS_SDK} CACHE PATH "Apple SDK" FORCE) +# No --sysroot: zig would then prefix every -L path with it, the superbuild's own lib directory +# included. The SDK is named outright instead: its usr/include (libDER and other non-libc pieces) +# after zig's own libc headers, its frameworks, and its stub libraries. The SDK's CMTag.h uses two +# macOS 14 types without an availability guard, and FFmpeg's configure makes that an error with +# -Werror=partial-availability; the trailing -Wno-unguarded-availability-new wins over it. +singeZigToolchain(aarch64-macos.${SINGE_MACOS_VERSION} "-idirafter ${SINGE_MACOS_SDK}/usr/include -F${SINGE_MACOS_SDK}/System/Library/Frameworks -L${SINGE_MACOS_SDK}/usr/lib -Wno-unguarded-availability-new") +# SDL3 compiles Objective-C, which the same zig wrappers handle, and CMake's Darwin support insists +# on an install_name_tool even though nothing here links dynamically; llvm's stands in. +set(CMAKE_OBJC_COMPILER ${CMAKE_C_COMPILER} CACHE FILEPATH "" FORCE) +set(CMAKE_OBJCXX_COMPILER ${CMAKE_CXX_COMPILER} CACHE FILEPATH "" FORCE) +find_program(SINGE_INSTALL_NAME_TOOL NAMES llvm-install-name-tool llvm-install-name-tool-21 llvm-install-name-tool-20 llvm-install-name-tool-19 llvm-install-name-tool-18 llvm-install-name-tool-17 llvm-install-name-tool-16 llvm-install-name-tool-15) +if(NOT SINGE_INSTALL_NAME_TOOL) + message(FATAL_ERROR "llvm-install-name-tool is needed for the macOS build (apt-get install llvm).") +endif() +set(CMAKE_INSTALL_NAME_TOOL ${SINGE_INSTALL_NAME_TOOL} CACHE FILEPATH "" FORCE) +set(CMAKE_OSX_SYSROOT "" CACHE PATH "Left empty; the wrappers name the SDK themselves" FORCE) set(CMAKE_OSX_ARCHITECTURES "" CACHE STRING "Left empty; the zig target names the architecture" FORCE) set(CMAKE_FIND_ROOT_PATH ${SINGE_MACOS_SDK}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/cmake/zig/zig.cmake b/cmake/zig/zig.cmake index 10d9202d9..8fa42db52 100644 --- a/cmake/zig/zig.cmake +++ b/cmake/zig/zig.cmake @@ -50,9 +50,10 @@ endif() function(singeZigToolchain target extra) set(dir ${CMAKE_BINARY_DIR}/zig-${target}) file(MAKE_DIRECTORY ${dir}) - # The compiler wrappers drop one flag: FFmpeg's configure adds binutils' --pic-executable ASLR - # workaround to every test link on Windows, and zig's linker driver refuses it. They also add - set(filter "for a in \"$@\"; do [[ $a == -Wl,--pic-executable* ]] || args+=(\"$a\"); done") + # The compiler wrappers drop two flags FFmpeg's configure adds to every test link and zig's linker + # driver refuses: binutils' --pic-executable ASLR workaround on Windows and Apple's + # -dynamic,-search_paths_first on macOS. They also add + set(filter "for a in \"$@\"; do [[ $a == -Wl,--pic-executable* || $a == -Wl,-dynamic* ]] || args+=(\"$a\"); done") # -fno-sanitize=undefined: zig cc instruments for UBSan by default and expects its runtime at # link time; a release build wants neither. # extra goes after the caller's arguments so the caller's -L directories are searched first. diff --git a/thirdparty/zlib/zutil.h b/thirdparty/zlib/zutil.h index 902a304cc..8907a5dcc 100644 --- a/thirdparty/zlib/zutil.h +++ b/thirdparty/zlib/zutil.h @@ -137,7 +137,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#if defined(MACOS) || defined(TARGET_OS_MAC) +#if defined(MACOS) # define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os