# # 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") if(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(KANGAROO_OS STREQUAL "pi") 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(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(KANGAROO_OS STREQUAL "pi") 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 builds the library again, then whatever depends on it # is rebuilt by the normal build. function(singeRebuildTarget name) ExternalProject_Get_Property(${name} STAMP_DIR) add_custom_target(rebuild-${name} COMMAND ${CMAKE_COMMAND} -E rm -rf ${STAMP_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 -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}") set(sdl3Dir -DSDL3_DIR=${SB_PREFIX}/lib/cmake/SDL3) singeCmakeProject(SDL3_image ${SB_THIRDPARTY}/SDL3_image "SDL3;zlib" "-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=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}") 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=off;-DSDLMIXER_MIDI_FLUIDSYNTH=off;${sdl3Dir};-DWAVPACK_ENABLE_ASM=no" "${SB_ENV}") singeCmakeProject(SDL3_ttf ${SB_THIRDPARTY}/SDL3_ttf "SDL3" "-DBUILD_SHARED_LIBS=off;-DSDLTTF_VENDORED=on;-DSDLTTF_HARFBUZZ=off;-DSDLTTF_PLUTOSVG=off;-DSDLTTF_SAMPLES=off;${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" ) # ===== 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 "pi") if(KANGAROO_ARCH STREQUAL "aarch64") set(opensslTarget linux-aarch64) else() set(opensslTarget linux-arm4) 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) ===== if(KANGAROO_OS STREQUAL "linux") set(hwaccel --enable-vaapi --enable-vdpau --enable-libdrm) elseif(KANGAROO_OS STREQUAL "pi") set(hwaccel --enable-v4l2-m2m --enable-libdrm) 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 "pi") OR ((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} CONFIGURE_COMMAND ${SB_ENV} ${SB_THIRDPARTY}/ffmpeg/configure --enable-static --disable-shared --disable-debug --disable-muxers ${hwaccel} --disable-encoders --disable-filters --disable-network --disable-devices --disable-bzlib --disable-lzma --disable-doc --disable-programs --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 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 ) message(STATUS "Superbuild for ${KANGAROO_OS}/${KANGAROO_ARCH}: libraries into ${SB_PREFIX}, binary ${singeBinaryName}")