# # Singe 3 # Copyright (C) 2006-2026 Scott Duensing # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public Licens # as published by the Free Software Foundation; either version 3 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # # Superbuild: builds every vendored library into BUILD_DIR, then Singe against them. # Entered from CMakeLists.txt when SINGE_SUPERBUILD is ON (the presets set it). Each # library is an ExternalProject with its own configure, build and install steps, so a # change to one rebuilds only it and what depends on it: # cmake --build --preset linux-x86_64 --target rebuild-ffmpeg # The toolchain environment (PATH, SYSROOT, osxcross variables) comes from # ../toolchains/toolchains.sh; build-all.sh sources it before calling cmake. include(ExternalProject) include(ProcessorCount) ProcessorCount(SB_JOBS) if(SB_JOBS EQUAL 0) set(SB_JOBS 4) endif() # Target description: the presets pass these; a plain cmake invocation can take them from # the environment toolchains.sh exports. foreach(pair "SINGE_TRIPLE;TRIPLE" "SINGE_CROSS_OS;CROSS_OS" "SINGE_SUFFIX;SUFFIX") list(GET pair 0 var) list(GET pair 1 env) if(NOT DEFINED ${var}) set(${var} "$ENV{${env}}") endif() endforeach() if(NOT SINGE_TRIPLE OR NOT SINGE_CROSS_OS) message(FATAL_ERROR "SINGE_TRIPLE and SINGE_CROSS_OS must be set (use a preset or source toolchains.sh).") endif() set(SB_PREFIX ${BUILD_DIR}) set(SB_THIRDPARTY ${CMAKE_SOURCE_DIR}/thirdparty) file(MAKE_DIRECTORY ${SB_PREFIX}/include ${SB_PREFIX}/lib) # Compilers as the toolchain resolved them; the configure based libraries get them by name. set(SB_CC "${CMAKE_C_COMPILER}") if(CMAKE_CXX_COMPILER) set(SB_CXX "${CMAKE_CXX_COMPILER}") elseif(DEFINED ENV{CXX}) set(SB_CXX "$ENV{CXX}") else() string(REGEX REPLACE "gcc$" "g++" SB_CXX "${SB_CC}") string(REGEX REPLACE "clang$" "clang++" SB_CXX "${SB_CXX}") endif() if(CMAKE_RANLIB) set(SB_RANLIB "${CMAKE_RANLIB}") elseif(DEFINED ENV{RANLIB}) set(SB_RANLIB "$ENV{RANLIB}") else() set(SB_RANLIB ranlib) endif() # The flags build-all.sh exported for every library: our install tree first, then the # platform's. The Pi sysroot and the osxcross linker come from the toolchain. set(SB_CFLAGS_BASE "$ENV{CFLAGS}") set(SB_LDFLAGS "-L${SB_PREFIX}/lib $ENV{LDFLAGS}") if(CMAKE_SYSROOT) set(SB_CFLAGS_BASE "--sysroot=${CMAKE_SYSROOT} ${SB_CFLAGS_BASE}") endif() set(SB_CFLAGS "-I${SB_PREFIX}/include ${SB_CFLAGS_BASE}") set(SB_PKG_CONFIG_LIBDIR "${SB_PREFIX}/lib/pkgconfig") # A cross build looks inside its sysroot; a build for this machine looks at this machine. The # sysroot is what tells them apart, not the platform's name. if(SINGE_SYSROOT) set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:${SINGE_SYSROOT}/usr/lib/${SINGE_TRIPLE}/pkgconfig:${SINGE_SYSROOT}/usr/lib/pkgconfig:${SINGE_SYSROOT}/usr/share/pkgconfig") elseif(KANGAROO_OS STREQUAL "linux") set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:/usr/lib/${SINGE_TRIPLE}/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig") elseif(DEFINED ENV{PKG_CONFIG_LIBDIR}) set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:$ENV{PKG_CONFIG_LIBDIR}") endif() # Environment every step runs under. CFLAGS is a parameter because SDL3_image's vendored # libpng must not see the zlib headers under the install prefix. function(singeStepEnv outVar cflags) set(env ${CMAKE_COMMAND} -E env "CC=${SB_CC}" "CXX=${SB_CXX}" "RANLIB=${SB_RANLIB}" "CFLAGS=${cflags}" "CXXFLAGS=${cflags}" "LDFLAGS=${SB_LDFLAGS}" "LD_LIBRARY_PATH=${SB_PREFIX}/lib" "PKG_CONFIG_LIBDIR=${SB_PKG_CONFIG_LIBDIR}" "MAKEFLAGS=-j${SB_JOBS}" ) if(CMAKE_AR) list(APPEND env "AR=${CMAKE_AR}") endif() if(SINGE_SYSROOT) list(APPEND env "PKG_CONFIG_SYSROOT_DIR=${SINGE_SYSROOT}") endif() set(${outVar} ${env} PARENT_SCOPE) endfunction() singeStepEnv(SB_ENV "${SB_CFLAGS}") singeStepEnv(SB_ENV_NO_PREFIX_INCLUDE "${SB_CFLAGS_BASE}") set(SB_CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${SB_PREFIX} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} ) # A vendored CMake project: name, source directory, its dependencies, extra cache arguments, # and the step environment to use. function(singeCmakeProject name source deps args env) set(binary ${SB_PREFIX}/build/${name}) ExternalProject_Add(${name} SOURCE_DIR ${source} BINARY_DIR ${binary} DEPENDS ${deps} CONFIGURE_COMMAND ${env} ${CMAKE_COMMAND} -S ${source} -B ${binary} ${SB_CMAKE_ARGS} ${args} BUILD_COMMAND ${env} ${CMAKE_COMMAND} --build ${binary} --parallel ${SB_JOBS} INSTALL_COMMAND ${env} ${CMAKE_COMMAND} --build ${binary} --target install LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON ) singeRebuildTarget(${name}) endfunction() # rebuild- forgets the stamps and the build directory and builds the library again, then whatever depends on it # is rebuilt by the normal build. function(singeRebuildTarget name) ExternalProject_Get_Property(${name} STAMP_DIR BINARY_DIR) add_custom_target(rebuild-${name} COMMAND ${CMAKE_COMMAND} -E rm -rf ${STAMP_DIR} ${BINARY_DIR} COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${name} COMMENT "Rebuilding ${name}" ) endfunction() # ===== zlib (configure script; the generated zconf.h lives in the build directory) ===== set(zlibBinary ${SB_PREFIX}/build/zlib) ExternalProject_Add(zlib SOURCE_DIR ${SB_THIRDPARTY}/zlib BINARY_DIR ${zlibBinary} CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${SB_CC}" "AR=${CMAKE_AR}" "RANLIB=${SB_RANLIB}" "CFLAGS=-I${zlibBinary} ${SB_CFLAGS_BASE}" ${SB_THIRDPARTY}/zlib/configure --prefix=${SB_PREFIX} --static BUILD_COMMAND ${SB_ENV} make INSTALL_COMMAND ${SB_ENV} make install LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON ) singeRebuildTarget(zlib) # ===== zstd ===== singeCmakeProject(zstd ${SB_THIRDPARTY}/zstd/build/cmake "" "-DZSTD_BUILD_SHARED=off;-DZSTD_BUILD_STATIC=on;-DZSTD_BUILD_PROGRAMS=off" "${SB_ENV}") # ===== SDL3 and its satellites ===== singeCmakeProject(SDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=off;-DSDL_STATIC=on;-DSDL_TESTS=off;-DSDL_EXAMPLES=off" "${SB_ENV}") # ===== Host tools: the shader compiler (see cmake/hostTools.cmake) ===== include(${CMAKE_CURRENT_LIST_DIR}/hostTools.cmake) # ===== Jolt Physics (C++; static, single precision, no tests or samples) ===== # The x86 baseline is SSE4.1/4.2 so a shipped binary runs on anything from 2008 on; AVX and the # bit-count instructions would silently raise it. No link-time optimisation through the cross # compiler, no warnings-as-errors with a clang Jolt has not met, nothing but the library; the GPU # compute paths (hair and soft bodies on DX12, Vulkan, Metal or a CPU fallback) would need a shader # compiler at build time and are not used. set(joltOptions -DTARGET_UNIT_TESTS=OFF -DTARGET_HELLO_WORLD=OFF -DTARGET_PERFORMANCE_TEST=OFF -DTARGET_SAMPLES=OFF -DTARGET_VIEWER=OFF -DJPH_BUILD_SHARED_LIBS=OFF -DDOUBLE_PRECISION=OFF -DCROSS_PLATFORM_DETERMINISTIC=OFF -DUSE_SSE4_1=ON -DUSE_SSE4_2=ON -DUSE_AVX=OFF -DUSE_AVX2=OFF -DUSE_AVX512=OFF -DUSE_F16C=OFF -DUSE_FMADD=OFF -DUSE_LZCNT=OFF -DUSE_TZCNT=OFF -DINTERPROCEDURAL_OPTIMIZATION=OFF -DENABLE_ALL_WARNINGS=OFF -DGENERATE_DEBUG_SYMBOLS=OFF -DDEBUG_RENDERER_IN_DEBUG_AND_RELEASE=OFF -DDEBUG_RENDERER_IN_DISTRIBUTION=ON -DFLOATING_POINT_EXCEPTIONS_ENABLED=OFF -DCPP_EXCEPTIONS_ENABLED=OFF -DCPP_RTTI_ENABLED=OFF -DJPH_USE_DX12=OFF -DJPH_USE_VK=OFF -DJPH_USE_MTL=OFF -DJPH_USE_CPU_COMPUTE=OFF ) singeCmakeProject(Jolt ${SB_THIRDPARTY}/JoltPhysics/Build "" "${joltOptions}" "${SB_ENV}") # ===== Recast Navigation (C++; static, the libraries only) ===== singeCmakeProject(recastnavigation ${SB_THIRDPARTY}/recastnavigation "" "-DRECASTNAVIGATION_DEMO=OFF;-DRECASTNAVIGATION_TESTS=OFF;-DRECASTNAVIGATION_EXAMPLES=OFF;-DBUILD_SHARED_LIBS=OFF" "${SB_ENV}") set(sdl3Dir -DSDL3_DIR=${SB_PREFIX}/lib/cmake/SDL3) # Brotli, for the WOFF2 web fonts FreeType reads only when it is present. It is built before # SDL3_image and SDL3_ttf so that it is in the prefix when FreeType looks for it. # # libjxl vendors brotli as well, and its install writes libbrotlidec.a and libbrotlicommon.a over # the ones built here. That is harmless and is left alone: it is the same upstream library, the # two FreeType links come from libjxl's tree as a matched pair, and only the decoder is ever linked # -- libbrotlienc.a is the one file of this project's own build that survives, and nothing asks for # it. Telling libjxl to use the copy already installed (JPEGXL_FORCE_SYSTEM_BROTLI) does work, but # SDL3_image then tries to install brotli targets that no longer exist, and patching its install # list is a worse trade than one duplicated build. singeCmakeProject(brotli ${SB_THIRDPARTY}/brotli "" "-DBUILD_SHARED_LIBS=off;-DBROTLI_BUILD_TOOLS=off;-DBROTLI_DISABLE_TESTS=on" "${SB_ENV}") singeCmakeProject(SDL3_image ${SB_THIRDPARTY}/SDL3_image "SDL3;zlib;brotli" "-DBUILD_SHARED_LIBS=off;-DSDLIMAGE_DEPS_SHARED=off;-DSDLIMAGE_SAMPLES=off;-DSDLIMAGE_TESTS=off;-DSDLIMAGE_VENDORED=on;-DSDLIMAGE_BACKEND_STB=off;-DSDLIMAGE_AVIF=off;-DSDLIMAGE_JXL=on;-DSDLIMAGE_JXL_SHARED=off;-DBUILD_TESTING=OFF;-DSDLIMAGE_TIF=off;-DSDLIMAGE_WEBP=on;${sdl3Dir};-DWEBP_BUILD_ANIM_UTILS=off;-DWEBP_BUILD_CWEBP=off;-DWEBP_BUILD_DWEBP=off;-DWEBP_BUILD_GIF2WEBP=off;-DWEBP_BUILD_IMG2WEBP=off;-DWEBP_BUILD_VWEBP=off;-DWEBP_BUILD_WEBPINFO=off;-DWEBP_BUILD_WEBPMUX=off;-DWEBP_BUILD_EXTRAS=off" "${SB_ENV_NO_PREFIX_INCLUDE}") # SDL3_image builds libpng and installs the archive but not its headers, and FreeType needs both # before it will read a font's colour bitmap glyphs. pnglibconf.h is generated, so it comes from # the build tree rather than the source one. ExternalProject_Add_Step(SDL3_image pngHeaders COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SB_THIRDPARTY}/SDL3_image/external/libpng/png.h ${SB_THIRDPARTY}/SDL3_image/external/libpng/pngconf.h ${SB_PREFIX}/build/SDL3_image/external/libpng-build/pnglibconf.h ${SB_PREFIX}/include DEPENDEES install COMMENT "Installing the libpng headers" ) singeCmakeProject(SDL3_mixer ${SB_THIRDPARTY}/SDL3_mixer "SDL3" "-DBUILD_SHARED_LIBS=off;-DSDLMIXER_DEPS_SHARED=off;-DSDLMIXER_VENDORED=on;-DSDLMIXER_EXAMPLES=off;-DSDLMIXER_TESTS=off;-DSDLMIXER_FLAC_LIBFLAC=off;-DSDLMIXER_MP3_MPG123=off;-DSDLMIXER_VORBIS_STB=on;-DSDLMIXER_VORBIS_VORBISFILE=off;-DSDLMIXER_VORBIS_TREMOR=off;-DSDLMIXER_GME=on;-DSDLMIXER_GME_SHARED=off;-DGME_BUILD_EXAMPLES=off;-DGME_BUILD_TESTING=off;-DGME_YM2612_EMU=MAME;-DSDLMIXER_MIDI_FLUIDSYNTH=off;${sdl3Dir};-DWAVPACK_ENABLE_ASM=no" "${SB_ENV}") singeCmakeProject(SDL3_ttf ${SB_THIRDPARTY}/SDL3_ttf "SDL3;zlib;brotli;SDL3_image" "-DBUILD_SHARED_LIBS=off;-DSDLTTF_VENDORED=on;-DSDLTTF_HARFBUZZ=on;-DSDLTTF_PLUTOSVG=on;-DHB_HAVE_CORETEXT=off;-DSDLTTF_SAMPLES=off;-DFT_DISABLE_ZLIB=off;-DFT_DISABLE_BROTLI=off;-DZLIB_INCLUDE_DIR=${SB_PREFIX}/include;-DZLIB_LIBRARY=${SB_PREFIX}/lib/libz.a;-DBROTLIDEC_INCLUDE_DIRS=${SB_PREFIX}/include;-DBROTLIDEC_LIBRARIES=${SB_PREFIX}/lib/libbrotlidec.a;-DFT_DISABLE_PNG=off;-DPNG_PNG_INCLUDE_DIR=${SB_PREFIX}/include;-DPNG_LIBRARY=${SB_PREFIX}/lib/libpng16.a;${sdl3Dir}" "${SB_ENV}") # SDL3_ttf builds its vendored FreeType but does not install it, and Singe links it directly. ExternalProject_Add_Step(SDL3_ttf freetype COMMAND ${CMAKE_COMMAND} -E copy ${SB_PREFIX}/build/SDL3_ttf/external/freetype/libfreetype.a ${SB_PREFIX}/lib/libfreetype.a DEPENDEES install COMMENT "Installing libfreetype.a" ) # ===== RmlUi (C++; static, the core, the Lua plugin and the debugger; no samples) ===== # FreeType is the copy SDL3_ttf builds (installed as libfreetype.a below, headers from its tree); # Lua is the engine's own, handed over as a header-only target by cmake/rmluiLua.cmake. set(rmluiOptions -DBUILD_SHARED_LIBS=OFF -DRMLUI_SAMPLES=OFF -DRMLUI_PRECOMPILED_HEADERS=OFF -DRMLUI_FONT_ENGINE=freetype -DRMLUI_LUA_BINDINGS=ON -DRMLUI_LUA_BINDINGS_LIBRARY=lua -DFREETYPE_LIBRARY=${SB_PREFIX}/lib/libfreetype.a -DFREETYPE_INCLUDE_DIR_ft2build=${SB_THIRDPARTY}/SDL3_ttf/external/freetype/include -DFREETYPE_INCLUDE_DIR_freetype2=${SB_THIRDPARTY}/SDL3_ttf/external/freetype/include -DSINGE_LUA_INCLUDE=${SB_THIRDPARTY}/lua/src -DCMAKE_PROJECT_RmlUi_INCLUDE=${CMAKE_CURRENT_LIST_DIR}/rmluiLua.cmake ) singeCmakeProject(RmlUi ${SB_THIRDPARTY}/RmlUi "SDL3_ttf" "${rmluiOptions}" "${SB_ENV}") # ===== OpenSSL (its own Configure; the target name follows the platform) ===== if(KANGAROO_OS STREQUAL "windows") if(KANGAROO_ARCH STREQUAL "x86") set(opensslTarget mingw) else() set(opensslTarget mingw64) endif() elseif(KANGAROO_OS STREQUAL "linux") if(KANGAROO_ARCH STREQUAL "aarch64") set(opensslTarget linux-aarch64) elseif(KANGAROO_ARCH STREQUAL "armhf") set(opensslTarget linux-armv4) elseif(SINGE_ZIG_TARGET) set(opensslTarget linux-x86_64) else() set(opensslTarget "") endif() elseif(KANGAROO_OS STREQUAL "macos") if(KANGAROO_ARCH STREQUAL "aarch64") set(opensslTarget darwin64-arm64) else() set(opensslTarget darwin64-x86_64) endif() elseif(SINGE_ZIG_TARGET) set(opensslTarget linux-x86_64) else() set(opensslTarget "") endif() # OpenSSL's generated assembly uses GNU as directives clang's assembler rejects; under zig the # portable C paths serve, which is all LuaSec's traffic needs. if(SINGE_ZIG_TARGET) set(opensslAsm no-asm) else() set(opensslAsm "") endif() # OpenSSL's Configure derives the source tree from the build directory's position and only # copes with a build directory nested inside its own tree, so this one lives there, one per target # so that switching presets never links another architecture's objects. set(opensslBinary ${SB_THIRDPARTY}/openssl/.builddir/${KANGAROO_OS}-${KANGAROO_ARCH}) ExternalProject_Add(openssl SOURCE_DIR ${SB_THIRDPARTY}/openssl BINARY_DIR ${opensslBinary} DEPENDS zlib zstd CONFIGURE_COMMAND ${SB_ENV} perl ../../Configure ${opensslTarget} --prefix=${SB_PREFIX} --libdir=lib --with-zlib-include=${SB_PREFIX}/include --with-zlib-lib=${SB_PREFIX}/lib --with-zstd-include=${SB_PREFIX}/include --with-zstd-lib=${SB_PREFIX}/lib ${opensslAsm} no-apps no-docs no-dso no-dynamic-engine no-module no-shared no-tests zlib enable-zstd BUILD_COMMAND ${SB_ENV} make build_sw INSTALL_COMMAND ${SB_ENV} make install_sw LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON ) singeRebuildTarget(openssl) # ===== FFmpeg (hardware decoding per platform; CMakeLists.txt reads its .pc files back) ===== # ===== Rockchip Media Process Platform (the vendor decode library for Rockchip SoCs) ===== # Rockchip's decoders are aarch64 only. Because the library links statically it costs nothing on a # board that is not a Rockchip, so it is built for the aarch64 targets rather than needing one of # its own; set SINGE_ROCKCHIP_MPP to override either way. if(NOT DEFINED SINGE_ROCKCHIP_MPP) if((KANGAROO_OS STREQUAL "linux") AND (KANGAROO_ARCH STREQUAL "aarch64")) set(SINGE_ROCKCHIP_MPP ON) else() set(SINGE_ROCKCHIP_MPP OFF) endif() endif() # MPP is packaged by no distribution, so it is vendored and built here like everything else. Only # the static library is kept: linked statically the Singe binary carries no runtime dependency on # it, so one ARM build runs on a Rockchip board and on anything else, where the decoder simply fails # to open a device and the player falls back. A shared build would put librockchip_mpp.so in the # binary's DT_NEEDED and stop it starting anywhere the library is absent. set(rkmppFlag "") set(rkmppDep "") if(SINGE_ROCKCHIP_MPP) # MPP strips its merged archive with ${CMAKE_STRIP}, and no cross strip here understands an ar # archive: zig's objcopy is ELF only. The step only drops debug symbols from an intermediate # library, so it is skipped; the release binary is stripped at its own link. singeCmakeProject(rockchipMpp ${SB_THIRDPARTY}/rockchip-mpp "" "-DBUILD_SHARED_LIBS=OFF;-DBUILD_TEST=OFF;-DHAVE_DRM=ON;-DCMAKE_STRIP=/bin/true" "${SB_ENV}") # Its install puts both the archive and the shared object down; the shared one is removed so # that -lrockchip_mpp can only resolve to the archive. ExternalProject_Add_Step(rockchipMpp staticOnly COMMAND ${CMAKE_COMMAND} -E rm -f ${SB_PREFIX}/lib/librockchip_mpp.so ${SB_PREFIX}/lib/librockchip_mpp.so.0 ${SB_PREFIX}/lib/librockchip_mpp.so.1 ${SB_PREFIX}/lib/librockchip_vpu.so ${SB_PREFIX}/lib/librockchip_vpu.so.0 ${SB_PREFIX}/lib/librockchip_vpu.so.1 DEPENDEES install COMMENT "Keeping only the static Rockchip MPP library" ) set(rkmppFlag --enable-rkmpp) set(rkmppDep rockchipMpp) message(STATUS "Rockchip MPP will be built and linked statically; the rkmpp decoders are enabled") endif() # ===== dav1d, the AV1 decoder ===== # FFmpeg's own AV1 decoder needs hardware behind it: with no hwaccel it says "Your platform doesn't # support hardware accelerated AV1 decoding" and decodes nothing, so an AV1 video does not play at # all on a machine without an AV1 capable GPU. dav1d is the software decoder, and it brings AVIF # stills with it, an AVIF being an AV1 picture in a HEIF box. It is the one vendored library whose # own build system is Meson rather than CMake or a configure script. set(dav1dBinary ${SB_PREFIX}/build/dav1d) set(dav1dCross "") if(SINGE_ZIG_TARGET) # Meson learns a cross build from a file rather than from the environment, and names both the # system and some architectures its own way. The system name is not cosmetic: Meson picks # nasm's output format from it, and calling Windows "mingw32" -- which is what FFmpeg calls it # -- leaves nasm emitting ELF objects that the Windows linker will not read. if(KANGAROO_OS STREQUAL "windows") set(dav1dSystem windows) elseif(KANGAROO_OS STREQUAL "macos") set(dav1dSystem darwin) else() set(dav1dSystem linux) endif() if(KANGAROO_ARCH STREQUAL "armhf") set(dav1dCpuFamily arm) else() set(dav1dCpuFamily ${KANGAROO_ARCH}) endif() set(dav1dCrossFile ${SB_PREFIX}/build/dav1dCross.txt) file(WRITE ${dav1dCrossFile} "[binaries]\nc = '${SB_CC}'\ncpp = '${SB_CXX}'\nar = '${CMAKE_AR}'\nranlib = '${SB_RANLIB}'\nstrip = 'true'\npkg-config = 'pkg-config'\nnasm = 'nasm'\n\n[host_machine]\nsystem = '${dav1dSystem}'\ncpu_family = '${dav1dCpuFamily}'\ncpu = '${KANGAROO_ARCH}'\nendian = 'little'\n") set(dav1dCross --cross-file ${dav1dCrossFile}) endif() ExternalProject_Add(dav1d SOURCE_DIR ${SB_THIRDPARTY}/dav1d BINARY_DIR ${dav1dBinary} CONFIGURE_COMMAND ${SB_ENV} meson setup --wipe --prefix=${SB_PREFIX} --libdir=lib --buildtype=release --default-library=static -Denable_tools=false -Denable_tests=false ${dav1dCross} ${dav1dBinary} ${SB_THIRDPARTY}/dav1d BUILD_COMMAND ${SB_ENV} ninja -C ${dav1dBinary} INSTALL_COMMAND ${SB_ENV} ninja -C ${dav1dBinary} install LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON ) singeRebuildTarget(dav1d) if(KANGAROO_OS STREQUAL "linux") # ARM boards decode through a V4L2 memory-to-memory device; desktops have VAAPI or VDPAU. if(KANGAROO_ARCH MATCHES "^(aarch64|armhf)$") set(hwaccel --enable-v4l2-m2m --enable-libdrm ${rkmppFlag}) else() set(hwaccel --enable-vaapi --enable-vdpau --enable-libdrm ${rkmppFlag}) endif() elseif(KANGAROO_OS STREQUAL "macos") set(hwaccel --enable-videotoolbox) elseif(KANGAROO_OS STREQUAL "windows") set(hwaccel --enable-d3d11va --enable-dxva2) else() set(hwaccel "") 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}) # 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_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}) elseif(KANGAROO_ARCH STREQUAL "x86_64") list(APPEND ffmpegTools --nm=nm) endif() else() set(ffmpegTools --cross-prefix=${SINGE_TRIPLE}- --cc=${SB_CC} --cxx=${SB_CXX} --ranlib=${SB_RANLIB}) endif() # FFmpeg's configure sees sys/sysctl.h in the Pi sysroot and in zig's glibc header set, then its # build cannot include it; an empty stand-in satisfies both (Linux uses sysconf, not sysctl). if((KANGAROO_OS STREQUAL "linux") AND SINGE_ZIG_TARGET) file(WRITE ${SB_PREFIX}/include/sys/sysctl.h "/* File no longer used */\n") endif() set(ffmpegBinary ${SB_PREFIX}/build/ffmpeg) ExternalProject_Add(ffmpeg SOURCE_DIR ${SB_THIRDPARTY}/ffmpeg BINARY_DIR ${ffmpegBinary} DEPENDS dav1d ${rkmppDep} CONFIGURE_COMMAND ${SB_ENV} ${SB_THIRDPARTY}/ffmpeg/configure --enable-static --disable-shared --disable-debug --disable-muxers ${hwaccel} --disable-encoders --disable-filters --enable-filter=bwdif --enable-filter=yadif --enable-filter=buffer --enable-filter=buffersink --enable-filter=format --disable-network --disable-devices --disable-vulkan --disable-d3d12va --disable-bzlib --disable-lzma --disable-doc --disable-programs --enable-libdav1d --enable-gpl --enable-version3 --extra-ldflags=-L${SB_PREFIX}/lib --prefix=${SB_PREFIX} --arch=${KANGAROO_ARCH} --target-os=${SINGE_CROSS_OS} ${ffmpegTools} BUILD_COMMAND ${SB_ENV} make INSTALL_COMMAND ${SB_ENV} make install LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON ) singeRebuildTarget(ffmpeg) # ===== Singe itself, against everything above ===== string(SUBSTRING ${KANGAROO_OS} 0 1 osInitial) string(SUBSTRING ${KANGAROO_OS} 1 -1 osRest) string(TOUPPER ${osInitial} osInitial) # The built file carries the platform's executable suffix; the copy in .builddir/ carries the # preset's, which is the same thing for Windows and lets an experimental build live beside another. set(singeBinaryBase "Singe-v${PROJECT_VERSION}-${osInitial}${osRest}-${KANGAROO_ARCH}") set(singeBinaryName "${singeBinaryBase}${SINGE_SUFFIX}") set(singeBinary ${SB_PREFIX}/singe) ExternalProject_Add(singe SOURCE_DIR ${CMAKE_SOURCE_DIR} BINARY_DIR ${singeBinary} DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg Jolt recastnavigation RmlUi shadercross CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SHADERCROSS=${SINGE_SHADERCROSS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} -DSINGE_TREE=${SINGE_TREE} BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS} INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryBase}${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName} BUILD_ALWAYS ON ) # Forge, packed into .builddir beside the binary so it can be copied out and tested. It is NOT # part of the engine and nothing of it is embedded; this only puts the distributable where the # binaries already are. Only a build that produces a runnable binary can pack it, since packing is # the engine's own --pack, so cross builds skip it and the host build does the work. if((KANGAROO_OS STREQUAL "linux") AND (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")) file(GLOB forgeSources ${CMAKE_SOURCE_DIR}/assets/Forge/*) set(forgePack ${CMAKE_SOURCE_DIR}/.builddir/Forge.game) add_custom_command( OUTPUT ${forgePack} # Packing refuses to overwrite, and a stale pack is worse than none. COMMAND ${CMAKE_COMMAND} -E rm -f ${forgePack} COMMAND ${CMAKE_COMMAND} -E make_directory ${SB_PREFIX}/forgepack # Run it somewhere harmless: the engine writes Singe/ and data/ into the working directory. COMMAND ${CMAKE_COMMAND} -E chdir ${SB_PREFIX}/forgepack ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName} --pack ${CMAKE_SOURCE_DIR}/assets/Forge ${forgePack} DEPENDS singe ${forgeSources} COMMENT "Packing Forge into .builddir/Forge.game" VERBATIM ) add_custom_target(forge ALL DEPENDS ${forgePack}) endif() message(STATUS "Superbuild for ${KANGAROO_OS}/${KANGAROO_ARCH}: libraries into ${SB_PREFIX}, binary ${singeBinaryName}")