singe/cmake/Superbuild.cmake

289 lines
12 KiB
CMake

#
# Singe 3
# Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
#
# 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}:${CMAKE_SYSROOT}/usr/lib/${SINGE_TRIPLE}/pkgconfig:${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_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=${CMAKE_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-<name> 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}")
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()
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.
set(opensslBinary ${SB_THIRDPARTY}/openssl/.builddir)
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)
# FFmpeg still includes a header the Pi's libc dropped.
file(WRITE ${SB_PREFIX}/include/sys/sysctl.h "/* File no longer used */\n")
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 <triple>-<tool>; with zig each is named outright and
# the host's nm reads the target's objects (binutils understands PE and Mach-O).
if(SINGE_ZIG_TARGET)
set(ffmpegTools --enable-cross-compile --cc=${SB_CC} --cxx=${SB_CXX} --ar=${CMAKE_AR} --ranlib=${SB_RANLIB} --nm=nm --windres=${CMAKE_RC_COMPILER})
else()
set(ffmpegTools --cross-prefix=${SINGE_TRIPLE}- --cc=${SB_CC} --cxx=${SB_CXX} --ranlib=${SB_RANLIB})
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)
set(singeBinaryName "Singe-v${PROJECT_VERSION}-${osInitial}${osRest}-${KANGAROO_ARCH}${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
CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH}
BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS}
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryName} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName}
BUILD_ALWAYS ON
)
message(STATUS "Superbuild for ${KANGAROO_OS}/${KANGAROO_ARCH}: libraries into ${SB_PREFIX}, binary ${singeBinaryName}")